1 package org.opentrafficsim.road.network.speed;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.List;
6 import java.util.SortedSet;
7 import java.util.TreeSet;
8
9 import org.djunits.unit.LengthUnit;
10 import org.djunits.value.vdouble.scalar.Acceleration;
11 import org.djunits.value.vdouble.scalar.Duration;
12 import org.djunits.value.vdouble.scalar.Length;
13 import org.djunits.value.vdouble.scalar.Speed;
14
15 import nl.tudelft.simulation.language.Throw;
16
17
18
19
20
21
22
23
24
25
26
27
28 public class SpeedLimitProspect implements Serializable
29 {
30
31
32 private static final long serialVersionUID = 20160501L;
33
34
35 private final SortedSet<SpeedLimitEntry<?>> prospect = new TreeSet<>();
36
37
38
39
40
41
42
43
44
45
46
47 public final <T> void addSpeedInfo(final Length distance, final SpeedLimitType<T> speedLimitType, final T speedInfo)
48 {
49 Throw.whenNull(distance, "Distance may not be null.");
50 Throw.whenNull(speedLimitType, "Speed limit type may not be null.");
51 Throw.whenNull(speedInfo, "Speed info may not be null.");
52 checkAndAdd(new SpeedLimitEntry<>(distance, speedLimitType, speedInfo));
53 }
54
55
56
57
58
59
60
61
62
63
64 @SuppressWarnings({"unchecked", "rawtypes"})
65 public final void removeSpeedInfo(final Length distance, final SpeedLimitType<?> speedLimitType)
66 {
67 Throw.whenNull(distance, "Distance may not be null.");
68 Throw.when(distance.si < 0, IllegalArgumentException.class, "Removing speed info in the past is not allowed. "
69 + "Only add still active speed info.");
70 Throw.whenNull(speedLimitType, "Speed limit type may not be null.");
71 Throw.when(speedLimitType.equals(SpeedLimitTypes.MAX_VEHICLE_SPEED), IllegalArgumentException.class,
72 "May not remove the maximum vehicle speed.");
73
74 checkAndAdd(new SpeedLimitEntry(distance, speedLimitType, null));
75 }
76
77
78
79
80
81
82 private void checkAndAdd(final SpeedLimitEntry<?> speedLimitEntry)
83 {
84 for (SpeedLimitEntry<?> s : this.prospect)
85 {
86 if (s.getSpeedLimitType().equals(speedLimitEntry.getSpeedLimitType()))
87 {
88
89
90
91
92 Throw.when(s.getDistance().equals(speedLimitEntry.getDistance()), IllegalStateException.class, "Info "
93 + "of speed limit type '%s' is set twice at the same location (%s). This is undefined. "
94 + "Either remove speed info, or overwrite with new speed info.", s.getSpeedLimitType(), s.getDistance());
95 }
96 }
97 this.prospect.add(speedLimitEntry);
98 }
99
100
101
102
103
104
105 public final List<Length> getDistances()
106 {
107 List<Length> list = new ArrayList<>();
108 for (SpeedLimitEntry<?> speedLimitEntry : this.prospect)
109 {
110 list.add(speedLimitEntry.getDistance());
111 }
112 return list;
113 }
114
115
116
117
118
119
120
121 public final List<Length> getDistances(final SpeedLimitType<?> speedLimitType)
122 {
123 return getDistancesInRange(speedLimitType, null, null);
124 }
125
126
127
128
129
130
131
132 public final List<Length> getUpstreamDistances(final SpeedLimitType<?> speedLimitType)
133 {
134 return getDistancesInRange(speedLimitType, null, Length.ZERO);
135 }
136
137
138
139
140
141
142
143 public final List<Length> getDownstreamDistances(final SpeedLimitType<?> speedLimitType)
144 {
145 return getDistancesInRange(speedLimitType, Length.ZERO, null);
146 }
147
148
149
150
151
152
153
154
155
156 private List<Length> getDistancesInRange(final SpeedLimitType<?> speedLimitType, final Length min, final Length max)
157 {
158 List<Length> list = new ArrayList<>();
159 for (SpeedLimitEntry<?> speedLimitEntry : this.prospect)
160 {
161 if (speedLimitEntry.getSpeedLimitType().equals(speedLimitType)
162 && (min == null || speedLimitEntry.getDistance().gt(min))
163 && (max == null || speedLimitEntry.getDistance().le(max)))
164 {
165 list.add(speedLimitEntry.getDistance());
166 }
167 }
168 return list;
169 }
170
171
172
173
174
175
176
177
178 public final boolean speedInfoChanged(final Length distance, final SpeedLimitType<?> speedLimitType)
179 {
180 Throw.whenNull(distance, "Distance may not be null.");
181 for (SpeedLimitEntry<?> sle : this.prospect)
182 {
183 if (sle.getDistance().eq(distance) && sle.getSpeedLimitType().equals(speedLimitType))
184 {
185 return true;
186 }
187 }
188 return false;
189 }
190
191
192
193
194
195
196
197
198
199
200 public final <T> T getSpeedInfoChange(final Length distance, final SpeedLimitType<T> speedLimitType)
201 {
202 for (SpeedLimitEntry<?> sle : this.prospect)
203 {
204 if (sle.getDistance().eq(distance) && sle.getSpeedLimitType().equals(speedLimitType))
205 {
206 @SuppressWarnings("unchecked")
207 T info = (T) sle.getSpeedInfo();
208 return info;
209 }
210 }
211 throw new IllegalArgumentException("Speed info of speed limit type '" + speedLimitType.getId()
212 + "' is requested at a distance '" + distance + "' where the info is not changed.");
213 }
214
215
216
217
218
219
220
221 public final SpeedLimitInfo getSpeedLimitInfo(final Length distance)
222 {
223 Throw.whenNull(distance, "Distance may not be null.");
224 SpeedLimitInfo speedLimitInfo = new SpeedLimitInfo();
225 for (SpeedLimitEntry<?> speedLimitEntry : this.prospect)
226 {
227
228 if (speedLimitEntry.getDistance().compareTo(distance) > 0)
229 {
230
231 return speedLimitInfo;
232 }
233
234 if (speedLimitEntry.getSpeedInfo() == null)
235 {
236 speedLimitInfo.removeSpeedInfo(speedLimitEntry.getSpeedLimitType());
237 }
238 else
239 {
240
241
242 setAsType(speedLimitInfo, speedLimitEntry);
243 }
244 }
245 return speedLimitInfo;
246 }
247
248
249
250
251
252
253
254
255
256 public final SpeedLimitInfo getSpeedLimitInfo(final Speed speed, final Acceleration acceleration, final Duration time)
257 {
258 Throw.whenNull(speed, "Speed may not be null.");
259 Throw.whenNull(acceleration, "Acceleration may not be null.");
260 Throw.whenNull(time, "Time may not be null.");
261 return getSpeedLimitInfo(new Length(speed.si * time.si + .5 * acceleration.si * time.si * time.si, LengthUnit.SI));
262 }
263
264
265
266
267
268
269
270
271
272
273 @SuppressWarnings("unchecked")
274 private <T> void setAsType(final SpeedLimitInfo speedLimitInfo, final SpeedLimitEntry<?> speedLimitEntry)
275 {
276 SpeedLimitType<T> speedLimitType = (SpeedLimitType<T>) speedLimitEntry.getSpeedLimitType();
277 T speedInfoOfType = (T) speedLimitEntry.getSpeedInfo();
278 speedLimitInfo.addSpeedInfo(speedLimitType, speedInfoOfType);
279 }
280
281
282
283
284
285
286
287
288
289 public final <T> SpeedLimitInfo buildSpeedLimitInfo(final Length distance, final SpeedLimitType<T> speedLimitType)
290 {
291 SpeedLimitInfo out = new SpeedLimitInfo();
292 out.addSpeedInfo(speedLimitType, getSpeedInfoChange(distance, speedLimitType));
293 for (SpeedLimitEntry<?> speedLimitEntry : this.prospect)
294 {
295 if (speedLimitEntry.getDistance().gt(distance))
296 {
297 break;
298 }
299 if (speedLimitEntry.getSpeedLimitType().equals(SpeedLimitTypes.MAX_VEHICLE_SPEED))
300 {
301 out.addSpeedInfo(SpeedLimitTypes.MAX_VEHICLE_SPEED, SpeedLimitTypes.MAX_VEHICLE_SPEED.getInfoClass().cast(
302 speedLimitEntry.getSpeedInfo()));
303 }
304 }
305 return out;
306 }
307
308
309 @Override
310 public final String toString()
311 {
312 StringBuilder stringBuilder = new StringBuilder("SpeedLimitProspect [");
313 String sep = "";
314 for (SpeedLimitEntry<?> sle : this.prospect)
315 {
316 stringBuilder.append(sep).append(sle.getDistance()).append(": ");
317 if (sle.getSpeedInfo() == null)
318 {
319 stringBuilder.append(sle.getSpeedLimitType().getId()).append("=END");
320 }
321 else
322 {
323 stringBuilder.append(sle.getSpeedLimitType().getId()).append("=");
324 stringBuilder.append(sle.getSpeedInfo());
325 }
326 sep = ", ";
327 }
328 stringBuilder.append("]");
329 return stringBuilder.toString();
330 }
331
332
333
334
335
336
337
338
339
340
341
342 private static class SpeedLimitEntry<T> implements Comparable<SpeedLimitEntry<?>>, Serializable
343 {
344
345
346 private static final long serialVersionUID = 20160501L;
347
348
349 private final Length distance;
350
351
352 private final SpeedLimitType<T> speedLimitType;
353
354
355 private final T speedInfo;
356
357
358
359
360
361
362
363 SpeedLimitEntry(final Length distance, final SpeedLimitType<T> speedLimitType, final T speedInfo)
364 {
365 this.distance = distance;
366 this.speedLimitType = speedLimitType;
367 this.speedInfo = speedInfo;
368 }
369
370
371
372
373
374 public final Length getDistance()
375 {
376 return this.distance;
377 }
378
379
380
381
382
383 public final SpeedLimitType<T> getSpeedLimitType()
384 {
385 return this.speedLimitType;
386 }
387
388
389
390
391
392 public final T getSpeedInfo()
393 {
394 return this.speedInfo;
395 }
396
397
398 @Override
399 public final int hashCode()
400 {
401 final int prime = 31;
402 int result = 1;
403 result = prime * result + this.distance.hashCode();
404 result = prime * result + this.speedInfo.hashCode();
405 result = prime * result + this.speedLimitType.hashCode();
406 return result;
407 }
408
409
410 @Override
411 public final boolean equals(final Object obj)
412 {
413 if (this == obj)
414 {
415 return true;
416 }
417 if (obj == null)
418 {
419 return false;
420 }
421 if (getClass() != obj.getClass())
422 {
423 return false;
424 }
425 SpeedLimitEntry<?> other = (SpeedLimitEntry<?>) obj;
426 if (!this.distance.equals(other.distance))
427 {
428 return false;
429 }
430 if (!this.speedLimitType.equals(other.speedLimitType))
431 {
432 return false;
433 }
434 if (this.speedInfo == null)
435 {
436 if (other.speedInfo != null)
437 {
438 return false;
439 }
440 }
441 else if (!this.speedInfo.equals(other.speedInfo))
442 {
443 return false;
444 }
445 return true;
446 }
447
448
449 @Override
450 public final int compareTo(final SpeedLimitEntry<?> speedLimitEntry)
451 {
452 if (this.equals(speedLimitEntry))
453 {
454 return 0;
455 }
456
457 int comp = this.distance.compareTo(speedLimitEntry.distance);
458 if (comp != 0)
459 {
460 return comp;
461 }
462
463 comp = this.speedLimitType.getId().compareTo(speedLimitEntry.speedLimitType.getId());
464 if (comp != 0)
465 {
466 return comp;
467 }
468
469
470 if (this.speedInfo == null)
471 {
472 if (speedLimitEntry.speedInfo == null)
473 {
474 return 0;
475 }
476 return -1;
477 }
478 else if (speedLimitEntry.speedInfo == null)
479 {
480 return 1;
481 }
482 return this.speedInfo.hashCode() < speedLimitEntry.speedInfo.hashCode() ? -1 : 1;
483 }
484
485
486 @Override
487 public final String toString()
488 {
489 return "SpeedLimitEntry [distance=" + this.distance + ", speedLimitType=" + this.speedLimitType + ", speedInfo="
490 + this.speedInfo + "]";
491 }
492
493 }
494
495 }