1 package org.opentrafficsim.road.network.factory.osm.output;
2
3 import java.awt.Color;
4 import java.io.Serializable;
5 import java.util.ArrayList;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Locale;
9 import java.util.Map;
10 import java.util.Objects;
11 import java.util.SortedMap;
12 import java.util.TreeMap;
13
14 import javax.naming.NamingException;
15 import javax.xml.crypto.dsig.TransformException;
16
17 import org.djunits.unit.LengthUnit;
18 import org.djunits.unit.SpeedUnit;
19 import org.djunits.value.vdouble.scalar.Length;
20 import org.djunits.value.vdouble.scalar.Speed;
21 import org.locationtech.jts.geom.Coordinate;
22 import org.opentrafficsim.core.compatibility.GTUCompatibility;
23 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
24 import org.opentrafficsim.core.geometry.OTSGeometryException;
25 import org.opentrafficsim.core.geometry.OTSLine3D;
26 import org.opentrafficsim.core.geometry.OTSPoint3D;
27 import org.opentrafficsim.core.gtu.GTUDirectionality;
28 import org.opentrafficsim.core.gtu.GTUType;
29 import org.opentrafficsim.core.network.LinkType;
30 import org.opentrafficsim.core.network.LongitudinalDirectionality;
31 import org.opentrafficsim.core.network.NetworkException;
32 import org.opentrafficsim.core.network.OTSNode;
33 import org.opentrafficsim.road.network.OTSRoadNetwork;
34 import org.opentrafficsim.road.network.factory.osm.OSMLink;
35 import org.opentrafficsim.road.network.factory.osm.OSMNetwork;
36 import org.opentrafficsim.road.network.factory.osm.OSMNode;
37 import org.opentrafficsim.road.network.factory.osm.OSMTag;
38 import org.opentrafficsim.road.network.factory.osm.events.ProgressEvent;
39 import org.opentrafficsim.road.network.factory.osm.events.ProgressListener;
40 import org.opentrafficsim.road.network.factory.osm.events.WarningEvent;
41 import org.opentrafficsim.road.network.factory.osm.events.WarningListener;
42 import org.opentrafficsim.road.network.lane.CrossSectionLink;
43 import org.opentrafficsim.road.network.lane.Lane;
44 import org.opentrafficsim.road.network.lane.LaneType;
45 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
46 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
47
48 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
49
50
51
52
53
54
55
56
57
58
59
60 public final class Convert
61 {
62
63
64
65
66 @SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
67 public Convert()
68 {
69 baseX = Double.NaN;
70 }
71
72
73 private static double baseX = Double.NaN;
74
75
76
77
78
79
80 public static Coordinate transform(final Coordinate c) throws TransformException
81 {
82
83
84
85
86
87
88
89
90
91
92
93
94
95 double radius = 6371000;
96 if (Double.isNaN(baseX))
97 {
98 baseX = c.x;
99 }
100 double x = radius * Math.toRadians(c.x - baseX) * Math.cos(Math.toRadians(c.y));
101 double y = radius * Math.toRadians(c.y);
102
103 return new Coordinate(x, y);
104 }
105
106
107
108
109
110
111
112
113
114
115
116
117 public CrossSectionLink convertLink(final OTSRoadNetwork network, final OSMLink link, final OTSSimulatorInterface simulator)
118 throws OTSGeometryException, NetworkException
119 {
120 if (null == link.getStart().getOtsNode())
121 {
122 link.getStart().setOtsNode(convertNode(network, link.getStart()));
123 }
124 if (null == link.getEnd().getOtsNode())
125 {
126 link.getEnd().setOtsNode(convertNode(network, link.getEnd()));
127 }
128 CrossSectionLink result;
129 Coordinate[] coordinates;
130 List<OSMNode> nodes = link.getSplineList();
131 int coordinateCount = 2 + nodes.size();
132 coordinates = new Coordinate[coordinateCount];
133 OTSNode start = link.getStart().getOtsNode();
134 coordinates[0] = new Coordinate(start.getPoint().x, start.getPoint().y, 0);
135 for (int i = 0; i < nodes.size(); i++)
136 {
137 coordinates[i + 1] = new Coordinate(nodes.get(i).getLongitude(), nodes.get(i).getLatitude());
138 }
139 OTSNode end = link.getEnd().getOtsNode();
140 coordinates[coordinates.length - 1] = new Coordinate(end.getPoint().x, end.getPoint().y, 0);
141 OTSLine3D designLine = new OTSLine3D(coordinates);
142
143
144 for (int subId = 0;; subId++)
145 {
146 String linkId = String.format("%s_%d", link.getId(), subId);
147 if (network.containsLink(linkId))
148 {
149 continue;
150 }
151 result = new CrossSectionLink(network, linkId, start, end, network.getLinkType(LinkType.DEFAULTS.ROAD), designLine,
152 simulator, LaneKeepingPolicy.KEEPRIGHT);
153 return result;
154 }
155 }
156
157
158
159
160
161
162
163
164 public OTSNode convertNode(final OTSRoadNetwork network, final OSMNode node) throws NetworkException
165 {
166 OSMTag tag = node.getTag("ele");
167 if (null != tag)
168 {
169 try
170 {
171 String ele = tag.getValue();
172 Double elevation = 0d;
173 if (ele.matches("[0-9]+(km)|m"))
174 {
175 String[] ele2 = ele.split("(km)|m");
176 ele = ele2[0];
177 if (ele2[1].equals("km"))
178 {
179 elevation = Double.parseDouble(ele) * 1000;
180 }
181 else if (ele2[1].equals("m"))
182 {
183 elevation = Double.parseDouble(ele);
184 }
185 else
186 {
187 throw new NumberFormatException("Cannot parse elevation value\"" + ele + "\"");
188 }
189 }
190 else if (ele.matches("[0-9]+"))
191 {
192 elevation = Double.parseDouble(ele);
193 }
194 Coordinate coordWGS84 = new Coordinate(node.getLongitude(), node.getLatitude(), elevation);
195 try
196 {
197 return new OTSNode(network, Objects.toString(node.getId()), new OTSPoint3D(transform(coordWGS84)));
198 }
199 catch (TransformException exception)
200 {
201 exception.printStackTrace();
202 }
203 }
204 catch (NumberFormatException exception)
205 {
206 exception.printStackTrace();
207 }
208 }
209
210 Coordinate coordWGS84 = new Coordinate(node.getLongitude(), node.getLatitude(), 0d);
211 try
212 {
213 String nodeName = Objects.toString(node.getId());
214 OTSNode otsNode = (OTSNode) network.getNode(nodeName);
215 if (null == otsNode)
216 {
217 otsNode = new OTSNode(network, nodeName, new OTSPoint3D(Convert.transform(coordWGS84)));
218 }
219 return otsNode;
220 }
221 catch (TransformException exception)
222 {
223 exception.printStackTrace();
224
225 return null;
226 }
227 }
228
229
230
231
232
233
234
235
236
237 private static Map<Double, LaneAttributes> makeStructure(final OTSRoadNetwork network, final OSMLink osmLink,
238 final WarningListener warningListener) throws NetworkException
239 {
240 SortedMap<Integer, LaneAttributes> structure = new TreeMap<Integer, LaneAttributes>();
241 int forwards = osmLink.getForwardLanes();
242 int backwards = osmLink.getLanes() - osmLink.getForwardLanes();
243 LaneType laneType;
244 LaneAttributes laneAttributes;
245 for (OSMTag tag : osmLink.getTags())
246 {
247 if (tag.getKey().equals("waterway"))
248 {
249 switch (tag.getValue())
250 {
251 case "river":
252 laneType = makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.SHIP));
253 break;
254 case "canal":
255 laneType = makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.SHIP));
256 break;
257 default:
258 laneType = makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.WATERWAY_USER));
259 break;
260 }
261 laneAttributes = new LaneAttributes(network, laneType, Color.CYAN, LongitudinalDirectionality.DIR_BOTH);
262 structure.put(0, laneAttributes);
263 }
264 }
265 for (OSMTag tag : osmLink.getTags())
266 {
267 if (tag.getKey().equals("highway") && (tag.getValue().equals("primary") || tag.getValue().equals("secondary")
268 || tag.getValue().equals("tertiary") || tag.getValue().equals("residential")
269 || tag.getValue().equals("trunk") || tag.getValue().equals("motorway") || tag.getValue().equals("service")
270 || tag.getValue().equals("unclassified") || tag.getValue().equals("motorway_link")
271 || tag.getValue().equals("primary_link") || tag.getValue().equals("secondary_link")
272 || tag.getValue().equals("tertiary_link") || tag.getValue().equals("trunk_link")
273 || tag.getValue().equals("road") || tag.getValue().equals("track")
274 || tag.getValue().equals("living_street")))
275 {
276 laneType = makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.CAR));
277 if (osmLink.getLanes() == 1 && !osmLink.isOneway())
278 {
279 laneAttributes =
280 new LaneAttributes(network, laneType, Color.LIGHT_GRAY, LongitudinalDirectionality.DIR_BOTH);
281 structure.put(0, laneAttributes);
282 }
283 else
284 {
285 for (int i = 0 - backwards; i < forwards; i++)
286 {
287 if (i < 0)
288 {
289 laneAttributes = new LaneAttributes(network, laneType, Color.LIGHT_GRAY,
290 LongitudinalDirectionality.DIR_MINUS);
291 structure.put(i, laneAttributes);
292 }
293 if (i >= 0)
294 {
295 laneAttributes = new LaneAttributes(network, laneType, Color.LIGHT_GRAY,
296 LongitudinalDirectionality.DIR_PLUS);
297 structure.put(i, laneAttributes);
298 }
299 }
300 }
301 }
302 else if (tag.getKey().equals("highway") && (tag.getValue().equals("path") || tag.getValue().equals("steps")))
303 {
304 List<GTUType> types = new ArrayList<GTUType>();
305 for (OSMTag t2 : osmLink.getTags())
306 {
307 if (t2.getKey().equals("bicycle"))
308 {
309 types.add(network.getGtuType(GTUType.DEFAULTS.BICYCLE));
310 }
311
312
313
314
315 }
316 laneType = makeLaneType(network, types);
317 types.add(network.getGtuType(GTUType.DEFAULTS.PEDESTRIAN));
318 if (!types.isEmpty())
319 {
320 if (osmLink.getLanes() == 1 && !osmLink.isOneway())
321 {
322 laneAttributes =
323 new LaneAttributes(network, laneType, Color.GREEN, LongitudinalDirectionality.DIR_BOTH);
324 structure.put(0, laneAttributes);
325 }
326 else
327 {
328 for (int i = 0 - backwards; i < forwards; i++)
329 {
330 if (i < 0)
331 {
332 laneAttributes = new LaneAttributes(network, laneType, Color.GREEN,
333 LongitudinalDirectionality.DIR_MINUS);
334 structure.put(i, laneAttributes);
335 }
336 if (i >= 0)
337 {
338 laneAttributes =
339 new LaneAttributes(network, laneType, Color.GREEN, LongitudinalDirectionality.DIR_PLUS);
340 structure.put(i, laneAttributes);
341 }
342 }
343 }
344 }
345 types.clear();
346 }
347 }
348 for (OSMTag tag : osmLink.getTags())
349 {
350 if (tag.getKey().equals("cycleway"))
351 {
352 laneType = makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.BICYCLE));
353 switch (tag.getValue())
354 {
355 case "lane":
356 forwards++;
357 backwards++;
358 laneAttributes =
359 new LaneAttributes(network, laneType, Color.ORANGE, LongitudinalDirectionality.DIR_MINUS);
360 structure.put(0 - backwards, laneAttributes);
361 laneAttributes =
362 new LaneAttributes(network, laneType, Color.ORANGE, LongitudinalDirectionality.DIR_PLUS);
363 structure.put(forwards - 1, laneAttributes);
364 break;
365 case "track":
366 forwards++;
367 backwards++;
368 laneAttributes =
369 new LaneAttributes(network, laneType, Color.ORANGE, LongitudinalDirectionality.DIR_MINUS);
370 structure.put(0 - backwards, laneAttributes);
371 laneAttributes =
372 new LaneAttributes(network, laneType, Color.ORANGE, LongitudinalDirectionality.DIR_PLUS);
373 structure.put(forwards - 1, laneAttributes);
374 break;
375 case "shared_lane":
376 List<GTUType> types = new ArrayList<GTUType>();
377 types.add(network.getGtuType(GTUType.DEFAULTS.BICYCLE));
378 types.add(network.getGtuType(GTUType.DEFAULTS.CAR));
379 laneType = makeLaneType(network, types);
380 laneAttributes =
381 new LaneAttributes(network, laneType, Color.ORANGE, LongitudinalDirectionality.DIR_MINUS);
382 structure.put(0 - backwards, laneAttributes);
383 laneAttributes =
384 new LaneAttributes(network, laneType, Color.ORANGE, LongitudinalDirectionality.DIR_PLUS);
385 structure.put(forwards - 1, laneAttributes);
386 break;
387 default:
388 break;
389 }
390 }
391 }
392 for (OSMTag tag : osmLink.getTags())
393 {
394 if (tag.getKey().equals("sidewalk"))
395 {
396 laneType = makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.PEDESTRIAN));
397 switch (tag.getValue())
398 {
399 case "both":
400 forwards++;
401 backwards++;
402 laneAttributes =
403 new LaneAttributes(network, laneType, Color.YELLOW, LongitudinalDirectionality.DIR_MINUS);
404 structure.put(0 - backwards, laneAttributes);
405 laneAttributes =
406 new LaneAttributes(network, laneType, Color.YELLOW, LongitudinalDirectionality.DIR_PLUS);
407 structure.put(forwards - 1, laneAttributes);
408 break;
409 case "left":
410 backwards++;
411 laneAttributes =
412 new LaneAttributes(network, laneType, Color.YELLOW, LongitudinalDirectionality.DIR_BOTH);
413 structure.put(0 - backwards, laneAttributes);
414 break;
415 case "right":
416 forwards++;
417 laneAttributes =
418 new LaneAttributes(network, laneType, Color.YELLOW, LongitudinalDirectionality.DIR_BOTH);
419 structure.put(forwards - 1, laneAttributes);
420 break;
421 default:
422 break;
423 }
424 }
425 }
426 for (OSMTag tag : osmLink.getTags())
427 {
428 if (tag.getKey().equals("highway") && (tag.getValue().equals("cycleway") || tag.getValue().equals("footway")
429 || tag.getValue().equals("pedestrian") || tag.getValue().equals("steps")))
430 {
431 if (tag.getValue().equals("footway") || tag.getValue().equals("pedestrian") || tag.getValue().equals("steps"))
432 {
433 laneType = makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.PEDESTRIAN));
434 if (osmLink.getLanes() == 1 && !osmLink.isOneway())
435 {
436 laneAttributes =
437 new LaneAttributes(network, laneType, Color.GREEN, LongitudinalDirectionality.DIR_BOTH);
438 structure.put(0, laneAttributes);
439 }
440 else
441 {
442 for (int i = 0 - backwards; i < forwards; i++)
443 {
444 if (i < 0)
445 {
446 laneAttributes = new LaneAttributes(network, laneType, Color.GREEN,
447 LongitudinalDirectionality.DIR_MINUS);
448 structure.put(i, laneAttributes);
449 }
450 if (i >= 0)
451 {
452 laneAttributes =
453 new LaneAttributes(network, laneType, Color.GREEN, LongitudinalDirectionality.DIR_PLUS);
454 structure.put(i, laneAttributes);
455 }
456 }
457 }
458 }
459 if (tag.getValue().equals("cycleway"))
460 {
461 laneType = makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.BICYCLE));
462 if (osmLink.getLanes() == 1 && !osmLink.isOneway())
463 {
464 laneAttributes =
465 new LaneAttributes(network, laneType, Color.GREEN, LongitudinalDirectionality.DIR_BOTH);
466 structure.put(0, laneAttributes);
467 }
468 for (int i = 0 - backwards; i < forwards; i++)
469 {
470 if (i < 0)
471 {
472 laneAttributes =
473 new LaneAttributes(network, laneType, Color.GREEN, LongitudinalDirectionality.DIR_MINUS);
474 structure.put(i, laneAttributes);
475 }
476 if (i >= 0)
477 {
478 laneAttributes =
479 new LaneAttributes(network, laneType, Color.GREEN, LongitudinalDirectionality.DIR_PLUS);
480 structure.put(i, laneAttributes);
481 }
482 }
483 }
484 }
485 }
486 return calculateOffsets(network, structure, osmLink, forwards, backwards, warningListener);
487 }
488
489
490
491
492
493
494
495
496
497
498
499
500 private static Map<Double, LaneAttributes> calculateOffsets(final OTSRoadNetwork network,
501 final SortedMap<Integer, LaneAttributes> structure, final OSMLink osmLink, final Integer forwards,
502 final Integer backwards, final WarningListener warningListener) throws NetworkException
503 {
504 HashMap<Double, LaneAttributes> structurewithOffset = new HashMap<Double, LaneAttributes>();
505 LaneAttributes laneAttributes;
506 double currentOffset = 0.0D;
507 if (structure.isEmpty())
508 {
509 warningListener.warning(new WarningEvent(osmLink, "Empty Structure at Link " + osmLink.getId()));
510 }
511 if (structure.lastKey() >= 0)
512 {
513 for (int i = 0; i < forwards; i++)
514 {
515 laneAttributes = structure.get(i);
516 if (null == laneAttributes)
517 {
518 break;
519 }
520 double useWidth = laneWidth(network, laneAttributes, osmLink, warningListener);
521 laneAttributes.setWidth(useWidth);
522 structurewithOffset.put(currentOffset, laneAttributes);
523 currentOffset += useWidth;
524 }
525 }
526 if (structure.firstKey() < 0)
527 {
528 currentOffset = 0.0d;
529 for (int i = -1; i >= (0 - backwards); i--)
530 {
531 laneAttributes = structure.get(i);
532 if (null == laneAttributes)
533 {
534 break;
535 }
536 LaneAttributes previousLaneAttributes = null;
537 for (int k = i + 1; k <= 0; k++)
538 {
539 previousLaneAttributes = structure.get(k);
540 if (null != previousLaneAttributes)
541 {
542 break;
543 }
544 }
545 if (null == previousLaneAttributes)
546 {
547 throw new NetworkException("reverse lane without main lane?");
548 }
549 double useWidth = laneWidth(network, laneAttributes, osmLink, warningListener);
550 laneAttributes.setWidth(useWidth);
551 currentOffset -= previousLaneAttributes.getWidth().getSI();
552 structurewithOffset.put(currentOffset, laneAttributes);
553 }
554 }
555 return structurewithOffset;
556 }
557
558
559
560
561
562
563
564
565
566 static double laneWidth(final OTSRoadNetwork network, final LaneAttributes laneAttributes, final OSMLink link,
567 final WarningListener warningListener)
568 {
569 Double defaultLaneWidth = 3.05d;
570 boolean widthOverride = false;
571 for (OSMTag tag : link.getTags())
572 {
573 if (tag.getKey().equals("width"))
574 {
575 String w = tag.getValue().replace(",", ".");
576 w = w.replace(" ", "");
577 w = w.replace("m", "");
578 w = w.replace("Meter", "");
579 try
580 {
581 defaultLaneWidth = Double.parseDouble(w) / link.getLanes();
582 }
583 catch (NumberFormatException nfe)
584 {
585 System.err.println("Bad lanewidth: \"" + tag.getValue() + "\"");
586 }
587 widthOverride = true;
588 }
589 }
590 LaneType laneType = laneAttributes.getLaneType();
591 if (laneType.isCompatible(network.getGtuType(GTUType.DEFAULTS.CAR), GTUDirectionality.DIR_PLUS)
592 || laneType.isCompatible(network.getGtuType(GTUType.DEFAULTS.CAR), GTUDirectionality.DIR_MINUS))
593 {
594 return defaultLaneWidth;
595 }
596 else if (laneType.isCompatible(network.getGtuType(GTUType.DEFAULTS.BICYCLE), GTUDirectionality.DIR_PLUS)
597 || laneType.isCompatible(network.getGtuType(GTUType.DEFAULTS.BICYCLE), GTUDirectionality.DIR_MINUS))
598 {
599 return 0.8d;
600 }
601 else if (laneType.isCompatible(network.getGtuType(GTUType.DEFAULTS.PEDESTRIAN), GTUDirectionality.DIR_PLUS)
602 || laneType.isCompatible(network.getGtuType(GTUType.DEFAULTS.PEDESTRIAN), GTUDirectionality.DIR_MINUS))
603 {
604 return 0.95d;
605 }
606 else if (laneType.isCompatible(network.getGtuType(GTUType.DEFAULTS.SHIP), GTUDirectionality.DIR_PLUS)
607 || laneType.isCompatible(network.getGtuType(GTUType.DEFAULTS.SHIP), GTUDirectionality.DIR_MINUS))
608 {
609 for (OSMTag tag : link.getTags())
610 {
611 if (tag.getKey().equals("waterway"))
612 {
613 switch (tag.getValue())
614 {
615 case "riverbank":
616 return 1d;
617 default:
618 return defaultLaneWidth;
619 }
620 }
621 else
622 {
623 return 5d;
624 }
625 }
626 }
627 if (!widthOverride)
628 {
629 warningListener.warning(new WarningEvent(link, "No width given; using default laneWidth for Link " + link.getId()));
630 }
631 return defaultLaneWidth;
632 }
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648 public List<Lane> makeLanes(final OTSRoadNetwork network, final OSMLink osmlink, final OTSSimulatorInterface simulator,
649 final WarningListener warningListener) throws NetworkException, NamingException, OTSGeometryException
650 {
651 CrossSectionLink otslink = convertLink(network, osmlink, simulator);
652 List<Lane> lanes = new ArrayList<Lane>();
653 Map<Double, LaneAttributes> structure = makeStructure(network, osmlink, warningListener);
654
655 int laneNum = 0;
656 for (Double offset : structure.keySet())
657 {
658 laneNum++;
659 LaneAttributes laneAttributes = structure.get(offset);
660 if (laneAttributes == null)
661 {
662 break;
663 }
664 Color color = Color.LIGHT_GRAY;
665 LaneType laneType = laneAttributes.getLaneType();
666 Length latPos = new Length(offset, LengthUnit.METER);
667 Map<GTUType, Speed> speedLimit = new HashMap<>();
668 speedLimit.put(network.getGtuType(GTUType.DEFAULTS.VEHICLE), new Speed(100, SpeedUnit.KM_PER_HOUR));
669 Lane newLane = null;
670
671 if (osmlink.hasTag("hasPreceding") && offset >= 0 || osmlink.hasTag("hasFollowing") && offset < 0)
672 {
673 color = Color.RED;
674
675 newLane = new Lane(otslink, "lane." + laneNum, latPos, latPos, laneAttributes.getWidth(),
676 laneAttributes.getWidth(), laneType, speedLimit);
677 new SinkSensor(newLane, new Length(0.25, LengthUnit.METER), simulator);
678 }
679 else if (osmlink.hasTag("hasPreceding") && offset < 0 || osmlink.hasTag("hasFollowing") && offset >= 0)
680 {
681 color = Color.BLUE;
682
683 newLane = new Lane(otslink, "lane." + laneNum, latPos, latPos, laneAttributes.getWidth(),
684 laneAttributes.getWidth(), laneType, speedLimit);
685 }
686 else
687 {
688 color = laneAttributes.getColor();
689
690 newLane = new Lane(otslink, "lane." + laneNum, latPos, latPos, laneAttributes.getWidth(),
691 laneAttributes.getWidth(), laneType, speedLimit);
692 }
693
694
695
696 lanes.add(newLane);
697 }
698 return lanes;
699 }
700
701
702
703
704
705
706
707 public static LaneType makeLaneType(final OTSRoadNetwork network, final List<GTUType> gtuTypes)
708 {
709 StringBuilder name = new StringBuilder();
710 for (GTUType gtu : gtuTypes)
711 {
712 if (name.length() > 0)
713 {
714 name.append("|");
715 }
716 name.append(gtu.getId());
717 }
718 GTUCompatibility<LaneType> compatibility = new GTUCompatibility<>((LaneType) null);
719 LaneType result = new LaneType(name.toString(), compatibility, network);
720 return result;
721 }
722
723
724
725
726
727
728
729 public static LaneType makeLaneType(final OTSRoadNetwork network, final GTUType gtuType)
730 {
731 List<GTUType> gtuTypes = new ArrayList<GTUType>(1);
732 gtuTypes.add(gtuType);
733 return makeLaneType(network, gtuTypes);
734
735
736
737
738 }
739
740
741
742
743
744
745
746 private static ArrayList<OSMLink> findBoundaryLinks(final List<OSMNode> nodes, final List<OSMLink> links)
747 {
748
749
750
751 for (OSMNode node : nodes)
752 {
753 node.linksOriginating = 0;
754 node.linksTerminating = 0;
755 }
756 for (OSMLink link : links)
757 {
758 link.getStart().linksOriginating++;
759 link.getEnd().linksTerminating++;
760 }
761 ArrayList<OSMNode> foundEndNodes = new ArrayList<OSMNode>();
762 for (OSMNode node : nodes)
763 {
764 if (0 == node.linksOriginating && node.linksTerminating > 0
765 || 0 == node.linksTerminating && node.linksOriginating > 0)
766 {
767 foundEndNodes.add(node);
768 }
769 }
770 ArrayList<OSMLink> result = new ArrayList<OSMLink>();
771 for (OSMLink link : links)
772 {
773 if (foundEndNodes.contains(link.getStart()) || foundEndNodes.contains(link.getEnd()))
774 {
775 result.add(link);
776 }
777 }
778 return result;
779 }
780
781
782
783
784
785
786 public static OSMNetwork findSinksandSources(final OSMNetwork net, final ProgressListener progressListener)
787 {
788 progressListener.progress(new ProgressEvent(net, "Counting number of links at each node"));
789 List<OSMNode> nodes = new ArrayList<OSMNode>();
790 nodes.addAll(net.getNodes().values());
791 ArrayList<OSMLink> foundEndpoints = findBoundaryLinks(nodes, net.getLinks());
792 progressListener.progress(new ProgressEvent(net, "Adding tags to non-sinks and non-sources"));
793 int progress = 0;
794 final int progressReportStep = 5000;
795
796 final OSMTag hasFollowing = new OSMTag("hasFollowing", "");
797 final OSMTag hasPreceding = new OSMTag("hasPreceding", "");
798 for (OSMLink l : net.getLinks())
799 {
800 if (foundEndpoints.contains(l))
801 {
802 if (net.hasFollowingLink(l))
803 {
804 l.addTag(hasFollowing);
805 }
806 else if (net.hasPrecedingLink(l))
807 {
808 l.addTag(hasPreceding);
809 }
810 }
811 if (0 == ++progress % progressReportStep)
812 {
813 progressListener.progress(new ProgressEvent(net, String.format(Locale.US, "%d of %d links processed (%.1f%%)",
814 progress, net.getLinks().size(), 100.0 * progress / net.getLinks().size())));
815 }
816 }
817 progressListener.progress(new ProgressEvent(net, "Found " + foundEndpoints.size() + " Sinks and Sources."));
818 return net;
819 }
820
821
822 @Override
823 public String toString()
824 {
825 return "Convert []";
826 }
827 }
828
829
830
831
832
833
834
835
836
837
838 class LaneAttributes implements Serializable
839 {
840
841 private static final long serialVersionUID = 20150303L;
842
843
844 private final LaneType laneType;
845
846
847 private final Color color;
848
849
850 private final LongitudinalDirectionality directionality;
851
852
853 private Length width;
854
855
856
857
858
859
860
861 LaneAttributes(final OTSRoadNetwork network, final LaneType lt, final Color c, final LongitudinalDirectionality d)
862 {
863 if (lt == null)
864 {
865 this.laneType = Convert.makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.VEHICLE));
866 }
867 else
868 {
869 this.laneType = lt;
870 }
871 this.color = c;
872 this.directionality = d;
873 }
874
875
876
877
878
879
880
881
882 LaneAttributes(final OTSRoadNetwork network, final LaneType laneType, final Color color,
883 final LongitudinalDirectionality directionality, final Double width)
884 {
885 if (laneType == null)
886 {
887 this.laneType = Convert.makeLaneType(network, network.getGtuType(GTUType.DEFAULTS.VEHICLE));
888 }
889 else
890 {
891 this.laneType = laneType;
892 }
893 this.color = color;
894 this.directionality = directionality;
895 this.setWidth(width);
896 }
897
898
899
900
901 public LaneType getLaneType()
902 {
903 return this.laneType;
904 }
905
906
907
908
909 public Color getColor()
910 {
911 return this.color;
912 }
913
914
915
916
917 public LongitudinalDirectionality getDirectionality()
918 {
919 return this.directionality;
920 }
921
922
923
924
925 public Length getWidth()
926 {
927 return this.width;
928 }
929
930
931
932
933 public void setWidth(final Double width)
934 {
935 Length w = new Length(width, LengthUnit.METER);
936 this.width = w;
937 }
938
939
940 @Override
941 public String toString()
942 {
943 return "Lane Attributes: " + this.laneType + "; " + this.color + "; " + this.directionality + "; " + this.width;
944 }
945
946 }