1 package org.opentrafficsim.road.gtu.lane.perception;
2
3 import java.io.Serializable;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.SortedSet;
10 import java.util.TreeMap;
11 import java.util.TreeSet;
12
13 import org.djunits.value.vdouble.scalar.Length;
14 import org.opentrafficsim.core.gtu.GTU;
15 import org.opentrafficsim.core.gtu.GTUException;
16 import org.opentrafficsim.core.gtu.RelativePosition;
17 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
18
19 import nl.tudelft.simulation.language.Throw;
20
21 /**
22 * This data structure can clearly indicate the lane structure ahead of us, e.g. in the following situation:
23 *
24 * <pre>
25 * (---- a ----)(---- b ----)(---- c ----)(---- d ----)(---- e ----)(---- f ----)(---- g ----)
26 * __________ __________
27 * / _________ 1 / _________ 2
28 * / / / /
29 * __________/ / _______________________/ /
30 * 1 ____________ ____________ /_ _ _ _ _ _/____________ /_ _ _ _ _ _ _ _ _ _ _ _ /
31 * 0 |_ _X_ _ _ _ |_ _ _ _ _ _ |_ _ _ _ _ _ |_ _ _ _ _ _ |_ _ _ _ _ _ |_ _ _ _ _ _ \____________
32 * -1 |____________|_ _ _ _ _ _ |____________|____________| __________|____________|____________| 3
33 * -2 / __________/ \ \
34 * ________/ / \ \___________
35 * 5 _________/ \____________ 4
36 * </pre>
37 *
38 * When the GTU is looking ahead, it needs to know that when it continues to destination 3, it needs to shift one lane to the
39 * right at some point, but <b>not</b> two lanes to the right in link b, and not later than at the end of link f. When it needs
40 * to go to destination 1, it needs to shift to the left in link c. When it has to go to destination 2, it has to shift to the
41 * left, but not earlier than at link e. At node [de], it is possible to leave the rightmost lane of link e, and go to
42 * destination 4. The rightmost lane just splits into two lanes at the end of link d, and the GTU can either continue driving to
43 * destination 3, turn right to destination 4. This means that the right lane of link d has <b>two</b> successor lanes.
44 * <p>
45 * In the data structures, lanes are numbered laterally. Suppose that the lane where vehicle X resides would be number 0.
46 * Consistent with "left is positive" for angles, the lane right of X would have number -1, and entry 5 would have number -2.
47 * <p>
48 * In the data structure, this can be indicated as follows (N = next, P = previous, L = left, R = right, D = lane drop, . =
49 * continued but not in this structure). The merge lane in b is considered "off limits" for the GTUs on the "main" lane -1; the
50 * "main" lane 0 is considered off limits from the exit lanes on c, e, and f. Still, we need to maintain pointers to these
51 * lanes, as we are interested in the GTUs potentially driving next to us, feeding into our lane, etc.
52 *
53 * <pre>
54 * 1 0 -1 -2
55 *
56 * ROOT
57 * _____|_____ ___________ ___________
58 * |_-_|_._|_R_|----|_L_|_._|_-_| |_-_|_._|_-_| a
59 * | | |
60 * _____V_____ _____V_____ _____V_____
61 * |_-_|_N_|_R_|----|_L_|_N_|_R_|<---|_L_|_D_|_-_| b
62 * | |
63 * ___________ _____V_____ _____V_____
64 * |_-_|_N_|_R_|<---|_L_|_N_|_R_|----|_L_|_N_|_-_| c
65 * | | |
66 * _____V_____ _____V_____ _____V_____
67 * |_-_|_._|_-_| |_-_|_N_|_R_|----|_L_|_NN|_-_| d
68 * | ||_______________
69 * ___________ _____V_____ _____V_____ _____V_____
70 * |_-_|_N_|_R_|<---|_L_|_N_|_R_|----|_L_|_N_|_-_| |_-_|_N_|_-_| e
71 * | | | |
72 * _____V_____ _____V_____ _____V_____ _____V_____
73 * |_-_|_N_|_R_|<---|_L_|_D_|_R_|----|_L_|_N_|_-_| |_-_|_._|_-_| f
74 * | |
75 * _____V_____ _____V_____
76 * |_-_|_._|_-_| |_-_|_._|_-_| g
77 * </pre>
78 * <p>
79 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
80 * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
81 * </p>
82 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
83 * initial version Feb 20, 2016 <br>
84 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
85 */
86 public class LaneStructure implements Serializable
87 {
88 /** */
89 private static final long serialVersionUID = 20160400L;
90
91 /** The lanes from which we observe the situation. */
92 private LaneStructureRecord rootLSR;
93
94 /** Look ahead distance. */
95 private Length lookAhead;
96
97 /** Lane structure records of the cross section. */
98 private TreeMap<RelativeLane, LaneStructureRecord> crossSectionRecords = new TreeMap<>();
99
100 /** Lane structure records grouped per relative lane. */
101 private final Map<RelativeLane, Set<LaneStructureRecord>> relativeLaneMap = new HashMap<>();
102
103 /**
104 * @param rootLSR the root record.
105 * @param lookAhead look ahead distance
106 */
107 public LaneStructure(final LaneStructureRecord rootLSR, final Length lookAhead)
108 {
109 this.rootLSR = rootLSR;
110 this.lookAhead = lookAhead;
111 }
112
113 /**
114 * @return rootLSR
115 */
116 public final LaneStructureRecord getRootLSR()
117 {
118 return this.rootLSR;
119 }
120
121 /**
122 * Returns the cross section.
123 * @return cross section
124 */
125 public final SortedSet<RelativeLane> getCrossSection()
126 {
127 return this.crossSectionRecords.navigableKeySet();
128 }
129
130 /**
131 * @param lane lane to check
132 * @return record at given lane
133 * @throws GTUException if the lane is not in the cross section
134 */
135 public final LaneStructureRecord getLaneLSR(final RelativeLane lane) throws GTUException
136 {
137 Throw.when(!this.crossSectionRecords.containsKey(lane), GTUException.class,
138 "The requested lane %s is not in the most recent cross section.", lane);
139 return this.crossSectionRecords.get(lane);
140 }
141
142 /**
143 * Removes all mappings to relative lanes that are not in the most recent cross section.
144 * @param map map to clear mappings from
145 */
146 public final void removeInvalidMappings(final Map<RelativeLane, ?> map)
147 {
148 Iterator<RelativeLane> iterator = map.keySet().iterator();
149 while (iterator.hasNext())
150 {
151 RelativeLane lane = iterator.next();
152 if (!this.crossSectionRecords.containsKey(lane))
153 {
154 iterator.remove();
155 }
156 }
157 }
158
159 /**
160 * Adds a lane structure record in a mapping from relative lanes.
161 * @param lsr lane structure record
162 * @param relativeLane relative lane
163 */
164 public final void addLaneStructureRecord(final LaneStructureRecord lsr, final RelativeLane relativeLane)
165 {
166 if (!this.relativeLaneMap.containsKey(relativeLane))
167 {
168 this.relativeLaneMap.put(relativeLane, new HashSet<>());
169 }
170 this.relativeLaneMap.get(relativeLane).add(lsr);
171 if (lsr.getStartDistance().le(Length.ZERO) && lsr.getStartDistance().plus(lsr.getLane().getLength()).ge(Length.ZERO))
172 {
173 this.crossSectionRecords.put(relativeLane, lsr);
174 }
175 }
176
177 /**
178 * Retrieve objects on a lane of a specific type. Returns objects over a maximum length of the look ahead distance
179 * downstream from the relative position, or as far as the lane map goes.
180 * @param lane lane
181 * @param clazz class of objects to find
182 * @param gtu gtu
183 * @param pos relative position to start search from
184 * @param <T> type of objects to find
185 * @return Sorted set of objects of requested type
186 * @throws GTUException if lane is not in current set
187 */
188 @SuppressWarnings("unchecked")
189 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjects(final RelativeLane lane,
190 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
191 {
192 LaneStructureRecord record = this.getLaneLSR(lane);
193 Length ds = gtu.getRelativePositions().get(pos).getDx().minus(gtu.getReference().getDx());
194 // the list is ordered, but only for DIR_PLUS, need to do our own ordering
195 Length minimumPosition;
196 Length maximumPosition;
197 if (record.getDirection().isPlus())
198 {
199 minimumPosition = ds.minus(record.getStartDistance());
200 maximumPosition = record.getLane().getLength();
201 }
202 else
203 {
204 minimumPosition = Length.ZERO;
205 maximumPosition = record.getLane().getLength().plus(record.getStartDistance()).minus(ds);
206 }
207 SortedSet<Entry<T>> set = new TreeSet<>();
208 Length distance;
209 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
210 {
211 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
212 if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead))
213 {
214 // unchecked, but the above isAssignableFrom assures correctness
215 set.add(new Entry<>(distance, (T) object));
216 }
217 }
218 getDownstreamObjectsRecursive(set, record, clazz, ds);
219 return set;
220 }
221
222 /**
223 * Recursive search for lane based objects downstream.
224 * @param set set to store entries into
225 * @param record current record
226 * @param clazz class of objects to find
227 * @param ds distance from reference to chosen relative position
228 * @param <T> type of objects to find
229 */
230 @SuppressWarnings("unchecked")
231 private <T extends LaneBasedObject> void getDownstreamObjectsRecursive(final SortedSet<Entry<T>> set,
232 final LaneStructureRecord record, final Class<T> clazz, final Length ds)
233 {
234 if (record.getNext().isEmpty() || record.getNext().get(0).getStartDistance().gt(this.lookAhead))
235 {
236 return;
237 }
238 for (LaneStructureRecord next : record.getNext())
239 {
240 Length distance;
241 for (LaneBasedObject object : next.getLane().getLaneBasedObjects())
242 {
243 distance = next.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
244 if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead))
245 {
246 // unchecked, but the above isAssignableFrom assures correctness
247 set.add(new Entry<>(distance, (T) object));
248 }
249 }
250 getDownstreamObjectsRecursive(set, next, clazz, ds);
251 }
252 }
253
254 /**
255 * Retrieve objects on a lane of a specific type. Returns upstream objects from the relative position for as far as the lane
256 * map goes. Distances to upstream objects are given as positive values.
257 * @param lane lane
258 * @param clazz class of objects to find
259 * @param gtu gtu
260 * @param pos relative position to start search from
261 * @param <T> type of objects to find
262 * @return Sorted set of objects of requested type
263 * @throws GTUException if lane is not in current set
264 */
265 @SuppressWarnings("unchecked")
266 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getUpstreamObjects(final RelativeLane lane,
267 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
268 {
269 LaneStructureRecord record = this.getLaneLSR(lane);
270 Length ds = gtu.getReference().getDx().minus(gtu.getRelativePositions().get(pos).getDx());
271 // the list is ordered, but only for DIR_PLUS, need to do our own ordering
272 Length minimumPosition;
273 Length maximumPosition;
274 if (record.getDirection().isPlus())
275 {
276 minimumPosition = Length.ZERO;
277 maximumPosition = record.getStartDistance().neg().minus(ds);
278 }
279 else
280 {
281 minimumPosition = record.getLane().getLength().plus(record.getStartDistance()).plus(ds);
282 maximumPosition = record.getLane().getLength();
283 }
284 SortedSet<Entry<T>> set = new TreeSet<>();
285 Length distance;
286 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
287 {
288 if (clazz.isAssignableFrom(object.getClass()))
289 {
290 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
291 // unchecked, but the above isAssignableFrom assures correctness
292 set.add(new Entry<>(distance, (T) object));
293 }
294 }
295 getUpstreamObjectsRecursive(set, record, clazz, ds);
296 return set;
297 }
298
299 /**
300 * Recursive search for lane based objects upstream.
301 * @param set set to store entries into
302 * @param record current record
303 * @param clazz class of objects to find
304 * @param ds distance from reference to chosen relative position
305 * @param <T> type of objects to find
306 */
307 @SuppressWarnings("unchecked")
308 private <T extends LaneBasedObject> void getUpstreamObjectsRecursive(final SortedSet<Entry<T>> set,
309 final LaneStructureRecord record, final Class<T> clazz, final Length ds)
310 {
311 for (LaneStructureRecord prev : record.getPrev())
312 {
313 Length distance;
314 for (LaneBasedObject object : prev.getLane().getLaneBasedObjects())
315 {
316 if (clazz.isAssignableFrom(object.getClass()))
317 {
318 distance = prev.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
319 // unchecked, but the above isAssignableFrom assures correctness
320 set.add(new Entry<>(distance, (T) object));
321 }
322 }
323 getUpstreamObjectsRecursive(set, prev, clazz, ds);
324 }
325 }
326
327 /** {@inheritDoc} */
328 @Override
329 public final String toString()
330 {
331 return "LaneStructure [rootLSR=" + this.rootLSR + "]";
332 }
333
334 /**
335 * <p>
336 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
337 * <br>
338 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
339 * <p>
340 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Sep 15, 2016 <br>
341 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
342 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
343 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
344 * @param <T> class of lane based object contained
345 */
346 public class Entry<T extends LaneBasedObject> implements Comparable<Entry<T>>
347 {
348
349 /** Distance to lane based object. */
350 private final Length distance;
351
352 /** Lane based object. */
353 private final T laneBasedObject;
354
355 /**
356 * @param distance distance to lane based object
357 * @param laneBasedObject lane based object
358 */
359 public Entry(final Length distance, final T laneBasedObject)
360 {
361 this.distance = distance;
362 this.laneBasedObject = laneBasedObject;
363 }
364
365 /**
366 * @return distance.
367 */
368 public final Length getDistance()
369 {
370 return this.distance;
371 }
372
373 /**
374 * @return laneBasedObject.
375 */
376 public final T getLaneBasedObject()
377 {
378 return this.laneBasedObject;
379 }
380
381 /** {@inheritDoc} */
382 @Override
383 public final int hashCode()
384 {
385 final int prime = 31;
386 int result = 1;
387 result = prime * result + ((this.distance == null) ? 0 : this.distance.hashCode());
388 result = prime * result + ((this.laneBasedObject == null) ? 0 : this.laneBasedObject.hashCode());
389 return result;
390 }
391
392 /** {@inheritDoc} */
393 @Override
394 public final boolean equals(final Object obj)
395 {
396 if (this == obj)
397 {
398 return true;
399 }
400 if (obj == null)
401 {
402 return false;
403 }
404 if (getClass() != obj.getClass())
405 {
406 return false;
407 }
408 Entry<?> other = (Entry<?>) obj;
409 if (this.distance == null)
410 {
411 if (other.distance != null)
412 {
413 return false;
414 }
415 }
416 else if (!this.distance.equals(other.distance))
417 {
418 return false;
419 }
420 if (this.laneBasedObject == null)
421 {
422 if (other.laneBasedObject != null)
423 {
424 return false;
425 }
426 }
427 // laneBasedObject does not implement equals...
428 else if (!this.laneBasedObject.equals(other.laneBasedObject))
429 {
430 return false;
431 }
432 return true;
433 }
434
435 /** {@inheritDoc} */
436 @Override
437 public final int compareTo(final Entry<T> arg)
438 {
439 int d = this.distance.compareTo(arg.distance);
440 if (d != 0 || this.laneBasedObject.equals(arg.laneBasedObject))
441 {
442 return d; // different distance (-1 or 1), or same distance but also equal lane based object (0)
443 }
444 return 1; // same distance, unequal lane based object (1)
445 }
446
447 /** {@inheritDoc} */
448 @Override
449 public final String toString()
450 {
451 return "LaneStructure.Entry [distance=" + this.distance + ", laneBasedObject=" + this.laneBasedObject + "]";
452 }
453
454 }
455
456 }