View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perception.object;
2   
3   import java.util.Optional;
4   
5   import org.djunits.value.vdouble.scalar.Acceleration;
6   import org.djunits.value.vdouble.scalar.Length;
7   import org.djunits.value.vdouble.scalar.Speed;
8   import org.djutils.base.Identifiable;
9   import org.djutils.exceptions.Throw;
10  
11  /**
12   * Interface for perceived objects including kinematics. Kinematics describe either a static or dynamic object at a certain
13   * distance of, or as adjacent to, a reference object (e.g. the perceiving GTU or a conflict).
14   * <p>
15   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
16   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
17   * </p>
18   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
19   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
20   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
21   */
22  public interface PerceivedObject extends Identifiable, Comparable<PerceivedObject>
23  {
24  
25      /**
26       * Returns object type.
27       * @return the (perceived) object Type
28       */
29      ObjectType getObjectType();
30  
31      /**
32       * Returns length.
33       * @return the length of the other object
34       */
35      Length getLength();
36  
37      /**
38       * Returns information on the kinematics of the perceived object. This includes position, speed, acceleration and overlap.
39       * @return information on the kinematics of the perceived object
40       */
41      Kinematics getKinematics();
42  
43      /**
44       * Returns the distance from the kinematics.
45       * @return the distance from the kinematics
46       */
47      default Length getDistance()
48      {
49          return getKinematics().getDistance();
50      }
51  
52      /**
53       * Returns the speed from the kinematics.
54       * @return the speed from the kinematics
55       */
56      default Speed getSpeed()
57      {
58          return getKinematics().getSpeed();
59      }
60  
61      /**
62       * Returns the acceleration from the kinematics.
63       * @return the acceleration from the kinematics
64       */
65      default Acceleration getAcceleration()
66      {
67          return getKinematics().getAcceleration();
68      }
69  
70      /**
71       * Type of object.
72       */
73      enum ObjectType
74      {
75          /** The observed object for headway is a GTU. */
76          GTU,
77  
78          /** The observed object for headway is a traffic light. */
79          TRAFFICLIGHT,
80  
81          /** The observed object for headway is a generic object. */
82          OBJECT,
83  
84          /** There is no observed object, just a distance. */
85          DISTANCEONLY,
86  
87          /** Intersection conflict. */
88          CONFLICT,
89  
90          /** Stop line. */
91          STOPLINE,
92  
93          /** Bus stop. */
94          BUSSTOP;
95  
96          /**
97           * Returns whether this object is a GTU or not.
98           * @return whether this object is a GTU or not.
99           */
100         public boolean isGtu()
101         {
102             return this.equals(GTU);
103         }
104 
105         /**
106          * Returns whether this object is a GTU or not.
107          * @return whether this object is a GTU or not.
108          */
109         public boolean isTrafficLight()
110         {
111             return this.equals(TRAFFICLIGHT);
112         }
113 
114         /**
115          * Returns whether this object is an object or not.
116          * @return whether this object is an object or not.
117          */
118         public boolean isObject()
119         {
120             return this.equals(OBJECT);
121         }
122 
123         /**
124          * Returns whether no object was observed and only a distance was stored.
125          * @return whether no object was observed and only a distance was stored.
126          */
127         public boolean isDistanceOnly()
128         {
129             return this.equals(DISTANCEONLY);
130         }
131 
132         /**
133          * Returns whether this object is a conflict or not.
134          * @return whether this object is a conflict or not.
135          */
136         public boolean isConflict()
137         {
138             return this.equals(CONFLICT);
139         }
140 
141         /**
142          * Returns whether this object is a stop line or not.
143          * @return whether this object is a stop line or not.
144          */
145         public boolean isStopLine()
146         {
147             return this.equals(STOPLINE);
148         }
149 
150         /**
151          * Returns whether this object is a bus stop or not.
152          * @return whether this object is a bus stop or not.
153          */
154         public boolean isBusStop()
155         {
156             return this.equals(BUSSTOP);
157         }
158     }
159 
160     /**
161      * Information on the kinematics of the perceived object.
162      */
163     interface Kinematics
164     {
165         /**
166          * Retrieve the strongly typed distance to the other object.
167          * @return the distance to the object
168          */
169         Length getDistance();
170 
171         /**
172          * Returns speed.
173          * @return the (perceived) speed of the other object
174          */
175         Speed getSpeed();
176 
177         /**
178          * Returns acceleration.
179          * @return the (perceived) acceleration of the other object
180          */
181         Acceleration getAcceleration();
182 
183         /**
184          * Returns whether the object is facing the same direction.
185          * @return whether the object is facing the same direction
186          */
187         boolean isFacingSameDirection();
188 
189         /**
190          * Returns information on the overlap for parallel objects. For objects fully ahead or behind that fact is provided,
191          * with {@code null} overlap values.
192          * @return information on the overlap for parallel objects
193          */
194         Overlap getOverlap();
195 
196         /**
197          * Return kinematics for a static object at given distance ahead. Overlap is considered non-existent. The object is
198          * considered to face the same direction, which might not mean much for a static object.
199          * @param distance distance to object
200          * @return kinematics for a static object at given distance
201          */
202         static Kinematics staticAhead(final Length distance)
203         {
204             return new Record(distance, Speed.ZERO, Acceleration.ZERO, true, Overlap.AHEAD);
205         }
206 
207         /**
208          * Return kinematics for a static object at given distance behind. Overlap is considered non-existent. The object is
209          * considered to face the same direction, which might not mean much for a static object.
210          * @param distance distance to object
211          * @return kinematics for a static object at given distance
212          */
213         static Kinematics staticBehind(final Length distance)
214         {
215             return new Record(distance, Speed.ZERO, Acceleration.ZERO, true, Overlap.BEHIND);
216         }
217 
218         /**
219          * Return kinematics for a dynamic object ahead. The distance may be negative up to an absolute value equal to the
220          * object length plus the ego length.
221          * @param distance distance from ego front to object rear (or front when not facing the same direction)
222          * @param objectSpeed speed of perceived object
223          * @param objectAcceleration acceleration of perceived object
224          * @param facingSameDirection whether the object is facing the same direction
225          * @param objectLength object length
226          * @param referenceLength length of reference object, usually the perceiving GTU
227          * @return kinematic for a dynamic object
228          * @throws IllegalArgumentException when the distance beyond the extent of object length plus reference length
229          */
230         static Kinematics dynamicAhead(final Length distance, final Speed objectSpeed, final Acceleration objectAcceleration,
231                 final boolean facingSameDirection, final Length objectLength, final Length referenceLength)
232         {
233             Throw.whenNull(distance, "distance");
234             Throw.whenNull(objectLength, "objectLength");
235             Throw.whenNull(referenceLength, "referenceLength");
236             Throw.when(distance.si < 0.0 && -distance.si > objectLength.si + referenceLength.si, IllegalArgumentException.class,
237                     "Distance is negative beyond the combined length of perceived object and ego.");
238             Overlap overlap;
239             if (distance.ge0())
240             {
241                 overlap = Overlap.AHEAD;
242             }
243             else
244             {
245                 Length overlapRear = distance.plus(referenceLength);
246                 Length overlapVal = distance.neg();
247                 Length overlapFront = distance.plus(objectLength);
248                 if (overlapRear.lt0())
249                 {
250                     overlapVal = overlapVal.plus(overlapRear);
251                 }
252                 if (overlapFront.lt0())
253                 {
254                     overlapVal = overlapVal.plus(overlapFront);
255                 }
256                 overlap = new Overlap.Record(overlapVal, overlapFront, overlapRear, false, false);
257             }
258             return new Record(distance, objectSpeed, objectAcceleration, facingSameDirection, overlap);
259         }
260 
261         /**
262          * Return kinematics for a dynamic object behind. The distance may be negative up to an absolute value equal to the
263          * object length plus the ego length.
264          * @param distance distance from ego front to object rear (or front when not facing the same direction)
265          * @param objectSpeed speed of perceived object
266          * @param objectAcceleration acceleration of perceived object
267          * @param facingSameDirection whether the object is facing the same direction
268          * @param objectLength object length
269          * @param referenceLength length of reference object, usually the perceiving GTU
270          * @return kinematic for a dynamic object
271          * @throws IllegalArgumentException when the distance beyond the extent of object length plus reference length
272          */
273         static Kinematics dynamicBehind(final Length distance, final Speed objectSpeed, final Acceleration objectAcceleration,
274                 final boolean facingSameDirection, final Length objectLength, final Length referenceLength)
275         {
276             Throw.whenNull(distance, "distance");
277             Throw.whenNull(objectLength, "objectLength");
278             Throw.whenNull(referenceLength, "referenceLength");
279             Throw.when(distance.si < 0.0 && -distance.si > objectLength.si + referenceLength.si, IllegalArgumentException.class,
280                     "Distance is negative beyond the combined length of perceived object and ego.");
281             Overlap overlap;
282             if (distance.ge0())
283             {
284                 overlap = Overlap.BEHIND;
285             }
286             else
287             {
288                 Length overlapRear = distance.plus(objectLength).neg();
289                 Length overlapVal = distance.neg();
290                 Length overlapFront = distance.plus(referenceLength).neg();
291                 if (overlapRear.gt0())
292                 {
293                     overlapVal = overlapVal.minus(overlapRear);
294                 }
295                 if (overlapFront.gt0())
296                 {
297                     overlapVal = overlapVal.minus(overlapFront);
298                 }
299                 overlap = new Overlap.Record(overlapVal, overlapFront, overlapRear, false, false);
300             }
301             return new Record(distance, objectSpeed, objectAcceleration, facingSameDirection, overlap);
302         }
303 
304         /**
305          * Record storing kinematics information.
306          * @param getDistance distance
307          * @param getSpeed speed
308          * @param getAcceleration acceleration
309          * @param isFacingSameDirection whether the object is facing the same direction
310          * @param getOverlap overlap
311          */
312         record Record(Length getDistance, Speed getSpeed, Acceleration getAcceleration, boolean isFacingSameDirection,
313                 Overlap getOverlap) implements Kinematics
314         {
315             /**
316              * Null checks.
317              * @param getDistance distance
318              * @param getSpeed speed
319              * @param getAcceleration acceleration
320              * @param isFacingSameDirection whether the object is facing the same direction
321              * @param getOverlap overlap
322              */
323             public Record
324             {
325                 Throw.whenNull(getDistance, "getDistance");
326                 Throw.whenNull(getSpeed, "getSpeed");
327                 Throw.whenNull(getAcceleration, "getAcceleration");
328                 Throw.whenNull(getOverlap, "getOverlap");
329             }
330         }
331 
332         /**
333          * Description of overlap information. If the object is fully ahead or behind, overlap values are {@code null}.
334          */
335         interface Overlap
336         {
337             /** Overlap information for objects ahead. */
338             Overlap AHEAD = new Record((Length) null, null, null, true, false);
339 
340             /** Overlap information for objects behind. */
341             Overlap BEHIND = new Record((Length) null, null, null, false, true);
342 
343             /**
344              * Return the (perceived) overlap with the other object. This value should be null if there is no overlap. In the
345              * figure below for two GTUs, it is distance b, positive for GTU1 and GTU2.
346              *
347              * <pre>
348              * ----------
349              * |  GTU 1 |          -----&gt;
350              * ----------
351              *      ---------------
352              *      |    GTU 2    |          -----&gt;
353              *      ---------------
354              * | a  | b |     c   |
355              * </pre>
356              *
357              * @return the (perceived) overlap with the other object or empty if there is no overlap
358              */
359             Optional<Length> getOverlap();
360 
361             /**
362              * Return the (perceived) front overlap to the other object. This value should be null if there is no overlap. In
363              * the figure for two GTUs below, it is distance c, positive for GTU1, negative for GTU2.
364              *
365              * <pre>
366              * ----------
367              * |  GTU 1 |          -----&gt;
368              * ----------
369              *      ---------------
370              *      |    GTU 2    |          -----&gt;
371              *      ---------------
372              * | a  | b |     c   |
373              * </pre>
374              *
375              * @return the (perceived) front overlap to the other object or empty if there is no overlap
376              */
377             Optional<Length> getOverlapFront();
378 
379             /**
380              * Return the (perceived) rear overlap to the other object. This value should be null if there is no overlap.In the
381              * figure below for two GTUs, it is distance a, positive for GTU1, negative for GTU2.
382              *
383              * <pre>
384              * ----------
385              * |  GTU 1 |          -----&gt;
386              * ----------
387              *      ---------------
388              *      |    GTU 2    |          -----&gt;
389              *      ---------------
390              * | a  | b |     c   |
391              * </pre>
392              *
393              * @return the (perceived) rear overlap to the other object or empty if there is no overlap
394              */
395             Optional<Length> getOverlapRear();
396 
397             /**
398              * Returns whether the object is fully ahead.
399              * @return whether the other object is in front of the reference object
400              */
401             boolean isAhead();
402 
403             /**
404              * Returns whether the object is fully behind.
405              * @return whether the other object is behind the reference object
406              */
407             boolean isBehind();
408 
409             /**
410              * Returns whether the object is parallel, partially or fully.
411              * @return whether the other object is parallel the reference object
412              */
413             default boolean isParallel()
414             {
415                 return getOverlap().isPresent();
416             }
417 
418             /**
419              * Record storing overlap information. The three overlap values are either all {@code null} or they all have a
420              * value. In the former case, either of {@code isAhead} and {@code isBehind} is true.
421              * @param getOverlap overlap
422              * @param getOverlapFront front overlap
423              * @param getOverlapRear rear overlap
424              * @param isAhead whether the object is ahead
425              * @param isBehind whether the object is behind
426              */
427             record Record(Optional<Length> getOverlap, Optional<Length> getOverlapFront, Optional<Length> getOverlapRear,
428                     boolean isAhead, boolean isBehind) implements Overlap
429             {
430 
431                 /**
432                  * Constructor.
433                  * @param getOverlap overlap
434                  * @param getOverlapFront front overlap
435                  * @param getOverlapRear rear overlap
436                  * @param isAhead whether the object is ahead
437                  * @param isBehind whether the object is behind
438                  * @throws NullPointerException when getOverlapFront or getOverlapRear is null while getOverlap is not
439                  * @throws IllegalArgumentException when getOverlapFront or getOverlapRear is not null while getOverlap is null
440                  * @throws IllegalArgumentException if isAhead or isBehind is true while overlap is specified
441                  */
442                 public Record(final Length getOverlap, final Length getOverlapFront, final Length getOverlapRear,
443                         final boolean isAhead, final boolean isBehind)
444                 {
445                     this(Optional.ofNullable(getOverlap), Optional.ofNullable(getOverlapFront),
446                             Optional.ofNullable(getOverlapRear), isAhead, isBehind);
447                     if (getOverlap == null)
448                     {
449                         Throw.when(getOverlapFront != null, IllegalArgumentException.class,
450                                 "getOverlapFront is not null while getOverlap is null.");
451                         Throw.when(getOverlapRear != null, IllegalArgumentException.class,
452                                 "getOverlapRear is not null while getOverlap is null.");
453                         Throw.when(isAhead == isBehind, IllegalArgumentException.class,
454                                 "if getOverlap is null either of isAhead or isBehind, but not both, should be true.");
455                     }
456                     else
457                     {
458                         Throw.whenNull(getOverlapFront, "getOverlapFront is null while getOverlap is not null.");
459                         Throw.whenNull(getOverlapRear, "getOverlapRear is null while getOverlap is not null.");
460                         Throw.when(isAhead, IllegalArgumentException.class,
461                                 "if getOverlap is not null isAhead should be false.");
462                         Throw.when(isBehind, IllegalArgumentException.class,
463                                 "if getOverlap is not null isBehind should be false.");
464                     }
465                 }
466             }
467         }
468     }
469 
470     @Override
471     default int compareTo(final PerceivedObject headway)
472     {
473         return getKinematics().getDistance().compareTo(headway.getKinematics().getDistance());
474     }
475 }