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  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  public class LaneBasedStrategicalRoutePlanner extends AbstractLaneBasedStrategicalPlanner
45          implements LaneBasedStrategicalPlanner, Serializable
46  {
47      
48      private static final long serialVersionUID = 20150724L;
49  
50      
51      private Route route;
52  
53      
54      private final Node origin;
55  
56      
57      private final Node destination;
58  
59      
60      private final LaneBasedTacticalPlanner fixedTacticalPlanner;
61  
62      
63      private final RouteGeneratorOD routeGenerator;
64  
65      
66  
67  
68  
69  
70  
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  
80  
81  
82  
83  
84  
85  
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  
95  
96  
97  
98  
99  
100 
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 
110 
111 
112 
113 
114 
115 
116 
117 
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     
134     @Override
135     public final LaneBasedTacticalPlanner getTacticalPlanner()
136     {
137         return this.fixedTacticalPlanner;
138     }
139 
140     
141     @Override
142     public LaneBasedTacticalPlanner getTacticalPlanner(final Time time)
143     {
144         return this.fixedTacticalPlanner; 
145     }
146 
147     
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     
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     
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     
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         
184         if (node.getLinks().size() == 1 && previousLink != null)
185         {
186             
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             
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         
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                 
217                 linkIterator.remove();
218             }
219             else
220             {
221                 
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                     
230                     boolean out = false;
231                     CrossSectionLink csLink = (CrossSectionLink) link;
232                     
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         
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             
283             
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     
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 
342 
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             
360             
361             
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             
370         }
371     }
372 
373     
374     @Override
375     public final Node getOrigin()
376     {
377         return this.origin;
378     }
379 
380     
381     @Override
382     public final Node getDestination()
383     {
384         return this.destination;
385     }
386 
387     
388     @Override
389     public final String toString()
390     {
391         return "LaneBasedStrategicalRoutePlanner [route=" + this.route + ", fixedTacticalPlanner=" + this.fixedTacticalPlanner
392                 + "]";
393     }
394 
395 }