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