View Javadoc
1   package org.opentrafficsim.road.network.route;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   
6   import org.djunits.unit.LengthUnit;
7   import org.djunits.unit.TimeUnit;
8   import org.opentrafficsim.core.gtu.GTUType;
9   import org.opentrafficsim.core.network.LateralDirectionality;
10  import org.opentrafficsim.core.network.Link;
11  import org.opentrafficsim.core.network.NetworkException;
12  import org.opentrafficsim.core.network.Node;
13  import org.opentrafficsim.core.network.route.CompleteRoute;
14  import org.opentrafficsim.road.network.lane.CrossSectionElement;
15  import org.opentrafficsim.road.network.lane.CrossSectionLink;
16  import org.opentrafficsim.road.network.lane.Lane;
17  
18  /**
19   * A RouteNavigator helps to navigate on a route. In addition, helper methods are available to see if the GTU needs to change
20   * lanes to reach the next link on the route.
21   * <p>
22   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
23   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
24   * <p>
25   * $LastChangedDate: 2015-07-16 10:20:53 +0200 (Thu, 16 Jul 2015) $, @version $Revision: 1124 $, by $Author: pknoppers $,
26   * initial version Jul 22, 2015 <br>
27   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
28   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
29   */
30  public class CompleteLaneBasedRouteNavigator extends AbstractLaneBasedRouteNavigator
31  {
32      /** */
33      private static final long serialVersionUID = 20150722L;
34  
35      /** The complete route. */
36      @SuppressWarnings("checkstyle:visibilitymodifier")
37      protected final CompleteRoute completeRoute;
38  
39      /** last visited node on the route. */
40      @SuppressWarnings("checkstyle:visibilitymodifier")
41      protected int lastVisitedNodeIndex = -1;
42  
43      /**
44       * Create a navigator.
45       * @param completeRoute the route to follow
46       */
47      public CompleteLaneBasedRouteNavigator(final CompleteRoute completeRoute)
48      {
49          this.completeRoute = completeRoute;
50      }
51  
52      /** {@inheritDoc} */
53      @Override
54      public final Node lastVisitedNode() throws NetworkException
55      {
56          if (this.lastVisitedNodeIndex == -1)
57          {
58              return null;
59          }
60          return this.completeRoute.getNodes().get(this.lastVisitedNodeIndex);
61      }
62  
63      /** {@inheritDoc} */
64      @Override
65      public final Node nextNodeToVisit() throws NetworkException
66      {
67          if (this.lastVisitedNodeIndex >= this.completeRoute.size() - 1)
68          {
69              return null;
70          }
71          return this.completeRoute.getNodes().get(this.lastVisitedNodeIndex + 1);
72      }
73  
74      /** {@inheritDoc} */
75      @Override
76      public final Node visitNextNode() throws NetworkException
77      {
78          if (this.lastVisitedNodeIndex >= this.completeRoute.size() - 1)
79          {
80              return null;
81          }
82          this.lastVisitedNodeIndex++;
83          return this.completeRoute.getNodes().get(this.lastVisitedNodeIndex);
84      }
85  
86      /** {@inheritDoc} */
87      @Override
88      public final Length.Rel suitability(final Lane lane, final Length.Rel longitudinalPosition, final GTUType gtuType,
89          final Time.Rel timeHorizon) throws NetworkException
90      {
91          double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
92          double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtuType).getSI();
93          // Find the first upcoming Node where there is a branch
94          Node nextNode = lane.getParentLink().getEndNode();
95          Node nextSplitNode = null;
96          Lane currentLane = lane;
97          CrossSectionLink linkBeforeBranch = lane.getParentLink();
98          while (null != nextNode)
99          {
100             if (spareTime <= 0)
101             {
102                 return NOLANECHANGENEEDED; // It is not yet time to worry; this lane will do as well as any other
103             }
104             int laneCount = countCompatibleLanes(linkBeforeBranch, gtuType);
105             if (0 == laneCount)
106             {
107                 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
108             }
109             if (1 == laneCount)
110             {
111                 return NOLANECHANGENEEDED; // Only one compatible lane available; we'll get there "automatically";
112                 // i.e. without influence from the Route
113             }
114             int branching = nextNode.getLinksOut().size();
115             if (branching > 1)
116             { // Found a split
117                 nextSplitNode = nextNode;
118                 break;
119             }
120             else if (0 == branching)
121             {
122                 return NOLANECHANGENEEDED; // dead end; no more choices to make
123             }
124             else
125             { // Look beyond this nextNode
126                 Link nextLink = nextNode.getLinksOut().iterator().next(); // cannot be null
127                 if (nextLink instanceof CrossSectionLink)
128                 {
129                     nextNode = nextLink.getEndNode();
130                     // Oops: wrong code added the length of linkBeforeBranch in stead of length of nextLink
131                     remainingDistance += nextLink.getLength().getSI();
132                     linkBeforeBranch = (CrossSectionLink) nextLink;
133                     // Figure out the new currentLane
134                     if (currentLane.nextLanes(gtuType).size() == 0)
135                     {
136                         // Lane drop; our lane disappears. This is a compulsory lane change; which is not controlled
137                         // by the Route. Perform the forced lane change.
138                         if (currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType).size() > 0)
139                         {
140                             for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT,
141                                 gtuType))
142                             {
143                                 if (adjacentLane.nextLanes(gtuType).size() > 0)
144                                 {
145                                     currentLane = adjacentLane;
146                                     break;
147                                 }
148                                 // If there are several adjacent lanes that have non empty nextLanes, we simple take the
149                                 // first in the set
150                             }
151                         }
152                         for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType))
153                         {
154                             if (adjacentLane.nextLanes(gtuType).size() > 0)
155                             {
156                                 currentLane = adjacentLane;
157                                 break;
158                             }
159                             // If there are several adjacent lanes that have non empty nextLanes, we simple take the
160                             // first in the set
161                         }
162                         if (currentLane.nextLanes(gtuType).size() == 0)
163                         {
164                             throw new NetworkException("Lane ends and there is not a compatible adjacent lane that does "
165                                 + "not end");
166                         }
167                     }
168                     // Any compulsory lane change(s) have been performed and there is guaranteed a compatible next lane.
169                     for (Lane nextLane : currentLane.nextLanes(gtuType))
170                     {
171                         if (nextLane.getLaneType().isCompatible(gtuType))
172                         {
173                             currentLane = currentLane.nextLanes(gtuType).iterator().next();
174                             break;
175                         }
176                     }
177                     spareTime -= currentLane.getLength().getSI() / currentLane.getSpeedLimit(gtuType).getSI();
178                 }
179                 else
180                 {
181                     // There is a non-CrossSectionLink on the path to the next branch. A non-CrossSectionLink does not
182                     // have identifiable Lanes, therefore we can't aim for a particular Lane
183                     return NOLANECHANGENEEDED; // Any Lane will do equally well
184                 }
185             }
186         }
187         if (null == nextNode)
188         {
189             throw new NetworkException("Cannot find the next branch or sink node");
190         }
191         // We have now found the first upcoming branching Node
192         // Which continuing link is the one we need?
193         Map<Lane, Length.Rel> suitabilityOfLanesBeforeBranch = new HashMap<Lane, Length.Rel>();
194         Link linkAfterBranch = null;
195         for (Link link : nextSplitNode.getLinksOut())
196         {
197             Node nextNodeOnLink = link.getEndNode();
198             for (int i = this.lastVisitedNodeIndex + 1; i < this.completeRoute.size(); i++)
199             {
200                 Node n = this.completeRoute.getNodes().get(i);
201                 if (nextNodeOnLink == n)
202                 {
203                     if (null != linkAfterBranch)
204                     {
205                         throw new NetworkException("Parallel Links at " + nextSplitNode + " go to " + nextNodeOnLink);
206                         // FIXME If all but one of these have no Lane compatible with gtuType, dying here is a bit
207                         // premature
208                     }
209                     linkAfterBranch = link;
210                     break;
211                 }
212             }
213         }
214         if (null == linkAfterBranch)
215         {
216             throw new NetworkException("Cannot identify the link to follow after " + nextSplitNode + " in " + this);
217         }
218         for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
219         {
220             if (cse instanceof Lane)
221             {
222                 Lane l = (Lane) cse;
223                 if (l.getLaneType().isCompatible(gtuType))
224                 {
225                     for (Lane connectingLane : l.nextLanes(gtuType))
226                     {
227                         if (connectingLane.getParentLink() == linkAfterBranch
228                             && connectingLane.getLaneType().isCompatible(gtuType))
229                         {
230                             Length.Rel currentValue = suitabilityOfLanesBeforeBranch.get(l);
231                             // Use recursion to find out HOW suitable this continuation lane is, but don't revert back
232                             // to the maximum time horizon (or we could end up in infinite recursion when there are
233                             // loops in the network).
234                             Length.Rel value =
235                                 suitability(connectingLane, new Length.Rel(0, LengthUnit.SI), gtuType, new Time.Rel(
236                                     spareTime, TimeUnit.SI));
237                             // Use the minimum of the value computed for the first split junction (if there is one)
238                             // and the value computed for the second split junction.
239                             suitabilityOfLanesBeforeBranch.put(l, null == currentValue || value.le(currentValue) ? value
240                                 : currentValue);
241                         }
242                     }
243                 }
244             }
245         }
246         if (suitabilityOfLanesBeforeBranch.size() == 0)
247         {
248             throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
249         }
250         Length.Rel currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
251         if (null != currentLaneSuitability)
252         {
253             return currentLaneSuitability; // Following the current lane will keep us on the Route
254         }
255         // Performing one or more lane changes (left or right) is required.
256         int totalLanes = countCompatibleLanes(currentLane.getParentLink(), gtuType);
257         Length.Rel leftSuitability =
258             computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
259                 LateralDirectionality.LEFT, gtuType);
260         Length.Rel rightSuitability =
261             computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
262                 LateralDirectionality.RIGHT, gtuType);
263         if (leftSuitability.ge(rightSuitability))
264         {
265             return leftSuitability;
266         }
267         else if (rightSuitability.ge(leftSuitability))
268         {
269             return rightSuitability;
270         }
271         if (leftSuitability.le(GETOFFTHISLANENOW))
272         {
273             throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
274         }
275         return leftSuitability; // left equals right; this is odd but topologically possible
276     }
277 
278     /**
279      * @return the (complete) route.
280      */
281     public final CompleteRoute getRoute()
282     {
283         return this.completeRoute;
284     }
285 
286     /** {@inheritDoc} */
287     @SuppressWarnings("checkstyle:designforextension")
288     @Override
289     public String toString()
290     {
291         StringBuilder result = new StringBuilder();
292         final String currentLocationMark = "<>";
293         result.append("Route: [");
294         String separator = "";
295         if (this.lastVisitedNodeIndex < 0)
296         {
297             result.append(currentLocationMark);
298         }
299         for (int index = 0; index < this.completeRoute.size(); index++)
300         {
301             Node node = this.completeRoute.getNodes().get(index);
302             result.append(separator + node);
303             if (index == this.lastVisitedNodeIndex)
304             {
305                 result.append(" " + currentLocationMark); // Indicate current position in the route
306             }
307             separator = ", ";
308         }
309         result.append("]");
310         return result.toString();
311     }
312 
313 }