View Javadoc
1   package org.opentrafficsim.road.gtu.strategical;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.Iterator;
6   import java.util.List;
7   import java.util.Set;
8   
9   import org.djunits.value.vdouble.scalar.Time;
10  import org.djutils.exceptions.Throw;
11  import org.djutils.exceptions.Try;
12  import org.opentrafficsim.core.gtu.GtuException;
13  import org.opentrafficsim.core.gtu.GtuType;
14  import org.opentrafficsim.core.network.Link;
15  import org.opentrafficsim.core.network.NetworkException;
16  import org.opentrafficsim.core.network.Node;
17  import org.opentrafficsim.core.network.route.Route;
18  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
19  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlanner;
20  import org.opentrafficsim.road.network.lane.CrossSectionLink;
21  import org.opentrafficsim.road.network.lane.Lane;
22  import org.opentrafficsim.road.network.lane.LanePosition;
23  
24  /**
25   * This is the standard strategical route planner with a fixed tactical planner. If no route is supplied, but there is a
26   * destination, a route will be drawn from a {@code RouteGenerator}, or using length-based shortest path if that is also not
27   * specified. If the route is ever {@code null}, a route will be constructed from the origin to the current link, and from the
28   * current link to the destination.
29   * <p>
30   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
31   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
32   * </p>
33   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
34   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
35   */
36  public class LaneBasedStrategicalRoutePlanner implements LaneBasedStrategicalPlanner, Serializable
37  {
38      /** */
39      private static final long serialVersionUID = 20151126L;
40  
41      /** GTU. */
42      private final LaneBasedGtu gtu;
43  
44      /** The route to drive. */
45      private Route route;
46  
47      /** Origin node. */
48      private final Node origin;
49  
50      /** Destination node. */
51      private Node destination;
52  
53      /** The fixed tactical planner to use for the GTU. */
54      private final LaneBasedTacticalPlanner fixedTacticalPlanner;
55  
56      /** Route supplier. */
57      private final RouteGenerator routeGenerator;
58  
59      /**
60       * Constructor for a strategical planner without route. This can only be used if the network does not have splits, or split
61       * fractions are used.
62       * @param fixedTacticalPlanner LaneBasedTacticalPlanner; the tactical planner to use for the GTU
63       * @param gtu LaneBasedGtu; GTU
64       * @throws GtuException if fixed tactical planner == null
65       */
66      public LaneBasedStrategicalRoutePlanner(final LaneBasedTacticalPlanner fixedTacticalPlanner, final LaneBasedGtu gtu)
67              throws GtuException
68      {
69          this(fixedTacticalPlanner, null, gtu, null, null, RouteGenerator.NULL);
70      }
71  
72      /**
73       * Constructor for a strategical planner with route. If the route is {@code null}, a shortest path to the destination is
74       * derived.
75       * @param fixedTacticalPlanner LaneBasedTacticalPlanner; the tactical planner to use for the GTU
76       * @param route Route; the route to drive
77       * @param gtu LaneBasedGtu; GTU
78       * @param origin Node; origin node
79       * @param destination Node; destination node
80       * @param routeGenerator RouteGeneratorOD; route generator
81       * @throws GtuException if fixed tactical planner == null
82       */
83      public LaneBasedStrategicalRoutePlanner(final LaneBasedTacticalPlanner fixedTacticalPlanner, final Route route,
84              final LaneBasedGtu gtu, final Node origin, final Node destination, final RouteGenerator routeGenerator)
85              throws GtuException
86      {
87          this.gtu = gtu;
88          this.route = route;
89          this.origin = origin;
90          this.destination = destination;
91          this.fixedTacticalPlanner = fixedTacticalPlanner;
92          Throw.when(fixedTacticalPlanner == null, GtuException.class,
93                  "Fixed Tactical Planner for a Strategical planner is null");
94          this.routeGenerator = routeGenerator;
95      }
96  
97      /** {@inheritDoc} */
98      @Override
99      public final LaneBasedGtu getGtu()
100     {
101         return this.gtu;
102     }
103 
104     /** {@inheritDoc} */
105     @Override
106     public final LaneBasedTacticalPlanner getTacticalPlanner()
107     {
108         return this.fixedTacticalPlanner;
109     }
110 
111     /** {@inheritDoc} */
112     @Override
113     public LaneBasedTacticalPlanner getTacticalPlanner(final Time time)
114     {
115         return this.fixedTacticalPlanner; // fixed anyway
116     }
117 
118     /** {@inheritDoc} */
119     @Override
120     public final Link nextLink(final Link previousLink, final GtuType gtuType) throws NetworkException
121     {
122         assureRoute(gtuType);
123 
124         Node node = previousLink.getEndNode();
125         // if there is no split, don't ask the route
126         if (node.getLinks().size() == 1 && previousLink != null)
127         {
128             // end node
129             throw new NetworkException(
130                     "LaneBasedStrategicalRoutePlanner is asked for a next link, but node " + node + " has no successors");
131         }
132         if (node.getLinks().size() == 1 && previousLink == null)
133         {
134             // start node
135             return node.getLinks().iterator().next();
136         }
137         if (node.getLinks().size() == 2)
138         {
139             for (Link link : node.getLinks())
140             {
141                 if (!link.equals(previousLink))
142                 {
143                     return link;
144                 }
145             }
146         }
147 
148         // if we only have one way to go, don't bother about the route yet
149         Set<Link> links = node.getLinks().toSet();
150         for (Iterator<Link> linkIterator = links.iterator(); linkIterator.hasNext();)
151         {
152             Link link = linkIterator.next();
153             if (link.equals(previousLink))
154             {
155                 // No u-turn...
156                 linkIterator.remove();
157             }
158             else
159             {
160                 // does the directionality of the link forbid us to go in?
161                 if (link.getEndNode().equals(node))
162                 {
163                     linkIterator.remove();
164                 }
165                 else
166                 {
167                     // are there no lanes from the node into this link in the outgoing direction?
168                     boolean out = false;
169                     CrossSectionLink csLink = (CrossSectionLink) link;
170                     for (Lane lane : csLink.getLanes())
171                     {
172                         if ((link.getStartNode().equals(node) && lane.getType().isCompatible(gtuType)))
173                         {
174                             out = true;
175                             break;
176                         }
177                     }
178                     if (!out)
179                     {
180                         linkIterator.remove();
181                     }
182                 }
183             }
184         }
185 
186         if (links.size() == 1)
187         {
188             return links.iterator().next();
189         }
190 
191         // more than 2 links... We have to check the route!
192         if (getRoute() == null)
193         {
194             throw new NetworkException("LaneBasedStrategicalRoutePlanner does not have a route");
195         }
196         int i = this.route.getNodes().indexOf(node);
197         if (i == -1)
198         {
199             throw new NetworkException("LaneBasedStrategicalRoutePlanner is asked for a next link coming from " + previousLink
200                     + ", but node " + node + " not in route " + this.route);
201         }
202         if (i == this.route.getNodes().size() - 1)
203         {
204             throw new NetworkException("LaneBasedStrategicalRoutePlanner is asked for a next link coming from " + previousLink
205                     + ", but the GTU reached the last node for route " + this.route);
206         }
207         Node nextNode = this.route.getNode(i + 1);
208         Link result = null;
209         for (Link link : links)
210         {
211             // TODO this takes the first in the set of links that connects the correct nodes; does not handle parallel links
212             // consistently
213             Link l = null;
214             if (link.getStartNode().equals(nextNode) && link.getEndNode().equals(node))
215             {
216                 l = link; // FIXME: Probably this test can be removed since we only go "forward"
217             }
218             if (link.getEndNode().equals(nextNode) && link.getStartNode().equals(node))
219             {
220                 l = link;
221             }
222             if (null != result && null != l)
223             {
224                 throw new NetworkException("Cannot choose among multiple links from " + node + " to " + nextNode);
225             }
226             else if (null == result)
227             {
228                 result = l;
229             }
230         }
231         if (null == result)
232         {
233             throw new NetworkException("LaneBasedStrategicalRoutePlanner is asked for a next link coming from "
234                     + previousLink.getId() + ", but no link could be found connecting node " + node + " and node " + nextNode
235                     + " for route " + this.route);
236         }
237         return result;
238     }
239 
240     /** {@inheritDoc} */
241     @Override
242     public final Route getRoute()
243     {
244         assureRoute(getGtu().getType());
245         // if assure route left the route null although there is a destination, we have no route generator, use shortest-path
246         if (this.route == null && this.destination != null)
247         {
248             try
249             {
250                 LanePosition pos = getGtu().getReferencePosition();
251                 CrossSectionLink link = pos.lane().getLink();
252                 Node from = link.getStartNode();
253                 this.route = link.getNetwork().getShortestRouteBetween(getGtu().getType(), from, this.destination);
254             }
255             catch (GtuException | NetworkException exception)
256             {
257                 throw new RuntimeException("Route could not be determined.", exception);
258             }
259         }
260         return this.route;
261     }
262 
263     /**
264      * Assures a route is available if a route is already present, or a destination and route supplier are provided.
265      * @param gtuType GtuType; the type of the GTU for which a route must be assured
266      */
267     private void assureRoute(final GtuType gtuType)
268     {
269         if (this.route == null && this.destination != null && !this.routeGenerator.equals(RouteGenerator.NULL))
270         {
271             LanePosition ref = Try.assign(() -> getGtu().getReferencePosition(), "Could not retrieve GTU reference position.");
272             List<Node> nodes = new ArrayList<>();
273             if (this.origin != null)
274             {
275                 nodes.addAll(this.routeGenerator.getRoute(this.origin, ref.lane().getLink().getStartNode(), gtuType)
276                         .getNodes());
277             }
278             else
279             {
280                 nodes.add(ref.lane().getLink().getStartNode());
281             }
282             Route newRoute =
283                     this.routeGenerator.getRoute(ref.lane().getLink().getEndNode(), this.destination, gtuType);
284             nodes.addAll(newRoute.getNodes());
285             this.route = Try.assign(() -> new Route("Route for " + gtuType + " from " + this.origin + "to " + this.destination
286                     + " via " + ref.lane().getLink(), gtuType, nodes), "No route possible over nodes %s", nodes);
287         }
288     }
289 
290     /** {@inheritDoc} */
291     @Override
292     public final Node getOrigin()
293     {
294         return this.origin;
295     }
296 
297     /** {@inheritDoc} */
298     @Override
299     public final Node getDestination()
300     {
301         return this.destination;
302     }
303 
304     /** {@inheritDoc} */
305     @Override
306     public final String toString()
307     {
308         return "LaneBasedStrategicalRoutePlanner [route=" + this.route + ", fixedTacticalPlanner=" + this.fixedTacticalPlanner
309                 + "]";
310     }
311 
312 }