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