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