View Javadoc
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&lt;Lane, Double&gt;; 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 }