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