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