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