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