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.unit.LengthUnit;
14 import org.djunits.value.vdouble.scalar.Length;
15 import org.opentrafficsim.core.gtu.GTU;
16 import org.opentrafficsim.core.gtu.GTUException;
17 import org.opentrafficsim.core.gtu.RelativePosition;
18 import org.opentrafficsim.core.network.route.Route;
19 import org.opentrafficsim.road.network.lane.CrossSectionLink;
20 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
21
22 import nl.tudelft.simulation.language.Throw;
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public class LaneStructure implements Serializable
90 {
91
92 private static final long serialVersionUID = 20160400L;
93
94
95 private final LaneStructureRecord rootLSR;
96
97
98 private Length lookAhead;
99
100
101 private TreeMap<RelativeLane, LaneStructureRecord> crossSectionRecords = new TreeMap<>();
102
103
104 private final Map<RelativeLane, Set<LaneStructureRecord>> relativeLaneMap = new HashMap<>();
105
106
107
108
109
110 public LaneStructure(final LaneStructureRecord rootLSR, final Length lookAhead)
111 {
112 this.rootLSR = rootLSR;
113 this.lookAhead = lookAhead;
114 }
115
116
117
118
119 public final LaneStructureRecord getRootLSR()
120 {
121 return this.rootLSR;
122 }
123
124
125
126
127
128 public final SortedSet<RelativeLane> getCrossSection()
129 {
130 return this.crossSectionRecords.navigableKeySet();
131 }
132
133
134
135
136
137
138 public final LaneStructureRecord getLaneLSR(final RelativeLane lane) throws GTUException
139 {
140 Throw.when(!this.crossSectionRecords.containsKey(lane), GTUException.class,
141 "The requested lane %s is not in the most recent cross section.", lane);
142 return this.crossSectionRecords.get(lane);
143 }
144
145
146
147
148
149 public final void removeInvalidMappings(final Map<RelativeLane, ?> map)
150 {
151 Iterator<RelativeLane> iterator = map.keySet().iterator();
152 while (iterator.hasNext())
153 {
154 RelativeLane lane = iterator.next();
155 if (!this.crossSectionRecords.containsKey(lane))
156 {
157 iterator.remove();
158 }
159 }
160 }
161
162
163
164
165
166
167
168
169 public final void addLaneStructureRecord(final LaneStructureRecord lsr, final RelativeLane relativeLane)
170 {
171 if (!this.relativeLaneMap.containsKey(relativeLane))
172 {
173 this.relativeLaneMap.put(relativeLane, new HashSet<>());
174 }
175 this.relativeLaneMap.get(relativeLane).add(lsr);
176 if (lsr.getStartDistance().le0() && lsr.getStartDistance().plus(lsr.getLane().getLength()).gt0()
177 && (!this.crossSectionRecords.containsKey(relativeLane)
178 || this.crossSectionRecords.get(relativeLane).getStartDistance().gt0() && lsr.getStartDistance().le0()))
179 {
180 this.crossSectionRecords.put(relativeLane, lsr);
181 }
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195 @SuppressWarnings("unchecked")
196 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjects(final RelativeLane lane,
197 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
198 {
199 LaneStructureRecord record = null;
200 if (this.crossSectionRecords.containsKey(lane))
201 {
202 record = this.getLaneLSR(lane);
203 }
204 else
205 {
206 Length minLength = new Length(Double.MAX_VALUE, LengthUnit.SI);
207
208 for (LaneStructureRecord rec : this.relativeLaneMap.get(lane))
209 {
210 if (rec.getStartDistance().ge0() && rec.getStartDistance().lt(minLength))
211 {
212 record = rec;
213 minLength = rec.getStartDistance();
214 }
215 }
216 }
217 Throw.when(record == null, GTUException.class, "Trying to get objects on %s, but that lane is not in the structure.",
218 lane);
219 Length ds = gtu.getRelativePositions().get(pos).getDx().minus(gtu.getReference().getDx());
220
221 Length minimumPosition;
222 Length maximumPosition;
223 if (record.getDirection().isPlus())
224 {
225 minimumPosition = ds.minus(record.getStartDistance());
226 maximumPosition = record.getLane().getLength();
227 }
228 else
229 {
230 minimumPosition = Length.ZERO;
231 maximumPosition = record.getLane().getLength().plus(record.getStartDistance()).minus(ds);
232 }
233 SortedSet<Entry<T>> set = new TreeSet<>();
234 Length distance;
235 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
236 {
237 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
238 if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead)
239 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
240 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
241 {
242
243 set.add(new Entry<>(distance, (T) object));
244 }
245 }
246 getDownstreamObjectsRecursive(set, record, clazz, ds);
247 return set;
248 }
249
250
251
252
253
254
255
256
257
258
259
260
261
262 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjectsOnRoute(final RelativeLane lane,
263 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos, final Route route) throws GTUException
264 {
265 SortedSet<Entry<T>> set = getDownstreamObjects(lane, clazz, gtu, pos);
266 if (route != null)
267 {
268 Iterator<Entry<T>> iterator = set.iterator();
269 while (iterator.hasNext())
270 {
271 Entry<T> entry = iterator.next();
272 CrossSectionLink link = entry.getLaneBasedObject().getLane().getParentLink();
273 if (!route.contains(link.getStartNode()) || !route.contains(link.getEndNode())
274 || Math.abs(route.indexOf(link.getStartNode()) - route.indexOf(link.getEndNode())) != 1)
275 {
276 iterator.remove();
277 }
278 }
279 }
280 return set;
281 }
282
283
284
285
286
287
288
289
290
291 @SuppressWarnings("unchecked")
292 private <T extends LaneBasedObject> void getDownstreamObjectsRecursive(final SortedSet<Entry<T>> set,
293 final LaneStructureRecord record, final Class<T> clazz, final Length ds)
294 {
295 if (record.getNext().isEmpty() || record.getNext().get(0).getStartDistance().gt(this.lookAhead))
296 {
297 return;
298 }
299 for (LaneStructureRecord next : record.getNext())
300 {
301 Length distance;
302 for (LaneBasedObject object : next.getLane().getLaneBasedObjects())
303 {
304 distance = next.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
305 if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead)
306 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
307 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
308 {
309
310 set.add(new Entry<>(distance, (T) object));
311 }
312 }
313 getDownstreamObjectsRecursive(set, next, clazz, ds);
314 }
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328 public <T extends LaneBasedObject> Map<RelativeLane, SortedSet<Entry<T>>> getDownstreamObjectsOnRoute(final Class<T> clazz,
329 final GTU gtu, final RelativePosition.TYPE pos, final Route route) throws GTUException
330 {
331 Map<RelativeLane, SortedSet<Entry<T>>> out = new HashMap<>();
332 for (RelativeLane relativeLane : this.relativeLaneMap.keySet())
333 {
334 out.put(relativeLane, getDownstreamObjectsOnRoute(relativeLane, clazz, gtu, pos, route));
335 }
336 return out;
337 }
338
339
340
341
342
343
344
345
346
347
348
349
350 @SuppressWarnings("unchecked")
351 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getUpstreamObjects(final RelativeLane lane,
352 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
353 {
354 LaneStructureRecord record = this.getLaneLSR(lane);
355 Length ds = gtu.getReference().getDx().minus(gtu.getRelativePositions().get(pos).getDx());
356
357 Length minimumPosition;
358 Length maximumPosition;
359 if (record.getDirection().isPlus())
360 {
361 minimumPosition = Length.ZERO;
362 maximumPosition = record.getStartDistance().neg().minus(ds);
363 }
364 else
365 {
366 minimumPosition = record.getLane().getLength().plus(record.getStartDistance()).plus(ds);
367 maximumPosition = record.getLane().getLength();
368 }
369 SortedSet<Entry<T>> set = new TreeSet<>();
370 Length distance;
371 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
372 {
373 if (clazz.isAssignableFrom(object.getClass())
374 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
375 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
376 {
377 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
378
379 set.add(new Entry<>(distance, (T) object));
380 }
381 }
382 getUpstreamObjectsRecursive(set, record, clazz, ds);
383 return set;
384 }
385
386
387
388
389
390
391
392
393
394 @SuppressWarnings("unchecked")
395 private <T extends LaneBasedObject> void getUpstreamObjectsRecursive(final SortedSet<Entry<T>> set,
396 final LaneStructureRecord record, final Class<T> clazz, final Length ds)
397 {
398 for (LaneStructureRecord prev : record.getPrev())
399 {
400 Length distance;
401 for (LaneBasedObject object : prev.getLane().getLaneBasedObjects())
402 {
403 if (clazz.isAssignableFrom(object.getClass())
404 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
405 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
406 {
407 distance = prev.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
408
409 set.add(new Entry<>(distance, (T) object));
410 }
411 }
412 getUpstreamObjectsRecursive(set, prev, clazz, ds);
413 }
414 }
415
416
417 @Override
418 public final String toString()
419 {
420 return "LaneStructure [rootLSR=" + this.rootLSR + "]";
421 }
422
423
424
425
426
427
428
429
430
431
432
433
434
435 public static class Entry<T extends LaneBasedObject> implements Comparable<Entry<T>>
436 {
437
438
439 private final Length distance;
440
441
442 private final T laneBasedObject;
443
444
445
446
447
448 public Entry(final Length distance, final T laneBasedObject)
449 {
450 this.distance = distance;
451 this.laneBasedObject = laneBasedObject;
452 }
453
454
455
456
457 public final Length getDistance()
458 {
459 return this.distance;
460 }
461
462
463
464
465 public final T getLaneBasedObject()
466 {
467 return this.laneBasedObject;
468 }
469
470
471 @Override
472 public final int hashCode()
473 {
474 final int prime = 31;
475 int result = 1;
476 result = prime * result + ((this.distance == null) ? 0 : this.distance.hashCode());
477 result = prime * result + ((this.laneBasedObject == null) ? 0 : this.laneBasedObject.hashCode());
478 return result;
479 }
480
481
482 @Override
483 public final boolean equals(final Object obj)
484 {
485 if (this == obj)
486 {
487 return true;
488 }
489 if (obj == null)
490 {
491 return false;
492 }
493 if (getClass() != obj.getClass())
494 {
495 return false;
496 }
497 Entry<?> other = (Entry<?>) obj;
498 if (this.distance == null)
499 {
500 if (other.distance != null)
501 {
502 return false;
503 }
504 }
505 else if (!this.distance.equals(other.distance))
506 {
507 return false;
508 }
509 if (this.laneBasedObject == null)
510 {
511 if (other.laneBasedObject != null)
512 {
513 return false;
514 }
515 }
516
517 else if (!this.laneBasedObject.equals(other.laneBasedObject))
518 {
519 return false;
520 }
521 return true;
522 }
523
524
525 @Override
526 public final int compareTo(final Entry<T> arg)
527 {
528 int d = this.distance.compareTo(arg.distance);
529 if (d != 0 || this.laneBasedObject.equals(arg.laneBasedObject))
530 {
531 return d;
532 }
533 return 1;
534 }
535
536
537 @Override
538 public final String toString()
539 {
540 return "LaneStructure.Entry [distance=" + this.distance + ", laneBasedObject=" + this.laneBasedObject + "]";
541 }
542
543 }
544
545 }