1 package org.opentrafficsim.road.gtu.lane.perception;
2
3 import java.util.HashMap;
4 import java.util.HashSet;
5 import java.util.Iterator;
6 import java.util.LinkedList;
7 import java.util.Map;
8 import java.util.Queue;
9 import java.util.Set;
10 import java.util.SortedMap;
11 import java.util.TreeMap;
12
13 import org.djunits.value.vdouble.scalar.Length;
14 import org.djutils.exceptions.Try;
15 import org.opentrafficsim.core.gtu.GTUException;
16 import org.opentrafficsim.core.gtu.RelativePosition;
17 import org.opentrafficsim.core.network.Link;
18 import org.opentrafficsim.core.network.route.Route;
19 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
20 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
21
22 /**
23 * Abstract iterable that figures out how to find the next nearest object, including splits.
24 * <p>
25 * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26 * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
27 * <p>
28 * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 feb. 2018 <br>
29 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
30 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
31 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
32 * @param <H> headway type
33 * @param <U> underlying object type
34 * @param <C> counter type
35 */
36 public abstract class AbstractPerceptionIterable<H extends Headway, U, C> extends AbstractPerceptionReiterable<H, U>
37 {
38
39 /** Root record. */
40 private final LaneRecord<?> root;
41
42 /** Initial position. */
43 private final Length initialPosition;
44
45 /** Search downstream (or upstream). */
46 private final boolean downstream;
47
48 /** Max distance. */
49 private final double maxDistance;
50
51 /** Position to which distance are calculated by subclasses. */
52 private final RelativePosition relativePosition;
53
54 /** Route of the GTU. */
55 private final Route route;
56
57 /**
58 * Constructor.
59 * @param perceivingGtu LaneBasedGTU; perceiving GTU
60 * @param root LaneRecord<?>; root record
61 * @param initialPosition Length; initial position
62 * @param downstream boolean; search downstream (or upstream)
63 * @param maxDistance Length; max distance to search
64 * @param relativePosition RelativePosition; position to which distance are calculated by subclasses
65 * @param route Route; route of the GTU, may be {@code null}
66 */
67 public AbstractPerceptionIterable(final LaneBasedGTU perceivingGtu, final LaneRecord<?> root, final Length initialPosition,
68 final boolean downstream, final Length maxDistance, final RelativePosition relativePosition, final Route route)
69 {
70 super(perceivingGtu);
71 this.root = root;
72 this.initialPosition = initialPosition;
73 this.downstream = downstream;
74 this.maxDistance = maxDistance.si;
75 this.relativePosition = relativePosition;
76 this.route = route;
77 }
78
79 /** {@inheritDoc} */
80 @Override
81 public Iterator<PrimaryIteratorEntry> primaryIterator()
82 {
83 return new PrimaryIterator();
84 }
85
86 /**
87 * Returns the next object(s) on the lane represented by the record. This should only consider objects on the given lane.
88 * This method should not check the distance towards objects with the maximum distance.
89 * @param record LaneRecord<?>; record representing the lane and direction
90 * @param position Length; position to look beyond
91 * @param counter C; counter
92 * @return next object(s) on the lane or {@code null} if none
93 * @throws GTUException on any exception in the process
94 */
95 protected abstract Entry getNext(LaneRecord<?> record, Length position, C counter) throws GTUException;
96
97 /**
98 * Returns the distance to the object. The position fed in to this method is directly taken from an {@code Entry} returned
99 * by {@code getNext}. The two methods need to be consistent with each other.
100 * @param object U; underlying object
101 * @param record LaneRecord<?>; record representing the lane and direction
102 * @param position Length; position of the object on the lane
103 * @return Length; distance to the object
104 */
105 protected abstract Length getDistance(U object, LaneRecord<?> record, Length position);
106
107 /**
108 * Returns the longitudinal length of the relevant relative position such that distances to this points can be calculated.
109 * @return Length; the longitudinal length of the relevant relative position such that distances to this points can be
110 * calculated
111 */
112 protected Length getDx()
113 {
114 return this.relativePosition.getDx();
115 }
116
117 /**
118 * The primary iterator is used by all returned iterators to find the next object. This contains the core algorithm to deal
119 * with splits and multiple objects at a single location.
120 * <p>
121 * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
122 * <br>
123 * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
124 * <p>
125 * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 feb. 2018 <br>
126 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
127 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
128 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
129 */
130 private class PrimaryIterator implements Iterator<PrimaryIteratorEntry>
131 {
132
133 /** Map containing the objects found per branch. */
134 private SortedMap<PrimaryIteratorEntry, LaneRecord<?>> map;
135
136 /** Position per record where the search was halted. */
137 private Map<LaneRecord<?>, Length> positions = new HashMap<>();
138
139 /** Items returned to prevent duplicates. */
140 private Set<U> returnedItems = new HashSet<>();
141
142 /** Sets of remaining objects at the same location. */
143 private Map<LaneRecord<?>, Queue<PrimaryIteratorEntry>> queues = new HashMap<>();
144
145 /** Counter objects per lane. */
146 private Map<LaneRecord<?>, C> counters = new HashMap<>();
147
148 /** Record regarding a postponed call to {@code getNext()}. */
149 private LaneRecord<?> postponedRecord = null;
150
151 /** Position regarding a postponed call to {@code getNext()}. */
152 private Length postponedPosition = null;
153
154 /** Constructor. */
155 PrimaryIterator()
156 {
157 //
158 }
159
160 /** {@inheritDoc} */
161 @Override
162 public boolean hasNext()
163 {
164 // (re)start the process
165 startProcess();
166
167 // check next value
168 return !this.map.isEmpty();
169 }
170
171 /** {@inheritDoc} */
172 @Override
173 public PrimaryIteratorEntry next()
174 {
175 // (re)start the process
176 startProcess();
177
178 // get and remove next
179 PrimaryIteratorEntry nextEntry = this.map.firstKey();
180 U next = nextEntry.getObject();
181 LaneRecord<?> record = this.map.get(nextEntry);
182 this.map.remove(nextEntry);
183
184 // see if we can obtain the next from a queue
185 Queue<PrimaryIteratorEntry> queue = this.queues.get(next);
186 if (queue != null)
187 {
188 PrimaryIteratorEntry nextNext = queue.poll();
189 this.map.put(nextNext, record); // next object is made available in the map
190 if (queue.isEmpty())
191 {
192 this.queues.remove(record);
193 }
194 preventDuplicateEntries(nextEntry.getObject());
195 return nextNext;
196 }
197
198 // prepare for next
199 this.postponedRecord = record;
200 this.postponedPosition = this.positions.get(record); // position;
201 preventDuplicateEntries(nextEntry.getObject());
202 return nextEntry;
203 }
204
205 /**
206 * Prevents that duplicate (and further) records are returned for the given object as splits later on merge.
207 * @param object U; object for which a {@code PrimaryIteratorEntry} will be returned
208 */
209 private void preventDuplicateEntries(final U object)
210 {
211 this.returnedItems.add(object); // prevents new items to be added over alive branches (that should die out)
212 Iterator<PrimaryIteratorEntry> it = this.map.keySet().iterator();
213 while (it.hasNext())
214 {
215 PrimaryIteratorEntry entry = it.next();
216 if (entry.getObject().equals(object))
217 {
218 it.remove();
219 }
220 }
221 }
222
223 /**
224 * Starts or restarts the process.
225 */
226 @SuppressWarnings("synthetic-access")
227 private void startProcess()
228 {
229 if (this.postponedRecord != null)
230 {
231 // restart the process; perform prepareNext() that was postponed
232 prepareNext(this.postponedRecord, this.postponedPosition);
233 this.postponedRecord = null;
234 this.postponedPosition = null;
235 }
236 else if (this.map == null)
237 {
238 // start the process
239 this.map = new TreeMap<>();
240 prepareNext(AbstractPerceptionIterable.this.root, AbstractPerceptionIterable.this.initialPosition);
241 }
242 }
243
244 /**
245 * Iterative method that continues a search on the next lanes if no object is found.
246 * @param record LaneRecord<?>; record
247 * @param position Length; position
248 */
249 @SuppressWarnings("synthetic-access")
250 private void prepareNext(final LaneRecord<?> record, final Length position)
251 {
252 Entry next = Try.assign(() -> AbstractPerceptionIterable.this.getNext(record, position, this.counters.get(record)),
253 "Exception while deriving next object.");
254 if (next == null)
255 {
256 this.counters.remove(record);
257 double distance = AbstractPerceptionIterable.this.downstream
258 ? record.getStartDistance().si + record.getLength().si : -record.getStartDistance().si;
259 // TODO this let's us ignore an object that is registered on the next lane, but who's tail may be on this lane
260 if (distance < AbstractPerceptionIterable.this.maxDistance)
261 {
262 if (AbstractPerceptionIterable.this.downstream)
263 {
264 for (LaneRecord<?> nextRecord : record.getNext())
265 {
266 if (isOnRoute(nextRecord))
267 {
268 prepareNext(nextRecord, Length.createSI(-1e-9));
269 }
270 }
271 }
272 else
273 {
274 for (LaneRecord<?> nextRecord : record.getPrev())
275 {
276 if (isOnRoute(nextRecord))
277 {
278 prepareNext(nextRecord, nextRecord.getLength());
279 }
280 }
281 }
282 }
283 }
284 else
285 {
286 this.counters.put(record, next.counter);
287 if (next.isSet())
288 {
289 Iterator<U> it = next.set.iterator();
290 U nextNext = it.next();
291 if (!this.returnedItems.contains(nextNext))
292 {
293 Length distance = getDistance(nextNext, record, next.position);
294 if (distance == null // null means the object overlaps and is close
295 || distance.si <= AbstractPerceptionIterable.this.maxDistance)
296 {
297 // next object is made available in the map
298 this.map.put(new PrimaryIteratorEntry(nextNext, distance), record);
299 this.positions.put(record, next.position);
300 if (next.set.size() > 1)
301 {
302 // remaining at this location are made available in a queue
303 Queue<PrimaryIteratorEntry> queue = new LinkedList<>();
304 while (it.hasNext())
305 {
306 nextNext = it.next();
307 queue.add(new PrimaryIteratorEntry(nextNext, getDistance(nextNext, record, next.position)));
308 }
309 this.queues.put(record, queue);
310 }
311 }
312 }
313 }
314 else if (!this.returnedItems.contains(next.object))
315 {
316 Length distance = getDistance(next.object, record, next.position);
317 if (distance == null // null means the object overlaps and is close
318 || distance.si <= AbstractPerceptionIterable.this.maxDistance)
319 {
320 // next object is made available in the map
321 this.map.put(new PrimaryIteratorEntry(next.object, distance), record);
322 this.positions.put(record, next.position);
323 }
324 }
325 }
326 }
327
328 }
329
330 /**
331 * Returns whether the record is on the route.
332 * @param record LaneRecord<?>; record
333 * @return boolean; whether the record is on the route
334 */
335 final boolean isOnRoute(final LaneRecord<?> record)
336 {
337 if (this.route == null)
338 {
339 return true;
340 }
341 Link link = record.getLane().getParentLink();
342 int from;
343 int to;
344 if (record.getDirection().isPlus())
345 {
346 from = this.route.indexOf(link.getStartNode());
347 to = this.route.indexOf(link.getEndNode());
348 }
349 else
350 {
351 from = this.route.indexOf(link.getEndNode());
352 to = this.route.indexOf(link.getStartNode());
353 }
354 return from != -1 && to != -1 && to - from == 1;
355 }
356
357 /**
358 * Class of objects for subclasses to return. This can contain either a single object, or a set if there are multiple
359 * objects at a single location.
360 * <p>
361 * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
362 * <br>
363 * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
364 * <p>
365 * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 feb. 2018 <br>
366 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
367 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
368 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
369 */
370 protected class Entry
371 {
372
373 /** Set. */
374 private final Set<U> set;
375
376 /** Object. */
377 private final U object;
378
379 /** Counter. */
380 private final C counter;
381
382 /** Position on the lane. */
383 private final Length position;
384
385 /**
386 * Constructor.
387 * @param object U; object
388 * @param counter C; counter, may be {@code null}
389 * @param position Length; position
390 */
391 public Entry(final U object, final C counter, final Length position)
392 {
393 this.set = null;
394 this.object = object;
395 this.counter = counter;
396 this.position = position;
397 }
398
399 /**
400 * Constructor.
401 * @param set Set<U>; set
402 * @param counter C; counter, may be {@code null}
403 * @param position Length; position
404 */
405 public Entry(final Set<U> set, final C counter, final Length position)
406 {
407 this.set = set;
408 this.object = null;
409 this.counter = counter;
410 this.position = position;
411 }
412
413 /**
414 * Returns whether this entry contains a set.
415 * @return whether this entry contains a set
416 */
417 final boolean isSet()
418 {
419 return this.set != null;
420 }
421
422 /**
423 * Returns the underlying object. Use {@code !isSet()} to check whether there is an object.
424 * @return U; underlying set
425 */
426 public U getObject()
427 {
428 return this.object;
429 }
430
431 /**
432 * Returns the underlying set. Use {@code isSet()} to check whether there is a set.
433 * @return Set<U>; underlying set
434 */
435 public Set<U> getSet()
436 {
437 return this.set;
438 }
439
440 }
441
442 }