1 package org.opentrafficsim.road.gtu.lane.perception.categories;
2
3 import java.util.HashMap;
4 import java.util.HashSet;
5 import java.util.Iterator;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.SortedSet;
9 import java.util.TreeSet;
10
11 import org.djunits.unit.LengthUnit;
12 import org.djunits.value.vdouble.scalar.Length;
13 import org.djunits.value.vdouble.scalar.Time;
14 import org.opentrafficsim.base.TimeStampedObject;
15 import org.opentrafficsim.core.gtu.GTUDirectionality;
16 import org.opentrafficsim.core.gtu.GTUException;
17 import org.opentrafficsim.core.gtu.RelativePosition;
18 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
19 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
20 import org.opentrafficsim.core.network.LateralDirectionality;
21 import org.opentrafficsim.core.network.NetworkException;
22 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
23 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
24 import org.opentrafficsim.road.gtu.lane.perception.LaneStructureRecord;
25 import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
26 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
27 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayDistance;
28 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
29 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
30 import org.opentrafficsim.road.gtu.lane.tactical.AbstractLaneBasedTacticalPlanner;
31 import org.opentrafficsim.road.gtu.lane.tactical.LanePathInfo;
32 import org.opentrafficsim.road.network.lane.Lane;
33 import org.opentrafficsim.road.network.lane.LaneDirection;
34
35 import nl.tudelft.simulation.language.Throw;
36
37
38
39
40
41
42
43
44
45
46
47
48 public class DirectNeighborsPerception extends LaneBasedAbstractPerceptionCategory implements NeighborsPerception
49 {
50
51
52 private static final long serialVersionUID = 20160811L;
53
54
55 private final Map<RelativeLane, TimeStampedObject<SortedSet<HeadwayGTU>>> followers = new HashMap<>();
56
57
58 private final Map<RelativeLane, TimeStampedObject<SortedSet<HeadwayGTU>>> leaders = new HashMap<>();
59
60
61 private final Map<LateralDirectionality, TimeStampedObject<SortedSet<HeadwayGTU>>> firstFollowers = new HashMap<>();
62
63
64 private final Map<LateralDirectionality, TimeStampedObject<SortedSet<HeadwayGTU>>> firstLeaders = new HashMap<>();
65
66
67 private final Map<LateralDirectionality, TimeStampedObject<Boolean>> gtuAlongside = new HashMap<>();
68
69
70 private final HeadwayGtuType headwayGtuType;
71
72
73
74
75
76 private static final Length MARGIN = new Length(0.000001, LengthUnit.SI);
77
78
79
80
81
82 public DirectNeighborsPerception(final LanePerception perception, final HeadwayGtuType headwayGtuType)
83 {
84 super(perception);
85 this.headwayGtuType = headwayGtuType;
86 }
87
88
89 @Override
90 public final void updateAll() throws GTUException, NetworkException, ParameterException
91 {
92 updateLanePathInfo();
93 this.firstLeaders.clear();
94 this.firstFollowers.clear();
95 this.gtuAlongside.clear();
96 if (getPerception().getLaneStructure().getCrossSection().contains(RelativeLane.LEFT))
97 {
98 updateFirstLeaders(LateralDirectionality.LEFT);
99 updateFirstFollowers(LateralDirectionality.LEFT);
100 updateGtuAlongside(LateralDirectionality.LEFT);
101 }
102 if (getPerception().getLaneStructure().getCrossSection().contains(RelativeLane.RIGHT))
103 {
104 updateFirstLeaders(LateralDirectionality.RIGHT);
105 updateFirstFollowers(LateralDirectionality.RIGHT);
106 updateGtuAlongside(LateralDirectionality.RIGHT);
107 }
108 this.leaders.clear();
109 this.followers.clear();
110 for (RelativeLane lane : getPerception().getLaneStructure().getCrossSection())
111 {
112 updateLeaders(lane);
113 updateFollowers(lane);
114 }
115 }
116
117
118 @Override
119 public final void updateFirstLeaders(final LateralDirectionality lat)
120 throws ParameterException, GTUException, NetworkException
121 {
122 checkLateralDirectionality(lat);
123 SortedSet<HeadwayGTU> headwaySet = getFirstDownstreamGTUs(lat, RelativePosition.FRONT, RelativePosition.REAR);
124 this.firstLeaders.put(lat, new TimeStampedObject<>(headwaySet, getTimestamp()));
125 }
126
127
128 @Override
129 public final void updateFirstFollowers(final LateralDirectionality lat)
130 throws GTUException, ParameterException, NetworkException
131 {
132 checkLateralDirectionality(lat);
133 SortedSet<HeadwayGTU> headwaySet = getFirstUpstreamGTUs(lat, RelativePosition.REAR, RelativePosition.FRONT);
134 this.firstFollowers.put(lat, new TimeStampedObject<>(headwaySet, getTimestamp()));
135 }
136
137
138 @Override
139 public final void updateGtuAlongside(final LateralDirectionality lat) throws GTUException, ParameterException
140 {
141
142 checkLateralDirectionality(lat);
143
144 SortedSet<HeadwayGTU> headwaySet = getFirstDownstreamGTUs(lat, RelativePosition.REAR, RelativePosition.FRONT);
145 if (!headwaySet.isEmpty() && headwaySet.first().getDistance().le0())
146 {
147 this.gtuAlongside.put(lat, new TimeStampedObject<>(true, getTimestamp()));
148 return;
149 }
150
151 headwaySet = getFirstUpstreamGTUs(lat, RelativePosition.FRONT, RelativePosition.REAR);
152 if (!headwaySet.isEmpty() && headwaySet.first().getDistance().le0())
153 {
154 this.gtuAlongside.put(lat, new TimeStampedObject<>(true, getTimestamp()));
155 return;
156 }
157
158 this.gtuAlongside.put(lat, new TimeStampedObject<>(false, getTimestamp()));
159
160 }
161
162
163
164
165
166
167
168
169
170
171
172 private SortedSet<HeadwayGTU> getFirstDownstreamGTUs(final LateralDirectionality lat,
173 final RelativePosition.TYPE egoRelativePosition, final RelativePosition.TYPE otherRelativePosition)
174 throws GTUException, ParameterException
175 {
176 SortedSet<HeadwayGTU> headwaySet = new TreeSet<>();
177 Set<LaneStructureRecord> currentSet = new HashSet<>();
178 Set<LaneStructureRecord> nextSet = new HashSet<>();
179 LaneStructureRecord record = getPerception().getLaneStructure().getLaneLSR(new RelativeLane(lat, 1));
180 Length dxSearch = getGtu().getRelativePositions().get(egoRelativePosition).getDx();
181 Length dxHeadway = getGtu().getFront().getDx();
182 branchUpstream(record, dxSearch, currentSet);
183
184 while (!currentSet.isEmpty())
185 {
186 Iterator<LaneStructureRecord> iterator = currentSet.iterator();
187 while (iterator.hasNext())
188 {
189 record = iterator.next();
190
191
192
193
194
195
196
197 LaneBasedGTU down = record.getLane().getGtuAhead(record.getStartDistance().neg().plus(dxSearch),
198 record.getDirection(), otherRelativePosition, getTimestamp());
199 if (down != null)
200 {
201
202 headwaySet.add(this.headwayGtuType.createHeadwayGtu(down,
203 record.getStartDistance().plus(down.position(record.getLane(), down.getRear())).minus(dxHeadway)));
204 }
205 else
206 {
207
208 for (LaneStructureRecord next : record.getNext())
209 {
210 nextSet.add(next);
211 }
212 }
213 }
214 currentSet = nextSet;
215 nextSet = new HashSet<>();
216 }
217 return headwaySet;
218 }
219
220
221
222
223
224
225
226
227 private void branchUpstream(final LaneStructureRecord record, final Length dx, final Set<LaneStructureRecord> set)
228 {
229 Length pos = record.getStartDistance().neg().minus(dx);
230 if (pos.lt0())
231 {
232 for (LaneStructureRecord prev : record.getPrev())
233 {
234 branchUpstream(prev, dx, set);
235 }
236 }
237 else
238 {
239 set.add(record);
240 }
241 }
242
243
244
245
246
247
248
249
250
251
252
253 private SortedSet<HeadwayGTU> getFirstUpstreamGTUs(final LateralDirectionality lat,
254 final RelativePosition.TYPE egoRelativePosition, final RelativePosition.TYPE otherRelativePosition)
255 throws GTUException, ParameterException
256 {
257 SortedSet<HeadwayGTU> headwaySet = new TreeSet<>();
258 Set<LaneStructureRecord> currentSet = new HashSet<>();
259 Set<LaneStructureRecord> prevSet = new HashSet<>();
260 LaneStructureRecord record = getPerception().getLaneStructure().getLaneLSR(new RelativeLane(lat, 1));
261 Length dxSearch = getGtu().getRelativePositions().get(egoRelativePosition).getDx();
262 Length dxHeadway = getGtu().getRear().getDx();
263 branchDownstream(record, dxSearch, currentSet);
264
265 while (!currentSet.isEmpty())
266 {
267 Iterator<LaneStructureRecord> iterator = currentSet.iterator();
268 while (iterator.hasNext())
269 {
270 record = iterator.next();
271
272
273
274
275
276
277
278 LaneBasedGTU up = record.getLane().getGtuBehind(record.getStartDistance().neg().plus(dxSearch),
279 record.getDirection(), otherRelativePosition, getTimestamp());
280 if (up != null)
281 {
282
283 headwaySet.add(this.headwayGtuType.createHeadwayGtu(up, record.getStartDistance().neg()
284 .minus(up.position(record.getLane(), up.getFront())).plus(dxHeadway)));
285 }
286 else
287 {
288
289 for (LaneStructureRecord prev : record.getPrev())
290 {
291 prevSet.add(prev);
292 }
293 }
294 }
295 currentSet = prevSet;
296 prevSet = new HashSet<>();
297 }
298 return headwaySet;
299 }
300
301
302
303
304
305
306
307
308 private void branchDownstream(final LaneStructureRecord record, final Length dx, final Set<LaneStructureRecord> set)
309 {
310 Length pos = record.getStartDistance().neg().plus(dx);
311 if (pos.gt(record.getLane().getLength()))
312 {
313 for (LaneStructureRecord next : record.getNext())
314 {
315 branchDownstream(next, dx, set);
316 }
317 }
318 else
319 {
320 set.add(record);
321 }
322 }
323
324
325 @Override
326 public final void updateLeaders(final RelativeLane lane) throws ParameterException, GTUException, NetworkException
327 {
328 Throw.whenNull(lane, "Lane may not be null.");
329 SortedSet<HeadwayGTU> headwaySet = new TreeSet<>();
330 Set<LaneStructureRecord> currentSet = new HashSet<>();
331 Set<LaneStructureRecord> nextSet = new HashSet<>();
332 LaneStructureRecord initRecord = getPerception().getLaneStructure().getLaneLSR(lane);
333 currentSet.add(initRecord);
334 Length lookahead = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD);
335 Length ds = getGtu().getFront().getDx().minus(getGtu().getReference().getDx());
336
337 while (!currentSet.isEmpty())
338 {
339 for (LaneStructureRecord record : currentSet)
340 {
341 int first;
342 Length loc = record.getStartDistance().neg().plus(ds);
343 if (lane.getLateralDirectionality().isLeft())
344 {
345 loc = record.getDirection().isPlus() ? loc.minus(MARGIN) : loc.plus(MARGIN);
346 }
347 else if (lane.getLateralDirectionality().isRight())
348 {
349 loc = record.getDirection().isPlus() ? loc.plus(MARGIN) : loc.minus(MARGIN);
350 }
351 LaneBasedGTU down =
352 record.getLane().getGtuAhead(loc, record.getDirection(), RelativePosition.FRONT, getTimestamp());
353 if (down == null)
354 {
355 first = record.getLane().getGtuList().size();
356 }
357 else
358 {
359 first = record.getLane().getGtuList().indexOf(down);
360 }
361
362 for (int i = first; i < record.getLane().getGtuList().size(); i++)
363 {
364 LaneBasedGTU gtu = record.getLane().getGtuList().get(i);
365 if (gtu.position(record.getLane(), gtu.getRear()).lt0() && !record.equals(initRecord))
366 {
367
368 continue;
369 }
370 Length distance = record.getStartDistance().plus(gtu.position(record.getLane(), gtu.getRear())).minus(ds);
371
372 if (distance.le(lookahead) && !gtu.equals(getGtu()))
373 {
374
375 if (!gtu.getId().equals("999999"))
376 {
377 headwaySet.add(this.headwayGtuType.createHeadwayGtu(gtu, distance));
378 }
379 }
380 }
381
382 for (LaneStructureRecord next : record.getNext())
383 {
384 nextSet.add(next);
385 }
386
387 }
388 currentSet = nextSet;
389 nextSet = new HashSet<>();
390 }
391 this.leaders.put(lane, new TimeStampedObject<>(headwaySet, getTimestamp()));
392
393 }
394
395
396 @Override
397 public final void updateFollowers(final RelativeLane lane) throws GTUException, NetworkException, ParameterException
398 {
399 Throw.whenNull(lane, "Lane may not be null.");
400 SortedSet<HeadwayGTU> headwaySet = new TreeSet<>();
401 Set<LaneStructureRecord> currentSet = new HashSet<>();
402 Set<LaneStructureRecord> prevSet = new HashSet<>();
403 LaneStructureRecord initRecord = getPerception().getLaneStructure().getLaneLSR(lane);
404 currentSet.add(initRecord);
405 Length lookback = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKBACK);
406 Length dsFront = getGtu().getFront().getDx().minus(getGtu().getReference().getDx());
407 Length dsRear = getGtu().getRear().getDx().minus(getGtu().getReference().getDx());
408
409 while (!currentSet.isEmpty())
410 {
411 for (LaneStructureRecord record : currentSet)
412 {
413 int first;
414 Length loc = record.getStartDistance().neg().plus(dsFront);
415 if (lane.getLateralDirectionality().isLeft())
416 {
417 loc = record.getDirection().isPlus() ? loc.plus(MARGIN) : loc.minus(MARGIN);
418 }
419 else if (lane.getLateralDirectionality().isRight())
420 {
421 loc = record.getDirection().isPlus() ? loc.minus(MARGIN) : loc.plus(MARGIN);
422 }
423 LaneBasedGTU up =
424 record.getLane().getGtuBehind(loc, record.getDirection(), RelativePosition.FRONT, getTimestamp());
425 if (up == null)
426 {
427 first = -1;
428 }
429 else
430 {
431 first = record.getLane().getGtuList().indexOf(up);
432 }
433
434 for (int i = first; i >= 0; i--)
435 {
436 LaneBasedGTU gtu = record.getLane().getGtuList().get(i);
437 Length distance =
438 record.getStartDistance().neg().minus(gtu.position(record.getLane(), gtu.getFront())).plus(dsRear);
439
440 if (distance.le(lookback) && !gtu.getId().equals(getGtu().getId()))
441 {
442 headwaySet.add(this.headwayGtuType.createHeadwayGtu(gtu, distance));
443 }
444 }
445
446 for (LaneStructureRecord prev : record.getPrev())
447 {
448 prevSet.add(prev);
449 }
450
451 }
452 currentSet = prevSet;
453 prevSet = new HashSet<>();
454 }
455 this.followers.put(lane, new TimeStampedObject<>(headwaySet, getTimestamp()));
456
457 }
458
459
460 @Override
461 public final SortedSet<HeadwayGTU> getFirstLeaders(final LateralDirectionality lat)
462 throws ParameterException, NullPointerException, IllegalArgumentException
463 {
464 checkLateralDirectionality(lat);
465 return this.firstLeaders.get(lat).getObject();
466 }
467
468
469 @Override
470 public final SortedSet<HeadwayGTU> getFirstFollowers(final LateralDirectionality lat)
471 throws ParameterException, NullPointerException, IllegalArgumentException
472 {
473 checkLateralDirectionality(lat);
474 return getObjectOrNull(this.firstFollowers.get(lat));
475 }
476
477
478 @Override
479 public final boolean isGtuAlongside(final LateralDirectionality lat)
480 throws ParameterException, NullPointerException, IllegalArgumentException
481 {
482 checkLateralDirectionality(lat);
483 return getObjectOrNull(this.gtuAlongside.get(lat));
484 }
485
486
487 @Override
488 public final SortedSet<HeadwayGTU> getLeaders(final RelativeLane lane)
489 {
490 return getObjectOrNull(this.leaders.get(lane));
491 }
492
493
494 @Override
495 public final SortedSet<HeadwayGTU> getFollowers(final RelativeLane lane)
496 {
497 return getObjectOrNull(this.followers.get(lane));
498 }
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520 public final TimeStampedObject<SortedSet<HeadwayGTU>> getTimeStampedFirstLeaders(final LateralDirectionality lat)
521 throws ParameterException, NullPointerException, IllegalArgumentException
522 {
523 checkLateralDirectionality(lat);
524 return this.firstLeaders.get(lat);
525 }
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548 public final TimeStampedObject<SortedSet<HeadwayGTU>> getTimeStampedFirstFollowers(final LateralDirectionality lat)
549 throws ParameterException, NullPointerException, IllegalArgumentException
550 {
551 checkLateralDirectionality(lat);
552 return this.firstFollowers.get(lat);
553 }
554
555
556
557
558
559
560
561
562
563 public final TimeStampedObject<Boolean> isGtuAlongsideTimeStamped(final LateralDirectionality lat)
564 throws ParameterException, NullPointerException, IllegalArgumentException
565 {
566 checkLateralDirectionality(lat);
567 return this.gtuAlongside.get(lat);
568 }
569
570
571
572
573
574
575
576 public final TimeStampedObject<SortedSet<HeadwayGTU>> getTimeStampedLeaders(final RelativeLane lane)
577 {
578 return this.leaders.get(lane);
579 }
580
581
582
583
584
585
586
587 public final TimeStampedObject<SortedSet<HeadwayGTU>> getTimeStampedFollowers(final RelativeLane lane)
588 {
589 return this.followers.get(lane);
590 }
591
592
593
594
595
596
597
598
599 private void checkLateralDirectionality(final LateralDirectionality lat)
600 throws ParameterException, NullPointerException, IllegalArgumentException
601 {
602
603 Throw.whenNull(lat, "Lateral directionality may not be null.");
604 Throw.when(lat.equals(LateralDirectionality.NONE), IllegalArgumentException.class,
605 "Lateral directionality may not be NONE.");
606 Throw.when(
607 (lat.equals(LateralDirectionality.LEFT)
608 && !getPerception().getLaneStructure().getCrossSection().contains(RelativeLane.LEFT))
609 || (lat.equals(LateralDirectionality.RIGHT)
610 && !getPerception().getLaneStructure().getCrossSection().contains(RelativeLane.RIGHT)),
611 IllegalArgumentException.class, "Lateral directionality may only point to an existing adjacent lane.");
612 }
613
614
615 public final String toString()
616 {
617 return "DirectNeighborsPerception";
618 }
619
620
621
622
623
624
625
626 private TimeStampedObject<LanePathInfo> lanePathInfo;
627
628
629
630
631
632
633 public final void updateLanePathInfo() throws GTUException, NetworkException, ParameterException
634 {
635 Time timestamp = getTimestamp();
636 this.lanePathInfo = new TimeStampedObject<>(AbstractLaneBasedTacticalPlanner.buildLanePathInfo(getGtu(),
637 getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD)), timestamp);
638 }
639
640
641
642
643
644 public final LanePathInfo getLanePathInfo()
645 {
646 return this.lanePathInfo.getObject();
647 }
648
649
650
651
652
653 public final TimeStampedObject<LanePathInfo> getTimeStampedLanePathInfo()
654 {
655 return this.lanePathInfo;
656 }
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674 private Headway forwardHeadway(final Length maxDistance) throws GTUException, NetworkException
675 {
676 LanePathInfo lpi = getLanePathInfo();
677 return forwardHeadway(lpi, maxDistance);
678 }
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698 private Headway forwardHeadway(final LanePathInfo lpi, final Length maxDistance) throws GTUException, NetworkException
699 {
700 Throw.when(maxDistance.le(Length.ZERO), GTUException.class, "forwardHeadway: maxDistance should be positive");
701
702 int ldIndex = 0;
703 LaneDirection ld = lpi.getReferenceLaneDirection();
704 double gtuPosFrontSI = lpi.getReferencePosition().si;
705 if (lpi.getReferenceLaneDirection().getDirection().isPlus())
706 {
707 gtuPosFrontSI += getGtu().getFront().getDx().si;
708 }
709 else
710 {
711 gtuPosFrontSI -= getGtu().getFront().getDx().si;
712 }
713
714 while ((gtuPosFrontSI > ld.getLane().getLength().si || gtuPosFrontSI < 0.0)
715 && ldIndex < lpi.getLaneDirectionList().size() - 1)
716 {
717 ldIndex++;
718 if (ld.getDirection().isPlus())
719 {
720 if (lpi.getLaneDirectionList().get(ldIndex).getDirection().isPlus())
721 {
722 gtuPosFrontSI -= ld.getLane().getLength().si;
723 }
724 else
725 {
726 gtuPosFrontSI = lpi.getLaneDirectionList().get(ldIndex).getLane().getLength().si - gtuPosFrontSI;
727 }
728 ld = lpi.getLaneDirectionList().get(ldIndex);
729 }
730 else
731
732 {
733 if (lpi.getLaneDirectionList().get(ldIndex).getDirection().isPlus())
734 {
735 gtuPosFrontSI += ld.getLane().getLength().si;
736 }
737 else
738 {
739 gtuPosFrontSI += lpi.getLaneDirectionList().get(ldIndex).getLane().getLength().si;
740 }
741 ld = lpi.getLaneDirectionList().get(ldIndex);
742 }
743 }
744
745 Time time = getGtu().getSimulator().getSimulatorTime().getTime();
746
747
748 return headwayLane(ld, gtuPosFrontSI, 0.0, time);
749
750 }
751
752
753
754
755
756
757
758
759
760
761
762 private Headway headwayLane(final LaneDirection laneDirection, final double startPosSI, final double cumDistSI,
763 final Time now) throws GTUException
764 {
765 Lane lane = laneDirection.getLane();
766 LaneBasedGTU laneBasedGTU = lane.getGtuAhead(new Length(startPosSI, LengthUnit.SI), laneDirection.getDirection(),
767 RelativePosition.REAR, now);
768 if (laneBasedGTU == null)
769 {
770 return null;
771 }
772 double distanceSI = Math.abs(laneBasedGTU.position(lane, laneBasedGTU.getRear()).si - startPosSI);
773 return new HeadwayGTUSimple(laneBasedGTU.getId(), laneBasedGTU.getGTUType(),
774 new Length(cumDistSI + distanceSI, LengthUnit.SI), laneBasedGTU.getLength(), laneBasedGTU.getSpeed(),
775 laneBasedGTU.getAcceleration());
776 }
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792 private Headway backwardHeadway(final Length maxDistance) throws GTUException, NetworkException
793 {
794 Throw.when(maxDistance.ge(Length.ZERO), GTUException.class, "backwardHeadway: maxDistance should be negative");
795 Time time = getGtu().getSimulator().getSimulatorTime().getTime();
796 double maxDistanceSI = maxDistance.si;
797 Headway foundHeadway = new HeadwayDistance(-maxDistanceSI);
798 for (Lane lane : getGtu().positions(getGtu().getRear()).keySet())
799 {
800 Headway closest = headwayRecursiveBackwardSI(lane, getGtu().getDirection(lane),
801 getGtu().position(lane, getGtu().getRear(), time).getSI(), 0.0, -maxDistanceSI, time);
802 if (closest.getDistance().si < -maxDistanceSI && closest.getDistance().si < -foundHeadway.getDistance().si)
803 {
804 foundHeadway = closest;
805 }
806 }
807 if (foundHeadway instanceof HeadwayGTU)
808 {
809 return new HeadwayGTUSimple(foundHeadway.getId(), ((HeadwayGTU) foundHeadway).getGtuType(),
810 foundHeadway.getDistance().neg(), foundHeadway.getLength(), foundHeadway.getSpeed(), null);
811 }
812 return null;
813 }
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831 private Headway headwayRecursiveBackwardSI(final Lane lane, final GTUDirectionality direction, final double lanePositionSI,
832 final double cumDistanceSI, final double maxDistanceSI, final Time when) throws GTUException
833 {
834 LaneBasedGTU otherGTU =
835 lane.getGtuBehind(new Length(lanePositionSI, LengthUnit.SI), direction, RelativePosition.FRONT, when);
836 if (otherGTU != null)
837 {
838 double distanceM = cumDistanceSI + lanePositionSI - otherGTU.position(lane, otherGTU.getFront(), when).getSI();
839 if (distanceM > 0 && distanceM <= maxDistanceSI)
840 {
841 return new HeadwayGTUSimple(otherGTU.getId(), otherGTU.getGTUType(), new Length(distanceM, LengthUnit.SI),
842 otherGTU.getLength(), otherGTU.getSpeed(), null);
843 }
844 return new HeadwayDistance(Double.MAX_VALUE);
845 }
846
847
848 if (cumDistanceSI + lanePositionSI < maxDistanceSI)
849 {
850
851 if (lane.prevLanes(getGtu().getGTUType()).size() > 0)
852 {
853 Headway foundMaxGTUDistanceSI = new HeadwayDistance(Double.MAX_VALUE);
854 for (Lane prevLane : lane.prevLanes(getGtu().getGTUType()).keySet())
855 {
856
857 double traveledDistanceSI = cumDistanceSI + lanePositionSI;
858
859 Headway closest = headwayRecursiveBackwardSI(prevLane, direction, prevLane.getLength().getSI(),
860 traveledDistanceSI, maxDistanceSI, when);
861 if (closest.getDistance().si < maxDistanceSI
862 && closest.getDistance().si < foundMaxGTUDistanceSI.getDistance().si)
863 {
864 foundMaxGTUDistanceSI = closest;
865 }
866 }
867 return foundMaxGTUDistanceSI;
868 }
869 }
870
871
872 return new HeadwayDistance(Double.MAX_VALUE);
873 }
874
875 }