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