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
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 public class LaneStructure implements Serializable
87 {
88
89 private static final long serialVersionUID = 20160400L;
90
91
92 private LaneStructureRecord rootLSR;
93
94
95 private Length lookAhead;
96
97
98 private TreeMap<RelativeLane, LaneStructureRecord> crossSectionRecords = new TreeMap<>();
99
100
101 private final Map<RelativeLane, Set<LaneStructureRecord>> relativeLaneMap = new HashMap<>();
102
103
104
105
106
107 public LaneStructure(final LaneStructureRecord rootLSR, final Length lookAhead)
108 {
109 this.rootLSR = rootLSR;
110 this.lookAhead = lookAhead;
111 }
112
113
114
115
116 public final LaneStructureRecord getRootLSR()
117 {
118 return this.rootLSR;
119 }
120
121
122
123
124
125 public final SortedSet<RelativeLane> getCrossSection()
126 {
127 return this.crossSectionRecords.navigableKeySet();
128 }
129
130
131
132
133
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
144
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
161
162
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
179
180
181
182
183
184
185
186
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
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
215 set.add(new Entry<>(distance, (T) object));
216 }
217 }
218 getDownstreamObjectsRecursive(set, record, clazz, ds);
219 return set;
220 }
221
222
223
224
225
226
227
228
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
247 set.add(new Entry<>(distance, (T) object));
248 }
249 }
250 getDownstreamObjectsRecursive(set, next, clazz, ds);
251 }
252 }
253
254
255
256
257
258
259
260
261
262
263
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
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
292 set.add(new Entry<>(distance, (T) object));
293 }
294 }
295 getUpstreamObjectsRecursive(set, record, clazz, ds);
296 return set;
297 }
298
299
300
301
302
303
304
305
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
320 set.add(new Entry<>(distance, (T) object));
321 }
322 }
323 getUpstreamObjectsRecursive(set, prev, clazz, ds);
324 }
325 }
326
327
328 @Override
329 public final String toString()
330 {
331 return "LaneStructure [rootLSR=" + this.rootLSR + "]";
332 }
333
334
335
336
337
338
339
340
341
342
343
344
345
346 public class Entry<T extends LaneBasedObject> implements Comparable<Entry<T>>
347 {
348
349
350 private final Length distance;
351
352
353 private final T laneBasedObject;
354
355
356
357
358
359 public Entry(final Length distance, final T laneBasedObject)
360 {
361 this.distance = distance;
362 this.laneBasedObject = laneBasedObject;
363 }
364
365
366
367
368 public final Length getDistance()
369 {
370 return this.distance;
371 }
372
373
374
375
376 public final T getLaneBasedObject()
377 {
378 return this.laneBasedObject;
379 }
380
381
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
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
428 else if (!this.laneBasedObject.equals(other.laneBasedObject))
429 {
430 return false;
431 }
432 return true;
433 }
434
435
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;
443 }
444 return 1;
445 }
446
447
448 @Override
449 public final String toString()
450 {
451 return "LaneStructure.Entry [distance=" + this.distance + ", laneBasedObject=" + this.laneBasedObject + "]";
452 }
453
454 }
455
456 }