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