1 package org.opentrafficsim.road.network.route; 2 3 import java.io.Serializable; 4 import java.util.Map; 5 6 import org.djunits.unit.LengthUnit; 7 import org.opentrafficsim.core.gtu.GTUType; 8 import org.opentrafficsim.core.network.LateralDirectionality; 9 import org.opentrafficsim.road.network.lane.CrossSectionElement; 10 import org.opentrafficsim.road.network.lane.CrossSectionLink; 11 import org.opentrafficsim.road.network.lane.Lane; 12 13 /** 14 * A RouteNavigator helps to navigate on a route. In addition, helper methods are available to see if the GTU needs to change 15 * lanes to reach the next link on the route. 16 * <p> 17 * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br> 18 * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>. 19 * <p> 20 * $LastChangedDate: 2015-07-16 10:20:53 +0200 (Thu, 16 Jul 2015) $, @version $Revision: 1124 $, by $Author: pknoppers $, 21 * initial version Jul 22, 2015 <br> 22 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a> 23 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a> 24 */ 25 public abstract class AbstractLaneBasedRouteNavigator implements LaneBasedRouteNavigator, Serializable 26 { 27 /** */ 28 private static final long serialVersionUID = 20150722L; 29 30 /** Return value of suitability when no lane change is required withing the time horizon. */ 31 public static final Length.Rel NOLANECHANGENEEDED = new Length.Rel(Double.MAX_VALUE, LengthUnit.SI); 32 33 /** Return value of suitability when a lane change is required <i>right now</i>. */ 34 public static final Length.Rel GETOFFTHISLANENOW = new Length.Rel(0, LengthUnit.SI); 35 36 /** 37 * Compute the suitability of a lane from which lane changes are required to get to the next point on the Route.<br> 38 * This method weighs the suitability of the nearest suitable lane by (m - n) / m where n is the number of lane changes 39 * required and m is the total number of lanes in the CrossSectionLink. 40 * @param startLane Lane; the current lane of the GTU 41 * @param remainingDistance double; distance in m of GTU to first branch 42 * @param suitabilities Map<Lane, Double>; the set of suitable lanes and their suitability 43 * @param totalLanes integer; total number of lanes compatible with the GTU type 44 * @param direction LateralDirectionality; the direction of the lane changes to attempt 45 * @param gtuType GTUType; the type of the GTU 46 * @return double; the suitability of the <cite>startLane</cite> for following the Route 47 */ 48 protected final Length.Rel computeSuitabilityWithLaneChanges(final Lane startLane, final double remainingDistance, 49 final Map<Lane, Length.Rel> suitabilities, final int totalLanes, final LateralDirectionality direction, 50 final GTUType gtuType) 51 { 52 /*- 53 * The time per required lane change seems more relevant than distance per required lane change. 54 * Total time required does not grow linearly with the number of required lane changes. Logarithmic, arc tangent 55 * is more like it. 56 * Rijkswaterstaat appears to use a fixed time for ANY number of lane changes (about 60s). 57 * TomTom navigation systems give more time (about 90s). 58 * In this method the returned suitability decreases linearly with the number of required lane changes. This 59 * ensures that there is a gradient that coaches the GTU towards the most suitable lane. 60 */ 61 int laneChangesUsed = 0; 62 Lane currentLane = startLane; 63 Length.Rel currentSuitability = null; 64 while (null == currentSuitability) 65 { 66 laneChangesUsed++; 67 if (currentLane.accessibleAdjacentLanes(direction, gtuType).size() == 0) 68 { 69 return GETOFFTHISLANENOW; 70 } 71 currentLane = currentLane.accessibleAdjacentLanes(direction, gtuType).iterator().next(); 72 currentSuitability = suitabilities.get(currentLane); 73 } 74 double fraction = currentSuitability == NOLANECHANGENEEDED ? 0 : 0.5; 75 int notSuitableLaneCount = totalLanes - suitabilities.size(); 76 return new Length.Rel(remainingDistance * (notSuitableLaneCount - laneChangesUsed + 1 + fraction) 77 / (notSuitableLaneCount + fraction), LengthUnit.SI); 78 } 79 80 /** 81 * Determine how many lanes on a CrossSectionLink are compatible with a particular GTU type.<br> 82 * TODO: this method should probably be moved into the CrossSectionLink class 83 * @param link CrossSectionLink; the link 84 * @param gtuType GTUType; the GTU type 85 * @return integer; the number of lanes on the link that are compatible with the GTU type 86 */ 87 protected final int countCompatibleLanes(final CrossSectionLink link, final GTUType gtuType) 88 { 89 int result = 0; 90 for (CrossSectionElement cse : link.getCrossSectionElementList()) 91 { 92 if (cse instanceof Lane) 93 { 94 Lane l = (Lane) cse; 95 if (l.getLaneType().isCompatible(gtuType)) 96 { 97 result++; 98 } 99 } 100 } 101 return result; 102 } 103 }