1 package org.opentrafficsim.road.network.lane;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import org.djutils.draw.line.PolyLine2d;
8 import org.djutils.draw.point.Point2d;
9 import org.djutils.event.EventType;
10 import org.djutils.metadata.MetaData;
11 import org.djutils.metadata.ObjectDescriptor;
12 import org.opentrafficsim.base.geometry.OtsLine2d;
13 import org.opentrafficsim.core.geometry.FractionalLengthData;
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.RoadNetwork;
19 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
20
21
22
23
24
25
26
27
28
29
30
31 public class CrossSectionLink extends Link implements Serializable
32 {
33
34 private static final long serialVersionUID = 20141015L;
35
36
37 private final List<CrossSectionElement> crossSectionElementList = new ArrayList<>();
38
39
40 private final List<Lane> lanes = new ArrayList<>();
41
42
43 private final List<Shoulder> shoulders = new ArrayList<>();
44
45
46 private final LaneKeepingPolicy laneKeepingPolicy;
47
48
49 private Priority priority = Priority.NONE;
50
51
52 private PolyLine2d startLine;
53
54
55 private PolyLine2d endLine;
56
57
58
59
60
61
62 public static final EventType LANE_ADD_EVENT = new EventType("LINK.LANE.ADD",
63 new MetaData("Lane data", "Lane data",
64 new ObjectDescriptor[] {new ObjectDescriptor("Network id", "Network id", String.class),
65 new ObjectDescriptor("Link id", "Link id", String.class),
66 new ObjectDescriptor("Lane id", "Lane id", String.class),
67 new ObjectDescriptor("Lane number", "Lane number", Integer.class)}));
68
69
70
71
72
73
74 public static final EventType LANE_REMOVE_EVENT = new EventType("LINK.LANE.REMOVE",
75 new MetaData("Lane data", "Lane data",
76 new ObjectDescriptor[] {new ObjectDescriptor("Network id", "Network id", String.class),
77 new ObjectDescriptor("Link id", "Link id", String.class),
78 new ObjectDescriptor("Lane id", "Lane id", String.class),
79 new ObjectDescriptor("Lane number", "Lane number", Integer.class)}));
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 @SuppressWarnings("checkstyle:parameternumber")
95 public CrossSectionLink(final RoadNetwork network, final String id, final Node startNode, final Node endNode,
96 final LinkType linkType, final OtsLine2d designLine, final FractionalLengthData elevation,
97 final LaneKeepingPolicy laneKeepingPolicy) throws NetworkException
98 {
99 super(network, id, startNode, endNode, linkType, designLine, elevation);
100 this.laneKeepingPolicy = laneKeepingPolicy;
101 }
102
103 @Override
104 public RoadNetwork getNetwork()
105 {
106 return (RoadNetwork) super.getNetwork();
107 }
108
109
110
111
112
113
114 protected final void addCrossSectionElement(final CrossSectionElement cse)
115 {
116 this.crossSectionElementList.add(cse);
117 if (cse instanceof Lane)
118 {
119 if (cse instanceof Shoulder)
120 {
121 this.shoulders.add((Shoulder) cse);
122 }
123 else
124 {
125 this.lanes.add((Lane) cse);
126 fireTimedEvent(LANE_ADD_EVENT,
127 new Object[] {getNetwork().getId(), getId(), cse.getId(), this.lanes.indexOf(cse)},
128 getSimulator().getSimulatorTime());
129 }
130 }
131 }
132
133
134
135
136
137 public final List<CrossSectionElement> getCrossSectionElementList()
138 {
139 return this.crossSectionElementList == null ? new ArrayList<>() : new ArrayList<>(this.crossSectionElementList);
140 }
141
142
143
144
145
146 public final LaneKeepingPolicy getLaneKeepingPolicy()
147 {
148 return this.laneKeepingPolicy;
149 }
150
151
152
153
154
155
156 public final CrossSectionElement getCrossSectionElement(final String id)
157 {
158 for (CrossSectionElement cse : this.crossSectionElementList)
159 {
160 if (cse.getId().equals(id))
161 {
162 return cse;
163 }
164 }
165 return null;
166 }
167
168
169
170
171
172 public final List<Lane> getLanes()
173 {
174 return new ArrayList<>(this.lanes);
175 }
176
177
178
179
180
181 public final List<Shoulder> getShoulders()
182 {
183 return new ArrayList<>(this.shoulders);
184 }
185
186
187
188
189
190 public final List<Lane> getLanesAndShoulders()
191 {
192 List<Lane> all = new ArrayList<>(this.lanes);
193 all.addAll(this.shoulders);
194 return all;
195 }
196
197
198
199
200 public final Priority getPriority()
201 {
202 return this.priority;
203 }
204
205
206
207
208 public final void setPriority(final Priority priority)
209 {
210 this.priority = priority;
211 }
212
213
214
215
216
217 public PolyLine2d getStartLine()
218 {
219 if (this.startLine == null)
220 {
221 double left = Double.NaN;
222 double right = Double.NaN;
223 for (Lane lane : getLanesAndShoulders())
224 {
225 double half = lane.getBeginWidth().si * .5;
226 if (!Double.isNaN(left))
227 {
228 left = Math.max(left, lane.getOffsetAtBegin().si + half);
229 right = Math.min(right, lane.getOffsetAtBegin().si - half);
230 }
231 else
232 {
233 left = lane.getOffsetAtBegin().si + half;
234 right = lane.getOffsetAtBegin().si - half;
235 }
236 }
237 Point2d start = getDesignLine().getFirst();
238 double heading = getStartNode().getHeading().si + .5 * Math.PI;
239 double cosHeading = Math.cos(heading);
240 double sinHeading = Math.sin(heading);
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 }