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