1 package org.opentrafficsim.road.gtu.strategical;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6 import java.util.Optional;
7 import java.util.Set;
8
9 import org.djunits.value.vdouble.scalar.Duration;
10 import org.djutils.exceptions.Throw;
11 import org.djutils.exceptions.Try;
12 import org.opentrafficsim.base.OtsRuntimeException;
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.NetworkException;
17 import org.opentrafficsim.core.network.Node;
18 import org.opentrafficsim.core.network.route.Route;
19 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
20 import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlanner;
21 import org.opentrafficsim.road.network.lane.CrossSectionLink;
22 import org.opentrafficsim.road.network.lane.Lane;
23 import org.opentrafficsim.road.network.lane.LanePosition;
24
25
26
27
28
29
30
31
32
33
34
35
36
37 public class LaneBasedStrategicalRoutePlanner implements LaneBasedStrategicalPlanner
38 {
39
40 private final LaneBasedGtu gtu;
41
42
43 private Route route;
44
45
46 private final Node origin;
47
48
49 private Node destination;
50
51
52 private final LaneBasedTacticalPlanner fixedTacticalPlanner;
53
54
55 private final RouteGenerator routeGenerator;
56
57
58
59
60
61
62
63
64 public LaneBasedStrategicalRoutePlanner(final LaneBasedTacticalPlanner fixedTacticalPlanner, final LaneBasedGtu gtu)
65 throws GtuException
66 {
67 this(fixedTacticalPlanner, null, gtu, null, null, RouteGenerator.NULL);
68 }
69
70
71
72
73
74
75
76
77
78
79
80
81 public LaneBasedStrategicalRoutePlanner(final LaneBasedTacticalPlanner fixedTacticalPlanner, final Route route,
82 final LaneBasedGtu gtu, final Node origin, final Node destination, final RouteGenerator routeGenerator)
83 throws GtuException
84 {
85 this.gtu = gtu;
86 this.route = route;
87 this.origin = origin;
88 this.destination = destination;
89 this.fixedTacticalPlanner = fixedTacticalPlanner;
90 Throw.when(fixedTacticalPlanner == null, GtuException.class,
91 "Fixed Tactical Planner for a Strategical planner is null");
92 this.routeGenerator = routeGenerator;
93 }
94
95 @Override
96 public final LaneBasedGtu getGtu()
97 {
98 return this.gtu;
99 }
100
101 @Override
102 public final LaneBasedTacticalPlanner getTacticalPlanner()
103 {
104 return this.fixedTacticalPlanner;
105 }
106
107 @Override
108 public LaneBasedTacticalPlanner getTacticalPlanner(final Duration time)
109 {
110 return this.fixedTacticalPlanner;
111 }
112
113 @Override
114 public final Link nextLink(final Link previousLink, final GtuType gtuType) throws NetworkException
115 {
116 assureRoute(gtuType);
117
118 Node node = previousLink.getEndNode();
119
120 if (node.getLinks().size() == 1 && previousLink != null)
121 {
122
123 return null;
124 }
125 if (node.getLinks().size() == 1 && previousLink == null)
126 {
127
128 return node.getLinks().iterator().next();
129 }
130 if (node.getLinks().size() == 2)
131 {
132 for (Link link : node.getLinks())
133 {
134 if (!link.equals(previousLink))
135 {
136 return link;
137 }
138 }
139 }
140
141
142 Set<Link> links = node.getLinks().toSet();
143 for (Iterator<Link> linkIterator = links.iterator(); linkIterator.hasNext();)
144 {
145 Link link = linkIterator.next();
146 if (link.equals(previousLink))
147 {
148
149 linkIterator.remove();
150 }
151 else
152 {
153
154 if (link.getEndNode().equals(node))
155 {
156 linkIterator.remove();
157 }
158 else
159 {
160
161 boolean out = false;
162 CrossSectionLink csLink = (CrossSectionLink) link;
163 for (Lane lane : csLink.getLanes())
164 {
165 if ((link.getStartNode().equals(node) && lane.getType().isCompatible(gtuType)))
166 {
167 out = true;
168 break;
169 }
170 }
171 if (!out)
172 {
173 linkIterator.remove();
174 }
175 }
176 }
177 }
178
179 if (links.size() == 1)
180 {
181 return links.iterator().next();
182 }
183
184
185 if (getRoute().isEmpty())
186 {
187 throw new NetworkException("LaneBasedStrategicalRoutePlanner does not have a route");
188 }
189 int i = this.route.getNodes().indexOf(node);
190 if (i == -1)
191 {
192 throw new NetworkException("LaneBasedStrategicalRoutePlanner is asked for a next link coming from " + previousLink
193 + ", but node " + node + " not in route " + this.route);
194 }
195 if (i == this.route.getNodes().size() - 1)
196 {
197 throw new NetworkException("LaneBasedStrategicalRoutePlanner is asked for a next link coming from " + previousLink
198 + ", but the GTU reached the last node for route " + this.route);
199 }
200 Node nextNode = this.route.getNode(i + 1);
201 Link result = null;
202 for (Link link : links)
203 {
204
205
206 Link l = null;
207 if (link.getStartNode().equals(nextNode) && link.getEndNode().equals(node))
208 {
209 l = link;
210 }
211 if (link.getEndNode().equals(nextNode) && link.getStartNode().equals(node))
212 {
213 l = link;
214 }
215 if (null != result && null != l)
216 {
217 throw new NetworkException("Cannot choose among multiple links from " + node + " to " + nextNode);
218 }
219 else if (null == result)
220 {
221 result = l;
222 }
223 }
224 if (null == result)
225 {
226 throw new NetworkException("LaneBasedStrategicalRoutePlanner is asked for a next link coming from "
227 + previousLink.getId() + ", but no link could be found connecting node " + node + " and node " + nextNode
228 + " for route " + this.route);
229 }
230 return result;
231 }
232
233 @Override
234 public final Optional<Route> getRoute()
235 {
236 assureRoute(getGtu().getType());
237
238 if (this.route == null && this.destination != null && !getGtu().isRoaming())
239 {
240 try
241 {
242 CrossSectionLink link = getGtu().getPosition().lane().getLink();
243 Node from = link.getStartNode();
244 this.route = link.getNetwork().getShortestRouteBetween(getGtu().getType(), from, this.destination);
245 }
246 catch (NetworkException exception)
247 {
248 throw new OtsRuntimeException("Route could not be determined.", exception);
249 }
250 }
251 return Optional.ofNullable(this.route);
252 }
253
254
255
256
257
258 private void assureRoute(final GtuType gtuType)
259 {
260 if (this.route == null && this.destination != null && !this.routeGenerator.equals(RouteGenerator.NULL)
261 && !getGtu().isRoaming())
262 {
263 LanePosition ref = Try.assign(() -> getGtu().getPosition(), "Could not retrieve GTU reference position.");
264 List<Node> nodes = new ArrayList<>();
265 if (this.origin != null)
266 {
267 nodes.addAll(
268 this.routeGenerator.getRoute(this.origin, ref.lane().getLink().getStartNode(), gtuType).getNodes());
269 }
270 else
271 {
272 nodes.add(ref.lane().getLink().getStartNode());
273 }
274 Route newRoute = this.routeGenerator.getRoute(ref.lane().getLink().getEndNode(), this.destination, gtuType);
275 nodes.addAll(newRoute.getNodes());
276 this.route = Try.assign(() -> new Route(
277 "Route for " + gtuType + " from " + this.origin + "to " + this.destination + " via " + ref.lane().getLink(),
278 gtuType, nodes), "No route possible over nodes %s", nodes);
279 }
280 }
281
282 @Override
283 public final Optional<Node> getOrigin()
284 {
285 return Optional.ofNullable(this.origin);
286 }
287
288 @Override
289 public final Optional<Node> getDestination()
290 {
291 return Optional.ofNullable(this.destination);
292 }
293
294 @Override
295 public final String toString()
296 {
297 return "LaneBasedStrategicalRoutePlanner [route=" + this.route + ", fixedTacticalPlanner=" + this.fixedTacticalPlanner
298 + "]";
299 }
300
301 }