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