1 package org.opentrafficsim.road.network.route;
2
3 import java.util.HashMap;
4 import java.util.Map;
5
6 import org.djunits.unit.LengthUnit;
7 import org.djunits.unit.TimeUnit;
8 import org.opentrafficsim.core.gtu.GTUType;
9 import org.opentrafficsim.core.network.LateralDirectionality;
10 import org.opentrafficsim.core.network.Link;
11 import org.opentrafficsim.core.network.NetworkException;
12 import org.opentrafficsim.core.network.Node;
13 import org.opentrafficsim.core.network.route.CompleteRoute;
14 import org.opentrafficsim.road.network.lane.CrossSectionElement;
15 import org.opentrafficsim.road.network.lane.CrossSectionLink;
16 import org.opentrafficsim.road.network.lane.Lane;
17
18
19
20
21
22
23
24
25
26
27
28
29
30 public class CompleteLaneBasedRouteNavigator extends AbstractLaneBasedRouteNavigator
31 {
32
33 private static final long serialVersionUID = 20150722L;
34
35
36 @SuppressWarnings("checkstyle:visibilitymodifier")
37 protected final CompleteRoute completeRoute;
38
39
40 @SuppressWarnings("checkstyle:visibilitymodifier")
41 protected int lastVisitedNodeIndex = -1;
42
43
44
45
46
47 public CompleteLaneBasedRouteNavigator(final CompleteRoute completeRoute)
48 {
49 this.completeRoute = completeRoute;
50 }
51
52
53 @Override
54 public final Node lastVisitedNode() throws NetworkException
55 {
56 if (this.lastVisitedNodeIndex == -1)
57 {
58 return null;
59 }
60 return this.completeRoute.getNodes().get(this.lastVisitedNodeIndex);
61 }
62
63
64 @Override
65 public final Node nextNodeToVisit() throws NetworkException
66 {
67 if (this.lastVisitedNodeIndex >= this.completeRoute.size() - 1)
68 {
69 return null;
70 }
71 return this.completeRoute.getNodes().get(this.lastVisitedNodeIndex + 1);
72 }
73
74
75 @Override
76 public final Node visitNextNode() throws NetworkException
77 {
78 if (this.lastVisitedNodeIndex >= this.completeRoute.size() - 1)
79 {
80 return null;
81 }
82 this.lastVisitedNodeIndex++;
83 return this.completeRoute.getNodes().get(this.lastVisitedNodeIndex);
84 }
85
86
87 @Override
88 public final Length.Rel suitability(final Lane lane, final Length.Rel longitudinalPosition, final GTUType gtuType,
89 final Time.Rel timeHorizon) throws NetworkException
90 {
91 double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
92 double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtuType).getSI();
93
94 Node nextNode = lane.getParentLink().getEndNode();
95 Node nextSplitNode = null;
96 Lane currentLane = lane;
97 CrossSectionLink linkBeforeBranch = lane.getParentLink();
98 while (null != nextNode)
99 {
100 if (spareTime <= 0)
101 {
102 return NOLANECHANGENEEDED;
103 }
104 int laneCount = countCompatibleLanes(linkBeforeBranch, gtuType);
105 if (0 == laneCount)
106 {
107 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
108 }
109 if (1 == laneCount)
110 {
111 return NOLANECHANGENEEDED;
112
113 }
114 int branching = nextNode.getLinksOut().size();
115 if (branching > 1)
116 {
117 nextSplitNode = nextNode;
118 break;
119 }
120 else if (0 == branching)
121 {
122 return NOLANECHANGENEEDED;
123 }
124 else
125 {
126 Link nextLink = nextNode.getLinksOut().iterator().next();
127 if (nextLink instanceof CrossSectionLink)
128 {
129 nextNode = nextLink.getEndNode();
130
131 remainingDistance += nextLink.getLength().getSI();
132 linkBeforeBranch = (CrossSectionLink) nextLink;
133
134 if (currentLane.nextLanes(gtuType).size() == 0)
135 {
136
137
138 if (currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType).size() > 0)
139 {
140 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT,
141 gtuType))
142 {
143 if (adjacentLane.nextLanes(gtuType).size() > 0)
144 {
145 currentLane = adjacentLane;
146 break;
147 }
148
149
150 }
151 }
152 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType))
153 {
154 if (adjacentLane.nextLanes(gtuType).size() > 0)
155 {
156 currentLane = adjacentLane;
157 break;
158 }
159
160
161 }
162 if (currentLane.nextLanes(gtuType).size() == 0)
163 {
164 throw new NetworkException("Lane ends and there is not a compatible adjacent lane that does "
165 + "not end");
166 }
167 }
168
169 for (Lane nextLane : currentLane.nextLanes(gtuType))
170 {
171 if (nextLane.getLaneType().isCompatible(gtuType))
172 {
173 currentLane = currentLane.nextLanes(gtuType).iterator().next();
174 break;
175 }
176 }
177 spareTime -= currentLane.getLength().getSI() / currentLane.getSpeedLimit(gtuType).getSI();
178 }
179 else
180 {
181
182
183 return NOLANECHANGENEEDED;
184 }
185 }
186 }
187 if (null == nextNode)
188 {
189 throw new NetworkException("Cannot find the next branch or sink node");
190 }
191
192
193 Map<Lane, Length.Rel> suitabilityOfLanesBeforeBranch = new HashMap<Lane, Length.Rel>();
194 Link linkAfterBranch = null;
195 for (Link link : nextSplitNode.getLinksOut())
196 {
197 Node nextNodeOnLink = link.getEndNode();
198 for (int i = this.lastVisitedNodeIndex + 1; i < this.completeRoute.size(); i++)
199 {
200 Node n = this.completeRoute.getNodes().get(i);
201 if (nextNodeOnLink == n)
202 {
203 if (null != linkAfterBranch)
204 {
205 throw new NetworkException("Parallel Links at " + nextSplitNode + " go to " + nextNodeOnLink);
206
207
208 }
209 linkAfterBranch = link;
210 break;
211 }
212 }
213 }
214 if (null == linkAfterBranch)
215 {
216 throw new NetworkException("Cannot identify the link to follow after " + nextSplitNode + " in " + this);
217 }
218 for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
219 {
220 if (cse instanceof Lane)
221 {
222 Lane l = (Lane) cse;
223 if (l.getLaneType().isCompatible(gtuType))
224 {
225 for (Lane connectingLane : l.nextLanes(gtuType))
226 {
227 if (connectingLane.getParentLink() == linkAfterBranch
228 && connectingLane.getLaneType().isCompatible(gtuType))
229 {
230 Length.Rel currentValue = suitabilityOfLanesBeforeBranch.get(l);
231
232
233
234 Length.Rel value =
235 suitability(connectingLane, new Length.Rel(0, LengthUnit.SI), gtuType, new Time.Rel(
236 spareTime, TimeUnit.SI));
237
238
239 suitabilityOfLanesBeforeBranch.put(l, null == currentValue || value.le(currentValue) ? value
240 : currentValue);
241 }
242 }
243 }
244 }
245 }
246 if (suitabilityOfLanesBeforeBranch.size() == 0)
247 {
248 throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
249 }
250 Length.Rel currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
251 if (null != currentLaneSuitability)
252 {
253 return currentLaneSuitability;
254 }
255
256 int totalLanes = countCompatibleLanes(currentLane.getParentLink(), gtuType);
257 Length.Rel leftSuitability =
258 computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
259 LateralDirectionality.LEFT, gtuType);
260 Length.Rel rightSuitability =
261 computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
262 LateralDirectionality.RIGHT, gtuType);
263 if (leftSuitability.ge(rightSuitability))
264 {
265 return leftSuitability;
266 }
267 else if (rightSuitability.ge(leftSuitability))
268 {
269 return rightSuitability;
270 }
271 if (leftSuitability.le(GETOFFTHISLANENOW))
272 {
273 throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
274 }
275 return leftSuitability;
276 }
277
278
279
280
281 public final CompleteRoute getRoute()
282 {
283 return this.completeRoute;
284 }
285
286
287 @SuppressWarnings("checkstyle:designforextension")
288 @Override
289 public String toString()
290 {
291 StringBuilder result = new StringBuilder();
292 final String currentLocationMark = "<>";
293 result.append("Route: [");
294 String separator = "";
295 if (this.lastVisitedNodeIndex < 0)
296 {
297 result.append(currentLocationMark);
298 }
299 for (int index = 0; index < this.completeRoute.size(); index++)
300 {
301 Node node = this.completeRoute.getNodes().get(index);
302 result.append(separator + node);
303 if (index == this.lastVisitedNodeIndex)
304 {
305 result.append(" " + currentLocationMark);
306 }
307 separator = ", ";
308 }
309 result.append("]");
310 return result.toString();
311 }
312
313 }