1 package org.opentrafficsim.road.gtu.lane.perception.headway;
2
3 import org.djunits.value.vdouble.scalar.Length;
4 import org.djutils.exceptions.Throw;
5 import org.opentrafficsim.core.gtu.GtuException;
6
7 /**
8 * Super class for non-delayed and non-erroneous perception. Sub classes should wrap the actual simulation object to obtain
9 * information. One exception to this is {@link AbstractHeadwayCopy} (and all it's sub classes), which contains such information
10 * directly, and is a super class for delayed and/or erroneous perception.
11 * <p>
12 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
13 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
14 * </p>
15 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
16 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
17 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
18 */
19 public abstract class AbstractHeadway implements Headway
20 {
21
22 /** */
23 private static final long serialVersionUID = 20170324L;
24
25 /** The (perceived) distance to the other object. When objects are parallel, the distance is null. */
26 private final Length distance;
27
28 /**
29 * The (perceived) front overlap to the other object. This value should be null if there is no overlap. In the figure below
30 * for two GTUs, it is distance c, positive for GTU1, negative for GTU2.
31 *
32 * <pre>
33 * ----------
34 * | GTU 1 | ----->
35 * ----------
36 * ---------------
37 * | GTU 2 | ----->
38 * ---------------
39 * | a | b | c |
40 * </pre>
41 */
42 private final Length overlapFront;
43
44 /**
45 * The (perceived) rear overlap to the other object. This value should be null if there is no overlap. In the figure below
46 * for two GTUs, it is distance a, positive for GTU1, negative for GTU2.
47 *
48 * <pre>
49 * ----------
50 * | GTU 1 | ----->
51 * ----------
52 * ---------------
53 * | GTU 2 | ----->
54 * ---------------
55 * | a | b | c |
56 * </pre>
57 */
58 private final Length overlapRear;
59
60 /**
61 * The (perceived) overlap with the other object. This value should be null if there is no overlap. In the figure below for
62 * two GTUs, it is distance b, positive for GTU1 and GTU2.
63 *
64 * <pre>
65 * ----------
66 * | GTU 1 | ----->
67 * ----------
68 * ---------------
69 * | GTU 2 | ----->
70 * ---------------
71 * | a | b | c |
72 * </pre>
73 */
74 private final Length overlap;
75
76 /**
77 * Construct a new Headway information object, for an object in front, behind, or in parallel with us. <br>
78 * @param distance Length; the distance to the other object
79 * @param overlapFront Length; the front-front distance to the other object
80 * @param overlap Length; the 'center' overlap with the other object
81 * @param overlapRear Length; the rear-rear distance to the other object
82 * @throws GtuException when id is null, or parameters are inconsistent
83 */
84 protected AbstractHeadway(final Length distance, final Length overlapFront, final Length overlap, final Length overlapRear)
85 throws GtuException
86 {
87 Throw.when(distance != null && (overlap != null || overlapFront != null || overlapRear != null), GtuException.class,
88 "overlap parameter cannot be null for front / rear headway");
89 this.distance = distance;
90
91 Throw.when(distance == null && (overlap == null || overlapFront == null || overlapRear == null), GtuException.class,
92 "overlap parameter cannot be null for parallel headway");
93 Throw.when(overlap != null && overlap.si < 0, GtuException.class, "overlap cannot be negative");
94 this.overlap = overlap;
95 this.overlapFront = overlapFront;
96 this.overlapRear = overlapRear;
97 }
98
99 /**
100 * Construct a new Headway information object, for an object ahead of us or behind us.
101 * @param distance the distance to the other object; if this constructor is used, distance cannot be null.
102 * @throws GtuException when id is null, or parameters are inconsistent
103 */
104 public AbstractHeadway(final Length distance) throws GtuException
105 {
106 this(distance, null, null, null);
107 }
108
109 /**
110 * Construct a new Headway information object, for an object parallel with us.
111 * @param overlapFront the front-front distance to the other object; if this constructor is used, this value cannot be null.
112 * @param overlap the 'center' overlap with the other object; if this constructor is used, this value cannot be null.
113 * @param overlapRear the rear-rear distance to the other object; if this constructor is used, this value cannot be null.
114 * @throws GtuException when id is null, or parameters are inconsistent
115 */
116 @SuppressWarnings("checkstyle:parameternumber")
117 public AbstractHeadway(final Length overlapFront, final Length overlap, final Length overlapRear) throws GtuException
118 {
119 this(null, overlapFront, overlap, overlapRear);
120 }
121
122 /** {@inheritDoc} */
123 @Override
124 public final Length getDistance()
125 {
126 return this.distance;
127 }
128
129 /** {@inheritDoc} */
130 @Override
131 public final Length getOverlapFront()
132 {
133 return this.overlapFront;
134 }
135
136 /** {@inheritDoc} */
137 @Override
138 public final Length getOverlapRear()
139 {
140 return this.overlapRear;
141 }
142
143 /** {@inheritDoc} */
144 @Override
145 public final Length getOverlap()
146 {
147 return this.overlap;
148 }
149
150 /** {@inheritDoc} */
151 @Override
152 public final boolean isAhead()
153 {
154 return this.distance != null && this.distance.si > 0.0;
155 }
156
157 /** {@inheritDoc} */
158 @Override
159 public final boolean isBehind()
160 {
161 return this.distance != null && this.distance.si < 0.0;
162 }
163
164 /** {@inheritDoc} */
165 @Override
166 public final boolean isParallel()
167 {
168 return this.overlap != null;
169 }
170
171 /** {@inheritDoc} */
172 @SuppressWarnings("checkstyle:designforextension")
173 @Override
174 public int hashCode()
175 {
176 final int prime = 31;
177 int result = 1;
178 result = prime * result + ((this.distance == null) ? 0 : this.distance.hashCode());
179 result = prime * result + ((this.overlap == null) ? 0 : this.overlap.hashCode());
180 result = prime * result + ((this.overlapFront == null) ? 0 : this.overlapFront.hashCode());
181 result = prime * result + ((this.overlapRear == null) ? 0 : this.overlapRear.hashCode());
182 return result;
183 }
184
185 /** {@inheritDoc} */
186 @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
187 @Override
188 public boolean equals(final Object obj)
189 {
190 if (this == obj)
191 return true;
192 if (obj == null)
193 return false;
194 if (getClass() != obj.getClass())
195 return false;
196 AbstractHeadway other = (AbstractHeadway) obj;
197 if (this.distance == null)
198 {
199 if (other.distance != null)
200 return false;
201 }
202 else if (!this.distance.equals(other.distance))
203 return false;
204 if (this.overlap == null)
205 {
206 if (other.overlap != null)
207 return false;
208 }
209 else if (!this.overlap.equals(other.overlap))
210 return false;
211 if (this.overlapFront == null)
212 {
213 if (other.overlapFront != null)
214 return false;
215 }
216 else if (!this.overlapFront.equals(other.overlapFront))
217 return false;
218 if (this.overlapRear == null)
219 {
220 if (other.overlapRear != null)
221 return false;
222 }
223 else if (!this.overlapRear.equals(other.overlapRear))
224 return false;
225 return true;
226 }
227
228 /** {@inheritDoc} */
229 @SuppressWarnings("checkstyle:designforextension")
230 @Override
231 public String toString()
232 {
233 if (isParallel())
234 {
235 return String.format("Parallel to object %s of type %s with speed %s", getId(), getObjectType(), getSpeed());
236 }
237 return String.format("Headway %s to object %s of type %s with speed %s", getDistance(), getId(), getObjectType(),
238 getSpeed());
239 }
240
241 }