1 package org.opentrafficsim.road.gtu.lane.perception;
2
3 import java.util.LinkedHashSet;
4 import java.util.Set;
5
6 import org.djunits.value.vdouble.scalar.Length;
7 import org.opentrafficsim.base.parameters.ParameterException;
8 import org.opentrafficsim.core.gtu.GtuException;
9 import org.opentrafficsim.core.gtu.RelativePosition;
10 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
11 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
12 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtu;
13
14 /**
15 * Iterable to find downstream GTU's.<br>
16 * <br>
17 * The behavior of this search is slightly altered using {@code boolean ignoreIfUpstream}. This is to deal with the following
18 * situations in case a GTU with it's rear upstream of the considered lane is found:<br>
19 * <br>
20 * Following downstream GTUs ({@code ignoreIfUpstream = true})
21 * <ol>
22 * <li>From the same direction (or not a merge): the GTU can be ignored as it is also found on the upstream lane.</li>
23 * <li>From the other direction of a merge: the GTU can be ignored as it is followed through considering the merge conflict.
24 * Note that we cannot follow the GTU in a regular fashion. If the rear of the GTU is upstream of the conflict, the subject GTU
25 * can move up to the conflict without hitting the GTU from the other direction. Considering the GTU through the conflict deals
26 * with this, and the GTU can be ignored for regular following of downstream GTUs.</li>
27 * </ol>
28 * <br>
29 * GTUs downstream of a conflict ({@code ignoreIfUpstream = false})
30 * <ol>
31 * <li>From the same direction: the GTU is considered both through the conflict and as a regular downstream GTU.</li>
32 * <li>From the other direction of a merge: the GTU needs to be considered.</li>
33 * </ol>
34 * <p>
35 * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
36 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
37 * </p>
38 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
39 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
40 * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
41 */
42 public class DownstreamNeighborsIterable extends AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer>
43 {
44
45 /** Margin in case of a left lane. */
46 private static final Length LEFT = Length.instantiateSI(-0.000001);
47
48 /** Margin in case of a right lane. */
49 private static final Length RIGHT = Length.instantiateSI(0.000001);
50
51 /** Headway GTU type that should be used. */
52 private final HeadwayGtuType headwayGtuType;
53
54 /** Added GTU's so far. */
55 private final Set<String> ids = new LinkedHashSet<>();
56
57 /**
58 * Margin used for neighbor search in some cases to prevent possible deadlock. This does not affect calculated distances to
59 * neighbors, but only whether they are considered a leader or follower.
60 */
61 private final Length margin;
62
63 /** Ignore downstream GTU's if their rear is upstream of the lane start. Note that equal GTU id's are always ignored. */
64 private final boolean ignoreIfUpstream;
65
66 /**
67 * Constructor.
68 * @param perceivingGtu LaneBasedGtu; perceiving GTU
69 * @param root LaneRecord<?>; root record
70 * @param initialPosition Length; position on the root record
71 * @param maxDistance Length; maximum distance to search
72 * @param relativePosition RelativePosition; position to which distance are calculated by subclasses
73 * @param headwayGtuType HeadwayGtuType; type of HeadwayGtu to return
74 * @param lane RelativeLane; relative lane (used for a left/right distinction to prevent dead-locks)
75 * @param ignoreIfUpstream boolean; whether to ignore GTU that are partially upstream of a record
76 */
77 public DownstreamNeighborsIterable(final LaneBasedGtu perceivingGtu, final LaneRecordInterface<?> root,
78 final Length initialPosition, final Length maxDistance, final RelativePosition relativePosition,
79 final HeadwayGtuType headwayGtuType, final RelativeLane lane, final boolean ignoreIfUpstream)
80 {
81 super(perceivingGtu, root, initialPosition, true, maxDistance, relativePosition, null);
82 this.headwayGtuType = headwayGtuType;
83 this.margin = lane.getLateralDirectionality().isLeft() ? LEFT : RIGHT;
84 if (perceivingGtu != null)
85 {
86 this.ids.add(perceivingGtu.getId());
87 }
88 this.ignoreIfUpstream = ignoreIfUpstream;
89 }
90
91 /** {@inheritDoc} */
92 @Override
93 protected Entry getNext(final LaneRecordInterface<?> record, final Length position, final Integer counter)
94 throws GtuException
95 {
96 int n;
97 LaneBasedGtu next;
98 Length pos;
99 if (counter == null)
100 {
101 Length searchPos = position.plus(this.margin);
102 next = record.getLane().getGtuAhead(searchPos, RelativePosition.FRONT,
103 record.getLane().getParentLink().getSimulator().getSimulatorAbsTime());
104 if (next == null)
105 {
106 return null;
107 }
108 n = record.getLane().indexOfGtu(next);
109 pos = next.position(record.getLane(), next.getRear());
110
111 if (this.ids.contains(next.getId()))
112 {
113 // rear still on previous lane; it is found there, get next gtu
114 pos = pos.plus(next.getLength());
115 return getNext(record, pos, n);
116 }
117 if (this.ignoreIfUpstream)
118 {
119 if (pos.si < 0.0)
120 {
121 pos = pos.plus(next.getLength());
122 return getNext(record, pos, n);
123 }
124 }
125 }
126 else
127 {
128 n = counter + 1;
129 if (n < 0 || n >= record.getLane().numberOfGtus())
130 {
131 return null;
132 }
133 next = record.getLane().getGtu(n);
134 pos = next.position(record.getLane(), next.getRear());
135 if (this.ids.contains(next.getId()))
136 {
137 // skip self
138 pos = pos.plus(next.getLength());
139 return getNext(record, pos, n);
140 }
141 }
142 return new Entry(next, n, pos);
143 }
144
145 /** {@inheritDoc} */
146 @Override
147 protected Length getDistance(final LaneBasedGtu object, final LaneRecordInterface<?> record, final Length position)
148 {
149 return record.getDistanceToPosition(position).minus(getDx());
150 }
151
152 /** {@inheritDoc} */
153 @Override
154 public HeadwayGtu perceive(final LaneBasedGtu perceivingGtu, final LaneBasedGtu object, final Length distance)
155 throws GtuException, ParameterException
156 {
157 return this.headwayGtuType.createDownstreamGtu(perceivingGtu, object, distance);
158 }
159
160 }