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