1 package org.opentrafficsim.road.gtu.lane.perception.categories.neighbors;
2
3 import java.util.Collection;
4 import java.util.Comparator;
5 import java.util.ConcurrentModificationException;
6 import java.util.Iterator;
7 import java.util.LinkedHashSet;
8 import java.util.Set;
9 import java.util.SortedMap;
10 import java.util.SortedSet;
11 import java.util.TreeMap;
12 import java.util.TreeSet;
13
14 import org.djunits.value.vdouble.scalar.Length;
15 import org.djunits.value.vdouble.scalar.Time;
16 import org.djutils.exceptions.Try;
17 import org.opentrafficsim.base.parameters.ParameterException;
18 import org.opentrafficsim.core.gtu.GTUException;
19 import org.opentrafficsim.core.gtu.RelativePosition;
20 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
21 import org.opentrafficsim.road.gtu.lane.perception.LaneStructureRecord;
22 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
23
24
25
26
27
28
29
30
31
32
33
34
35 public final class NeighborsUtil
36 {
37
38
39
40
41 private NeighborsUtil()
42 {
43
44 }
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public static SortedSet<DistanceGTU> getFirstDownstreamGTUs(final LaneStructureRecord startRecord,
59 final RelativePosition egoRelativePosition, final RelativePosition egoFrontPosition,
60 final RelativePosition.TYPE otherRelativePosition, final Time now) throws GTUException, ParameterException
61 {
62 SortedSet<DistanceGTU> headwaySet = new TreeSet<>();
63 Set<LaneStructureRecord> currentSet = new LinkedHashSet<>();
64 Set<LaneStructureRecord> nextSet = new LinkedHashSet<>();
65 Length dxSearch = egoRelativePosition.getDx();
66 Length dxHeadway = egoFrontPosition.getDx();
67 LaneStructureRecord record = startRecord;
68 branchUpstream(record, dxSearch, currentSet);
69
70 while (!currentSet.isEmpty())
71 {
72 Iterator<LaneStructureRecord> iterator = currentSet.iterator();
73 while (iterator.hasNext())
74 {
75 record = iterator.next();
76
77
78
79
80
81
82
83 LaneBasedGTU down = record.getLane().getGtuAhead(record.getStartDistance().neg().plus(dxSearch),
84 record.getDirection(), otherRelativePosition, now);
85 if (down != null)
86 {
87
88 headwaySet.add(new DistanceGTU(down,
89 record.getStartDistance().plus(down.position(record.getLane(), down.getRear())).minus(dxHeadway)));
90 }
91 else
92 {
93
94 for (LaneStructureRecord next : record.getNext())
95 {
96 nextSet.add(next);
97 }
98 }
99 }
100 currentSet = nextSet;
101 nextSet = new LinkedHashSet<>();
102 }
103 return headwaySet;
104 }
105
106
107
108
109
110
111
112
113 private static void branchUpstream(final LaneStructureRecord record, final Length dx, final Set<LaneStructureRecord> set)
114 {
115 Length pos = record.getStartDistance().neg().minus(dx);
116 if (pos.lt0() && !record.getPrev().isEmpty())
117 {
118 for (LaneStructureRecord prev : record.getPrev())
119 {
120 branchUpstream(prev, dx, set);
121 }
122 }
123 else
124 {
125 set.add(record);
126 }
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140
141 public static SortedSet<DistanceGTU> getFirstUpstreamGTUs(final LaneStructureRecord startRecord,
142 final RelativePosition egoRelativePosition, final RelativePosition egoRearPosition,
143 final RelativePosition.TYPE otherRelativePosition, final Time now) throws GTUException, ParameterException
144 {
145 SortedSet<DistanceGTU> headwaySet = new TreeSet<>();
146 Set<LaneStructureRecord> currentSet = new LinkedHashSet<>();
147 Set<LaneStructureRecord> prevSet = new LinkedHashSet<>();
148 Length dxSearch = egoRelativePosition.getDx();
149 Length dxHeadway = egoRearPosition.getDx();
150 LaneStructureRecord record = startRecord;
151 branchDownstream(record, dxSearch, currentSet);
152
153 while (!currentSet.isEmpty())
154 {
155 Iterator<LaneStructureRecord> iterator = currentSet.iterator();
156 while (iterator.hasNext())
157 {
158 record = iterator.next();
159
160
161
162
163
164
165
166 LaneBasedGTU up = record.getLane().getGtuBehind(record.getStartDistance().neg().plus(dxSearch),
167 record.getDirection(), otherRelativePosition, now);
168 if (up != null)
169 {
170
171 headwaySet.add(new DistanceGTU(up, record.getStartDistance().neg()
172 .minus(up.position(record.getLane(), up.getFront())).plus(dxHeadway)));
173 }
174 else
175 {
176
177 for (LaneStructureRecord prev : record.getPrev())
178 {
179 prevSet.add(prev);
180 }
181 }
182 }
183 currentSet = prevSet;
184 prevSet = new LinkedHashSet<>();
185 }
186 return headwaySet;
187 }
188
189
190
191
192
193
194
195
196 private static void branchDownstream(final LaneStructureRecord record, final Length dx, final Set<LaneStructureRecord> set)
197 {
198 Length pos = record.getStartDistance().neg().plus(dx);
199 if (pos.gt(record.getLane().getLength()))
200 {
201 for (LaneStructureRecord next : record.getNext())
202 {
203 branchDownstream(next, dx, set);
204 }
205 }
206 else
207 {
208 set.add(record);
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221 public static SortedSet<HeadwayGTU> perceive(final SortedSet<DistanceGTU> base, final HeadwayGtuType headwayGtuType,
222 final LaneBasedGTU perceivingGtu, final boolean downstream)
223 {
224 return new SortedNeighborsSet(base, headwayGtuType, perceivingGtu, downstream);
225 }
226
227
228
229
230
231
232
233
234
235
236
237
238
239 public static class DistanceGTU implements Comparable<DistanceGTU>
240 {
241
242
243 private LaneBasedGTU gtu;
244
245
246 private Length distance;
247
248
249
250
251
252
253 DistanceGTU(final LaneBasedGTU gtu, final Length distance)
254 {
255 this.gtu = gtu;
256 this.distance = distance;
257 }
258
259
260
261
262
263 public LaneBasedGTU getGTU()
264 {
265 return this.gtu;
266 }
267
268
269
270
271
272 public Length getDistance()
273 {
274 return this.distance;
275 }
276
277
278 @Override
279 public int compareTo(final DistanceGTU o)
280 {
281 return this.distance.compareTo(o.distance);
282 }
283 }
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298 private static class SortedNeighborsSet implements SortedSet<HeadwayGTU>
299 {
300
301
302 private final SortedSet<DistanceGTU> base;
303
304
305 private final HeadwayGtuType headwayGtuType;
306
307
308 private final LaneBasedGTU perceivingGtu;
309
310
311 private final boolean downstream;
312
313
314 private final SortedMap<String, HeadwayGTU> all = new TreeMap<>();
315
316
317
318
319
320
321
322
323 SortedNeighborsSet(final SortedSet<DistanceGTU> base, final HeadwayGtuType headwayGtuType,
324 final LaneBasedGTU perceivingGtu, final boolean downstream)
325 {
326 this.base = base;
327 this.headwayGtuType = headwayGtuType;
328 this.perceivingGtu = perceivingGtu;
329 this.downstream = downstream;
330 }
331
332
333 @Override
334 public int size()
335 {
336 return this.base.size();
337 }
338
339
340 @Override
341 public boolean isEmpty()
342 {
343 return this.base.isEmpty();
344 }
345
346
347
348
349 private void getAll()
350 {
351 Iterator<HeadwayGTU> it = iterator();
352 while (it.hasNext())
353 {
354 @SuppressWarnings("unused")
355 HeadwayGTU gtu = it.next();
356 }
357 }
358
359
360 @Override
361 public boolean contains(final Object o)
362 {
363 getAll();
364 return this.all.containsValue(o);
365 }
366
367
368 @Override
369 public Iterator<HeadwayGTU> iterator()
370 {
371 return new Iterator<HeadwayGTU>()
372 {
373 @SuppressWarnings("synthetic-access")
374 private Iterator<DistanceGTU> it = SortedNeighborsSet.this.base.iterator();
375
376 @Override
377 public boolean hasNext()
378 {
379 return this.it.hasNext();
380 }
381
382 @SuppressWarnings("synthetic-access")
383 @Override
384 public HeadwayGTU next()
385 {
386 DistanceGTU next = this.it.next();
387 if (next == null)
388 {
389 throw new ConcurrentModificationException();
390 }
391 HeadwayGTU out = SortedNeighborsSet.this.all.get(next.getGTU().getId());
392 if (out == null)
393 {
394 out = Try.assign(() -> SortedNeighborsSet.this.headwayGtuType.createHeadwayGtu(
395 SortedNeighborsSet.this.perceivingGtu, next.getGTU(), next.getDistance(),
396 SortedNeighborsSet.this.downstream), "Exception while perceiving a neighbor.");
397 SortedNeighborsSet.this.all.put(next.getGTU().getId(), out);
398 }
399 return out;
400 }
401 };
402 }
403
404
405 @Override
406 public Object[] toArray()
407 {
408 getAll();
409 return this.all.values().toArray();
410 }
411
412
413 @Override
414 public <T> T[] toArray(final T[] a)
415 {
416 getAll();
417 return this.all.values().toArray(a);
418 }
419
420
421 @Override
422 public boolean add(final HeadwayGTU e)
423 {
424 throw new UnsupportedOperationException();
425 }
426
427
428 @Override
429 public boolean remove(final Object o)
430 {
431 throw new UnsupportedOperationException();
432 }
433
434
435 @Override
436 public boolean containsAll(final Collection<?> c)
437 {
438 getAll();
439 return this.all.values().containsAll(c);
440 }
441
442
443 @Override
444 public boolean addAll(final Collection<? extends HeadwayGTU> c)
445 {
446 throw new UnsupportedOperationException();
447 }
448
449
450 @Override
451 public boolean retainAll(final Collection<?> c)
452 {
453 throw new UnsupportedOperationException();
454 }
455
456
457 @Override
458 public boolean removeAll(final Collection<?> c)
459 {
460 throw new UnsupportedOperationException();
461 }
462
463
464 @Override
465 public void clear()
466 {
467 throw new UnsupportedOperationException();
468 }
469
470
471 @Override
472 public Comparator<? super HeadwayGTU> comparator()
473 {
474 return null;
475 }
476
477
478
479
480
481
482 private DistanceGTU getGTU(final HeadwayGTU element)
483 {
484 for (DistanceGTU distanceGtu : this.base)
485 {
486 if (distanceGtu.getGTU().getId().equals(element.getId()))
487 {
488 return distanceGtu;
489 }
490 }
491 throw new IllegalArgumentException("GTU used to obtain a subset is not in the set.");
492 }
493
494
495 @Override
496 public SortedSet<HeadwayGTU> subSet(final HeadwayGTU fromElement, final HeadwayGTU toElement)
497 {
498 return new SortedNeighborsSet(this.base.subSet(getGTU(fromElement), getGTU(toElement)), this.headwayGtuType,
499 this.perceivingGtu, this.downstream);
500 }
501
502
503 @Override
504 public SortedSet<HeadwayGTU> headSet(final HeadwayGTU toElement)
505 {
506 return new SortedNeighborsSet(this.base.headSet(getGTU(toElement)), this.headwayGtuType, this.perceivingGtu,
507 this.downstream);
508 }
509
510
511 @Override
512 public SortedSet<HeadwayGTU> tailSet(final HeadwayGTU fromElement)
513 {
514 return new SortedNeighborsSet(this.base.tailSet(getGTU(fromElement)), this.headwayGtuType, this.perceivingGtu,
515 this.downstream);
516 }
517
518
519 @Override
520 public HeadwayGTU first()
521 {
522 return iterator().next();
523 }
524
525
526 @Override
527 public HeadwayGTU last()
528 {
529 getAll();
530 return this.all.get(this.all.lastKey());
531 }
532
533 }
534
535 }