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 }