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