Convert.java

  1. package org.opentrafficsim.road.network.factory.osm.output;

  2. import java.awt.Color;
  3. import java.rmi.RemoteException;
  4. import java.util.ArrayList;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Locale;
  8. import java.util.Map;
  9. import java.util.Objects;
  10. import java.util.SortedMap;
  11. import java.util.TreeMap;

  12. import javax.naming.NamingException;

  13. import org.djunits.unit.LengthUnit;
  14. import org.djunits.unit.SpeedUnit;
  15. import org.djunits.value.vdouble.scalar.Length;
  16. import org.djunits.value.vdouble.scalar.Speed;
  17. import org.opengis.referencing.FactoryException;
  18. import org.opengis.referencing.operation.TransformException;
  19. import org.opentrafficsim.core.dsol.OTSAnimatorInterface;
  20. import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
  21. import org.opentrafficsim.core.geometry.OTSGeometryException;
  22. import org.opentrafficsim.core.geometry.OTSLine3D;
  23. import org.opentrafficsim.core.geometry.OTSPoint3D;
  24. import org.opentrafficsim.core.gtu.GTUType;
  25. import org.opentrafficsim.core.network.LinkType;
  26. import org.opentrafficsim.core.network.LongitudinalDirectionality;
  27. import org.opentrafficsim.core.network.NetworkException;
  28. import org.opentrafficsim.core.network.OTSNode;
  29. import org.opentrafficsim.road.network.animation.LaneAnimation;
  30. import org.opentrafficsim.road.network.factory.osm.OSMLink;
  31. import org.opentrafficsim.road.network.factory.osm.OSMNetwork;
  32. import org.opentrafficsim.road.network.factory.osm.OSMNode;
  33. import org.opentrafficsim.road.network.factory.osm.OSMTag;
  34. import org.opentrafficsim.road.network.factory.osm.events.ProgressEvent;
  35. import org.opentrafficsim.road.network.factory.osm.events.ProgressListener;
  36. import org.opentrafficsim.road.network.factory.osm.events.WarningEvent;
  37. import org.opentrafficsim.road.network.factory.osm.events.WarningListener;
  38. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  39. import org.opentrafficsim.road.network.lane.Lane;
  40. import org.opentrafficsim.road.network.lane.LaneType;
  41. import org.opentrafficsim.road.network.lane.SinkSensor;
  42. import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
  43. import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;

  44. import com.vividsolutions.jts.geom.Coordinate;

  45. /**
  46.  * <p>
  47.  * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  48.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  49.  * <p>
  50.  * $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
  51.  * initial version 30.12.2014 <br>
  52.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  53.  * @author <a>Moritz Bergmann</a>
  54.  */
  55. public final class Convert
  56. {

  57.     /**
  58.      * Construct a converter.
  59.      */
  60.     public Convert()
  61.     {
  62.         baseX = Double.NaN;
  63.     }

  64.     /** Meridian of least distortion. */
  65.     private static double baseX = Double.NaN;

  66.     /**
  67.      * @param c Coordinate in WGS84
  68.      * @return Coordinate in Geocentric Cartesian system
  69.      * @throws FactoryException on ???
  70.      * @throws TransformException on problems with the coordinate transformation
  71.      */
  72.     public static Coordinate transform(final Coordinate c) throws FactoryException, TransformException
  73.     {
  74.         // final CoordinateReferenceSystem wgs84 = DefaultGeographicCRS.WGS84;
  75.         // final CoordinateReferenceSystem cartesianCRS = DefaultGeocentricCRS.CARTESIAN;
  76.         // final MathTransform mathTransform;
  77.         // mathTransform = CRS.findMathTransform(wgs84, cartesianCRS, false);
  78.         // double[] srcPt = {c.x, c.y};
  79.         // double[] dstPt = new double[mathTransform.getTargetDimensions()];

  80.         // mathTransform.transform(srcPt, 0, dstPt, 0, 1);
  81.         // System.out.println(String.format(Locale.US, "%fkm, %fkm, %fkm", dstPt[0] / 1000, dstPt[1] / 1000, dstPt[2] /
  82.         // 1000));
  83.         // return new Coordinate(dstPt[1], -dstPt[0]);

  84.         // Simple-minded DIY solution
  85.         double radius = 6371000; // Assume Earth is a perfect sphere
  86.         if (Double.isNaN(baseX))
  87.         {
  88.             baseX = c.x; // Use first coordinate as the reference
  89.         }
  90.         double x = radius * Math.toRadians(c.x - baseX) * Math.cos(Math.toRadians(c.y));
  91.         double y = radius * Math.toRadians(c.y);
  92.         // System.out.println(String.format(Locale.US, "%fkm, %fkm, %fkm", x / 1000, y / 1000, radius / 1000));
  93.         return new Coordinate(x, y);
  94.     }

  95.     /**
  96.      * This method converts an OSM link to an OTS link.
  97.      * @param link OSM Link to be converted
  98.      * @return OTS Link
  99.      * @throws OTSGeometryException on failure
  100.      */
  101.     public CrossSectionLink convertLink(final OSMLink link) throws OTSGeometryException
  102.     {
  103.         if (null == link.getStart().getOtsNode())
  104.         {
  105.             link.getStart().setOtsNode(convertNode(link.getStart()));
  106.         }
  107.         if (null == link.getEnd().getOtsNode())
  108.         {
  109.             link.getEnd().setOtsNode(convertNode(link.getEnd()));
  110.         }
  111.         CrossSectionLink result;
  112.         Coordinate[] coordinates;
  113.         List<OSMNode> nodes = link.getSplineList();
  114.         int coordinateCount = 2 + nodes.size();
  115.         coordinates = new Coordinate[coordinateCount];
  116.         OTSNode start = link.getStart().getOtsNode();
  117.         coordinates[0] = new Coordinate(start.getPoint().x, start.getPoint().y, 0);
  118.         for (int i = 0; i < nodes.size(); i++)
  119.         {
  120.             coordinates[i + 1] = new Coordinate(nodes.get(i).getLongitude(), nodes.get(i).getLatitude());
  121.         }
  122.         OTSNode end = link.getEnd().getOtsNode();
  123.         coordinates[coordinates.length - 1] = new Coordinate(end.getPoint().x, end.getPoint().y, 0);
  124.         OTSLine3D designLine = new OTSLine3D(coordinates);
  125.         // XXX How to figure out whether to keep left, right or keep lane?
  126.         // XXX How to figure out if this is a lane in one or two directions? For now, two is assumed...
  127.         result =
  128.             new CrossSectionLink(link.getId(), start, end, LinkType.ALL, designLine,
  129.                 LongitudinalDirectionality.DIR_BOTH, LaneKeepingPolicy.KEEP_RIGHT);
  130.         return result;
  131.     }

  132.     /**
  133.      * This method converts an OSM node to an OTS node.
  134.      * @param node OSM Node to be converted
  135.      * @return OTS Node
  136.      */
  137.     public OTSNode convertNode(final OSMNode node)
  138.     {
  139.         OSMTag tag = node.getTag("ele");
  140.         if (null != tag)
  141.         {
  142.             try
  143.             {
  144.                 String ele = tag.getValue();
  145.                 Double elevation = 0d;
  146.                 if (ele.matches("[0-9]+(km)|m"))
  147.                 {
  148.                     String[] ele2 = ele.split("(km)|m");
  149.                     ele = ele2[0];
  150.                     if (ele2[1].equals("km"))
  151.                     {
  152.                         elevation = Double.parseDouble(ele) * 1000;
  153.                     }
  154.                     else if (ele2[1].equals("m"))
  155.                     {
  156.                         elevation = Double.parseDouble(ele);
  157.                     }
  158.                     else
  159.                     {
  160.                         throw new NumberFormatException("Cannot parse elevation value\"" + ele + "\"");
  161.                     }
  162.                 }
  163.                 else if (ele.matches("[0-9]+"))
  164.                 {
  165.                     elevation = Double.parseDouble(ele);
  166.                 }
  167.                 Coordinate coordWGS84 = new Coordinate(node.getLongitude(), node.getLatitude(), elevation);
  168.                 try
  169.                 {
  170.                     return new OTSNode(Objects.toString(node.getId()), new OTSPoint3D(transform(coordWGS84)));
  171.                 }
  172.                 catch (FactoryException | TransformException exception)
  173.                 {
  174.                     exception.printStackTrace();
  175.                 }
  176.             }
  177.             catch (NumberFormatException exception)
  178.             {
  179.                 exception.printStackTrace();
  180.             }
  181.         }
  182.         // No elevation specified, or we could not parse it; assume elevation is 0
  183.         Coordinate coordWGS84 = new Coordinate(node.getLongitude(), node.getLatitude(), 0d);
  184.         try
  185.         {
  186.             return new OTSNode(Objects.toString(node.getId()), new OTSPoint3D(Convert.transform(coordWGS84)));
  187.         }
  188.         catch (FactoryException | TransformException exception)
  189.         {
  190.             exception.printStackTrace();
  191.             // FIXME: how does the caller deal with a null result? (Answer: not!)
  192.             return null;
  193.         }
  194.     }

  195.     /**
  196.      * Determine the positions of the various lanes on an OSMLink.
  197.      * @param osmLink - The OSM Link on which the conversion is based.
  198.      * @param warningListener WarningListener; the warning listener that receives warning events
  199.      * @return Map&lt;Double, LaneAttributes&gt;; the lane structure
  200.      * @throws NetworkException on failure
  201.      */
  202.     private static Map<Double, LaneAttributes> makeStructure(final OSMLink osmLink,
  203.         final WarningListener warningListener) throws NetworkException
  204.     {
  205.         SortedMap<Integer, LaneAttributes> structure = new TreeMap<Integer, LaneAttributes>();
  206.         int forwards = osmLink.getForwardLanes();
  207.         int backwards = osmLink.getLanes() - osmLink.getForwardLanes();
  208.         LaneType laneType;
  209.         LaneAttributes laneAttributes;
  210.         for (OSMTag tag : osmLink.getTags())
  211.         {
  212.             if (tag.getKey().equals("waterway"))
  213.             {
  214.                 switch (tag.getValue())
  215.                 {
  216.                     case "river":
  217.                         laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BOAT);
  218.                         break;
  219.                     case "canal":
  220.                         laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BOAT);
  221.                         break;
  222.                     default:
  223.                         laneType = makeLaneType(GTUType.NONE);
  224.                         break;
  225.                 }
  226.                 laneAttributes = new LaneAttributes(laneType, Color.CYAN, LongitudinalDirectionality.DIR_BOTH);
  227.                 structure.put(0, laneAttributes);
  228.             }
  229.         }
  230.         for (OSMTag tag : osmLink.getTags())
  231.         {
  232.             if (tag.getKey().equals("highway")
  233.                 && (tag.getValue().equals("primary") || tag.getValue().equals("secondary")
  234.                     || tag.getValue().equals("tertiary") || tag.getValue().equals("residential")
  235.                     || tag.getValue().equals("trunk") || tag.getValue().equals("motorway")
  236.                     || tag.getValue().equals("service") || tag.getValue().equals("unclassified")
  237.                     || tag.getValue().equals("motorway_link") || tag.getValue().equals("primary_link")
  238.                     || tag.getValue().equals("secondary_link") || tag.getValue().equals("tertiary_link")
  239.                     || tag.getValue().equals("trunk_link") || tag.getValue().equals("road")
  240.                     || tag.getValue().equals("track") || tag.getValue().equals("living_street")))
  241.             {
  242.                 laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.CAR);
  243.                 if (osmLink.getLanes() == 1 && !osmLink.isOneway())
  244.                 {
  245.                     laneAttributes =
  246.                         new LaneAttributes(laneType, Color.LIGHT_GRAY, LongitudinalDirectionality.DIR_BOTH);
  247.                     structure.put(0, laneAttributes);
  248.                 }
  249.                 else
  250.                 {
  251.                     for (int i = 0 - backwards; i < forwards; i++)
  252.                     {
  253.                         if (i < 0)
  254.                         {
  255.                             laneAttributes =
  256.                                 new LaneAttributes(laneType, Color.LIGHT_GRAY, LongitudinalDirectionality.DIR_MINUS);
  257.                             structure.put(i, laneAttributes);
  258.                         }
  259.                         if (i >= 0)
  260.                         {
  261.                             laneAttributes =
  262.                                 new LaneAttributes(laneType, Color.LIGHT_GRAY, LongitudinalDirectionality.DIR_PLUS);
  263.                             structure.put(i, laneAttributes);
  264.                         }
  265.                     }
  266.                 }
  267.             }
  268.             else if (tag.getKey().equals("highway")
  269.                 && (tag.getValue().equals("path") || tag.getValue().equals("steps")))
  270.             {
  271.                 List<GTUType> types = new ArrayList<GTUType>();
  272.                 for (OSMTag t2 : osmLink.getTags())
  273.                 {
  274.                     if (t2.getKey().equals("bicycle"))
  275.                     {
  276.                         types.add(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE);
  277.                     }
  278.                     /*
  279.                      * if (t2.getKey().equals("foot")) {
  280.                      * types.add(org.opentrafficsim.importexport.osm.PredefinedGTUTypes.pedestrian); }
  281.                      */
  282.                 }
  283.                 laneType = makeLaneType(types);
  284.                 types.add(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.PEDESTRIAN);
  285.                 if (!types.isEmpty())
  286.                 {
  287.                     if (osmLink.getLanes() == 1 && !osmLink.isOneway())
  288.                     {
  289.                         laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_BOTH);
  290.                         structure.put(0, laneAttributes);
  291.                     }
  292.                     else
  293.                     {
  294.                         for (int i = 0 - backwards; i < forwards; i++)
  295.                         {
  296.                             if (i < 0)
  297.                             {
  298.                                 laneAttributes =
  299.                                     new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_MINUS);
  300.                                 structure.put(i, laneAttributes);
  301.                             }
  302.                             if (i >= 0)
  303.                             {
  304.                                 laneAttributes =
  305.                                     new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_PLUS);
  306.                                 structure.put(i, laneAttributes);
  307.                             }
  308.                         }
  309.                     }
  310.                 }
  311.                 types.clear();
  312.             }
  313.         }
  314.         for (OSMTag tag : osmLink.getTags())
  315.         {
  316.             if (tag.getKey().equals("cycleway"))
  317.             {
  318.                 laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE);
  319.                 switch (tag.getValue())
  320.                 {
  321.                     case "lane": // cycleway:lane is directly adjacent to the highway.
  322.                         forwards++;
  323.                         backwards++;
  324.                         laneAttributes =
  325.                             new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_MINUS);
  326.                         structure.put(0 - backwards, laneAttributes);
  327.                         laneAttributes =
  328.                             new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_PLUS);
  329.                         structure.put(forwards - 1, laneAttributes);
  330.                         break;
  331.                     case "track": // cycleway:track is separated by a gap from the highway.
  332.                         forwards++;
  333.                         backwards++;
  334.                         laneAttributes =
  335.                             new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_MINUS);
  336.                         structure.put(0 - backwards, laneAttributes);
  337.                         laneAttributes =
  338.                             new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_PLUS);
  339.                         structure.put(forwards - 1, laneAttributes);
  340.                         break;
  341.                     case "shared_lane": // cycleway:shared_lane is embedded into the highway.
  342.                         List<GTUType> types = new ArrayList<GTUType>();
  343.                         types.add(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE);
  344.                         types.add(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.CAR);
  345.                         laneType = makeLaneType(types);
  346.                         laneAttributes =
  347.                             new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_MINUS);
  348.                         structure.put(0 - backwards, laneAttributes);
  349.                         laneAttributes =
  350.                             new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_PLUS);
  351.                         structure.put(forwards - 1, laneAttributes);
  352.                         break;
  353.                     default:
  354.                         break;
  355.                 }
  356.             }
  357.         }
  358.         for (OSMTag tag : osmLink.getTags())
  359.         {
  360.             if (tag.getKey().equals("sidewalk"))
  361.             {
  362.                 laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.PEDESTRIAN);
  363.                 switch (tag.getValue())
  364.                 {
  365.                     case "both":
  366.                         forwards++;
  367.                         backwards++;
  368.                         laneAttributes =
  369.                             new LaneAttributes(laneType, Color.YELLOW, LongitudinalDirectionality.DIR_MINUS);
  370.                         structure.put(0 - backwards, laneAttributes);
  371.                         laneAttributes =
  372.                             new LaneAttributes(laneType, Color.YELLOW, LongitudinalDirectionality.DIR_PLUS);
  373.                         structure.put(forwards - 1, laneAttributes);
  374.                         break;
  375.                     case "left":
  376.                         backwards++;
  377.                         laneAttributes =
  378.                             new LaneAttributes(laneType, Color.YELLOW, LongitudinalDirectionality.DIR_BOTH);
  379.                         structure.put(0 - backwards, laneAttributes);
  380.                         break;
  381.                     case "right":
  382.                         forwards++;
  383.                         laneAttributes =
  384.                             new LaneAttributes(laneType, Color.YELLOW, LongitudinalDirectionality.DIR_BOTH);
  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("highway")
  395.                 && (tag.getValue().equals("cycleway") || tag.getValue().equals("footway")
  396.                     || tag.getValue().equals("pedestrian") || tag.getValue().equals("steps")))
  397.             {
  398.                 if (tag.getValue().equals("footway") || tag.getValue().equals("pedestrian")
  399.                     || tag.getValue().equals("steps"))
  400.                 {
  401.                     laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.PEDESTRIAN);
  402.                     if (osmLink.getLanes() == 1 && !osmLink.isOneway())
  403.                     {
  404.                         laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_BOTH);
  405.                         structure.put(0, laneAttributes);
  406.                     }
  407.                     else
  408.                     {
  409.                         for (int i = 0 - backwards; i < forwards; i++)
  410.                         {
  411.                             if (i < 0)
  412.                             {
  413.                                 laneAttributes =
  414.                                     new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_MINUS);
  415.                                 structure.put(i, laneAttributes);
  416.                             }
  417.                             if (i >= 0)
  418.                             {
  419.                                 laneAttributes =
  420.                                     new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_PLUS);
  421.                                 structure.put(i, laneAttributes);
  422.                             }
  423.                         }
  424.                     }
  425.                 }
  426.                 if (tag.getValue().equals("cycleway"))
  427.                 {
  428.                     laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE);
  429.                     if (osmLink.getLanes() == 1 && !osmLink.isOneway())
  430.                     {
  431.                         laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_BOTH);
  432.                         structure.put(0, laneAttributes);
  433.                     }
  434.                     for (int i = 0 - backwards; i < forwards; i++)
  435.                     {
  436.                         if (i < 0)
  437.                         {
  438.                             laneAttributes =
  439.                                 new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_MINUS);
  440.                             structure.put(i, laneAttributes);
  441.                         }
  442.                         if (i >= 0)
  443.                         {
  444.                             laneAttributes =
  445.                                 new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_PLUS);
  446.                             structure.put(i, laneAttributes);
  447.                         }
  448.                     }
  449.                 }
  450.             }
  451.         }
  452.         return calculateOffsets(structure, osmLink, forwards, backwards, warningListener);
  453.     }

  454.     /**
  455.      * Calculates the actual offsets of the individual lanes.
  456.      * @param structure - Sorted Map of Lane Positions and Attributes
  457.      * @param osmLink - The osmLink on which the conversion is based.
  458.      * @param forwards - Number of forwards oriented lanes.
  459.      * @param backwards - Number of backwards oriented lanes.
  460.      * @param warningListener WarningListener; the warning listener that receives warning events
  461.      * @return Map containing the lane structure with offsets.
  462.      * @throws NetworkException on failure
  463.      */
  464.     private static Map<Double, LaneAttributes> calculateOffsets(final SortedMap<Integer, LaneAttributes> structure,
  465.         final OSMLink osmLink, final Integer forwards, final Integer backwards, final WarningListener warningListener)
  466.         throws NetworkException
  467.     {
  468.         HashMap<Double, LaneAttributes> structurewithOffset = new HashMap<Double, LaneAttributes>();
  469.         LaneAttributes laneAttributes;
  470.         double currentOffset = 0.0D;
  471.         if (structure.isEmpty())
  472.         {
  473.             warningListener.warning(new WarningEvent(osmLink, "Empty Structure at Link " + osmLink.getId()));
  474.         }
  475.         if (structure.lastKey() >= 0)
  476.         {
  477.             for (int i = 0; i < forwards; i++)
  478.             {
  479.                 laneAttributes = structure.get(i);
  480.                 if (null == laneAttributes)
  481.                 {
  482.                     break;
  483.                 }
  484.                 double useWidth = laneWidth(laneAttributes, osmLink, warningListener);
  485.                 laneAttributes.setWidth(useWidth);
  486.                 structurewithOffset.put(currentOffset, laneAttributes);
  487.                 currentOffset += useWidth;
  488.             }
  489.         }
  490.         if (structure.firstKey() < 0)
  491.         {
  492.             currentOffset = 0.0d;
  493.             for (int i = -1; i >= (0 - backwards); i--)
  494.             {
  495.                 laneAttributes = structure.get(i);
  496.                 if (null == laneAttributes)
  497.                 {
  498.                     break;
  499.                 }
  500.                 LaneAttributes previousLaneAttributes = null;
  501.                 for (int k = i + 1; k <= 0; k++)
  502.                 {
  503.                     previousLaneAttributes = structure.get(k);
  504.                     if (null != previousLaneAttributes)
  505.                     {
  506.                         break;
  507.                     }
  508.                 }
  509.                 if (null == previousLaneAttributes)
  510.                 {
  511.                     throw new NetworkException("reverse lane without main lane?");
  512.                 }
  513.                 double useWidth = laneWidth(laneAttributes, osmLink, warningListener);
  514.                 laneAttributes.setWidth(useWidth);
  515.                 currentOffset -= previousLaneAttributes.getWidth().getSI();
  516.                 structurewithOffset.put(currentOffset, laneAttributes);
  517.             }
  518.         }
  519.         return structurewithOffset;
  520.     }

  521.     /**
  522.      * Figure out a reasonable width for a lane.
  523.      * @param laneAttributes LaneAttributes; the attributes of the lane
  524.      * @param link OSMLink; the link that owns the lane
  525.      * @param warningListener WarningListener; the warning listener that receives warning events
  526.      * @return double; the width (in meters) of the lane
  527.      */
  528.     static double laneWidth(final LaneAttributes laneAttributes, final OSMLink link,
  529.         final WarningListener warningListener)
  530.     {
  531.         Double defaultLaneWidth = 3.05d; // TODO This is the German standard car lane width
  532.         boolean widthOverride = false;
  533.         for (OSMTag tag : link.getTags())
  534.         {
  535.             if (tag.getKey().equals("width"))
  536.             {
  537.                 String w = tag.getValue().replace(",", ".");
  538.                 w = w.replace(" ", "");
  539.                 w = w.replace("m", "");
  540.                 w = w.replace("Meter", "");
  541.                 try
  542.                 {
  543.                     defaultLaneWidth = Double.parseDouble(w) / link.getLanes();
  544.                 }
  545.                 catch (NumberFormatException nfe)
  546.                 {
  547.                     System.err.println("Bad lanewidth: \"" + tag.getValue() + "\"");
  548.                 }
  549.                 widthOverride = true;
  550.             }
  551.         }
  552.         LaneType laneType = laneAttributes.getLaneType();
  553.         if (laneType.isCompatible(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.CAR))
  554.         {
  555.             return defaultLaneWidth;
  556.         }
  557.         else if (laneType.isCompatible(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE))
  558.         {
  559.             return 0.8d; // TODO German default bikepath width
  560.         }
  561.         else if (laneType.isCompatible(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.PEDESTRIAN))
  562.         {
  563.             return 0.95d; // TODO German default footpath width
  564.         }
  565.         else if (laneType.isCompatible(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BOAT))
  566.         {
  567.             for (OSMTag tag : link.getTags())
  568.             {
  569.                 if (tag.getKey().equals("waterway"))
  570.                 {
  571.                     switch (tag.getValue())
  572.                     {
  573.                         case "riverbank":
  574.                             return 1d;
  575.                         default:
  576.                             return defaultLaneWidth;
  577.                     }
  578.                 }
  579.                 else
  580.                 {
  581.                     return 5d;
  582.                 }
  583.             }
  584.         }
  585.         if (!widthOverride)
  586.         {
  587.             warningListener.warning(new WarningEvent(link, "No width given; using default laneWidth for Link "
  588.                 + link.getId()));
  589.         }
  590.         return defaultLaneWidth;
  591.     }

  592.     /**
  593.      * This method creates lanes out of an OSM link LaneTypes are not yet extensive and can be further increased through Tags
  594.      * provided by OSM. The standard lane width of 3.05 is an estimation based on the European width limitation for vehicles
  595.      * (2.55m) + 25cm each side.
  596.      * @param osmlink Link OSMLink; the OSM link to make lanes for
  597.      * @param simulator OTSDEVSSimulatorInterface; the simulator that will animate the generates lanes (if it happens to be an
  598.      *            instance of OTSAnimatorInterface)
  599.      * @param warningListener WarningListener; the warning listener that will receive warning events
  600.      * @return List&lt;Lane&gt;
  601.      * @throws NetworkException on network inconsistency
  602.      * @throws NamingException on naming problems (in the animator)
  603.      * @throws OTSGeometryException when lane contour or center line cannot be instantiated
  604.      */
  605.     public List<Lane> makeLanes(final OSMLink osmlink, final OTSDEVSSimulatorInterface simulator,
  606.         final WarningListener warningListener) throws NetworkException, NamingException, OTSGeometryException
  607.     {
  608.         CrossSectionLink otslink = convertLink(osmlink);
  609.         List<Lane> lanes = new ArrayList<Lane>();
  610.         Map<Double, LaneAttributes> structure = makeStructure(osmlink, warningListener);

  611.         int laneNum = 0;
  612.         for (Double offset : structure.keySet())
  613.         {
  614.             laneNum++;
  615.             LaneAttributes laneAttributes = structure.get(offset);
  616.             if (laneAttributes == null)
  617.             {
  618.                 break;
  619.             }
  620.             Color color = Color.LIGHT_GRAY;
  621.             LaneType laneType = laneAttributes.getLaneType();
  622.             Length.Rel latPos = new Length.Rel(offset, LengthUnit.METER);
  623.             Map<GTUType, LongitudinalDirectionality> directionality = new HashMap<>();
  624.             directionality.put(GTUType.ALL, laneAttributes.getDirectionality());
  625.             Map<GTUType, Speed> speedLimit = new HashMap<>();
  626.             speedLimit.put(GTUType.ALL, new Speed(100, SpeedUnit.KM_PER_HOUR));
  627.             Lane newLane = null;
  628.             // FIXME the following code assumes right-hand-side driving.
  629.             if (osmlink.hasTag("hasPreceding") && offset >= 0 || osmlink.hasTag("hasFollowing") && offset < 0)
  630.             {
  631.                 color = Color.RED;
  632.                 // FIXME overtaking conditions per country and/or type of road?
  633.                 newLane =
  634.                     new Lane(otslink, "lane." + laneNum, latPos, latPos, laneAttributes.getWidth(),
  635.                         laneAttributes.getWidth(), laneType, directionality, speedLimit,
  636.                         new OvertakingConditions.LeftAndRight());
  637.                 SinkSensor sensor = new SinkSensor(newLane, new Length.Rel(0.25, LengthUnit.METER), simulator);
  638.                 newLane.addSensor(sensor, GTUType.ALL);
  639.             }
  640.             else if (osmlink.hasTag("hasPreceding") && offset < 0 || osmlink.hasTag("hasFollowing") && offset >= 0)
  641.             {
  642.                 color = Color.BLUE;
  643.                 // FIXME overtaking conditions per country and/or type of road?
  644.                 newLane =
  645.                     new Lane(otslink, "lane." + laneNum, latPos, latPos, laneAttributes.getWidth(),
  646.                         laneAttributes.getWidth(), laneType, directionality, speedLimit,
  647.                         new OvertakingConditions.LeftAndRight());
  648.             }
  649.             else
  650.             {
  651.                 color = laneAttributes.getColor();
  652.                 // FIXME overtaking conditions per country and/or type of road?
  653.                 newLane =
  654.                     new Lane(otslink, "lane." + laneNum, latPos, latPos, laneAttributes.getWidth(),
  655.                         laneAttributes.getWidth(), laneType, directionality, speedLimit,
  656.                         new OvertakingConditions.LeftAndRight());
  657.             }
  658.             if (simulator instanceof OTSAnimatorInterface)
  659.             {
  660.                 try
  661.                 {
  662.                     new LaneAnimation(newLane, simulator, color, false);
  663.                 }
  664.                 catch (RemoteException exception)
  665.                 {
  666.                     exception.printStackTrace();
  667.                 }
  668.             }
  669.             lanes.add(newLane);
  670.         }
  671.         return lanes;
  672.     }

  673.     /**
  674.      * This method creates a LaneType which supports all GTUTypes that have been specified in the GTUType List "GTUs".
  675.      * @param gtuTypes List&lt;GTUType&lt;String&gt;&gt;; list of GTUTypes
  676.      * @return LaneType permeable for all of the specific GTUTypes
  677.      */
  678.     public static LaneType makeLaneType(final List<GTUType> gtuTypes)
  679.     {
  680.         StringBuilder name = new StringBuilder();
  681.         for (GTUType gtu : gtuTypes)
  682.         {
  683.             if (name.length() > 0)
  684.             {
  685.                 name.append("|");
  686.             }
  687.             name.append(gtu.getId());
  688.         }
  689.         LaneType result = new LaneType(name.toString());
  690.         for (GTUType gtu : gtuTypes)
  691.         {
  692.             result.addCompatibility(gtu);
  693.         }
  694.         return result;
  695.     }

  696.     /**
  697.      * This method creates a LaneType which supports the specified GTUType.
  698.      * @param gtuType GTUType; the type of GTU that can travel on the new LaneType
  699.      * @return LaneType
  700.      */
  701.     public static LaneType makeLaneType(final GTUType gtuType)
  702.     {
  703.         List<GTUType> gtuTypes = new ArrayList<GTUType>(1);
  704.         gtuTypes.add(gtuType);
  705.         return makeLaneType(gtuTypes);
  706.         // String name = gtuType.getId();
  707.         // LaneType result = new LaneType(name);
  708.         // result.addPermeability(gtuType);
  709.         // return result;
  710.     }

  711.     /**
  712.      * Identify Links that are sources or sinks.
  713.      * @param nodes List of Nodes
  714.      * @param links List of Links
  715.      * @return List of Links which are candidates for becoming sinks/sources.
  716.      */
  717.     private static ArrayList<OSMLink> findBoundaryLinks(final List<OSMNode> nodes, final List<OSMLink> links)
  718.     {
  719.         // TODO: test performance (memory- and time-wise) when the counters are replaced by ArrayList<OSMLink> which
  720.         // would obviate the need to do full searches over all Links to find OSMNodes that are source or sink.
  721.         // Reset the counters (should not be necessary unless this method is called more than once)
  722.         for (OSMNode node : nodes)
  723.         {
  724.             node.linksOriginating = 0;
  725.             node.linksTerminating = 0;
  726.         }
  727.         for (OSMLink link : links)
  728.         {
  729.             link.getStart().linksOriginating++;
  730.             link.getEnd().linksTerminating++;
  731.         }
  732.         ArrayList<OSMNode> foundEndNodes = new ArrayList<OSMNode>();
  733.         for (OSMNode node : nodes)
  734.         {
  735.             if (0 == node.linksOriginating && node.linksTerminating > 0 || 0 == node.linksTerminating
  736.                 && node.linksOriginating > 0)
  737.             {
  738.                 foundEndNodes.add(node);
  739.             }
  740.         }
  741.         ArrayList<OSMLink> result = new ArrayList<OSMLink>();
  742.         for (OSMLink link : links)
  743.         {
  744.             if (foundEndNodes.contains(link.getStart()) || foundEndNodes.contains(link.getEnd()))
  745.             {
  746.                 result.add(link);
  747.             }
  748.         }
  749.         return result;
  750.     }

  751.     /**
  752.      * @param net The OSM network which is to be searched for Sinks and Sources.
  753.      * @param progressListener ProgressListener; the progress listener that will receive progress events
  754.      * @return Network with all possible sinks and sources tagged.
  755.      */
  756.     public static OSMNetwork findSinksandSources(final OSMNetwork net, final ProgressListener progressListener)
  757.     {
  758.         progressListener.progress(new ProgressEvent(net, "Counting number of links at each node"));
  759.         List<OSMNode> nodes = new ArrayList<OSMNode>();
  760.         nodes.addAll(net.getNodes().values());
  761.         ArrayList<OSMLink> foundEndpoints = findBoundaryLinks(nodes, net.getLinks());
  762.         progressListener.progress(new ProgressEvent(net, "Adding tags to non-sinks and non-sources"));
  763.         int progress = 0;
  764.         final int progressReportStep = 5000;
  765.         // As tags are immutable we make ONE for following and ONE for preceding
  766.         final OSMTag hasFollowing = new OSMTag("hasFollowing", "");
  767.         final OSMTag hasPreceding = new OSMTag("hasPreceding", "");
  768.         for (OSMLink l : net.getLinks())
  769.         {
  770.             if (foundEndpoints.contains(l))
  771.             {
  772.                 if (net.hasFollowingLink(l))
  773.                 {
  774.                     l.addTag(hasFollowing);
  775.                 }
  776.                 else if (net.hasPrecedingLink(l))
  777.                 {
  778.                     l.addTag(hasPreceding);
  779.                 }
  780.             }
  781.             if (0 == ++progress % progressReportStep)
  782.             {
  783.                 progressListener.progress(new ProgressEvent(net, String.format(Locale.US,
  784.                     "%d of %d links processed (%.1f%%)", progress, net.getLinks().size(), 100.0 * progress
  785.                         / net.getLinks().size())));
  786.             }
  787.         }
  788.         progressListener.progress(new ProgressEvent(net, "Found " + foundEndpoints.size() + " Sinks and Sources."));
  789.         return net;
  790.     }
  791. }

  792. /**
  793.  * <p>
  794.  * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  795.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  796.  * <p>
  797.  * $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
  798.  * initial version ar 3, 2015 <br>
  799.  * @author <a>Moritz Bergmann</a>
  800.  */
  801. class LaneAttributes
  802. {
  803.     /** Type of the lane (immutable). */
  804.     private final LaneType laneType;

  805.     /** Drawing color of the lane (immutable). */
  806.     private final Color color;

  807.     /** LongitudinalDirectionality of the lane (immutable). */
  808.     private final LongitudinalDirectionality directionality;

  809.     /** Width of the lane. */
  810.     private Length.Rel width;

  811.     /**
  812.      * @param lt - LaneType
  813.      * @param c - Color
  814.      * @param d - LongitudinalDIrectionality
  815.      */
  816.     public LaneAttributes(final LaneType lt, final Color c, final LongitudinalDirectionality d)
  817.     {
  818.         if (lt == null)
  819.         {
  820.             this.laneType = Convert.makeLaneType(GTUType.NONE);
  821.         }
  822.         else
  823.         {
  824.             this.laneType = lt;
  825.         }
  826.         this.color = c;
  827.         this.directionality = d;
  828.     }

  829.     /**
  830.      * @param laneType - LaneType
  831.      * @param color - Color
  832.      * @param directionality - LongitudinalDIrectionality
  833.      * @param width - width
  834.      */
  835.     public LaneAttributes(final LaneType laneType, final Color color, final LongitudinalDirectionality directionality,
  836.         final Double width)
  837.     {
  838.         if (laneType == null)
  839.         {
  840.             this.laneType = Convert.makeLaneType(GTUType.NONE);
  841.         }
  842.         else
  843.         {
  844.             this.laneType = laneType;
  845.         }
  846.         this.color = color;
  847.         this.directionality = directionality;
  848.         this.setWidth(width);
  849.     }

  850.     /**
  851.      * @return LaneType
  852.      */
  853.     public LaneType getLaneType()
  854.     {
  855.         return this.laneType;
  856.     }

  857.     /**
  858.      * @return Color
  859.      */
  860.     public Color getColor()
  861.     {
  862.         return this.color;
  863.     }

  864.     /**
  865.      * @return LongitudinalDirectionality
  866.      */
  867.     public LongitudinalDirectionality getDirectionality()
  868.     {
  869.         return this.directionality;
  870.     }

  871.     /**
  872.      * @return width.
  873.      */
  874.     public Length.Rel getWidth()
  875.     {
  876.         return this.width;
  877.     }

  878.     /**
  879.      * @param width set width.
  880.      */
  881.     public void setWidth(final Double width)
  882.     {
  883.         Length.Rel w = new Length.Rel(width, LengthUnit.METER);
  884.         this.width = w;
  885.     }

  886.     /** {@inheritDoc} */
  887.     public String toString()
  888.     {
  889.         return "Lane Attributes: " + this.laneType + "; " + this.color + "; " + this.directionality + "; " + this.width;
  890.     }

  891. }