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.event.TimedEventType;
8 import org.djutils.exceptions.Throw;
9 import org.djutils.exceptions.Try;
10 import org.djutils.metadata.MetaData;
11 import org.djutils.metadata.ObjectDescriptor;
12 import org.opentrafficsim.core.geometry.OTSLine3D;
13 import org.opentrafficsim.core.geometry.OTSPoint3D;
14 import org.opentrafficsim.core.network.LinkType;
15 import org.opentrafficsim.core.network.NetworkException;
16 import org.opentrafficsim.core.network.OTSLink;
17 import org.opentrafficsim.core.network.OTSNetwork;
18 import org.opentrafficsim.road.network.OTSRoadNetwork;
19 import org.opentrafficsim.road.network.RoadNetwork;
20 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
21
22 import nl.tudelft.simulation.language.d3.DirectedPoint;
23
24
25
26
27
28
29
30
31
32
33
34
35
36 public class CrossSectionLink extends OTSLink implements Serializable
37 {
38
39 private static final long serialVersionUID = 20141015L;
40
41
42 private final List<CrossSectionElement> crossSectionElementList = new ArrayList<>();
43
44
45 private final List<Lane> lanes = new ArrayList<>();
46
47
48 private final LaneKeepingPolicy laneKeepingPolicy;
49
50
51
52 private Priority priority = Priority.NONE;
53
54
55 private Double demandWeight = null;
56
57
58 private OTSLine3D startLine;
59
60
61 private OTSLine3D endLine;
62
63
64
65
66
67
68 public static final TimedEventType LANE_ADD_EVENT = new TimedEventType("LINK.LANE.ADD",
69 new MetaData("Lane data", "Lane data",
70 new ObjectDescriptor[] { new ObjectDescriptor("Network id", "Network id", String.class),
71 new ObjectDescriptor("Link id", "Link id", String.class),
72 new ObjectDescriptor("Lane id", "Lane id", String.class),
73 new ObjectDescriptor("Lane number", "Lane number", Integer.class) }));
74
75
76
77
78
79
80 public static final TimedEventType LANE_REMOVE_EVENT = new TimedEventType("LINK.LANE.REMOVE",
81 new MetaData("Lane data", "Lane data",
82 new ObjectDescriptor[] { new ObjectDescriptor("Network id", "Network id", String.class),
83 new ObjectDescriptor("Link id", "Link id", String.class),
84 new ObjectDescriptor("Lane id", "Lane id", String.class),
85 new ObjectDescriptor("Lane number", "Lane number", Integer.class) }));
86
87
88
89
90
91
92
93
94
95
96
97
98
99 @SuppressWarnings("checkstyle:parameternumber")
100 public CrossSectionLink(final OTSRoadNetwork network, final String id, final OTSRoadNode startNode,
101 final OTSRoadNode endNode, final LinkType linkType, final OTSLine3D designLine,
102 final LaneKeepingPolicy laneKeepingPolicy) throws NetworkException
103 {
104 super(network, id, startNode, endNode, linkType, designLine);
105 this.laneKeepingPolicy = laneKeepingPolicy;
106 }
107
108
109
110
111
112
113
114
115 protected CrossCrossSectionLinkfinal OTSRoadNetwork newNetwork, final CrossSectionLink link) throws NetworkException
116 {
117 super(newNetwork, link);
118 this.laneKeepingPolicy = link.laneKeepingPolicy;
119 for (CrossSectionElement cse : link.crossSectionElementList)
120 {
121 cse.clone(this, newNetwork.getSimulator());
122
123 }
124 }
125
126
127 @Override
128 public OTSRoadNetwork getNetwork()
129 {
130 return (OTSRoadNetwork) super.getNetwork();
131 }
132
133
134
135
136
137
138 protected final void addCrossSectionElement(final CrossSectionElement cse)
139 {
140 this.crossSectionElementList.add(cse);
141 if (cse instanceof Lane)
142 {
143 this.lanes.add((Lane) cse);
144 fireTimedEvent(LANE_ADD_EVENT, new Object[] { getNetwork().getId(), getId(), cse.getId(), this.lanes.indexOf(cse) },
145 getSimulator().getSimulatorTime());
146 }
147 }
148
149
150
151
152
153 public final List<CrossSectionElement> getCrossSectionElementList()
154 {
155 return this.crossSectionElementList == null ? new ArrayList<>() : new ArrayList<>(this.crossSectionElementList);
156 }
157
158
159
160
161
162 public final LaneKeepingPolicy getLaneKeepingPolicy()
163 {
164 return this.laneKeepingPolicy;
165 }
166
167
168
169
170
171
172 public final CrossSectionElement getCrossSectionElement(final String id)
173 {
174 for (CrossSectionElement cse : this.crossSectionElementList)
175 {
176 if (cse.getId().equals(id))
177 {
178 return cse;
179 }
180 }
181 return null;
182 }
183
184
185
186
187
188 public final List<Lane> getLanes()
189 {
190 return this.lanes == null ? new ArrayList<>() : new ArrayList<>(this.lanes);
191 }
192
193
194
195
196 public final Priority getPriority()
197 {
198 return this.priority;
199 }
200
201
202
203
204 public final void setPriority(final Priority priority)
205 {
206 this.priority = priority;
207 }
208
209
210
211
212
213 public final void setDemandWeight(final double demandWeight)
214 {
215 Throw.when(demandWeight < 0.0, IllegalArgumentException.class, "Demand weight should be positive.");
216 Throw.when(!getLinkType().isConnector(), IllegalArgumentException.class,
217 "Demand weight can only be set on connectors.");
218 this.demandWeight = demandWeight;
219 }
220
221
222
223
224 public final void clearDemandWeight()
225 {
226 this.demandWeight = null;
227 }
228
229
230
231
232
233 public final Double getDemandWeight()
234 {
235 return this.demandWeight;
236 }
237
238
239
240
241
242 public OTSLine3D getStartLine()
243 {
244 if (this.startLine == null)
245 {
246 double left = Double.NaN;
247 double right = Double.NaN;
248 for (Lane lane : this.lanes)
249 {
250 double half = lane.getBeginWidth().si * .5;
251 if (!Double.isNaN(left))
252 {
253 left = Math.max(left, lane.getDesignLineOffsetAtBegin().si + half);
254 right = Math.min(right, lane.getDesignLineOffsetAtBegin().si - half);
255 }
256 else
257 {
258 left = lane.getDesignLineOffsetAtBegin().si + half;
259 right = lane.getDesignLineOffsetAtBegin().si - half;
260 }
261 }
262 OTSPoint3D start = getStartNode().getPoint();
263 double heading = getStartNode().getHeading() + .5 * Math.PI;
264 double cosHeading = Math.cos(heading);
265 double sinHeading = Math.sin(heading);
266 OTSPoint3D leftPoint = new OTSPoint3D(start.x + cosHeading * left, start.y + sinHeading * left);
267 OTSPoint3D rightPoint = new OTSPoint3D(start.x - cosHeading * right, start.y - sinHeading * right);
268 this.startLine = Try.assign(() -> new OTSLine3D(leftPoint, rightPoint), "Invalid startline on CrossSectionLink.");
269 }
270 return this.startLine;
271 }
272
273
274
275
276
277 public OTSLine3D getEndLine()
278 {
279 if (this.endLine == null)
280 {
281 double left = Double.NaN;
282 double right = Double.NaN;
283 for (Lane lane : this.lanes)
284 {
285 double half = lane.getEndWidth().si * .5;
286 if (!Double.isNaN(left))
287 {
288 left = Math.max(left, lane.getDesignLineOffsetAtEnd().si + half);
289 right = Math.min(right, lane.getDesignLineOffsetAtEnd().si - half);
290 }
291 else
292 {
293 left = lane.getDesignLineOffsetAtEnd().si + half;
294 right = lane.getDesignLineOffsetAtEnd().si - half;
295 }
296 }
297 OTSPoint3D start = getEndNode().getPoint();
298 DirectedPoint p = Try.assign(() -> getEndNode().getLocation(), "Unexpected remote exception.");
299 double heading = p.getRotZ() + .5 * Math.PI;
300 double cosHeading = Math.cos(heading);
301 double sinHeading = Math.sin(heading);
302 OTSPoint3D leftPoint = new OTSPoint3D(start.x + cosHeading * left, start.y + sinHeading * left);
303 OTSPoint3D rightPoint = new OTSPoint3D(start.x + cosHeading * right, start.y + sinHeading * right);
304 this.endLine = Try.assign(() -> new OTSLine3D(leftPoint, rightPoint), "Invalid endline on CrossSectionLink.");
305 }
306 return this.endLine;
307 }
308
309
310 @Override
311 public final String toString()
312 {
313 return "CrossSectionLink [name=" + this.getId() + ", nodes=" + getStartNode().getId() + "-" + getEndNode().getId()
314 + ", crossSectionElementList=" + this.crossSectionElementList + ", lanes=" + this.lanes + ", laneKeepingPolicy="
315 + this.laneKeepingPolicy + "]";
316 }
317
318
319 @Override
320 @SuppressWarnings("checkstyle:designforextension")
321 public CrossSectionLink clone(final OTSNetwork newNetwork) throws NetworkException
322 {
323 Throw.when(!(newNetwork instanceof RoadNetwork), NetworkException.class,
324 "CrossSectionLink.clone. newNetwork not of the type Roadnetwork");
325 return new CrossSectionLink((OTSRoadNetwork) newNetwork, this);
326 }
327
328
329
330
331
332
333
334
335
336
337
338
339
340 public enum Priority
341 {
342
343 PRIORITY,
344
345
346 NONE,
347
348
349 TURN_ON_RED,
350
351
352 YIELD,
353
354
355 STOP,
356
357
358 ALL_STOP,
359
360
361 BUS_STOP;
362
363
364
365
366
367 public boolean isPriority()
368 {
369 return this.equals(PRIORITY);
370 }
371
372
373
374
375
376 public boolean isNone()
377 {
378 return this.equals(NONE);
379 }
380
381
382
383
384
385 public boolean isTurnOnRed()
386 {
387 return this.equals(TURN_ON_RED);
388 }
389
390
391
392
393
394 public boolean isYield()
395 {
396 return this.equals(YIELD);
397 }
398
399
400
401
402
403 public boolean isStop()
404 {
405 return this.equals(STOP);
406 }
407
408
409
410
411
412 public boolean isAllStop()
413 {
414 return this.equals(ALL_STOP);
415 }
416
417
418
419
420
421 public boolean isBusStop()
422 {
423 return this.equals(BUS_STOP);
424 }
425
426 }
427
428 }