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.core.network.route.Route;
18 import org.opentrafficsim.road.network.lane.CrossSectionLink;
19 import org.opentrafficsim.road.network.lane.Lane;
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().le(Length.ZERO) && lsr.getStartDistance().plus(lsr.getLane().getLength()).ge(Length.ZERO)
177 && !this.crossSectionRecords.containsKey(relativeLane))
178 {
179 this.crossSectionRecords.put(relativeLane, lsr);
180 }
181 }
182
183
184
185
186
187
188
189
190
191
192
193
194 @SuppressWarnings("unchecked")
195 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjects(final RelativeLane lane,
196 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
197 {
198 LaneStructureRecord record = this.getLaneLSR(lane);
199 Length ds = gtu.getRelativePositions().get(pos).getDx().minus(gtu.getReference().getDx());
200
201 Length minimumPosition;
202 Length maximumPosition;
203 if (record.getDirection().isPlus())
204 {
205 minimumPosition = ds.minus(record.getStartDistance());
206 maximumPosition = record.getLane().getLength();
207 }
208 else
209 {
210 minimumPosition = Length.ZERO;
211 maximumPosition = record.getLane().getLength().plus(record.getStartDistance()).minus(ds);
212 }
213 SortedSet<Entry<T>> set = new TreeSet<>();
214 Length distance;
215 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
216 {
217 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
218 if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead)
219 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
220 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
221 {
222
223 set.add(new Entry<>(distance, (T) object));
224 }
225 }
226 getDownstreamObjectsRecursive(set, record, clazz, ds);
227 return set;
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjectsOnRoute(final RelativeLane lane,
243 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos, final Route route) throws GTUException
244 {
245 SortedSet<Entry<T>> set = getDownstreamObjects(lane, clazz, gtu, pos);
246 if (route != null)
247 {
248 Iterator<Entry<T>> iterator = set.iterator();
249 while (iterator.hasNext())
250 {
251 Entry<T> entry = iterator.next();
252 CrossSectionLink link = entry.getLaneBasedObject().getLane().getParentLink();
253 if (!route.contains(link.getStartNode()) || !route.contains(link.getEndNode())
254 || Math.abs(route.indexOf(link.getStartNode()) - route.indexOf(link.getEndNode())) != 1)
255 {
256 iterator.remove();
257 }
258 }
259 }
260 return set;
261 }
262
263
264
265
266
267
268
269
270
271 @SuppressWarnings("unchecked")
272 private <T extends LaneBasedObject> void getDownstreamObjectsRecursive(final SortedSet<Entry<T>> set,
273 final LaneStructureRecord record, final Class<T> clazz, final Length ds)
274 {
275 if (record.getNext().isEmpty() || record.getNext().get(0).getStartDistance().gt(this.lookAhead))
276 {
277 return;
278 }
279 for (LaneStructureRecord next : record.getNext())
280 {
281 Length distance;
282 for (LaneBasedObject object : next.getLane().getLaneBasedObjects())
283 {
284 distance = next.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
285 if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead)
286 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
287 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
288 {
289
290 set.add(new Entry<>(distance, (T) object));
291 }
292 }
293 getDownstreamObjectsRecursive(set, next, clazz, ds);
294 }
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308 @SuppressWarnings("unchecked")
309 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getUpstreamObjects(final RelativeLane lane,
310 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
311 {
312 LaneStructureRecord record = this.getLaneLSR(lane);
313 Length ds = gtu.getReference().getDx().minus(gtu.getRelativePositions().get(pos).getDx());
314
315 Length minimumPosition;
316 Length maximumPosition;
317 if (record.getDirection().isPlus())
318 {
319 minimumPosition = Length.ZERO;
320 maximumPosition = record.getStartDistance().neg().minus(ds);
321 }
322 else
323 {
324 minimumPosition = record.getLane().getLength().plus(record.getStartDistance()).plus(ds);
325 maximumPosition = record.getLane().getLength();
326 }
327 SortedSet<Entry<T>> set = new TreeSet<>();
328 Length distance;
329 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
330 {
331 if (clazz.isAssignableFrom(object.getClass())
332 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
333 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
334 {
335 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
336
337 set.add(new Entry<>(distance, (T) object));
338 }
339 }
340 getUpstreamObjectsRecursive(set, record, clazz, ds);
341 return set;
342 }
343
344
345
346
347
348
349
350
351
352 @SuppressWarnings("unchecked")
353 private <T extends LaneBasedObject> void getUpstreamObjectsRecursive(final SortedSet<Entry<T>> set,
354 final LaneStructureRecord record, final Class<T> clazz, final Length ds)
355 {
356 for (LaneStructureRecord prev : record.getPrev())
357 {
358 Length distance;
359 for (LaneBasedObject object : prev.getLane().getLaneBasedObjects())
360 {
361 if (clazz.isAssignableFrom(object.getClass())
362 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
363 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
364 {
365 distance = prev.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
366
367 set.add(new Entry<>(distance, (T) object));
368 }
369 }
370 getUpstreamObjectsRecursive(set, prev, clazz, ds);
371 }
372 }
373
374
375 @Override
376 public final String toString()
377 {
378 return "LaneStructure [rootLSR=" + this.rootLSR + "]";
379 }
380
381
382
383
384
385
386
387
388
389
390
391
392
393 public static class Entry<T extends LaneBasedObject> implements Comparable<Entry<T>>
394 {
395
396
397 private final Length distance;
398
399
400 private final T laneBasedObject;
401
402
403
404
405
406 public Entry(final Length distance, final T laneBasedObject)
407 {
408 this.distance = distance;
409 this.laneBasedObject = laneBasedObject;
410 }
411
412
413
414
415 public final Length getDistance()
416 {
417 return this.distance;
418 }
419
420
421
422
423 public final T getLaneBasedObject()
424 {
425 return this.laneBasedObject;
426 }
427
428
429 @Override
430 public final int hashCode()
431 {
432 final int prime = 31;
433 int result = 1;
434 result = prime * result + ((this.distance == null) ? 0 : this.distance.hashCode());
435 result = prime * result + ((this.laneBasedObject == null) ? 0 : this.laneBasedObject.hashCode());
436 return result;
437 }
438
439
440 @Override
441 public final boolean equals(final Object obj)
442 {
443 if (this == obj)
444 {
445 return true;
446 }
447 if (obj == null)
448 {
449 return false;
450 }
451 if (getClass() != obj.getClass())
452 {
453 return false;
454 }
455 Entry<?> other = (Entry<?>) obj;
456 if (this.distance == null)
457 {
458 if (other.distance != null)
459 {
460 return false;
461 }
462 }
463 else if (!this.distance.equals(other.distance))
464 {
465 return false;
466 }
467 if (this.laneBasedObject == null)
468 {
469 if (other.laneBasedObject != null)
470 {
471 return false;
472 }
473 }
474
475 else if (!this.laneBasedObject.equals(other.laneBasedObject))
476 {
477 return false;
478 }
479 return true;
480 }
481
482
483 @Override
484 public final int compareTo(final Entry<T> arg)
485 {
486 int d = this.distance.compareTo(arg.distance);
487 if (d != 0 || this.laneBasedObject.equals(arg.laneBasedObject))
488 {
489 return d;
490 }
491 return 1;
492 }
493
494
495 @Override
496 public final String toString()
497 {
498 return "LaneStructure.Entry [distance=" + this.distance + ", laneBasedObject=" + this.laneBasedObject + "]";
499 }
500
501 }
502
503 }