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