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