1 package org.opentrafficsim.road.network.lane;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.Optional;
6
7 import org.djutils.draw.function.ContinuousPiecewiseLinearFunction;
8 import org.djutils.draw.line.PolyLine2d;
9 import org.djutils.draw.point.Point2d;
10 import org.djutils.event.EventType;
11 import org.djutils.metadata.MetaData;
12 import org.djutils.metadata.ObjectDescriptor;
13 import org.opentrafficsim.base.geometry.OtsLine2d;
14 import org.opentrafficsim.core.network.Link;
15 import org.opentrafficsim.core.network.LinkType;
16 import org.opentrafficsim.core.network.NetworkException;
17 import org.opentrafficsim.core.network.Node;
18 import org.opentrafficsim.road.network.LaneKeepingPolicy;
19 import org.opentrafficsim.road.network.RoadNetwork;
20
21
22
23
24
25
26
27
28
29
30
31 public class CrossSectionLink extends Link
32 {
33
34 private final List<CrossSectionElement> crossSectionElementList = new ArrayList<>();
35
36
37 private final List<Lane> lanes = new ArrayList<>();
38
39
40 private final List<Shoulder> shoulders = new ArrayList<>();
41
42
43 private final LaneKeepingPolicy laneKeepingPolicy;
44
45
46 private Priority priority = Priority.NONE;
47
48
49 private PolyLine2d startLine;
50
51
52 private PolyLine2d endLine;
53
54
55
56
57
58
59 public static final EventType LANE_ADD_EVENT = new EventType("LINK.LANE.ADD",
60 new MetaData("Lane data", "Lane data",
61 new ObjectDescriptor[] {new ObjectDescriptor("Network id", "Network id", String.class),
62 new ObjectDescriptor("Link id", "Link id", String.class),
63 new ObjectDescriptor("Lane id", "Lane id", String.class),
64 new ObjectDescriptor("Lane number", "Lane number", Integer.class)}));
65
66
67
68
69
70
71 public static final EventType LANE_REMOVE_EVENT = new EventType("LINK.LANE.REMOVE",
72 new MetaData("Lane data", "Lane data",
73 new ObjectDescriptor[] {new ObjectDescriptor("Network id", "Network id", String.class),
74 new ObjectDescriptor("Link id", "Link id", String.class),
75 new ObjectDescriptor("Lane id", "Lane id", String.class),
76 new ObjectDescriptor("Lane number", "Lane number", Integer.class)}));
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 @SuppressWarnings("checkstyle:parameternumber")
92 public CrossSectionLink(final RoadNetwork network, final String id, final Node startNode, final Node endNode,
93 final LinkType linkType, final OtsLine2d designLine, final ContinuousPiecewiseLinearFunction elevation,
94 final LaneKeepingPolicy laneKeepingPolicy) throws NetworkException
95 {
96 super(network, id, startNode, endNode, linkType, designLine, elevation);
97 this.laneKeepingPolicy = laneKeepingPolicy;
98 }
99
100 @Override
101 public RoadNetwork getNetwork()
102 {
103 return (RoadNetwork) super.getNetwork();
104 }
105
106
107
108
109
110
111 protected final void addCrossSectionElement(final CrossSectionElement cse)
112 {
113 this.crossSectionElementList.add(cse);
114 if (cse instanceof Lane)
115 {
116 if (cse instanceof Shoulder)
117 {
118 this.shoulders.add((Shoulder) cse);
119 }
120 else
121 {
122 this.lanes.add((Lane) cse);
123 fireTimedEvent(LANE_ADD_EVENT,
124 new Object[] {getNetwork().getId(), getId(), cse.getId(), this.lanes.indexOf(cse)},
125 getSimulator().getSimulatorTime());
126 }
127 }
128 }
129
130
131
132
133
134 public final List<CrossSectionElement> getCrossSectionElementList()
135 {
136 return this.crossSectionElementList == null ? new ArrayList<>() : new ArrayList<>(this.crossSectionElementList);
137 }
138
139
140
141
142
143 public final LaneKeepingPolicy getLaneKeepingPolicy()
144 {
145 return this.laneKeepingPolicy;
146 }
147
148
149
150
151
152
153 public final Optional<CrossSectionElement> getCrossSectionElement(final String id)
154 {
155 for (CrossSectionElement cse : this.crossSectionElementList)
156 {
157 if (cse.getId().equals(id))
158 {
159 return Optional.of(cse);
160 }
161 }
162 return Optional.empty();
163 }
164
165
166
167
168
169 public final List<Lane> getLanes()
170 {
171 return new ArrayList<>(this.lanes);
172 }
173
174
175
176
177
178 public final List<Shoulder> getShoulders()
179 {
180 return new ArrayList<>(this.shoulders);
181 }
182
183
184
185
186
187 public final List<Lane> getLanesAndShoulders()
188 {
189 List<Lane> all = new ArrayList<>(this.lanes);
190 all.addAll(this.shoulders);
191 return all;
192 }
193
194
195
196
197
198 public final Priority getPriority()
199 {
200 return this.priority;
201 }
202
203
204
205
206
207 public final void setPriority(final Priority priority)
208 {
209 this.priority = priority;
210 }
211
212
213
214
215
216 public PolyLine2d getStartLine()
217 {
218 if (this.startLine == null)
219 {
220 double left = Double.NaN;
221 double right = Double.NaN;
222 for (Lane lane : getLanesAndShoulders())
223 {
224 double half = lane.getBeginWidth().si * .5;
225 if (!Double.isNaN(left))
226 {
227 left = Math.max(left, lane.getOffsetAtBegin().si + half);
228 right = Math.min(right, lane.getOffsetAtBegin().si - half);
229 }
230 else
231 {
232 left = lane.getOffsetAtBegin().si + half;
233 right = lane.getOffsetAtBegin().si - half;
234 }
235 }
236 Point2d start = getDesignLine().getFirst();
237 double heading = getStartNode().getHeading().si + .5 * Math.PI;
238 double cosHeading = Math.cos(heading);
239 double sinHeading = Math.sin(heading);
240
241 Point2d leftPoint = new Point2d(start.x + cosHeading * left, start.y + sinHeading * left);
242 Point2d rightPoint = new Point2d(start.x + cosHeading * right, start.y + sinHeading * right);
243 this.startLine = new PolyLine2d(leftPoint, rightPoint);
244 }
245 return this.startLine;
246 }
247
248
249
250
251
252 public PolyLine2d getEndLine()
253 {
254 if (this.endLine == null)
255 {
256 double left = Double.NaN;
257 double right = Double.NaN;
258 for (Lane lane : getLanesAndShoulders())
259 {
260 double half = lane.getEndWidth().si * .5;
261 if (!Double.isNaN(left))
262 {
263 left = Math.max(left, lane.getOffsetAtEnd().si + half);
264 right = Math.min(right, lane.getOffsetAtEnd().si - half);
265 }
266 else
267 {
268 left = lane.getOffsetAtEnd().si + half;
269 right = lane.getOffsetAtEnd().si - half;
270 }
271 }
272 Point2d start = getDesignLine().getLast();
273 double heading = getEndNode().getHeading().si + .5 * Math.PI;
274 double cosHeading = Math.cos(heading);
275 double sinHeading = Math.sin(heading);
276 Point2d leftPoint = new Point2d(start.x + cosHeading * left, start.y + sinHeading * left);
277 Point2d rightPoint = new Point2d(start.x + cosHeading * right, start.y + sinHeading * right);
278 this.endLine = new PolyLine2d(leftPoint, rightPoint);
279 }
280 return this.endLine;
281 }
282
283 @Override
284 public final String toString()
285 {
286 return "CrossSectionLink [name=" + this.getId() + ", nodes=" + getStartNode().getId() + "-" + getEndNode().getId()
287 + ", crossSectionElementList=" + this.crossSectionElementList + ", lanes=" + this.lanes + ", laneKeepingPolicy="
288 + this.laneKeepingPolicy + "]";
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302 public enum Priority
303 {
304
305 PRIORITY,
306
307
308 NONE,
309
310
311 YIELD,
312
313
314 STOP,
315
316
317 ALL_STOP,
318
319
320 BUS_STOP;
321
322
323
324
325
326 public boolean isPriority()
327 {
328 return this.equals(PRIORITY);
329 }
330
331
332
333
334
335 public boolean isNone()
336 {
337 return this.equals(NONE);
338 }
339
340
341
342
343
344 public boolean isYield()
345 {
346 return this.equals(YIELD);
347 }
348
349
350
351
352
353 public boolean isStop()
354 {
355 return this.equals(STOP);
356 }
357
358
359
360
361
362 public boolean isAllStop()
363 {
364 return this.equals(ALL_STOP);
365 }
366
367
368
369
370
371 public boolean isBusStop()
372 {
373 return this.equals(BUS_STOP);
374 }
375
376 }
377
378 }