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://github.com/peter-knoppers">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 the distance to the other object
79 * @param overlapFront the front-front distance to the other object
80 * @param overlap the 'center' overlap with the other object
81 * @param overlapRear 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 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 @Override
123 public final Length getDistance()
124 {
125 return this.distance;
126 }
127
128 @Override
129 public final Length getOverlapFront()
130 {
131 return this.overlapFront;
132 }
133
134 @Override
135 public final Length getOverlapRear()
136 {
137 return this.overlapRear;
138 }
139
140 @Override
141 public final Length getOverlap()
142 {
143 return this.overlap;
144 }
145
146 @Override
147 public final boolean isAhead()
148 {
149 return this.distance != null && this.distance.si > 0.0;
150 }
151
152 @Override
153 public final boolean isBehind()
154 {
155 return this.distance != null && this.distance.si < 0.0;
156 }
157
158 @Override
159 public final boolean isParallel()
160 {
161 return this.overlap != null;
162 }
163
164 @SuppressWarnings("checkstyle:designforextension")
165 @Override
166 public int hashCode()
167 {
168 final int prime = 31;
169 int result = 1;
170 result = prime * result + ((this.distance == null) ? 0 : this.distance.hashCode());
171 result = prime * result + ((this.overlap == null) ? 0 : this.overlap.hashCode());
172 result = prime * result + ((this.overlapFront == null) ? 0 : this.overlapFront.hashCode());
173 result = prime * result + ((this.overlapRear == null) ? 0 : this.overlapRear.hashCode());
174 return result;
175 }
176
177 @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
178 @Override
179 public boolean equals(final Object obj)
180 {
181 if (this == obj)
182 return true;
183 if (obj == null)
184 return false;
185 if (getClass() != obj.getClass())
186 return false;
187 AbstractHeadway other = (AbstractHeadway) obj;
188 if (this.distance == null)
189 {
190 if (other.distance != null)
191 return false;
192 }
193 else if (!this.distance.equals(other.distance))
194 return false;
195 if (this.overlap == null)
196 {
197 if (other.overlap != null)
198 return false;
199 }
200 else if (!this.overlap.equals(other.overlap))
201 return false;
202 if (this.overlapFront == null)
203 {
204 if (other.overlapFront != null)
205 return false;
206 }
207 else if (!this.overlapFront.equals(other.overlapFront))
208 return false;
209 if (this.overlapRear == null)
210 {
211 if (other.overlapRear != null)
212 return false;
213 }
214 else if (!this.overlapRear.equals(other.overlapRear))
215 return false;
216 return true;
217 }
218
219 @SuppressWarnings("checkstyle:designforextension")
220 @Override
221 public String toString()
222 {
223 if (isParallel())
224 {
225 return String.format("Parallel to object %s of type %s with speed %s", getId(), getObjectType(), getSpeed());
226 }
227 return String.format("Headway %s to object %s of type %s with speed %s", getDistance(), getId(), getObjectType(),
228 getSpeed());
229 }
230
231 }