1 package org.opentrafficsim.road.network.lane;
2
3 import java.awt.Color;
4 import java.io.Serializable;
5 import java.rmi.RemoteException;
6 import java.util.ArrayList;
7 import java.util.HashMap;
8 import java.util.LinkedHashMap;
9 import java.util.LinkedHashSet;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.NavigableMap;
13 import java.util.Set;
14 import java.util.SortedMap;
15 import java.util.TreeMap;
16
17 import javax.naming.NamingException;
18
19 import org.djunits.unit.LengthUnit;
20 import org.djunits.unit.TimeUnit;
21 import org.djunits.value.vdouble.scalar.Length;
22 import org.djunits.value.vdouble.scalar.Speed;
23 import org.djunits.value.vdouble.scalar.Time;
24 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
25 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
26 import org.opentrafficsim.core.geometry.OTSGeometryException;
27 import org.opentrafficsim.core.gtu.GTUDirectionality;
28 import org.opentrafficsim.core.gtu.GTUException;
29 import org.opentrafficsim.core.gtu.GTUType;
30 import org.opentrafficsim.core.gtu.RelativePosition;
31 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
32 import org.opentrafficsim.core.network.LateralDirectionality;
33 import org.opentrafficsim.core.network.Link;
34 import org.opentrafficsim.core.network.LongitudinalDirectionality;
35 import org.opentrafficsim.core.network.NetworkException;
36 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
37 import org.opentrafficsim.road.network.animation.LaneAnimation;
38 import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
39 import org.opentrafficsim.road.network.lane.object.AbstractLaneBasedObject;
40 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
41 import org.opentrafficsim.road.network.lane.object.sensor.AbstractSensor;
42 import org.opentrafficsim.road.network.lane.object.sensor.Sensor;
43
44 import nl.tudelft.simulation.dsol.SimRuntimeException;
45 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
46 import nl.tudelft.simulation.event.EventType;
47
48 /**
49 * The Lane is the CrossSectionElement of a CrossSectionLink on which GTUs can drive. The Lane stores several important
50 * properties, such as the successor lane(s), predecessor lane(s), and adjacent lane(s), all separated per GTU type. It can, for
51 * instance, be that a truck is not allowed to move into an adjacent lane, while a car is allowed to do so. Furthermore, the
52 * lane contains sensors that can be triggered by passing GTUs. The Lane class also contains methods to determine to trigger the
53 * sensors at exactly calculated and scheduled times, given the movement of the GTUs. <br>
54 * Finally, the Lane stores the GTUs on the lane, and contains several access methods to determine successor and predecessor
55 * GTUs, as well as methods to add a GTU to a lane (either at the start or in the middle when changing lanes), and remove a GTU
56 * from the lane (either at the end, or in the middle when changing onto another lane).
57 * <p>
58 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
59 * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
60 * <p>
61 * $LastChangedDate: 2015-09-24 14:17:07 +0200 (Thu, 24 Sep 2015) $, @version $Revision: 1407 $, by $Author: averbraeck $,
62 * initial version Aug 19, 2014 <br>
63 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
64 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
65 */
66 public class Lane extends CrossSectionElement implements Serializable
67 {
68 /** */
69 private static final long serialVersionUID = 20150826L;
70
71 /** Type of lane to deduce compatibility with GTU types. */
72 private final LaneType laneType;
73
74 /**
75 * The direction in which vehicles can drive, i.e., in direction of geometry, reverse, or both. This can differ per GTU
76 * type. In an overtake lane, cars might overtake and trucks not. It might be that the lane (e.g., a street in a city) is
77 * FORWARD (from start node of the link to end node of the link) for the GTU type CAR, but BOTH for the GTU type BICYCLE
78 * (i.e., bicycles can also go in the other direction, opposite to the drawing direction of the Link). If the directionality
79 * for a GTUType is set to NONE, this means that the given GTUType cannot use the Lane. If a Directionality is set for
80 * GTUType.ALL, the getDirectionality will default to these settings when there is no specific entry for a given
81 * directionality. This means that the settings can be used additive, or restrictive. <br>
82 * In <b>additive use</b>, set the directionality for GTUType.ALL to NONE, or do not set the directionality for GTUType.ALL.
83 * Now, one by one, the allowed directionalities can be added. An example is a lane on a highway, which we only open for
84 * CAR, TRUCK and BUS. <br>
85 * In <b>restrictive use</b>, set the directionality for GTUType.ALL to BOTH, FORWARD, or BACKWARD. Override the
86 * directionality for certain GTUTypes to a more restrictive access, e.g. to NONE. An example is a lane that is open for all
87 * road users, except TRUCK.
88 */
89 private final Map<GTUType, LongitudinalDirectionality> directionalityMap;
90
91 /**
92 * The speed limit of this lane, which can differ per GTU type. Cars might be allowed to drive 120 km/h and trucks 90 km/h.
93 * If the speed limit is the same for all GTU types, GTUType.ALL will be used. This means that the settings can be used
94 * additive, or subtractive. <br>
95 * In <b>additive use</b>, do not set the speed limit for GTUType.ALL. Now, one by one, the allowed maximum speeds for each
96 * of the GTU Types have be added. Do this when there are few GTU types or the speed limits per TU type are very different.
97 * <br>
98 * In <b>subtractive use</b>, set the speed limit for GTUType.ALL to the most common one. Override the speed limit for
99 * certain GTUTypes to a different value. An example is a lane on a highway where all vehicles, except truck (CAR, BUS,
100 * MOTORCYCLE, etc.), can drive 120 km/h, but trucks are allowed only 90 km/h. In that case, set the speed limit for
101 * GTUType.ALL to 120 km/h, and for TRUCK to 90 km/h.
102 */
103 // TODO allow for direction-dependent speed limit
104 private Map<GTUType, Speed> speedLimitMap;
105
106 /**
107 * Sensors on the lane to trigger behavior of the GTU, sorted by longitudinal position. The triggering of sensors is done
108 * per GTU type, so different GTUs can trigger different sensors.
109 */
110 // TODO allow for direction-dependent sensors
111 private final SortedMap<Double, List<Sensor>> sensors = new TreeMap<>();
112
113 /**
114 * Objects on the lane can be observed by the GTU. Examples are signs, speed signs, blocks, and traffic lights. They are
115 * sorted by longitudinal position.
116 */
117 // TODO allow for direction-dependent lane objects
118 private final SortedMap<Double, List<LaneBasedObject>> laneBasedObjects = new TreeMap<>();
119
120 /** GTUs ordered by increasing longitudinal position; increasing in the direction of the center line. */
121 private final List<LaneBasedGTU> gtuList = new ArrayList<LaneBasedGTU>();
122
123 /**
124 * Adjacent left lanes that some GTU types can change onto. Left is defined relative to the direction of the design line of
125 * the link (and the direction of the center line of the lane). In terms of offsets, 'left' lanes always have a more
126 * positive offset than the current lane. Initially null so we can calculate and cache the first time the method is called.
127 */
128 private Map<GTUType, Set<Lane>> leftNeighbors = null;
129
130 /**
131 * Adjacent right lanes that some GTU types can change onto. Right is defined relative to the direction of the design line
132 * of the link (and the direction of the center line of the lane). In terms of offsets, 'right' lanes always have a more
133 * negative offset than the current lane. Initially null so we can calculate and cache the first time the method is called.
134 */
135 private Map<GTUType, Set<Lane>> rightNeighbors = null;
136
137 /**
138 * Next lane(s) following this lane that some GTU types can drive from or onto. Next is defined in the direction of the
139 * design line. Initially null so we can calculate and cache the first time the method is called.
140 */
141 private Map<GTUType, Map<Lane, GTUDirectionality>> nextLanes = null;
142
143 /**
144 * Previous lane(s) preceding this lane that some GTU types can drive from or onto. Previous is defined relative to the
145 * direction of the design line. Initially null so we can calculate and cache the first time the method is called.
146 */
147 private Map<GTUType, Map<Lane, GTUDirectionality>> prevLanes = null;
148
149 /** The conditions for overtaking another GTU, viewed from this lane. */
150 // TODO allow for direction-dependent overtaking conditions
151 private final OvertakingConditions overtakingConditions;
152
153 /**
154 * The <b>timed</b> event type for pub/sub indicating the addition of a GTU to the lane. <br>
155 * Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
156 */
157 public static final EventType GTU_ADD_EVENT = new EventType("GTU.ADD");
158
159 /**
160 * The <b>timed</b> event type for pub/sub indicating the removal of a GTU from the lane. <br>
161 * Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal}
162 */
163 public static final EventType GTU_REMOVE_EVENT = new EventType("GTU.REMOVE");
164
165 /**
166 * The <b>timed</b> event type for pub/sub indicating the addition of a Sensor to the lane. <br>
167 * Payload: Object[] {String sensorId, Sensor sensor}
168 */
169 public static final EventType SENSOR_ADD_EVENT = new EventType("SENSOR.ADD");
170
171 /**
172 * The <b>timed</b> event type for pub/sub indicating the removal of a Sensor from the lane. <br>
173 * Payload: Object[] {String sensorId, Sensor sensor}
174 */
175 public static final EventType SENSOR_REMOVE_EVENT = new EventType("SENSOR.REMOVE");
176
177 /**
178 * The event type for pub/sub indicating the addition of a LaneBasedObject to the lane. <br>
179 * Payload: Object[] {LaneBasedObject laneBasedObject}
180 */
181 public static final EventType OBJECT_ADD_EVENT = new EventType("OBJECT.ADD");
182
183 /**
184 * The event type for pub/sub indicating the removal of a LaneBasedObject from the lane. <br>
185 * Payload: Object[] {LaneBasedObject laneBasedObject}
186 */
187 public static final EventType OBJECT_REMOVE_EVENT = new EventType("OBJECT.REMOVE");
188
189 /**
190 * Construct a new Lane.
191 * @param parentLink CrossSectionLink; the link to which the new Lane will belong (must be constructed first)
192 * @param id String; the id of this lane within the link; should be unique within the link.
193 * @param lateralOffsetAtStart Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
194 * design line of the parent Link at the start of the parent Link
195 * @param lateralOffsetAtEnd Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
196 * design line of the parent Link at the end of the parent Link
197 * @param beginWidth Length; start width, positioned <i>symmetrically around</i> the design line
198 * @param endWidth Length; end width, positioned <i>symmetrically around</i> the design line
199 * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
200 * @param directionalityMap Map<GTUType, LongitudinalDirectionality>; in direction of geometry, reverse, or both,
201 * specified per GTU Type
202 * @param speedLimitMap Map<GTUType, Speed>; speed limit on this lane, specified per GTU Type
203 * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
204 * @throws OTSGeometryException when creation of the center line or contour geometry fails
205 * @throws NetworkException when id equal to null or not unique
206 */
207 @SuppressWarnings("checkstyle:parameternumber")
208 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtStart,
209 final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth, final LaneType laneType,
210 final Map<GTUType, LongitudinalDirectionality> directionalityMap, final Map<GTUType, Speed> speedLimitMap,
211 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
212 {
213 super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
214 this.laneType = laneType;
215 this.directionalityMap = directionalityMap;
216 checkDirectionality();
217 this.speedLimitMap = speedLimitMap;
218 this.overtakingConditions = overtakingConditions;
219 }
220
221 /**
222 * Construct a new Lane.
223 * @param parentLink CrossSectionLink; the link to which the element will belong (must be constructed first)
224 * @param id String; the id of this lane within the link; should be unique within the link.
225 * @param lateralOffsetAtStart Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
226 * design line of the parent Link at the start of the parent Link
227 * @param lateralOffsetAtEnd Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
228 * design line of the parent Link at the end of the parent Link
229 * @param beginWidth Length; start width, positioned <i>symmetrically around</i> the design line
230 * @param endWidth Length; end width, positioned <i>symmetrically around</i> the design line
231 * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
232 * @param directionality LongitudinalDirectionality; in direction of geometry, reverse, or both
233 * @param speedLimit Speed; speed limit on this lane
234 * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
235 * @throws OTSGeometryException when creation of the center line or contour geometry fails
236 * @throws NetworkException when id equal to null or not unique
237 */
238 @SuppressWarnings("checkstyle:parameternumber")
239 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtStart,
240 final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth, final LaneType laneType,
241 final LongitudinalDirectionality directionality, final Speed speedLimit,
242 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
243 {
244 super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
245 this.laneType = laneType;
246 this.directionalityMap = new LinkedHashMap<>(1);
247 this.directionalityMap.put(GTUType.ALL, directionality);
248 checkDirectionality();
249 this.speedLimitMap = new LinkedHashMap<>();
250 this.speedLimitMap.put(GTUType.ALL, speedLimit);
251 this.overtakingConditions = overtakingConditions;
252 }
253
254 /**
255 * Construct a new Lane.
256 * @param parentLink CrossSectionLink; the link to which the element will belong (must be constructed first)
257 * @param id String; the id of this lane within the link; should be unique within the link.
258 * @param lateralOffset Length; the lateral offset of the design line of the new CrossSectionLink with respect to the design
259 * line of the parent Link
260 * @param width Length; width, positioned <i>symmetrically around</i> the design line
261 * @param laneType type of lane to deduce compatibility with GTU types
262 * @param directionalityMap Map<GTUType, LongitudinalDirectionality>; in direction of geometry, reverse, or both,
263 * specified per GTU Type
264 * @param speedLimitMap Map<GTUType, Speed>; the speed limit on this lane, specified per GTU Type
265 * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
266 * @throws OTSGeometryException when creation of the center line or contour geometry fails
267 * @throws NetworkException when id equal to null or not unique
268 */
269 @SuppressWarnings("checkstyle:parameternumber")
270 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffset, final Length width,
271 final LaneType laneType, final Map<GTUType, LongitudinalDirectionality> directionalityMap,
272 final Map<GTUType, Speed> speedLimitMap, final OvertakingConditions overtakingConditions)
273 throws OTSGeometryException, NetworkException
274 {
275 super(parentLink, id, lateralOffset, width);
276 this.laneType = laneType;
277 this.directionalityMap = directionalityMap;
278 checkDirectionality();
279 this.speedLimitMap = speedLimitMap;
280 this.overtakingConditions = overtakingConditions;
281 }
282
283 /**
284 * Construct a new Lane.
285 * @param parentLink CrossSectionLink; the link to which the element belongs (must be constructed first)
286 * @param id String; the id of this lane within the link; should be unique within the link
287 * @param lateralOffset Length; the lateral offset of the design line of the new CrossSectionLink with respect to the design
288 * line of the parent Link
289 * @param width Length; width, positioned <i>symmetrically around</i> the design line
290 * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
291 * @param directionality in direction of geometry, reverse, or both
292 * @param speedLimit Speed; the speed limit on this lane
293 * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
294 * @throws OTSGeometryException when creation of the center line or contour geometry fails
295 * @throws NetworkException when id equal to null or not unique
296 */
297 @SuppressWarnings("checkstyle:parameternumber")
298 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffset, final Length width,
299 final LaneType laneType, final LongitudinalDirectionality directionality, final Speed speedLimit,
300 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
301 {
302 super(parentLink, id, lateralOffset, width);
303 this.laneType = laneType;
304 this.directionalityMap = new LinkedHashMap<>(1);
305 this.directionalityMap.put(GTUType.ALL, directionality);
306 checkDirectionality();
307 this.speedLimitMap = new LinkedHashMap<>();
308 this.speedLimitMap.put(GTUType.ALL, speedLimit);
309 this.overtakingConditions = overtakingConditions;
310 }
311
312 /**
313 * Construct a new Lane.
314 * @param parentLink CrossSectionLink; the link to which the element belongs (must be constructed first)
315 * @param id String; the id of this lane within the link; should be unique within the link.
316 * @param crossSectionSlices List<CrossSectionSlice>; the offsets and widths at positions along the line, relative to
317 * the design line of the parent link. If there is just one with and offset, there should just be one element in
318 * the list with Length = 0. If there are more slices, the last one should be at the length of the design line.
319 * If not, a NetworkException is thrown.
320 * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
321 * @param directionalityMap Map<GTUType, LongitudinalDirectionality>; in direction of geometry, reverse, or both,
322 * specified per GTU Type
323 * @param speedLimitMap Map<GTUType, Speed>; the speed limit on this lane, specified per GTU Type
324 * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
325 * @throws OTSGeometryException when creation of the center line or contour geometry fails
326 * @throws NetworkException when id equal to null or not unique
327 */
328 @SuppressWarnings("checkstyle:parameternumber")
329 public Lane(final CrossSectionLink parentLink, final String id, final List<CrossSectionSlice> crossSectionSlices,
330 final LaneType laneType, final Map<GTUType, LongitudinalDirectionality> directionalityMap,
331 final Map<GTUType, Speed> speedLimitMap, final OvertakingConditions overtakingConditions)
332 throws OTSGeometryException, NetworkException
333 {
334 super(parentLink, id, crossSectionSlices);
335 this.laneType = laneType;
336 this.directionalityMap = directionalityMap;
337 checkDirectionality();
338 this.speedLimitMap = speedLimitMap;
339 this.overtakingConditions = overtakingConditions;
340 }
341
342 /**
343 * Construct a new Lane.
344 * @param parentLink CrossSectionLink; the link to which the element belongs (must be constructed first)
345 * @param id String; the id of this lane within the link; should be unique within the link.
346 * @param crossSectionSlices List<CrossSectionSlice>; the offsets and widths at positions along the line, relative to
347 * the design line of the parent link. If there is just one with and offset, there should just be one element in
348 * the list with Length = 0. If there are more slices, the last one should be at the length of the design line.
349 * If not, a NetworkException is thrown.
350 * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
351 * @param directionality LongitudinalDirectionality; in direction of geometry, reverse, or both
352 * @param speedLimit Speed; the speed limit on this lane
353 * @param overtakingConditions OvertakingCondition; the conditions for overtaking another GTU, viewed from this lane
354 * @throws OTSGeometryException when creation of the center line or contour geometry fails
355 * @throws NetworkException when id equal to null or not unique
356 */
357 @SuppressWarnings("checkstyle:parameternumber")
358 public Lane(final CrossSectionLink parentLink, final String id, final List<CrossSectionSlice> crossSectionSlices,
359 final LaneType laneType, final LongitudinalDirectionality directionality, final Speed speedLimit,
360 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
361 {
362 super(parentLink, id, crossSectionSlices);
363 this.laneType = laneType;
364 this.directionalityMap = new LinkedHashMap<>(1);
365 this.directionalityMap.put(GTUType.ALL, directionality);
366 checkDirectionality();
367 this.speedLimitMap = new LinkedHashMap<>();
368 this.speedLimitMap.put(GTUType.ALL, speedLimit);
369 this.overtakingConditions = overtakingConditions;
370 }
371
372 /**
373 * Clone a Lane for a new network.
374 * @param newParentLink the new link to which the clone belongs
375 * @param newSimulator the new simulator for this network
376 * @param animation whether to (re)create animation or not
377 * @param cse the element to clone from
378 * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
379 * or the end node of the link are not registered in the network.
380 */
381 protected Lane(final CrossSectionLink newParentLink, final OTSSimulatorInterface newSimulator, final boolean animation,
382 final Lane cse) throws NetworkException
383 {
384 super(newParentLink, newSimulator, animation, cse);
385 this.laneType = cse.laneType;
386 this.directionalityMap = new HashMap<GTUType, LongitudinalDirectionality>(cse.directionalityMap);
387 this.speedLimitMap = new HashMap<GTUType, Speed>(cse.speedLimitMap);
388 this.overtakingConditions = cse.overtakingConditions;
389
390 }
391
392 // TODO constructor calls with this(...)
393
394 /**
395 * Retrieve one of the sets of neighboring Lanes that is accessible for the given type of GTU. A defensive copy of the
396 * internal data structure is returned.
397 * @param direction LateralDirectionality; either LEFT or RIGHT, relative to the DESIGN LINE of the link (and the direction
398 * of the center line of the lane). In terms of offsets, 'left' lanes always have a more positive offset than the
399 * current lane, and 'right' lanes a more negative offset.
400 * @param gtuType GTUType; the GTU type to check the accessibility for
401 * @return Set<Lane>; the indicated set of neighboring Lanes
402 */
403 private Set<Lane> neighbors(final LateralDirectionality direction, final GTUType gtuType)
404 {
405 if (this.leftNeighbors == null || this.rightNeighbors == null)
406 {
407 this.leftNeighbors = new LinkedHashMap<>(1);
408 this.rightNeighbors = new LinkedHashMap<>(1);
409 }
410
411 if (!this.leftNeighbors.containsKey(gtuType) || !this.rightNeighbors.containsKey(gtuType))
412 {
413 Set<Lane> leftSet = new LinkedHashSet<>(1);
414 Set<Lane> rightSet = new LinkedHashSet<>(1);
415 this.leftNeighbors.put(gtuType, leftSet);
416 this.rightNeighbors.put(gtuType, rightSet);
417 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
418 {
419 if (cse instanceof Lane && !cse.equals(this))
420 {
421 Lane lane = (Lane) cse;
422 if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.LEFT, gtuType))
423 {
424 leftSet.add(lane);
425 }
426 if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.RIGHT, gtuType))
427 {
428 rightSet.add(lane);
429 }
430 }
431 }
432 }
433
434 Set<Lane> lanes = new LinkedHashSet<>();
435 if (direction.equals(LateralDirectionality.LEFT))
436 {
437 lanes.addAll(this.leftNeighbors.get(gtuType));
438 }
439 else
440 {
441 lanes.addAll(this.rightNeighbors.get(gtuType));
442 }
443 return lanes;
444 }
445
446 /** Lateral alignment margin for longitudinally connected Lanes. */
447 static final Length ADJACENT_MARGIN = new Length(0.2, LengthUnit.METER);
448
449 /**
450 * Determine whether another lane is adjacent to this lane (dependent on distance) and accessible (dependent on stripes) for
451 * a certain GTU type (dependent on usability of the adjacent lane for that GTU type). This method assumes that when there
452 * is NO stripe between two adjacent lanes that are accessible for the GTU type, the GTU can enter that lane. <br>
453 * @param lane Lane; the other lane to evaluate
454 * @param direction LateralDirectionality; the direction to look at, relative to the DESIGN LINE of the link. This is a very
455 * important aspect to note: all information is stored relative to the direction of the design line, and not in a
456 * driving direction, which can vary for lanes that can be driven in two directions (e.g. at overtaking).
457 * @param gtuType GTUType; the GTU type to check the accessibility for
458 * @return boolean; true if the other lane is adjacent to this lane and accessible for the given GTU type; false otherwise
459 */
460 private boolean laterallyAdjacentAndAccessible(final Lane lane, final LateralDirectionality direction,
461 final GTUType gtuType)
462 {
463 if (!lane.getLaneType().isCompatible(gtuType) || gtuType.equals(GTUType.ALL) || gtuType.equals(GTUType.NONE))
464 {
465 // not accessible for the given GTU type
466 return false;
467 }
468
469 if (direction.equals(LateralDirectionality.LEFT))
470 {
471 // TODO take the cross section slices into account...
472 if (Math.abs((getDesignLineOffsetAtBegin().getSI() + getBeginWidth().getSI() / 2.0)
473 - (lane.getDesignLineOffsetAtBegin().getSI() - lane.getBeginWidth().getSI() / 2.0)) < ADJACENT_MARGIN
474 .getSI()
475 && Math.abs((getDesignLineOffsetAtEnd().getSI() + getEndWidth().getSI() / 2.0)
476 - (lane.getDesignLineOffsetAtEnd().getSI() - lane.getEndWidth().getSI() / 2.0)) < ADJACENT_MARGIN
477 .getSI())
478 {
479 // look at stripes between the two lanes
480 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
481 {
482 if (cse instanceof Stripe)
483 {
484 Stripe stripe = (Stripe) cse;
485 // TODO take the cross section slices into account...
486 if (Math.abs((getDesignLineOffsetAtBegin().getSI() + getBeginWidth().getSI() / 2.0)
487 - stripe.getDesignLineOffsetAtBegin().getSI()) < ADJACENT_MARGIN.getSI()
488 && Math.abs((getDesignLineOffsetAtEnd().getSI() + getEndWidth().getSI() / 2.0)
489 - stripe.getDesignLineOffsetAtEnd().getSI()) < ADJACENT_MARGIN.getSI())
490 {
491 if (!stripe.isPermeable(gtuType, LateralDirectionality.LEFT))
492 {
493 // there is a stripe forbidding to cross to the adjacent lane
494 return false;
495 }
496 }
497 }
498 }
499 // the lanes are adjacent, and there is no stripe forbidding us to enter that lane
500 // or there is no stripe at all
501 return true;
502 }
503 }
504
505 else
506 // direction.equals(LateralDirectionality.RIGHT)
507 {
508 // TODO take the cross section slices into account...
509 if (Math.abs((getDesignLineOffsetAtBegin().getSI() - getBeginWidth().getSI() / 2.0)
510 - (lane.getDesignLineOffsetAtBegin().getSI() + lane.getBeginWidth().getSI() / 2.0)) < ADJACENT_MARGIN
511 .getSI()
512 && Math.abs((getDesignLineOffsetAtEnd().getSI() - getEndWidth().getSI() / 2.0)
513 - (lane.getDesignLineOffsetAtEnd().getSI() + lane.getEndWidth().getSI() / 2.0)) < ADJACENT_MARGIN
514 .getSI())
515 {
516 // look at stripes between the two lanes
517 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
518 {
519 if (cse instanceof Stripe)
520 {
521 Stripe stripe = (Stripe) cse;
522 // TODO take the cross section slices into account...
523 if (Math.abs((getDesignLineOffsetAtBegin().getSI() - getBeginWidth().getSI() / 2.0)
524 - stripe.getDesignLineOffsetAtBegin().getSI()) < ADJACENT_MARGIN.getSI()
525 && Math.abs((getDesignLineOffsetAtEnd().getSI() - getEndWidth().getSI() / 2.0)
526 - stripe.getDesignLineOffsetAtEnd().getSI()) < ADJACENT_MARGIN.getSI())
527 {
528 if (!stripe.isPermeable(gtuType, LateralDirectionality.RIGHT))
529 {
530 // there is a stripe forbidding to cross to the adjacent lane
531 return false;
532 }
533 }
534 }
535 }
536 // the lanes are adjacent, and there is no stripe forbidding us to enter that lane
537 // or there is no stripe at all
538 return true;
539 }
540 }
541
542 // no lanes were found that are close enough laterally.
543 return false;
544 }
545
546 /**
547 * Insert a sensor at the right place in the sensor list of this Lane.
548 * @param sensor Sensor; the sensor to add
549 * @throws NetworkException when the position of the sensor is beyond (or before) the range of this Lane
550 */
551 public final void addSensor(final Sensor sensor) throws NetworkException
552 {
553 double position = sensor.getLongitudinalPosition().si;
554 if (position < 0 || position > getLength().getSI())
555 {
556 throw new NetworkException("Illegal position for sensor " + position + " valid range is 0.." + getLength().getSI());
557 }
558 List<Sensor> sensorList = this.sensors.get(position);
559 if (null == sensorList)
560 {
561 sensorList = new ArrayList<Sensor>(1);
562 this.sensors.put(position, sensorList);
563 }
564 sensorList.add(sensor);
565 fireTimedEvent(Lane.SENSOR_ADD_EVENT, new Object[] { sensor.getId(), sensor },
566 sensor.getSimulator().getSimulatorTime());
567 }
568
569 /**
570 * Remove a sensor from the sensor list of this Lane.
571 * @param sensor Sensoe; the sensor to remove.
572 * @throws NetworkException when the sensor was not found on this Lane
573 */
574 public final void removeSensor(final Sensor sensor) throws NetworkException
575 {
576 fireTimedEvent(Lane.SENSOR_REMOVE_EVENT, new Object[] { sensor.getId(), sensor },
577 sensor.getSimulator().getSimulatorTime());
578 List<Sensor> sensorList = this.sensors.get(sensor.getLongitudinalPosition().si);
579 if (null == sensorList)
580 {
581 throw new NetworkException("No sensor at " + sensor.getLongitudinalPosition().si);
582 }
583 sensorList.remove(sensor);
584 if (sensorList.size() == 0)
585 {
586 this.sensors.remove(sensor.getLongitudinalPosition().si);
587 }
588 }
589
590 /**
591 * Retrieve the list of Sensors of this Lane in the specified distance range for the given GTUType. The sensors that are
592 * triggered by GTUTypes.ALL are added as well. The resulting list is a defensive copy.
593 * @param minimumPosition Length; the minimum distance on the Lane (inclusive)
594 * @param maximumPosition Length; the maximum distance on the Lane (inclusive)
595 * @param gtuType the GTU type to provide the sensors for
596 * @return List<Sensor>; list of the sensor in the specified range. This is a defensive copy.
597 */
598 public final List<Sensor> getSensors(final Length minimumPosition, final Length maximumPosition, final GTUType gtuType)
599 {
600 List<Sensor> sensorList = new ArrayList<>(1);
601 for (List<Sensor> sl : this.sensors.values())
602 {
603 for (Sensor sensor : sl)
604 {
605 if ((sensor.getTriggeringGTUTypes().contains(gtuType) || sensor.getTriggeringGTUTypes().contains(GTUType.ALL))
606 && sensor.getLongitudinalPosition().ge(minimumPosition)
607 && sensor.getLongitudinalPosition().le(maximumPosition))
608 {
609 sensorList.add(sensor);
610 }
611 }
612 }
613 return sensorList;
614 }
615
616 /**
617 * Retrieve the list of Sensors of this Lane that are triggered by the given GTUType. The sensors that are triggered by
618 * GTUTypes.ALL are added as well. The resulting list is a defensive copy.
619 * @param gtuType GTUType; the GTU type to provide the sensors for
620 * @return List<Sensor>; list of the sensors, in ascending order for the location on the Lane
621 */
622 public final List<Sensor> getSensors(final GTUType gtuType)
623 {
624 List<Sensor> sensorList = new ArrayList<>(1);
625 for (List<Sensor> sl : this.sensors.values())
626 {
627 for (Sensor sensor : sl)
628 {
629 if ((sensor.getTriggeringGTUTypes().contains(gtuType) || sensor.getTriggeringGTUTypes().contains(GTUType.ALL)))
630 {
631 sensorList.add(sensor);
632 }
633 }
634 }
635 return sensorList;
636 }
637
638 /**
639 * Retrieve the list of all Sensors of this Lane. The resulting list is a defensive copy.
640 * @return List<Sensor>; list of the sensors, in ascending order for the location on the Lane
641 */
642 public final List<Sensor> getSensors()
643 {
644 if (this.sensors == null)
645 {
646 return new ArrayList<>();
647 }
648 List<Sensor> sensorList = new ArrayList<>(1);
649 for (List<Sensor> sl : this.sensors.values())
650 {
651 for (Sensor sensor : sl)
652 {
653 sensorList.add(sensor);
654 }
655 }
656 return sensorList;
657 }
658
659 /**
660 * Retrieve the list of Sensors of this Lane for the given GTUType. The sensors that are triggered by GTUTypes.ALL are added
661 * as well. The resulting Map is a defensive copy.
662 * @param gtuType GTUType; the GTU type to provide the sensors for
663 * @return SortedMap<Double, List<Sensor>>; all sensors on this lane for the given GTUType as a map per distance
664 */
665 public final SortedMap<Double, List<Sensor>> getSensorMap(final GTUType gtuType)
666 {
667 SortedMap<Double, List<Sensor>> sensorMap = new TreeMap<>();
668 for (double d : this.sensors.keySet())
669 {
670 List<Sensor> sensorList = new ArrayList<>(1);
671 for (List<Sensor> sl : this.sensors.values())
672 {
673 for (Sensor sensor : sl)
674 {
675 if ((sensor.getTriggeringGTUTypes().contains(gtuType)
676 || sensor.getTriggeringGTUTypes().contains(GTUType.ALL)))
677 {
678 sensorList.add(sensor);
679 }
680 }
681 }
682 if (sensorList.size() > 0)
683 {
684 sensorMap.put(d, sensorList);
685 }
686 }
687 return sensorMap;
688 }
689
690 /**
691 * Schedule triggering of the sensors for a certain time step; from now until the nextEvaluationTime of the GTU.
692 * @param gtu LaneBasedGTU; the lane based GTU for which to schedule triggering of the sensors.
693 * @param referenceStartSI double; the SI distance of the GTU reference point on the lane at the current time
694 * @param referenceMoveSI double; the SI distance traveled in the next time step.
695 * @throws NetworkException when GTU not on this lane.
696 * @throws SimRuntimeException when method cannot be scheduled.
697 */
698 public final void scheduleSensorTriggers(final LaneBasedGTU gtu, final double referenceStartSI,
699 final double referenceMoveSI) throws NetworkException, SimRuntimeException
700 {
701 for (List<Sensor> sensorList : getSensorMap(gtu.getGTUType()).values())
702 {
703 for (Sensor sensor : sensorList)
704 {
705 for (RelativePosition relativePosition : gtu.getRelativePositions().values())
706 {
707 // System.out.println("GTU relative position " + relativePosition + " sensor relative position " +
708 // sensor.getPositionType());
709 if (sensor.getPositionType().equals(relativePosition.getType())
710 && referenceStartSI + relativePosition.getDx().getSI() <= sensor.getLongitudinalPosition().si
711 && referenceStartSI + referenceMoveSI
712 + relativePosition.getDx().getSI() > sensor.getLongitudinalPosition().si)
713 {
714 // the exact time of triggering is based on the distance between the current position of the
715 // relative position on the GTU and the location of the sensor.
716 // TODO make sure triggering is done right when driving in DIR_MINUS direction
717 double d = sensor.getLongitudinalPosition().si - referenceStartSI - relativePosition.getDx().getSI();
718 if (d < 0)
719 {
720 throw new NetworkException("scheduleTriggers for gtu: " + gtu + ", d<0 d=" + d);
721 }
722
723 OperationalPlan oPlan = gtu.getOperationalPlan();
724 Time triggerTime = oPlan.timeAtDistance(new Length(d, LengthUnit.METER));
725 if (triggerTime.gt(oPlan.getEndTime()))
726 {
727 System.err.println("Time=" + gtu.getSimulator().getSimulatorTime().getTime().getSI()
728 + " - Scheduling trigger at " + triggerTime.getSI() + "s. > " + oPlan.getEndTime().getSI()
729 + "s. (nextEvalTime) for sensor " + sensor + " , gtu " + gtu);
730 System.err.println(" v=" + gtu.getSpeed() + ", a=" + gtu.getAcceleration() + ", lane=" + toString()
731 + ", refStartSI=" + referenceStartSI + ", moveSI=" + referenceMoveSI);
732 triggerTime =
733 new Time(oPlan.getEndTime().getSI() - Math.ulp(oPlan.getEndTime().getSI()), TimeUnit.SI);
734 // gtu.timeAtDistance(new Length(-d, METER));
735 // System.exit(-1);
736 }
737 // System.out.println("Scheduling a trigger for relativePosition " + relativePosition);
738 // System.out.println("Time=" + gtu.getSimulator().getSimulatorTime().toString()
739 // + " - Scheduling trigger at " + triggerTime + " for sensor " + sensor + " , gtu " + gtu);
740 SimEvent<OTSSimTimeDouble> event = new SimEvent<OTSSimTimeDouble>(new OTSSimTimeDouble(triggerTime),
741 this, sensor, "trigger", new Object[] { gtu });
742 gtu.getSimulator().scheduleEvent(event);
743 gtu.addTrigger(this, event);
744 }
745 }
746 }
747 }
748 }
749
750 /**
751 * Insert a laneBasedObject at the right place in the laneBasedObject list of this Lane.
752 * @param laneBasedObject LaneBasedObject; the laneBasedObject to add
753 * @throws NetworkException when the position of the laneBasedObject is beyond (or before) the range of this Lane
754 */
755 public final void addLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
756 {
757 double position = laneBasedObject.getLongitudinalPosition().si;
758 if (position < 0 || position > getLength().getSI())
759 {
760 throw new NetworkException(
761 "Illegal position for laneBasedObject " + position + " valid range is 0.." + getLength().getSI());
762 }
763 List<LaneBasedObject> laneBasedObjectList = this.laneBasedObjects.get(position);
764 if (null == laneBasedObjectList)
765 {
766 laneBasedObjectList = new ArrayList<LaneBasedObject>(1);
767 this.laneBasedObjects.put(position, laneBasedObjectList);
768 }
769 laneBasedObjectList.add(laneBasedObject);
770 fireEvent(Lane.OBJECT_ADD_EVENT, new Object[] { laneBasedObject });
771 }
772
773 /**
774 * Remove a laneBasedObject from the laneBasedObject list of this Lane.
775 * @param laneBasedObject Sensoe; the laneBasedObject to remove.
776 * @throws NetworkException when the laneBasedObject was not found on this Lane
777 */
778 public final void removeLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
779 {
780 fireEvent(Lane.OBJECT_REMOVE_EVENT, new Object[] { laneBasedObject });
781 List<LaneBasedObject> laneBasedObjectList =
782 this.laneBasedObjects.get(laneBasedObject.getLongitudinalPosition().getSI());
783 if (null == laneBasedObjectList)
784 {
785 throw new NetworkException("No laneBasedObject at " + laneBasedObject.getLongitudinalPosition().si);
786 }
787 laneBasedObjectList.remove(laneBasedObject);
788 if (laneBasedObjectList.isEmpty())
789 {
790 this.laneBasedObjects.remove(laneBasedObject.getLongitudinalPosition().doubleValue());
791 }
792 }
793
794 /**
795 * Retrieve the list of LaneBasedObjects of this Lane in the specified distance range. The resulting list is a defensive
796 * copy.
797 * @param minimumPosition Length; the minimum distance on the Lane (inclusive)
798 * @param maximumPosition Length; the maximum distance on the Lane (inclusive)
799 * @return List<LaneBasedObject>; list of the laneBasedObject in the specified range. This is a defensive copy.
800 */
801 public final List<LaneBasedObject> getLaneBasedObjects(final Length minimumPosition, final Length maximumPosition)
802 {
803 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
804 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
805 {
806 for (LaneBasedObject lbo : lbol)
807 {
808 if (lbo.getLongitudinalPosition().ge(minimumPosition) && lbo.getLongitudinalPosition().le(maximumPosition))
809 {
810 laneBasedObjectList.add(lbo);
811 }
812 }
813 }
814 return laneBasedObjectList;
815 }
816
817 /**
818 * Retrieve the list of all LaneBasedObjects of this Lane. The resulting list is a defensive copy.
819 * @return List<LaneBasedObject>; list of the laneBasedObjects, in ascending order for the location on the Lane
820 */
821 public final List<LaneBasedObject> getLaneBasedObjects()
822 {
823 if (this.laneBasedObjects == null)
824 {
825 return new ArrayList<>();
826 }
827 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
828 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
829 {
830 for (LaneBasedObject lbo : lbol)
831 {
832 laneBasedObjectList.add(lbo);
833 }
834 }
835 return laneBasedObjectList;
836 }
837
838 /**
839 * Retrieve the list of LaneBasedObjects of this Lane. The resulting Map is a defensive copy.
840 * @return SortedMap<Double, List<LaneBasedObject>>; all laneBasedObjects on this lane
841 */
842 public final SortedMap<Double, List<LaneBasedObject>> getLaneBasedObjectMap()
843 {
844 SortedMap<Double, List<LaneBasedObject>> laneBasedObjectMap = new TreeMap<>();
845 for (double d : this.laneBasedObjects.keySet())
846 {
847 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
848 for (LaneBasedObject lbo : this.laneBasedObjects.get(d))
849 {
850 laneBasedObjectList.add(lbo);
851 }
852 laneBasedObjectMap.put(d, laneBasedObjectList);
853 }
854 return laneBasedObjectMap;
855 }
856
857 /**
858 * Transform a fraction on the lane to a relative length (can be less than zero or larger than the lane length).
859 * @param fraction double; fraction relative to the lane length.
860 * @return Length; the longitudinal length corresponding to the fraction.
861 */
862 public final Length position(final double fraction)
863 {
864 if (this.length.getUnit().isBaseSIUnit())
865 {
866 return new Length(this.length.si * fraction, LengthUnit.SI);
867 }
868 return new Length(this.length.getInUnit() * fraction, this.length.getUnit());
869 }
870
871 /**
872 * Transform a fraction on the lane to a relative length in SI units (can be less than zero or larger than the lane length).
873 * @param fraction double; fraction relative to the lane length.
874 * @return double; length corresponding to the fraction, in SI units.
875 */
876 public final double positionSI(final double fraction)
877 {
878 return this.length.si * fraction;
879 }
880
881 /**
882 * Transform a position on the lane (can be less than zero or larger than the lane length) to a fraction.
883 * @param position Length; relative length on the lane (may be less than zero or larger than the lane length).
884 * @return fraction double; fraction relative to the lane length.
885 */
886 public final double fraction(final Length position)
887 {
888 return position.si / this.length.si;
889 }
890
891 /**
892 * Transform a position on the lane in SI units (can be less than zero or larger than the lane length) to a fraction.
893 * @param positionSI double; relative length on the lane in SI units (may be less than zero or larger than the lane length).
894 * @return double; fraction relative to the lane length.
895 */
896 public final double fractionSI(final double positionSI)
897 {
898 return positionSI / this.length.si;
899 }
900
901 /**
902 * Add a LaneBasedGTU to the list of this Lane.
903 * @param gtu LaneBasedGTU; the GTU to add
904 * @param fractionalPosition double; the fractional position that the newly added GTU will have on this Lane
905 * @return int; the rank that the newly added GTU has on this Lane (should be 0, except when the GTU enters this Lane due to
906 * a lane change operation)
907 * @throws GTUException when the fractionalPosition is outside the range 0..1, or the GTU is already registered on this Lane
908 */
909 public final int addGTU(final LaneBasedGTU gtu, final double fractionalPosition) throws GTUException
910 {
911 // figure out the rank for the new GTU
912 int index;
913 for (index = 0; index < this.gtuList.size(); index++)
914 {
915 LaneBasedGTU otherGTU = this.gtuList.get(index);
916 if (gtu == otherGTU)
917 {
918 throw new GTUException(gtu + " already registered on Lane " + this + " [registered lanes: "
919 + gtu.positions(gtu.getFront()).keySet() + "] locations: " + gtu.positions(gtu.getFront()).values()
920 + " time: " + gtu.getSimulator().getSimulatorTime().getTime());
921 }
922 if (otherGTU.fractionalPosition(this, otherGTU.getFront()) >= fractionalPosition)
923 {
924 break;
925 }
926 }
927 this.gtuList.add(index, gtu);
928 fireTimedEvent(Lane.GTU_ADD_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size() },
929 gtu.getSimulator().getSimulatorTime());
930 getParentLink().addGTU(gtu);
931 return index;
932 }
933
934 /**
935 * Add a LaneBasedGTU to the list of this Lane.
936 * @param gtu LaneBasedGTU; the GTU to add
937 * @param longitudinalPosition Length; the longitudinal position that the newly added GTU will have on this Lane
938 * @return int; the rank that the newly added GTU has on this Lane (should be 0, except when the GTU enters this Lane due to
939 * a lane change operation)
940 * @throws GTUException when longitudinalPosition is negative or exceeds the length of this Lane
941 */
942 public final int addGTU(final LaneBasedGTU gtu, final Length longitudinalPosition) throws GTUException
943 {
944 return addGTU(gtu, longitudinalPosition.getSI() / getLength().getSI());
945 }
946
947 /**
948 * Remove a GTU from the GTU list of this lane.
949 * @param gtu the GTU to remove.
950 * @param removeFromParentLink when the GTU leaves the last lane of the parentLink of this Lane
951 */
952 public final void removeGTU(final LaneBasedGTU gtu, final boolean removeFromParentLink)
953 {
954 this.gtuList.remove(gtu);
955 fireTimedEvent(Lane.GTU_REMOVE_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size() },
956 gtu.getSimulator().getSimulatorTime());
957 if (removeFromParentLink)
958 {
959 this.parentLink.removeGTU(gtu);
960 }
961 }
962
963 /**
964 * Get the first GTU where the relativePosition is in front of a certain position on the lane, in a driving direction on
965 * this lane, compared to the DESIGN LINE.
966 * @param position Length; the position after which the relative position of a GTU will be searched.
967 * @param direction GTUDirectionality; whether we are looking in the the center line direction or against the center line
968 * direction.
969 * @param relativePosition RelativePosition.TYPE; the relative position we want to compare against
970 * @param when Time; the time for which to evaluate the positions.
971 * @return LaneBasedGTU; the first GTU after a position on this lane in the given direction, or null if no GTU could be
972 * found.
973 * @throws GTUException when there is a problem with the position of the GTUs on the lane.
974 */
975 public final LaneBasedGTU getGtuAhead(final Length position, final GTUDirectionality direction,
976 final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
977 {
978 if (direction.equals(GTUDirectionality.DIR_PLUS))
979 {
980 for (LaneBasedGTU gtu : this.gtuList)
981 {
982 if (gtu.position(this, gtu.getRelativePositions().get(relativePosition), when).gt(position))
983 {
984 return gtu;
985 }
986 }
987 }
988 else
989 {
990 for (int i = this.gtuList.size() - 1; i >= 0; i--)
991 {
992 LaneBasedGTU gtu = this.gtuList.get(i);
993 if (gtu.position(this, gtu.getRelativePositions().get(relativePosition), when).lt(position))
994 {
995 return gtu;
996 }
997 }
998 }
999 return null;
1000 }
1001
1002 /**
1003 * Get the first object where the relativePosition is in front of a certain position on the lane, in a driving direction on
1004 * this lane, compared to the DESIGN LINE. Perception should iterate over results from this method to see what is most
1005 * limiting.
1006 * @param position Length; the position after which the relative position of an object will be searched.
1007 * @param direction GTUDirectionality; whether we are looking in the the center line direction or against the center line
1008 * direction.
1009 * @return Set<LaneBasedObject>; the first object(s) after a position on this lane in the given direction, or null if
1010 * no object could be found.
1011 */
1012 public final List<LaneBasedObject> getObjectAhead(final Length position, final GTUDirectionality direction)
1013 {
1014 if (direction.equals(GTUDirectionality.DIR_PLUS))
1015 {
1016 for (double distance : this.laneBasedObjects.keySet())
1017 {
1018 if (distance > position.si)
1019 {
1020 return new ArrayList<>(this.laneBasedObjects.get(distance));
1021 }
1022 }
1023 }
1024 else
1025 {
1026 NavigableMap<Double, List<LaneBasedObject>> reverseLBO =
1027 (NavigableMap<Double, List<LaneBasedObject>>) this.laneBasedObjects;
1028 for (double distance : reverseLBO.descendingKeySet())
1029 {
1030 if (distance < position.si)
1031 {
1032 return new ArrayList<>(this.laneBasedObjects.get(distance));
1033 }
1034 }
1035 }
1036 return null;
1037 }
1038
1039 /**
1040 * Get the first GTU where the relativePosition is behind a certain position on the lane, in a driving direction on this
1041 * lane, compared to the DESIGN LINE.
1042 * @param position Length; the position before which the relative position of a GTU will be searched.
1043 * @param direction GTUDirectionality; whether we are looking in the the center line direction or against the center line
1044 * direction.
1045 * @param relativePosition RelativePosition.TYPE; the relative position of the GTU we are looking for.
1046 * @param when Time; the time for which to evaluate the positions.
1047 * @return LaneBasedGTU; the first GTU before a position on this lane in the given direction, or null if no GTU could be
1048 * found.
1049 * @throws GTUException when there is a problem with the position of the GTUs on the lane.
1050 */
1051 public final LaneBasedGTU getGtuBehind(final Length position, final GTUDirectionality direction,
1052 final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
1053 {
1054 if (direction.equals(GTUDirectionality.DIR_PLUS))
1055 {
1056 return getGtuAhead(position, GTUDirectionality.DIR_MINUS, relativePosition, when);
1057 }
1058 return getGtuAhead(position, direction, relativePosition, when);
1059 }
1060
1061 /*
1062 * TODO only center position? Or also width? What is a good cutoff? Base on average width of the GTU type that can drive on
1063 * this Lane? E.g., for a Tram or Train, a 5 cm deviation is a problem; for a Car or a Bicycle, more deviation is
1064 * acceptable.
1065 */
1066 /** Lateral alignment margin for longitudinally connected Lanes. */
1067 public static final Length MARGIN = new Length(0.5, LengthUnit.METER);
1068
1069 /**
1070 * NextLanes returns the successor lane(s) in the design line direction, if any exist.<br>
1071 * The next lane(s) are cached, as it is too expensive to make the calculation every time. There are several possibilities:
1072 * (1) Returning an empty set when there is no successor lane in the design direction or there is no longitudinal transfer
1073 * possible to a successor lane in the design direction. (2) Returning a set with just one lane if the lateral position of
1074 * the successor lane matches the lateral position of this lane (based on an overlap of the lateral positions of the two
1075 * joining lanes of more than a certain percentage). (3) Multiple lanes in case the Node where the underlying Link for this
1076 * Lane has multiple "outgoing" Links, and there are multiple lanes that match the lateral position of this lane.<br>
1077 * The next lanes can differ per GTU type. For instance, a lane where cars and buses are allowed can have a next lane where
1078 * only buses are allowed, forcing the cars to leave that lane.
1079 * @param gtuType the GTU type for which we return the next lanes.
1080 * @return set of Lanes following this lane for the given GTU type.
1081 */
1082 public final Map<Lane, GTUDirectionality> nextLanes(final GTUType gtuType)
1083 {
1084 if (this.nextLanes == null)
1085 {
1086 this.nextLanes = new LinkedHashMap<>(1);
1087 }
1088 if (!this.nextLanes.containsKey(gtuType))
1089 {
1090 Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1091 this.nextLanes.put(gtuType, laneMap);
1092 // Construct (and cache) the result.
1093 for (Link link : getParentLink().getEndNode().getLinks())
1094 {
1095 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1096 {
1097 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1098 {
1099 if (cse instanceof Lane)
1100 {
1101 Lane lane = (Lane) cse;
1102 Length df = this.getCenterLine().getLast().distance(lane.getCenterLine().getFirst());
1103 Length dl = this.getCenterLine().getLast().distance(lane.getCenterLine().getLast());
1104 // this, parentLink ---> O ---> lane, link
1105 if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getEndNode()))
1106 {
1107 // does the GTU move in the design line direction or against it?
1108 // TODO And is it aligned with its next lane?
1109 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1110 {
1111 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1112 }
1113 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1114 {
1115 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1116 }
1117 }
1118 // this, parentLink ---> O <--- lane, link
1119 else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getEndNode()))
1120 {
1121 // does the GTU move in the design line direction or against it?
1122 // TODO And is it aligned with its next lane?
1123 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1124 {
1125 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1126 }
1127 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1128 {
1129 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1130 }
1131 }
1132 }
1133 }
1134 }
1135 }
1136 }
1137 return this.nextLanes.get(gtuType);
1138 }
1139
1140 /**
1141 * PrevLanes returns the predecessor lane(s) relative to the design line direction, if any exist.<br>
1142 * The previous lane(s) are cached, as it is too expensive to make the calculation every time. There are several
1143 * possibilities: (1) Returning an empty set when there is no predecessor lane relative to the design direction or there is
1144 * no longitudinal transfer possible to a predecessor lane relative to the design direction. (2) Returning a set with just
1145 * one lane if the lateral position of the predecessor lane matches the lateral position of this lane (based on an overlap
1146 * of the lateral positions of the two joining lanes of more than a certain percentage). (3) Multiple lanes in case the Node
1147 * where the underlying Link for this Lane has multiple "incoming" Links, and there are multiple lanes that match the
1148 * lateral position of this lane.<br>
1149 * The previous lanes can differ per GTU type. For instance, a lane where cars and buses are allowed can be preceded by a
1150 * lane where only buses are allowed.
1151 * @param gtuType the GTU type for which we return the next lanes.
1152 * @return set of Lanes following this lane for the given GTU type.
1153 */
1154 public final Map<Lane, GTUDirectionality> prevLanes(final GTUType gtuType)
1155 {
1156 if (this.prevLanes == null)
1157 {
1158 this.prevLanes = new LinkedHashMap<>(1);
1159 }
1160 if (!this.prevLanes.containsKey(gtuType))
1161 {
1162 Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1163 this.prevLanes.put(gtuType, laneMap);
1164 // Construct (and cache) the result.
1165 for (Link link : getParentLink().getStartNode().getLinks())
1166 {
1167 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1168 {
1169 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1170 {
1171 if (cse instanceof Lane)
1172 {
1173 Lane lane = (Lane) cse;
1174 Length df = this.getCenterLine().getFirst().distance(lane.getCenterLine().getFirst());
1175 Length dl = this.getCenterLine().getFirst().distance(lane.getCenterLine().getLast());
1176 // this, parentLink <--- O ---> lane, link
1177 if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getStartNode()))
1178 {
1179 // does the GTU move in the design line direction or against it?
1180 // TODO And is it aligned with its next lane?
1181 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1182 {
1183 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1184 }
1185 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1186 {
1187 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1188 }
1189 }
1190 // this, parentLink <--- O <--- lane, link
1191 else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getStartNode()))
1192 {
1193 // does the GTU move in the design line direction or against it?
1194 // TODO And is it aligned with its next lane?
1195 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1196 {
1197 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1198 }
1199 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1200 {
1201 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1202 }
1203 }
1204 }
1205 }
1206 }
1207 }
1208 }
1209 return this.prevLanes.get(gtuType);
1210 }
1211
1212 /**
1213 * Determine the set of lanes to the left or to the right of this lane, which are accessible from this lane, or an empty set
1214 * if no lane could be found. The method takes the LongitidinalDirectionality of the lane into account. In other words, if
1215 * we drive in the DIR_PLUS direction and look for a lane on the LEFT, and there is a lane but the Directionality of that
1216 * lane is not DIR_PLUS or DIR_BOTH, it will not be included.<br>
1217 * A lane is called adjacent to another lane if the lateral edges are not more than a delta distance apart. This means that
1218 * a lane that <i>overlaps</i> with another lane is <b>not</b> returned as an adjacent lane. <br>
1219 * <b>Note:</b> LEFT and RIGHT are seen from the direction of the GTU, in its forward driving direction. <br>
1220 * @param lateralDirection LEFT or RIGHT.
1221 * @param gtuType the type of GTU for which to return the adjacent lanes.
1222 * @return the set of lanes that are accessible, or null if there is no lane that is accessible with a matching driving
1223 * direction.
1224 */
1225 public final Set<Lane> accessibleAdjacentLanes(final LateralDirectionality lateralDirection, final GTUType gtuType)
1226 {
1227 Set<Lane> candidates = new LinkedHashSet<>(1);
1228 LateralDirectionality dir = this.getDirectionality(gtuType).isForwardOrBoth() ? lateralDirection
1229 : lateralDirection.isLeft() ? LateralDirectionality.RIGHT : LateralDirectionality.LEFT;
1230 for (Lane lane : neighbors(dir, gtuType))
1231 {
1232 if (lane.getDirectionality(gtuType).equals(LongitudinalDirectionality.DIR_BOTH)
1233 || lane.getDirectionality(gtuType).equals(this.getDirectionality(gtuType)))
1234 {
1235 candidates.add(lane);
1236 }
1237 }
1238 return candidates;
1239 }
1240
1241 /**
1242 * Get the speed limit of this lane, which can differ per GTU type. E.g., cars might be allowed to drive 120 km/h and trucks
1243 * 90 km/h.
1244 * @param gtuType the GTU type to provide the speed limit for
1245 * @return the speedLimit.
1246 * @throws NetworkException on network inconsistency
1247 */
1248 public final Speed getSpeedLimit(final GTUType gtuType) throws NetworkException
1249 {
1250 if (this.speedLimitMap.containsKey(gtuType))
1251 {
1252 return this.speedLimitMap.get(gtuType);
1253 }
1254 if (this.speedLimitMap.containsKey(GTUType.ALL))
1255 {
1256 return this.speedLimitMap.get(GTUType.ALL);
1257 }
1258 throw new NetworkException("No speed limit set for GTUType " + gtuType + " on lane " + toString());
1259 }
1260
1261 /**
1262 * Set the speed limit of this lane, which can differ per GTU type. Cars might be allowed to drive 120 km/h and trucks 90
1263 * km/h. If the speed limit is the same for all GTU types, GTUType.ALL will be used. This means that the settings can be
1264 * used additive, or subtractive. <br>
1265 * In <b>additive use</b>, do not set the speed limit for GTUType.ALL. Now, one by one, the allowed maximum speeds for each
1266 * of the GTU Types have be added. Do this when there are few GTU types or the speed limits per TU type are very different.
1267 * <br>
1268 * In <b>subtractive use</b>, set the speed limit for GTUType.ALL to the most common one. Override the speed limit for
1269 * certain GTUTypes to a different value. An example is a lane on a highway where all vehicles, except truck (CAR, BUS,
1270 * MOTORCYCLE, etc.), can drive 120 km/h, but trucks are allowed only 90 km/h. In that case, set the speed limit for
1271 * GTUType.ALL to 120 km/h, and for TRUCK to 90 km/h.
1272 * @param gtuType the GTU type to provide the speed limit for
1273 * @param speedLimit the speed limit for this gtu type
1274 */
1275 public final void setSpeedLimit(final GTUType gtuType, final Speed speedLimit)
1276 {
1277 this.speedLimitMap.put(gtuType, speedLimit);
1278 }
1279
1280 /**
1281 * Remove the set speed limit for a GTUType. If the speed limit for GTUType.ALL will be removed, there will not be a
1282 * 'default' speed limit anymore. If the speed limit for a certain GTUType is removed, its speed limit will default to the
1283 * speed limit of GTUType.ALL. <br>
1284 * <b>Note</b>: if no speed limit is known for a GTUType, getSpeedLimit will throw a NetworkException when the speed limit
1285 * is retrieved for that GTUType.
1286 * @param gtuType the GTU type to provide the speed limit for
1287 */
1288 public final void removeSpeedLimit(final GTUType gtuType)
1289 {
1290 this.speedLimitMap.remove(gtuType);
1291 }
1292
1293 /**
1294 * @return laneType.
1295 */
1296 public final LaneType getLaneType()
1297 {
1298 return this.laneType;
1299 }
1300
1301 /**
1302 * The direction in which vehicles can drive, i.e., in direction of geometry, reverse, or both. This can differ per GTU
1303 * type. In an overtake lane, cars might overtake and trucks not. It might be that the lane (e.g., a street in a city) is
1304 * FORWARD (from start node of the link to end node of the link) for the GTU type CAR, but BOTH for the GTU type BICYCLE
1305 * (i.e., bicycles can also go in the other direction, opposite to the drawing direction of the Link). If the directionality
1306 * for a GTUType is set to NONE, this means that the given GTUType cannot use the Lane. If a Directionality is set for
1307 * GTUType.ALL, the getDirectionality will default to these settings when there is no specific entry for a given
1308 * directionality. This means that the settings can be used additive, or restrictive. <br>
1309 * In <b>additive use</b>, set the directionality for GTUType.ALL to NONE, or do not set the directionality for GTUType.ALL.
1310 * Now, one by one, the allowed directionalities can be added. An example is a lane on a highway, which we only open for
1311 * CAR, TRUCK and BUS. <br>
1312 * In <b>restrictive use</b>, set the directionality for GTUType.ALL to BOTH, FORWARD, or BACKWARD. Override the
1313 * directionality for certain GTUTypes to a more restrictive access, e.g. to NONE. An example is a lane that is open for all
1314 * road users, except TRUCK.
1315 * @param gtuType the GTU type to provide the directionality for
1316 * @return the directionality.
1317 */
1318 public final LongitudinalDirectionality getDirectionality(final GTUType gtuType)
1319 {
1320 if (this.directionalityMap.containsKey(gtuType))
1321 {
1322 return this.directionalityMap.get(gtuType);
1323 }
1324 if (this.directionalityMap.containsKey(GTUType.ALL))
1325 {
1326 return this.directionalityMap.get(GTUType.ALL);
1327 }
1328 return LongitudinalDirectionality.DIR_NONE;
1329 }
1330
1331 /**
1332 * This method sets the directionality of the lane for a GTU type. It might be that the driving direction in the lane is
1333 * FORWARD (from start node of the link to end node of the link) for the GTU type CAR, but BOTH for the GTU type BICYCLE
1334 * (i.e., bicycles can also go in the other direction; we see this on some city streets). If the directionality for a
1335 * GTUType is set to NONE, this means that the given GTUType cannot use the Lane. If a Directionality is set for
1336 * GTUType.ALL, the getDirectionality will default to these settings when there is no specific entry for a given
1337 * directionality. This means that the settings can be used additive, or restrictive. <br>
1338 * In <b>additive use</b>, set the directionality for GTUType.ALL to NONE, or do not set the directionality for GTUType.ALL.
1339 * Now, one by one, the allowed directionalities can be added. An example is a lane on a highway, which we only open for
1340 * CAR, TRUCK and BUS. <br>
1341 * In <b>restrictive use</b>, set the directionality for GTUType.ALL to BOTH, FORWARD, or BACKWARD. Override the
1342 * directionality for certain GTUTypes to a more restrictive access, e.g. to NONE. An example is a lane that is open for all
1343 * road users, except TRUCK.
1344 * @param gtuType the GTU type to set the directionality for.
1345 * @param directionality the longitudinal directionality of the link (FORWARD, BACKWARD, BOTH or NONE) for the given GTU
1346 * type.
1347 * @throws NetworkException when the lane directionality for the given GTUType is inconsistent with the Link directionality
1348 * to which the lane belongs.
1349 */
1350 public void addDirectionality(final GTUType gtuType, final LongitudinalDirectionality directionality)
1351 throws NetworkException
1352 {
1353 this.directionalityMap.put(gtuType, directionality);
1354 checkDirectionality();
1355 }
1356
1357 /**
1358 * This method removes an earlier provided directionality of the lane for a given GTU type, e.g. for maintenance of the
1359 * lane. After removing, the directionality for the GTU will fall back to the provided directionality for GTUType.ALL (if
1360 * present). Thereby removing a directionality is different from setting the directionality to NONE.
1361 * @param gtuType the GTU type to remove the directionality for on this lane.
1362 */
1363 public void removeDirectionality(final GTUType gtuType)
1364 {
1365 this.directionalityMap.remove(gtuType);
1366 }
1367
1368 /**
1369 * Check whether the directionalities for the GTU types for this lane are consistent with the directionalities of the
1370 * overarching Link.
1371 * @throws NetworkException when the lane directionality for a given GTUType is inconsistent with the Link directionality to
1372 * which the lane belongs.
1373 */
1374 private void checkDirectionality() throws NetworkException
1375 {
1376 for (GTUType gtuType : this.directionalityMap.keySet())
1377 {
1378 LongitudinalDirectionality directionality = this.directionalityMap.get(gtuType);
1379 if (!getParentLink().getDirectionality(gtuType).contains(directionality))
1380 {
1381 throw new NetworkException("Lane " + toString() + " allows " + gtuType + " a directionality of "
1382 + directionality + " which is not present in the overarching link " + getParentLink().toString());
1383 }
1384 }
1385 }
1386
1387 /**
1388 * @return gtuList.
1389 */
1390 public final List<LaneBasedGTU> getGtuList()
1391 {
1392 return this.gtuList == null ? new ArrayList<>() : this.gtuList;
1393 }
1394
1395 /** {@inheritDoc} */
1396 @Override
1397 @SuppressWarnings("checkstyle:designforextension")
1398 protected double getZ()
1399 {
1400 return 0.0;
1401 }
1402
1403 /**
1404 * @return overtakingConditions
1405 */
1406 public final OvertakingConditions getOvertakingConditions()
1407 {
1408 return this.overtakingConditions;
1409 }
1410
1411 /** {@inheritDoc} */
1412 public final String toString()
1413 {
1414 CrossSectionLink link = getParentLink();
1415 return String.format("Lane %s of %s", getId(), link.getId());
1416 }
1417
1418 /** Cache of the hashCode. */
1419 private Integer cachedHashCode = null;
1420
1421 /** {@inheritDoc} */
1422 @SuppressWarnings("checkstyle:designforextension")
1423 @Override
1424 public int hashCode()
1425 {
1426 if (this.cachedHashCode == null)
1427 {
1428 final int prime = 31;
1429 int result = super.hashCode();
1430 result = prime * result + ((this.laneType == null) ? 0 : this.laneType.hashCode());
1431 this.cachedHashCode = result;
1432 }
1433 return this.cachedHashCode;
1434 }
1435
1436 /** {@inheritDoc} */
1437 @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
1438 @Override
1439 public boolean equals(final Object obj)
1440 {
1441 if (this == obj)
1442 return true;
1443 if (!super.equals(obj))
1444 return false;
1445 if (getClass() != obj.getClass())
1446 return false;
1447 Lane other = (Lane) obj;
1448 if (this.laneType == null)
1449 {
1450 if (other.laneType != null)
1451 return false;
1452 }
1453 else if (!this.laneType.equals(other.laneType))
1454 return false;
1455 return true;
1456 }
1457
1458 /** {@inheritDoc} */
1459 @Override
1460 @SuppressWarnings("checkstyle:designforextension")
1461 public Lane clone(final CrossSectionLink newParentLink, final OTSSimulatorInterface newSimulator, final boolean animation)
1462 throws NetworkException
1463 {
1464 try
1465 {
1466 Lane newLane = new Lane(newParentLink, newSimulator, animation, this);
1467 // nextLanes, prevLanes, nextNeighbors, rightNeighbors are filled at first request
1468
1469 SortedMap<Double, List<Sensor>> newSensorMap = new TreeMap<>();
1470 for (double distance : this.sensors.keySet())
1471 {
1472 List<Sensor> newSensorList = new ArrayList<>();
1473 for (Sensor sensor : this.sensors.get(distance))
1474 {
1475 Sensor newSensor = ((AbstractSensor) sensor).clone(newLane, newSimulator, animation);
1476 newSensorList.add(newSensor);
1477 }
1478 newSensorMap.put(distance, newSensorList);
1479 }
1480 newLane.sensors.clear();
1481 newLane.sensors.putAll(newSensorMap);
1482
1483 SortedMap<Double, List<LaneBasedObject>> newLaneBasedObjectMap = new TreeMap<>();
1484 for (double distance : this.laneBasedObjects.keySet())
1485 {
1486 List<LaneBasedObject> newLaneBasedObjectList = new ArrayList<>();
1487 for (LaneBasedObject lbo : this.laneBasedObjects.get(distance))
1488 {
1489 AbstractLaneBasedObject laneBasedObject = (AbstractLaneBasedObject) lbo;
1490 LaneBasedObject newLbo = laneBasedObject.clone(newLane, newSimulator, animation);
1491 newLaneBasedObjectList.add(newLbo);
1492 }
1493 newLaneBasedObjectMap.put(distance, newLaneBasedObjectList);
1494 }
1495 newLane.laneBasedObjects.clear();
1496 newLane.laneBasedObjects.putAll(newLaneBasedObjectMap);
1497
1498 if (animation)
1499 {
1500 new LaneAnimation(newLane, newSimulator, Color.DARK_GRAY, false);
1501 }
1502 return newLane;
1503 }
1504 catch (NamingException | RemoteException exception)
1505 {
1506 throw new NetworkException(exception);
1507 }
1508 }
1509 }