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.core.geometry.FractionalLengthData;
13 import org.opentrafficsim.core.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.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
104 @Override
105 public RoadNetwork getNetwork()
106 {
107 return (RoadNetwork) super.getNetwork();
108 }
109
110
111
112
113
114
115 protected final void addCrossSectionElement(final CrossSectionElement cse)
116 {
117 this.crossSectionElementList.add(cse);
118 if (cse instanceof Lane)
119 {
120 if (cse instanceof Shoulder)
121 {
122 this.shoulders.add((Shoulder) cse);
123 }
124 else
125 {
126 this.lanes.add((Lane) cse);
127 fireTimedEvent(LANE_ADD_EVENT,
128 new Object[] {getNetwork().getId(), getId(), cse.getId(), this.lanes.indexOf(cse)},
129 getSimulator().getSimulatorTime());
130 }
131 }
132 }
133
134
135
136
137
138 public final List<CrossSectionElement> getCrossSectionElementList()
139 {
140 return this.crossSectionElementList == null ? new ArrayList<>() : new ArrayList<>(this.crossSectionElementList);
141 }
142
143
144
145
146
147 public final LaneKeepingPolicy getLaneKeepingPolicy()
148 {
149 return this.laneKeepingPolicy;
150 }
151
152
153
154
155
156
157 public final CrossSectionElement getCrossSectionElement(final String id)
158 {
159 for (CrossSectionElement cse : this.crossSectionElementList)
160 {
161 if (cse.getId().equals(id))
162 {
163 return cse;
164 }
165 }
166 return null;
167 }
168
169
170
171
172
173 public final List<Lane> getLanes()
174 {
175 return new ArrayList<>(this.lanes);
176 }
177
178
179
180
181
182 public final List<Shoulder> getShoulders()
183 {
184 return new ArrayList<>(this.shoulders);
185 }
186
187
188
189
190
191 public final List<Lane> getLanesAndShoulders()
192 {
193 List<Lane> all = new ArrayList<>(this.lanes);
194 all.addAll(this.shoulders);
195 return all;
196 }
197
198
199
200
201 public final Priority getPriority()
202 {
203 return this.priority;
204 }
205
206
207
208
209 public final void setPriority(final Priority priority)
210 {
211 this.priority = priority;
212 }
213
214
215
216
217
218 public PolyLine2d getStartLine()
219 {
220 if (this.startLine == null)
221 {
222 double left = Double.NaN;
223 double right = Double.NaN;
224 for (Lane lane : this.lanes)
225 {
226 double half = lane.getBeginWidth().si * .5;
227 if (!Double.isNaN(left))
228 {
229 left = Math.max(left, lane.getOffsetAtBegin().si + half);
230 right = Math.min(right, lane.getOffsetAtBegin().si - half);
231 }
232 else
233 {
234 left = lane.getOffsetAtBegin().si + half;
235 right = lane.getOffsetAtBegin().si - half;
236 }
237 }
238 Point2d start = getDesignLine().getFirst();
239 double heading = getStartNode().getHeading().si + .5 * Math.PI;
240 double cosHeading = Math.cos(heading);
241 double sinHeading = Math.sin(heading);
242 Point2d leftPoint = new Point2d(start.x + cosHeading * left, start.y + sinHeading * left);
243 Point2d rightPoint = new Point2d(start.x - cosHeading * right, start.y - sinHeading * right);
244 this.startLine = new PolyLine2d(leftPoint, rightPoint);
245 }
246 return this.startLine;
247 }
248
249
250
251
252
253 public PolyLine2d getEndLine()
254 {
255 if (this.endLine == null)
256 {
257 double left = Double.NaN;
258 double right = Double.NaN;
259 for (Lane lane : this.lanes)
260 {
261 double half = lane.getEndWidth().si * .5;
262 if (!Double.isNaN(left))
263 {
264 left = Math.max(left, lane.getOffsetAtEnd().si + half);
265 right = Math.min(right, lane.getOffsetAtEnd().si - half);
266 }
267 else
268 {
269 left = lane.getOffsetAtEnd().si + half;
270 right = lane.getOffsetAtEnd().si - half;
271 }
272 }
273 Point2d start = getDesignLine().getLast();
274 double heading = getEndNode().getHeading().si + .5 * Math.PI;
275 double cosHeading = Math.cos(heading);
276 double sinHeading = Math.sin(heading);
277 Point2d leftPoint = new Point2d(start.x + cosHeading * left, start.y + sinHeading * left);
278 Point2d rightPoint = new Point2d(start.x + cosHeading * right, start.y + sinHeading * right);
279 this.endLine = new PolyLine2d(leftPoint, rightPoint);
280 }
281 return this.endLine;
282 }
283
284
285 @Override
286 public final String toString()
287 {
288 return "CrossSectionLink [name=" + this.getId() + ", nodes=" + getStartNode().getId() + "-" + getEndNode().getId()
289 + ", crossSectionElementList=" + this.crossSectionElementList + ", lanes=" + this.lanes + ", laneKeepingPolicy="
290 + this.laneKeepingPolicy + "]";
291 }
292
293
294
295
296
297
298
299
300
301
302
303
304 public enum Priority
305 {
306
307 PRIORITY,
308
309
310 NONE,
311
312
313 YIELD,
314
315
316 STOP,
317
318
319 ALL_STOP,
320
321
322 BUS_STOP;
323
324
325
326
327
328 public boolean isPriority()
329 {
330 return this.equals(PRIORITY);
331 }
332
333
334
335
336
337 public boolean isNone()
338 {
339 return this.equals(NONE);
340 }
341
342
343
344
345
346 public boolean isYield()
347 {
348 return this.equals(YIELD);
349 }
350
351
352
353
354
355 public boolean isStop()
356 {
357 return this.equals(STOP);
358 }
359
360
361
362
363
364 public boolean isAllStop()
365 {
366 return this.equals(ALL_STOP);
367 }
368
369
370
371
372
373 public boolean isBusStop()
374 {
375 return this.equals(BUS_STOP);
376 }
377
378 }
379
380 }