View Javadoc
1   package org.opentrafficsim.road.gtu.strategical.route;
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.GTUDirectionality;
13  import org.opentrafficsim.core.gtu.GTUException;
14  import org.opentrafficsim.core.gtu.GTUType;
15  import org.opentrafficsim.core.network.Link;
16  import org.opentrafficsim.core.network.LinkDirection;
17  import org.opentrafficsim.core.network.NetworkException;
18  import org.opentrafficsim.core.network.Node;
19  import org.opentrafficsim.core.network.route.CompleteRoute;
20  import org.opentrafficsim.core.network.route.Route;
21  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
22  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlanner;
23  import org.opentrafficsim.road.gtu.strategical.AbstractLaneBasedStrategicalPlanner;
24  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
25  import org.opentrafficsim.road.network.lane.CrossSectionElement;
26  import org.opentrafficsim.road.network.lane.CrossSectionLink;
27  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
28  import org.opentrafficsim.road.network.lane.Lane;
29  
30  /**
31   * Strategical planner, route-based, with personal driving characteristics, which contain settings for the tactical planner. The
32   * tactical planner will only consult the route when the GTU has multiple possibilities on a node, so the route does not have to
33   * be complete. As long as all 'splitting' nodes are part of the route and have a valid successor node (connected by a Link),
34   * the strategical planner is able to make a plan.
35   * <p>
36   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
37   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
38   * </p>
39   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
40   * initial version Nov 26, 2015 <br>
41   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
42   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
43   */
44  public class LaneBasedStrategicalRoutePlanner extends AbstractLaneBasedStrategicalPlanner
45          implements LaneBasedStrategicalPlanner, Serializable
46  {
47      /** */
48      private static final long serialVersionUID = 20150724L;
49  
50      /** The route to drive. */
51      private Route route;
52  
53      /** Origin node. */
54      private final Node origin;
55  
56      /** Destination node. */
57      private final Node destination;
58  
59      /** The fixed tactical planner to use for the GTU. */
60      private final LaneBasedTacticalPlanner fixedTacticalPlanner;
61  
62      /** Route supplier. */
63      private final RouteGeneratorOD routeGenerator;
64  
65      /**
66       * Constructor for a strategical planner without route. This can only be used if the network does not have splits, or split
67       * fractions are used.
68       * @param fixedTacticalPlanner LaneBasedTacticalPlanner; the tactical planner to use for the GTU
69       * @param gtu LaneBasedGTU; GTU
70       * @throws GTUException if fixed tactical planner == null
71       */
72      public LaneBasedStrategicalRoutePlanner(final LaneBasedTacticalPlanner fixedTacticalPlanner, final LaneBasedGTU gtu)
73              throws GTUException
74      {
75          this(fixedTacticalPlanner, null, gtu, null, null, RouteGeneratorOD.NULL);
76      }
77  
78      /**
79       * Constructor for a strategical planner with route.
80       * @param fixedTacticalPlanner LaneBasedTacticalPlanner; the tactical planner to use for the GTU
81       * @param route Route; the route to drive
82       * @param gtu LaneBasedGTU; GTU
83       * @param origin Node; origin node
84       * @param destination Node; destination node
85       * @throws GTUException if fixed tactical planner == null
86       */
87      public LaneBasedStrategicalRoutePlanner(final LaneBasedTacticalPlanner fixedTacticalPlanner, final Route route,
88              final LaneBasedGTU gtu, final Node origin, final Node destination) throws GTUException
89      {
90          this(fixedTacticalPlanner, route, gtu, origin, destination, RouteGeneratorOD.NULL);
91      }
92  
93      /**
94       * Constructor for a strategical planner with route generator.
95       * @param fixedTacticalPlanner LaneBasedTacticalPlanner; the tactical planner to use for the GTU
96       * @param gtu LaneBasedGTU; GTU
97       * @param origin Node; origin node
98       * @param destination Node; destination node
99       * @param routeGenerator RouteGeneratorOD; route generator
100      * @throws GTUException if fixed tactical planner == null
101      */
102     public LaneBasedStrategicalRoutePlanner(final LaneBasedTacticalPlanner fixedTacticalPlanner, final LaneBasedGTU gtu,
103             final Node origin, final Node destination, final RouteGeneratorOD routeGenerator) throws GTUException
104     {
105         this(fixedTacticalPlanner, null, gtu, origin, destination, routeGenerator);
106     }
107 
108     /**
109      * Constructor for a strategical planner with route. If the route is {@code null}, a shortest path to the destination is
110      * derived.
111      * @param fixedTacticalPlanner LaneBasedTacticalPlanner; the tactical planner to use for the GTU
112      * @param route Route; the route to drive
113      * @param gtu LaneBasedGTU; GTU
114      * @param origin Node; origin node
115      * @param destination Node; destination node
116      * @param routeGenerator RouteGeneratorOD; route generator
117      * @throws GTUException if fixed tactical planner == null
118      */
119     public LaneBasedStrategicalRoutePlanner(final LaneBasedTacticalPlanner fixedTacticalPlanner, final Route route,
120             final LaneBasedGTU gtu, final Node origin, final Node destination, final RouteGeneratorOD routeGenerator)
121             throws GTUException
122     {
123         super(gtu);
124         this.route = route;
125         this.origin = origin;
126         this.destination = destination;
127         this.fixedTacticalPlanner = fixedTacticalPlanner;
128         Throw.when(fixedTacticalPlanner == null, GTUException.class,
129                 "Fixed Tactical Planner for a Strategical planner is null");
130         this.routeGenerator = routeGenerator;
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public final LaneBasedTacticalPlanner getTacticalPlanner()
136     {
137         return this.fixedTacticalPlanner;
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public LaneBasedTacticalPlanner getTacticalPlanner(final Time time)
143     {
144         return this.fixedTacticalPlanner; // fixed anyway
145     }
146 
147     /** {@inheritDoc} */
148     @Override
149     public final Node nextNode(final Link link, final GTUDirectionality direction, final GTUType gtuType)
150             throws NetworkException
151     {
152         assureRoute(gtuType);
153         LinkDirection linkDirection = nextLinkDirection(link, direction, gtuType);
154         return linkDirection.getNodeTo();
155     }
156 
157     /** {@inheritDoc} */
158     @Override
159     public final LinkDirection nextLinkDirection(final Link link, final GTUDirectionality direction, final GTUType gtuType)
160             throws NetworkException
161     {
162         assureRoute(gtuType);
163         Node nextNode = direction.equals(GTUDirectionality.DIR_PLUS) ? link.getEndNode() : link.getStartNode();
164         return nextLinkDirection(nextNode, link, gtuType);
165     }
166 
167     /** {@inheritDoc} */
168     @Override
169     public final Node nextNode(final Node node, final Link previousLink, final GTUType gtuType) throws NetworkException
170     {
171         assureRoute(gtuType);
172         LinkDirection linkDirection = nextLinkDirection(node, previousLink, gtuType);
173         return linkDirection.getNodeTo();
174     }
175 
176     /** {@inheritDoc} */
177     @Override
178     public final LinkDirection nextLinkDirection(final Node node, final Link previousLink, final GTUType gtuType)
179             throws NetworkException
180     {
181         assureRoute(gtuType);
182 
183         // if there is no split, don't ask the route
184         if (node.getLinks().size() == 1 && previousLink != null)
185         {
186             // end node
187             throw new NetworkException(
188                     "LaneBasedStrategicalRoutePlanner is asked for a next link, but node " + node + " has no successors");
189         }
190         if (node.getLinks().size() == 1 && previousLink == null)
191         {
192             // start node
193             Link link = node.getLinks().iterator().next();
194             return link.getStartNode().equals(node) ? new LinkDirection(link, GTUDirectionality.DIR_PLUS)
195                     : new LinkDirection(link, GTUDirectionality.DIR_MINUS);
196         }
197         if (node.getLinks().size() == 2)
198         {
199             for (Link link : node.getLinks())
200             {
201                 if (!link.equals(previousLink))
202                 {
203                     return link.getStartNode().equals(node) ? new LinkDirection(link, GTUDirectionality.DIR_PLUS)
204                             : new LinkDirection(link, GTUDirectionality.DIR_MINUS);
205                 }
206             }
207         }
208 
209         // if we only have one way to go, don't bother about the route yet
210         Set<Link> links = node.getLinks().toSet();
211         for (Iterator<Link> linkIterator = links.iterator(); linkIterator.hasNext();)
212         {
213             Link link = linkIterator.next();
214             if (link.equals(previousLink))
215             {
216                 // No u-turn...
217                 linkIterator.remove();
218             }
219             else
220             {
221                 // does the directionality of the link forbid us to go in?
222                 if ((link.getStartNode().equals(node) && !link.getDirectionality(gtuType).isForwardOrBoth())
223                         || (link.getEndNode().equals(node) && !link.getDirectionality(gtuType).isBackwardOrBoth()))
224                 {
225                     linkIterator.remove();
226                 }
227                 else
228                 {
229                     // are there no lanes from the node into this link in the outgoing direction?
230                     boolean out = false;
231                     CrossSectionLink csLink = (CrossSectionLink) link;
232                     // TODO: Is there a reason not to iterate over csLink.getLanes()?
233                     for (CrossSectionElement cse : csLink.getCrossSectionElementList())
234                     {
235                         if (cse instanceof Lane)
236                         {
237                             Lane lane = (Lane) cse;
238                             if ((link.getStartNode().equals(node)
239                                     && lane.getLaneType().isCompatible(gtuType, GTUDirectionality.DIR_PLUS))
240                                     || (link.getEndNode().equals(node)
241                                             && lane.getLaneType().isCompatible(gtuType, GTUDirectionality.DIR_MINUS)))
242                             {
243                                 out = true;
244                             }
245                         }
246                     }
247                     if (!out)
248                     {
249                         linkIterator.remove();
250                     }
251                 }
252             }
253         }
254 
255         if (links.size() == 1)
256         {
257             Link link = links.iterator().next();
258             return link.getStartNode().equals(node) ? new LinkDirection(link, GTUDirectionality.DIR_PLUS)
259                     : new LinkDirection(link, GTUDirectionality.DIR_MINUS);
260         }
261 
262         // more than 2 links... We have to check the route!
263         if (getRoute() == null)
264         {
265             throw new NetworkException("LaneBasedStrategicalRoutePlanner does not have a route");
266         }
267         int i = this.route.getNodes().indexOf(node);
268         if (i == -1)
269         {
270             throw new NetworkException("LaneBasedStrategicalRoutePlanner is asked for a next link coming from " + previousLink
271                     + ", but node " + node + " not in route " + this.route);
272         }
273         if (i == this.route.getNodes().size() - 1)
274         {
275             throw new NetworkException("LaneBasedStrategicalRoutePlanner is asked for a next link coming from " + previousLink
276                     + ", but the GTU reached the last node for route " + this.route);
277         }
278         Node nextNode = this.route.getNode(i + 1);
279         LinkDirection result = null;
280         for (Link link : links)
281         {
282             // TODO this takes the first in the set of links that connects the correct nodes; does not handle parallel links
283             // consistently
284             LinkDirection ld = null;
285             if (link.getStartNode().equals(nextNode) && link.getEndNode().equals(node))
286             {
287                 ld = new LinkDirection(link, GTUDirectionality.DIR_MINUS);
288             }
289             if (link.getEndNode().equals(nextNode) && link.getStartNode().equals(node))
290             {
291                 ld = new LinkDirection(link, GTUDirectionality.DIR_PLUS);
292             }
293             if (null != result && null != ld)
294             {
295                 throw new NetworkException("Cannot choose among multiple links from " + node + " to " + nextNode);
296             }
297             else if (null == result)
298             {
299                 result = ld;
300             }
301         }
302         if (null == result)
303         {
304             throw new NetworkException("LaneBasedStrategicalRoutePlanner is asked for a next link coming from "
305                     + previousLink.getId() + ", but no link could be found connecting node " + node + " and node " + nextNode
306                     + " for route " + this.route);
307         }
308         return result;
309     }
310 
311     /** {@inheritDoc} */
312     @Override
313     public final Route getRoute()
314     {
315         assureRoute(getGtu().getGTUType());
316         if (this.route == null && this.destination != null)
317         {
318             try
319             {
320                 DirectedLanePosition pos = getGtu().getReferencePosition();
321                 CrossSectionLink link = pos.getLane().getParentLink();
322                 Node from = pos.getGtuDirection().isPlus() ? link.getStartNode() : link.getEndNode();
323                 if (this.routeGenerator != null)
324                 {
325                     this.route = this.routeGenerator.getRoute(from, this.destination, getGtu().getGTUType());
326                 }
327                 if (this.route == null)
328                 {
329                     this.route = link.getNetwork().getShortestRouteBetween(getGtu().getGTUType(), from, this.destination);
330                 }
331             }
332             catch (GTUException | NetworkException exception)
333             {
334                 throw new RuntimeException("Route could not be determined.", exception);
335             }
336         }
337         return this.route;
338     }
339 
340     /**
341      * Assures a route is available if a route is already present, or a destination and route supplier are provided.
342      * @param gtuType GTUType; the type of the GTU for which a route must be assured
343      */
344     private void assureRoute(final GTUType gtuType)
345     {
346         if (this.route == null && this.destination != null && !this.routeGenerator.equals(RouteGeneratorOD.NULL))
347         {
348             DirectedLanePosition ref = Try.assign(() -> getGtu().getReferencePosition(), "GTU could not be obtained.");
349             List<Node> nodes = new ArrayList<>();
350             if (this.origin != null)
351             {
352                 nodes.addAll(
353                         this.routeGenerator.getRoute(this.origin, ref.getLinkDirection().getNodeFrom(), gtuType).getNodes());
354             }
355             else
356             {
357                 nodes.add(ref.getLinkDirection().getNodeFrom());
358             }
359             // LinkDirection ld = ref.getLinkDirection();
360             // Node n = ld.getNodeTo();
361             // Route r = this.routeSupplier.getRoute(n, this.destination, gtuType);
362             nodes.addAll(
363                     this.routeGenerator.getRoute(ref.getLinkDirection().getNodeTo(), this.destination, gtuType).getNodes());
364             this.route =
365                     Try.assign(
366                             () -> new CompleteRoute("Route for " + gtuType + " from " + this.origin + "to " + this.destination
367                                     + " via " + ref.getLinkDirection(), gtuType, nodes),
368                             "No route possible over nodes %s", nodes);
369             // System.out.println("RouteSupplier route for GTU " + getGtu().getId() + ": " + this.route);
370         }
371     }
372 
373     /** {@inheritDoc} */
374     @Override
375     public final Node getOrigin()
376     {
377         return this.origin;
378     }
379 
380     /** {@inheritDoc} */
381     @Override
382     public final Node getDestination()
383     {
384         return this.destination;
385     }
386 
387     /** {@inheritDoc} */
388     @Override
389     public final String toString()
390     {
391         return "LaneBasedStrategicalRoutePlanner [route=" + this.route + ", fixedTacticalPlanner=" + this.fixedTacticalPlanner
392                 + "]";
393     }
394 
395 }