1 package org.opentrafficsim.road.network.lane;
2
3 import org.djunits.value.vdouble.scalar.Length;
4 import org.djutils.base.Identifiable;
5 import org.djutils.draw.bounds.Bounds2d;
6 import org.djutils.draw.function.ContinuousPiecewiseLinearFunction;
7 import org.djutils.draw.line.Polygon2d;
8 import org.djutils.draw.point.DirectedPoint2d;
9 import org.djutils.event.LocalEventProducer;
10 import org.djutils.exceptions.Throw;
11 import org.djutils.exceptions.Try;
12 import org.opentrafficsim.base.geometry.OtsLine2d;
13 import org.opentrafficsim.base.geometry.OtsShape;
14 import org.opentrafficsim.core.network.LateralDirectionality;
15 import org.opentrafficsim.road.network.RoadNetwork;
16
17
18
19
20
21
22
23
24
25
26
27 public abstract class CrossSectionElement extends LocalEventProducer implements OtsShape, Identifiable
28 {
29
30 private final String id;
31
32
33 @SuppressWarnings("checkstyle:visibilitymodifier")
34 protected final CrossSectionLink link;
35
36
37 private final OtsLine2d centerLine;
38
39
40 private final Polygon2d absoluteContour;
41
42
43 private final Polygon2d relativeContour;
44
45
46 private final ContinuousPiecewiseLinearFunction offset;
47
48
49 private final ContinuousPiecewiseLinearFunction width;
50
51
52 private final DirectedPoint2d location;
53
54
55
56
57
58
59
60 public CrossSectionElement(final CrossSectionLink link, final String id, final CrossSectionGeometry geometry)
61 {
62 Throw.whenNull(link, "link");
63 Throw.whenNull(id, "id");
64 Throw.whenNull(geometry, "geometry");
65 this.link = link;
66 this.id = id;
67 this.centerLine = geometry.centerLine();
68 this.location = geometry.centerLine().getLocationPointFractionExtended(0.5);
69 this.absoluteContour = geometry.absoluteContour();
70 this.relativeContour =
71 new Polygon2d(OtsShape.toRelativeTransform(this.location).transform(this.absoluteContour.iterator()));
72 this.offset = geometry.offset();
73 this.width = geometry.width();
74
75 link.addCrossSectionElement(this);
76
77
78 link.getNetwork().clearLaneChangeInfoCache();
79 }
80
81
82
83
84
85 public final CrossSectionLink getLink()
86 {
87 return this.link;
88 }
89
90
91
92
93
94 public final RoadNetwork getNetwork()
95 {
96 return this.link.getNetwork();
97 }
98
99
100
101
102
103
104 public final Length getLateralCenterPosition(final double fractionalPosition)
105 {
106 return Length.ofSI(this.offset.get(fractionalPosition));
107 }
108
109
110
111
112
113
114 public final Length getLateralCenterPosition(final Length longitudinalPosition)
115 {
116 return getLateralCenterPosition(longitudinalPosition.si / getLength().si);
117 }
118
119
120
121
122
123
124 public final Length getWidth(final Length longitudinalPosition)
125 {
126 return getWidth(longitudinalPosition.si / getLength().si);
127 }
128
129
130
131
132
133
134 public final Length getWidth(final double fractionalPosition)
135 {
136 return Length.ofSI(this.width.get(fractionalPosition));
137 }
138
139
140
141
142
143 public final Length getLength()
144 {
145 return this.centerLine.getTypedLength();
146 }
147
148
149
150
151
152 public final Length getOffsetAtBegin()
153 {
154 return Length.ofSI(this.offset.get(0.0));
155 }
156
157
158
159
160
161 public final Length getOffsetAtEnd()
162 {
163 return Length.ofSI(this.offset.get(1.0));
164 }
165
166
167
168
169
170 public final Length getBeginWidth()
171 {
172 return Length.ofSI(this.width.get(0.0));
173 }
174
175
176
177
178
179 public final Length getEndWidth()
180 {
181 return Length.ofSI(this.width.get(1.0));
182 }
183
184
185
186
187
188 @Override
189 public double getZ()
190 {
191
192 return Try.assign(() -> OtsShape.super.getZ(), "Remote exception on calling getZ()");
193 }
194
195
196
197
198
199 public final OtsLine2d getCenterLine()
200 {
201 return this.centerLine;
202 }
203
204 @Override
205 public final Polygon2d getAbsoluteContour()
206 {
207 return this.absoluteContour;
208 }
209
210 @Override
211 public final Polygon2d getRelativeContour()
212 {
213 return this.relativeContour;
214 }
215
216 @Override
217 public final String getId()
218 {
219 return this.id;
220 }
221
222
223
224
225
226 public final String getFullId()
227 {
228 return getLink().getId() + "." + this.id;
229 }
230
231
232
233
234
235
236
237
238
239 public final Length getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
240 final double fractionalLongitudinalPosition)
241 {
242 Length offsetAt = getLateralCenterPosition(fractionalLongitudinalPosition);
243 Length halfWidth = getWidth(fractionalLongitudinalPosition).times(0.5);
244
245 switch (lateralDirection)
246 {
247 case LEFT:
248 return offsetAt.minus(halfWidth);
249 case RIGHT:
250 return offsetAt.plus(halfWidth);
251 default:
252 throw new IllegalArgumentException("Bad value for LateralDirectionality " + lateralDirection);
253 }
254 }
255
256
257
258
259
260
261
262
263 public final Length getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
264 final Length longitudinalPosition)
265 {
266 return getLateralBoundaryPosition(lateralDirection, longitudinalPosition.getSI() / getLength().getSI());
267 }
268
269 @Override
270 @SuppressWarnings("checkstyle:designforextension")
271 public DirectedPoint2d getLocation()
272 {
273 return this.location;
274 }
275
276 @Override
277 @SuppressWarnings("checkstyle:designforextension")
278 public Bounds2d getRelativeBounds()
279 {
280 return this.relativeContour.getAbsoluteBounds();
281 }
282
283
284
285
286
287
288 public Length getElevation(final Length position)
289 {
290 return getElevation(position.si / getLength().si);
291 }
292
293
294
295
296
297
298 public Length getElevation(final double fractionalPosition)
299 {
300 return getLink().getElevation(fractionalPosition);
301 }
302
303
304
305
306
307
308 public double getGrade(final Length position)
309 {
310 return getGrade(position.si / getLength().si);
311 }
312
313
314
315
316
317
318 public double getGrade(final double fractionalPosition)
319 {
320 return getLink().getGrade(fractionalPosition);
321 }
322
323 @Override
324 @SuppressWarnings("checkstyle:designforextension")
325 public String toString()
326 {
327 return String.format("CSE offset %.2fm..%.2fm, width %.2fm..%.2fm", getOffsetAtBegin().getSI(),
328 getOffsetAtEnd().getSI(), getBeginWidth().getSI(), getEndWidth().getSI());
329 }
330
331 @Override
332 @SuppressWarnings("checkstyle:designforextension")
333 public int hashCode()
334 {
335 final int prime = 31;
336 int result = 1;
337 result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
338 result = prime * result + ((this.link == null) ? 0 : this.link.hashCode());
339 return result;
340 }
341
342 @Override
343 @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
344 public boolean equals(final Object obj)
345 {
346 if (this == obj)
347 return true;
348 if (obj == null)
349 return false;
350 if (getClass() != obj.getClass())
351 return false;
352 CrossSectionElement other = (CrossSectionElement) obj;
353 if (this.id == null)
354 {
355 if (other.id != null)
356 return false;
357 }
358 else if (!this.id.equals(other.id))
359 return false;
360 if (this.link == null)
361 {
362 if (other.link != null)
363 return false;
364 }
365 else if (!this.link.equals(other.link))
366 return false;
367 return true;
368 }
369 }