1 package org.opentrafficsim.core.network.lane;
2
3 import java.rmi.RemoteException;
4 import java.util.ArrayList;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Set;
8 import java.util.SortedMap;
9 import java.util.TreeMap;
10
11 import nl.tudelft.simulation.dsol.SimRuntimeException;
12
13 import org.opentrafficsim.core.gtu.GTUType;
14 import org.opentrafficsim.core.gtu.RelativePosition;
15 import org.opentrafficsim.core.gtu.lane.AbstractLaneBasedGTU;
16 import org.opentrafficsim.core.gtu.lane.LaneBasedGTU;
17 import org.opentrafficsim.core.network.LateralDirectionality;
18 import org.opentrafficsim.core.network.Link;
19 import org.opentrafficsim.core.network.LongitudinalDirectionality;
20 import org.opentrafficsim.core.network.NetworkException;
21 import org.opentrafficsim.core.unit.FrequencyUnit;
22 import org.opentrafficsim.core.unit.LengthUnit;
23 import org.opentrafficsim.core.unit.TimeUnit;
24 import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
25 import org.opentrafficsim.graphs.LaneBasedGTUSampler;
26
27
28
29
30
31
32
33
34
35
36
37
38 public class Lane extends CrossSectionElement
39 {
40
41 private final LaneType<?> laneType;
42
43
44 private final LongitudinalDirectionality directionality;
45
46
47 private DoubleScalar.Abs<FrequencyUnit> capacity;
48
49
50 private final SortedMap<Double, List<Sensor>> sensors = new TreeMap<>();
51
52
53 private final List<LaneBasedGTU<?>> gtuList = new ArrayList<LaneBasedGTU<?>>();
54
55
56 private Set<Lane> leftNeighbors = new HashSet<Lane>(1);
57
58
59 private Set<Lane> rightNeighbors = new HashSet<Lane>(1);
60
61
62
63
64
65 private Set<Lane> nextLanes = null;
66
67
68
69
70
71 private Set<Lane> prevLanes = null;
72
73
74
75 private ArrayList<LaneBasedGTUSampler> samplers = new ArrayList<LaneBasedGTUSampler>();
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 @SuppressWarnings("checkstyle:parameternumber")
93 public Lane(final CrossSectionLink<?, ?> parentLink, final DoubleScalar.Rel<LengthUnit> lateralOffsetAtStart,
94 final DoubleScalar.Rel<LengthUnit> lateralOffsetAtEnd, final DoubleScalar.Rel<LengthUnit> beginWidth,
95 final DoubleScalar.Rel<LengthUnit> endWidth, final LaneType<?> laneType,
96 final LongitudinalDirectionality directionality, final DoubleScalar.Abs<FrequencyUnit> capacity)
97 throws NetworkException
98 {
99 super(parentLink, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
100 this.laneType = laneType;
101 this.directionality = directionality;
102 this.capacity = capacity;
103
104 try
105 {
106 addSensor(new SensorLaneStart(this));
107 addSensor(new SensorLaneEnd(this));
108 }
109 catch (NetworkException exception)
110 {
111 throw new Error("Oops - Caught NetworkException adding sensor at begin or and of Lane " + exception);
112 }
113 }
114
115
116
117
118
119
120 private Set<Lane> neighbors(final LateralDirectionality direction)
121 {
122 return direction == LateralDirectionality.LEFT ? this.leftNeighbors : this.rightNeighbors;
123 }
124
125
126
127
128
129
130 public final void addAccessibleAdjacentLane(final Lane adjacentLane, final LateralDirectionality direction)
131 {
132 neighbors(direction).add(adjacentLane);
133 }
134
135
136
137
138
139
140
141
142 public final void removeAccessibleAdjacentLane(final Lane adjacentLane, final LateralDirectionality direction)
143 throws NetworkException
144 {
145 Set<Lane> neighbors = neighbors(direction);
146 if (!neighbors.contains(adjacentLane))
147 {
148 throw new NetworkException("Lane " + adjacentLane + " is not among the " + direction
149 + " neighbors of this Lane");
150 }
151 neighbors.remove(adjacentLane);
152 }
153
154
155
156
157
158
159 public final void addSensor(final Sensor sensor) throws NetworkException
160 {
161 double position = sensor.getLongitudinalPositionSI();
162 if (position < 0 || position > getLength().getSI())
163 {
164 throw new NetworkException("Illegal position for sensor " + position + " valid range is 0.."
165 + getLength().getSI());
166 }
167 List<Sensor> sensorList = this.sensors.get(position);
168 if (null == sensorList)
169 {
170 sensorList = new ArrayList<Sensor>(1);
171 this.sensors.put(position, sensorList);
172 }
173 sensorList.add(sensor);
174 }
175
176
177
178
179
180
181 public final void removeSensor(final Sensor sensor) throws NetworkException
182 {
183 List<Sensor> sensorList = this.sensors.get(sensor.getLongitudinalPosition().getSI());
184 if (null == sensorList)
185 {
186 throw new NetworkException("No sensor at " + sensor.getLongitudinalPositionSI());
187 }
188 sensorList.remove(sensor);
189 if (sensorList.size() == 0)
190 {
191 this.sensors.remove(sensor.getLongitudinalPosition().getSI());
192 }
193 }
194
195
196
197
198
199
200
201 public final List<Sensor> getSensors(final DoubleScalar.Rel<LengthUnit> minimumPosition,
202 final DoubleScalar.Rel<LengthUnit> maximumPosition)
203 {
204 ArrayList<Sensor> result = new ArrayList<Sensor>();
205 for (List<Sensor> sensorList : this.sensors.subMap(minimumPosition.getSI(), maximumPosition.getSI()).values())
206 {
207 result.addAll(sensorList);
208 }
209 return result;
210 }
211
212
213
214
215 protected final SortedMap<Double, List<Sensor>> getSensors()
216 {
217 return this.sensors;
218 }
219
220
221
222
223
224
225
226
227
228
229 public final void scheduleTriggers(final LaneBasedGTU<?> gtu, final double referenceStartSI,
230 final double referenceMoveSI) throws RemoteException, NetworkException, SimRuntimeException
231 {
232 for (List<Sensor> sensorList : this.sensors.values())
233 {
234 for (Sensor sensor : sensorList)
235 {
236 for (RelativePosition relativePosition : gtu.getRelativePositions().values())
237 {
238 if (sensor.getPositionType().equals(relativePosition.getType())
239 && referenceStartSI + relativePosition.getDx().getSI() <= sensor
240 .getLongitudinalPositionSI()
241 && referenceStartSI + referenceMoveSI + relativePosition.getDx().getSI() > sensor
242 .getLongitudinalPositionSI())
243 {
244
245
246
247 double d =
248 Math.max(0.0, sensor.getLongitudinalPositionSI() - referenceStartSI
249 - relativePosition.getDx().getSI());
250 DoubleScalar.Abs<TimeUnit> triggerTime =
251 gtu.timeAtDistance(new DoubleScalar.Rel<LengthUnit>(d, LengthUnit.METER));
252 gtu.getSimulator().scheduleEventAbs(triggerTime, this, sensor, "trigger", new Object[]{gtu});
253 }
254 }
255 }
256 }
257 }
258
259
260
261
262
263
264 public final DoubleScalar.Rel<LengthUnit> position(final double fraction)
265 {
266 return new DoubleScalar.Rel<LengthUnit>(this.getLength().getInUnit() * fraction, this.getLength().getUnit());
267 }
268
269
270
271
272
273
274
275 public final double positionSI(final double fraction)
276 {
277 return this.getLength().getSI() * fraction;
278 }
279
280
281
282
283
284
285 public final double fraction(final DoubleScalar.Rel<LengthUnit> position)
286 {
287 return position.getSI() / this.getLength().getSI();
288 }
289
290
291
292
293
294
295
296 public final double fractionSI(final double positionSI)
297 {
298 return positionSI / this.getLength().getSI();
299 }
300
301
302
303
304
305
306
307
308
309
310
311 public final int addGTU(final LaneBasedGTU<?> gtu, final double fractionalPosition) throws RemoteException,
312 NetworkException
313 {
314
315 int index;
316 for (index = 0; index < this.gtuList.size(); index++)
317 {
318 LaneBasedGTU<?> otherGTU = this.gtuList.get(index);
319 if (gtu == otherGTU)
320 {
321 System.err.println("GTU " + gtu + " already registered on Lane " + this + " [registered lanes: "
322 + gtu.positions(gtu.getFront()).keySet() + "] locations: "
323 + gtu.positions(gtu.getFront()).values() + " time: "
324 + gtu.getSimulator().getSimulatorTime().get());
325 new Exception().printStackTrace();
326 return index;
327
328
329
330
331
332
333 }
334 try
335 {
336 if (otherGTU.fractionalPosition(this, otherGTU.getFront()) >= fractionalPosition)
337 {
338 break;
339 }
340 }
341 catch (NetworkException exception)
342 {
343
344 exception.printStackTrace();
345 }
346 }
347 this.gtuList.add(index, gtu);
348
349 return index;
350 }
351
352
353
354
355
356
357
358
359
360
361
362 public final int addGTU(final LaneBasedGTU<?> gtu, final DoubleScalar.Rel<LengthUnit> longitudinalPosition)
363 throws RemoteException, NetworkException
364 {
365 return addGTU(gtu, longitudinalPosition.getSI() / getLength().getSI());
366 }
367
368
369
370
371
372 public final void removeGTU(final LaneBasedGTU<?> gtu)
373 {
374 this.gtuList.remove(gtu);
375 }
376
377
378
379
380
381
382
383
384 public final LaneBasedGTU<?> getGtuAfter(final DoubleScalar.Rel<LengthUnit> position,
385 final RelativePosition.TYPE relativePosition, final DoubleScalar.Abs<TimeUnit> when)
386 throws NetworkException
387 {
388 for (LaneBasedGTU<?> gtu : this.gtuList)
389 {
390 if (relativePosition.equals(RelativePosition.FRONT))
391 {
392 if (gtu.position(this, gtu.getFront(), when).getSI() > position.getSI())
393 {
394 return gtu;
395 }
396 }
397 else if (relativePosition.equals(RelativePosition.REAR))
398 {
399 if (gtu.position(this, gtu.getRear(), when).getSI() >= position.getSI())
400 {
401 return gtu;
402 }
403 }
404 else
405 {
406 throw new NetworkException("Can only use Lane.getGtuAfter(...) method with FRONT and REAR positions");
407 }
408 }
409 return null;
410 }
411
412
413
414
415
416
417
418
419 public final LaneBasedGTU<?> getGtuBefore(final DoubleScalar.Rel<LengthUnit> position,
420 final RelativePosition.TYPE relativePosition, final DoubleScalar.Abs<TimeUnit> when)
421 throws NetworkException
422 {
423 for (int i = this.gtuList.size() - 1; i >= 0; i--)
424 {
425 LaneBasedGTU<?> gtu = this.gtuList.get(i);
426 if (relativePosition.equals(RelativePosition.FRONT))
427 {
428 if (gtu.position(this, gtu.getFront(), when).getSI() < position.getSI())
429 {
430 return gtu;
431 }
432 }
433 else if (relativePosition.equals(RelativePosition.REAR))
434 {
435 if (gtu.position(this, gtu.getRear(), when).getSI() < position.getSI())
436 {
437 return gtu;
438 }
439 }
440 else
441 {
442 throw new NetworkException("Can only use Lane.getGtuBefore(...) method with FRONT and REAR positions");
443 }
444 }
445 return null;
446 }
447
448
449
450
451
452
453
454
455 private boolean laterallyCloseEnough(final CrossSectionElement incomingCSE, final CrossSectionElement outgoingCSE,
456 final DoubleScalar.Rel<LengthUnit> margin)
457 {
458 return Math.abs(DoubleScalar.minus(incomingCSE.getLateralCenterPosition(1),
459 outgoingCSE.getLateralCenterPosition(0)).getSI()) <= margin.getSI();
460 }
461
462
463
464
465
466
467
468 static final DoubleScalar.Rel<LengthUnit> LATERAL_MARGIN = new DoubleScalar.Rel<LengthUnit>(0.5, LengthUnit.METER);
469
470
471
472
473
474
475
476
477
478
479 public final Set<Lane> nextLanes()
480 {
481 if (this.nextLanes == null)
482 {
483
484 this.nextLanes = new HashSet<Lane>(1);
485 for (Link<?, ?> link : getParentLink().getEndNode().getLinksOut())
486 {
487 if (link instanceof CrossSectionLink<?, ?>)
488 {
489 for (CrossSectionElement cse : ((CrossSectionLink<?, ?>) link).getCrossSectionElementList())
490 {
491 if (cse instanceof Lane && laterallyCloseEnough(this, cse, LATERAL_MARGIN))
492 {
493 this.nextLanes.add((Lane) cse);
494 }
495 }
496 }
497 }
498 }
499 return this.nextLanes;
500 }
501
502
503
504
505
506
507
508
509
510
511 public final Set<Lane> prevLanes()
512 {
513 if (this.prevLanes == null)
514 {
515
516 this.prevLanes = new HashSet<Lane>(1);
517 for (Link<?, ?> link : getParentLink().getStartNode().getLinksIn())
518 {
519 if (link instanceof CrossSectionLink<?, ?>)
520 {
521 for (CrossSectionElement cse : ((CrossSectionLink<?, ?>) link).getCrossSectionElementList())
522 {
523 if (cse instanceof Lane && laterallyCloseEnough(cse, this, LATERAL_MARGIN))
524 {
525 this.prevLanes.add((Lane) cse);
526 }
527 }
528 }
529 }
530 }
531 return this.prevLanes;
532 }
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550 public final Set<Lane> accessibleAdjacentLanes(final LateralDirectionality lateralDirection,
551 final GTUType<?> gtuType)
552 {
553 Set<Lane> candidates = new HashSet<>();
554 for (Lane l : neighbors(lateralDirection))
555 {
556 if (l.getLaneType().isCompatible(gtuType)
557 && (l.getDirectionality().equals(LongitudinalDirectionality.BOTH) || l.getDirectionality().equals(
558 this.getDirectionality())))
559 {
560 candidates.add(l);
561 }
562 }
563 return candidates;
564 }
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586 public final Lane bestAccessibleAdjacentLane(final LateralDirectionality lateralDirection,
587 final DoubleScalar.Rel<LengthUnit> longitudinalPosition, final GTUType<?> gtuType)
588 {
589 Set<Lane> candidates = accessibleAdjacentLanes(lateralDirection, gtuType);
590
591 if (candidates.isEmpty())
592 {
593 return null;
594 }
595 if (candidates.size() == 1)
596 {
597 return candidates.iterator().next();
598 }
599
600 Lane bestLane = null;
601 double widthM = -1.0;
602 for (Lane lane : candidates)
603 {
604 if (lane.getWidth(longitudinalPosition).getSI() > widthM)
605 {
606 widthM = lane.getWidth(longitudinalPosition).getSI();
607 bestLane = lane;
608 }
609 }
610 return bestLane;
611 }
612
613
614
615
616
617 public final void addSampler(final LaneBasedGTUSampler sampler)
618 {
619 this.samplers.add(sampler);
620 }
621
622
623
624
625
626 public final void removeSampler(final LaneBasedGTUSampler sampler)
627 {
628 this.samplers.remove(sampler);
629 }
630
631
632
633
634
635
636
637 public final void sample(final AbstractLaneBasedGTU<?> gtu) throws RemoteException, NetworkException
638 {
639 for (LaneBasedGTUSampler sampler : this.samplers)
640 {
641 sampler.addData(gtu, this);
642 }
643 }
644
645
646
647
648 public final DoubleScalar.Abs<FrequencyUnit> getCapacity()
649 {
650 return this.capacity;
651 }
652
653
654
655
656 public final void setCapacity(final DoubleScalar.Abs<FrequencyUnit> capacity)
657 {
658 this.capacity = capacity;
659 }
660
661
662
663
664 public final LaneType<?> getLaneType()
665 {
666 return this.laneType;
667 }
668
669
670
671
672 public final LongitudinalDirectionality getDirectionality()
673 {
674 return this.directionality;
675 }
676
677
678
679
680 public final List<LaneBasedGTU<?>> getGtuList()
681 {
682 return this.gtuList;
683 }
684
685
686 @Override
687 @SuppressWarnings("checkstyle:designforextension")
688 protected double getZ()
689 {
690 return 0.0;
691 }
692
693
694 public final String toString()
695 {
696 CrossSectionLink<?, ?> link = getParentLink();
697
698 return String.format("Lane %d of %s", link.getCrossSectionElementList().indexOf(this), link.toString());
699 }
700
701 }