1 package org.opentrafficsim.draw.graphs.road;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.LinkedHashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Set;
12
13 import org.djunits.value.vdouble.scalar.Length;
14 import org.djunits.value.vdouble.scalar.Speed;
15 import org.djutils.exceptions.Throw;
16 import org.djutils.immutablecollections.ImmutableSet;
17 import org.opentrafficsim.core.gtu.GTUDirectionality;
18 import org.opentrafficsim.core.network.DirectedLinkPosition;
19 import org.opentrafficsim.core.network.Link;
20 import org.opentrafficsim.core.network.NetworkException;
21 import org.opentrafficsim.draw.graphs.GraphCrossSection;
22 import org.opentrafficsim.draw.graphs.GraphPath;
23 import org.opentrafficsim.draw.graphs.GraphPath.Section;
24 import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality;
25 import org.opentrafficsim.kpi.sampling.KpiLaneDirection;
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 import org.opentrafficsim.road.network.lane.LaneDirection;
30 import org.opentrafficsim.road.network.sampling.LaneData;
31
32
33
34
35
36
37
38
39
40
41
42
43 public final class GraphLaneUtil
44 {
45
46
47
48
49 private GraphLaneUtil()
50 {
51
52 }
53
54
55
56
57
58
59
60
61 public static GraphPath<KpiLaneDirection> createPath(final String name, final LaneDirection first) throws NetworkException
62 {
63 Throw.whenNull(name, "Name may not be null.");
64 Throw.whenNull(first, "First may not be null.");
65 List<Section<KpiLaneDirection>> sections = new ArrayList<>();
66 Set<LaneDirection> set = new LinkedHashSet<>();
67 LaneDirection lane = first;
68 while (lane != null && !set.contains(lane))
69 {
70 KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(lane.getLane()),
71 lane.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS);
72 List<KpiLaneDirection> list = new ArrayList<>();
73 list.add(kpiLaneDirection);
74 Speed speed = lane.getLane().getLowestSpeedLimit();
75 Length length = lane.getLength();
76 sections.add(new Section<KpiLaneDirection>()
77 {
78
79 @Override
80 public Iterator<KpiLaneDirection> iterator()
81 {
82 return list.iterator();
83 }
84
85
86 @Override
87 public Length getLength()
88 {
89 return length;
90 }
91
92
93 @Override
94 public Speed getSpeedLimit()
95 {
96 return speed;
97 }
98
99
100 @Override
101 public KpiLaneDirection getSource(final int series)
102 {
103 return kpiLaneDirection;
104 }
105 });
106 set.add(lane);
107 Map<Lane, GTUDirectionality> map = lane.getLane().downstreamLanes(lane.getDirection(), null);
108 if (map.size() == 1)
109 {
110 Map.Entry<Lane, GTUDirectionality> entry = map.entrySet().iterator().next();
111 lane = new LaneDirection(entry.getKey(), entry.getValue());
112 }
113 }
114 return new GraphPath<>(name, sections);
115 }
116
117
118
119
120
121
122
123
124
125 public static GraphPath<KpiLaneDirection> createPath(final List<String> names, final List<LaneDirection> first)
126 throws NetworkException
127 {
128 Throw.whenNull(names, "Names may not be null.");
129 Throw.whenNull(first, "First may not be null.");
130 Throw.when(names.size() != first.size(), IllegalArgumentException.class, "Size of 'names' and 'first' must be equal.");
131 List<Section<KpiLaneDirection>> sections = new ArrayList<>();
132 Set<LaneDirection> set = new LinkedHashSet<>();
133 List<LaneDirection> lanes = first;
134 while (lanes != null && Collections.disjoint(set, lanes))
135 {
136 List<KpiLaneDirection> list = new ArrayList<>();
137 Speed speed = null;
138 for (LaneDirection lane : lanes)
139 {
140 speed = speed == null ? lane.getLane().getLowestSpeedLimit()
141 : Speed.min(speed, lane.getLane().getLowestSpeedLimit());
142 KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(lane.getLane()),
143 lane.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS);
144 list.add(kpiLaneDirection);
145 }
146 Speed finalSpeed = speed;
147 Length length = lanes.get(0).getLane().getLength();
148 sections.add(new Section<KpiLaneDirection>()
149 {
150
151 @Override
152 public Iterator<KpiLaneDirection> iterator()
153 {
154 return list.iterator();
155 }
156
157
158 @Override
159 public Length getLength()
160 {
161 return length;
162 }
163
164
165 @Override
166 public Speed getSpeedLimit()
167 {
168 return finalSpeed;
169 }
170
171
172 @Override
173 public KpiLaneDirection getSource(final int series)
174 {
175 return list.get(series);
176 }
177 });
178 set.addAll(lanes);
179
180 Map<Link, List<LaneDirection>> linkMap = new LinkedHashMap<>();
181 Link link = lanes.get(0).getLane().getParentLink();
182 ImmutableSet<Link> links =
183 (lanes.get(0).getDirection().isPlus() ? link.getEndNode() : link.getStartNode()).getLinks();
184 for (Link nextLink : links)
185 {
186 if (!link.equals(nextLink))
187 {
188 List<LaneDirection> nextLanes = new ArrayList<>();
189 for (LaneDirection laneDir : lanes)
190 {
191 Map<Lane, GTUDirectionality> map = laneDir.getLane().downstreamLanes(laneDir.getDirection(), null);
192 int n = 0;
193 for (Map.Entry<Lane, GTUDirectionality> entry : map.entrySet())
194 {
195 if (entry.getKey().getParentLink().equals(nextLink))
196 {
197 n++;
198 nextLanes.add(new LaneDirection(entry.getKey(), entry.getValue()));
199 }
200 }
201 if (n > 1)
202 {
203
204 nextLanes.clear();
205 break;
206 }
207 else if (n == 0)
208 {
209 nextLanes.addAll(null);
210 }
211 }
212 if (nextLanes.size() == lanes.size())
213 {
214 linkMap.put(nextLink, nextLanes);
215 }
216 }
217 }
218
219 if (linkMap.size() > 1)
220 {
221 Iterator<List<LaneDirection>> it = linkMap.values().iterator();
222 while (it.hasNext())
223 {
224 if (it.next().contains(null))
225 {
226 it.remove();
227 }
228 }
229 }
230 if (linkMap.size() == 1)
231 {
232 lanes = linkMap.values().iterator().next();
233 }
234 else
235 {
236 lanes = null;
237 }
238 }
239 return new GraphPath<>(names, sections);
240 }
241
242
243
244
245
246
247
248
249 public static GraphPath<KpiLaneDirection> createSingleLanePath(final String name, final LaneDirection lane)
250 throws NetworkException
251 {
252 List<KpiLaneDirection> lanes = new ArrayList<>();
253 lanes.add(new KpiLaneDirection(new LaneData(lane.getLane()),
254 lane.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS));
255 List<Section<KpiLaneDirection>> sections = new ArrayList<>();
256 Speed speed = lane.getLane().getLowestSpeedLimit();
257 sections.add(new Section<KpiLaneDirection>()
258 {
259
260
261 @Override
262 public Iterator<KpiLaneDirection> iterator()
263 {
264 return lanes.iterator();
265 }
266
267
268 @Override
269 public Length getLength()
270 {
271 return lane.getLength();
272 }
273
274
275 @Override
276 public Speed getSpeedLimit()
277 {
278 return speed;
279 }
280
281
282 @Override
283 public KpiLaneDirection getSource(final int series)
284 {
285 return lanes.get(0);
286 }
287
288 });
289 return new GraphPath<>(name, sections);
290 }
291
292
293
294
295
296
297
298
299 public static GraphCrossSection<KpiLaneDirection> createCrossSection(final String name,
300 final DirectedLanePosition lanePosition) throws NetworkException
301 {
302 Throw.whenNull(name, "Name may not be null.");
303 Throw.whenNull(lanePosition, "Lane position may not be null.");
304 List<KpiLaneDirection> list = new ArrayList<>();
305 List<String> names = new ArrayList<>();
306 List<Length> positions = new ArrayList<>();
307 names.add(name);
308 positions.add(lanePosition.getPosition());
309 list.add(new KpiLaneDirection(new LaneData(lanePosition.getLane()),
310 lanePosition.getGtuDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS));
311 Speed speed = lanePosition.getLane().getLowestSpeedLimit();
312 return createCrossSection(names, list, positions, speed);
313 }
314
315
316
317
318
319
320
321
322 public static GraphCrossSection<KpiLaneDirection> createCrossSection(final List<String> names,
323 final DirectedLinkPosition linkPosition) throws NetworkException
324 {
325 Throw.whenNull(names, "Names may not be null.");
326 Throw.whenNull(linkPosition, "Link position may not be null.");
327 Throw.when(!(linkPosition.getLink() instanceof CrossSectionLink), IllegalArgumentException.class,
328 "The link is not a CrossEctionLink.");
329 List<Lane> lanes = ((CrossSectionLink) linkPosition.getLink()).getLanes();
330 Throw.when(names.size() != lanes.size(), IllegalArgumentException.class,
331 "Size of 'names' not equal to the number of lanes.");
332 Collections.sort(lanes, new Comparator<Lane>()
333 {
334
335 @Override
336 public int compare(final Lane o1, final Lane o2)
337 {
338 int comp = o1.getDesignLineOffsetAtBegin().compareTo(o2.getDesignLineOffsetAtEnd());
339 return linkPosition.getDirection().isPlus() ? comp : -comp;
340 }
341
342 });
343 List<KpiLaneDirection> list = new ArrayList<>();
344 List<Length> positions = new ArrayList<>();
345 Speed speed = null;
346 for (Lane lane : lanes)
347 {
348 speed = speed == null ? lane.getLowestSpeedLimit() : Speed.min(speed, lane.getLowestSpeedLimit());
349 list.add(new KpiLaneDirection(new LaneData(lane),
350 linkPosition.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS));
351 positions.add(lane.getLength().multiplyBy(linkPosition.getFractionalLongitudinalPosition()));
352 }
353 return createCrossSection(names, list, positions, speed);
354 }
355
356
357
358
359
360
361
362
363
364 public static GraphCrossSection<KpiLaneDirection> createCrossSection(final List<String> names,
365 final List<KpiLaneDirection> lanes, final List<Length> positions, final Speed speed)
366 {
367 Section<KpiLaneDirection> section = new Section<KpiLaneDirection>()
368 {
369
370 @Override
371 public Iterator<KpiLaneDirection> iterator()
372 {
373 return lanes.iterator();
374 }
375
376
377 @Override
378 public Length getLength()
379 {
380 return lanes.get(0).getLaneData().getLength();
381 }
382
383
384 @Override
385 public Speed getSpeedLimit()
386 {
387 return speed;
388 }
389
390
391 @Override
392 public KpiLaneDirection getSource(final int series)
393 {
394 return lanes.get(series);
395 }
396 };
397 return new GraphCrossSection<>(names, section, positions);
398 }
399
400 }