Convert.java
package org.opentrafficsim.road.network.factory.osm.output;
import java.awt.Color;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.naming.NamingException;
import org.djunits.unit.LengthUnit;
import org.djunits.unit.SpeedUnit;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Speed;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
import org.opentrafficsim.core.dsol.OTSAnimatorInterface;
import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
import org.opentrafficsim.core.geometry.OTSGeometryException;
import org.opentrafficsim.core.geometry.OTSLine3D;
import org.opentrafficsim.core.geometry.OTSPoint3D;
import org.opentrafficsim.core.gtu.GTUType;
import org.opentrafficsim.core.network.LinkType;
import org.opentrafficsim.core.network.LongitudinalDirectionality;
import org.opentrafficsim.core.network.NetworkException;
import org.opentrafficsim.core.network.OTSNode;
import org.opentrafficsim.road.network.animation.LaneAnimation;
import org.opentrafficsim.road.network.factory.osm.OSMLink;
import org.opentrafficsim.road.network.factory.osm.OSMNetwork;
import org.opentrafficsim.road.network.factory.osm.OSMNode;
import org.opentrafficsim.road.network.factory.osm.OSMTag;
import org.opentrafficsim.road.network.factory.osm.events.ProgressEvent;
import org.opentrafficsim.road.network.factory.osm.events.ProgressListener;
import org.opentrafficsim.road.network.factory.osm.events.WarningEvent;
import org.opentrafficsim.road.network.factory.osm.events.WarningListener;
import org.opentrafficsim.road.network.lane.CrossSectionLink;
import org.opentrafficsim.road.network.lane.Lane;
import org.opentrafficsim.road.network.lane.LaneType;
import org.opentrafficsim.road.network.lane.SinkSensor;
import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
import com.vividsolutions.jts.geom.Coordinate;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* <p>
* Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* <p>
* $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
* initial version 30.12.2014 <br>
* @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
* @author <a>Moritz Bergmann</a>
*/
public final class Convert
{
/**
* Construct a converter.
*/
@SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
public Convert()
{
baseX = Double.NaN;
}
/** Meridian of least distortion. */
private static double baseX = Double.NaN;
/**
* @param c Coordinate in WGS84
* @return Coordinate in Geocentric Cartesian system
* @throws FactoryException on ???
* @throws TransformException on problems with the coordinate transformation
*/
public static Coordinate transform(final Coordinate c) throws FactoryException, TransformException
{
// final CoordinateReferenceSystem wgs84 = DefaultGeographicCRS.WGS84;
// final CoordinateReferenceSystem cartesianCRS = DefaultGeocentricCRS.CARTESIAN;
// final MathTransform mathTransform;
// mathTransform = CRS.findMathTransform(wgs84, cartesianCRS, false);
// double[] srcPt = {c.x, c.y};
// double[] dstPt = new double[mathTransform.getTargetDimensions()];
// mathTransform.transform(srcPt, 0, dstPt, 0, 1);
// System.out.println(String.format(Locale.US, "%fkm, %fkm, %fkm", dstPt[0] / 1000, dstPt[1] / 1000, dstPt[2] /
// 1000));
// return new Coordinate(dstPt[1], -dstPt[0]);
// Simple-minded DIY solution
double radius = 6371000; // Assume Earth is a perfect sphere
if (Double.isNaN(baseX))
{
baseX = c.x; // Use first coordinate as the reference
}
double x = radius * Math.toRadians(c.x - baseX) * Math.cos(Math.toRadians(c.y));
double y = radius * Math.toRadians(c.y);
// System.out.println(String.format(Locale.US, "%fkm, %fkm, %fkm", x / 1000, y / 1000, radius / 1000));
return new Coordinate(x, y);
}
/**
* This method converts an OSM link to an OTS link.
* @param link OSM Link to be converted
* @return OTS Link
* @throws OTSGeometryException on failure
*/
public CrossSectionLink convertLink(final OSMLink link) throws OTSGeometryException
{
if (null == link.getStart().getOtsNode())
{
link.getStart().setOtsNode(convertNode(link.getStart()));
}
if (null == link.getEnd().getOtsNode())
{
link.getEnd().setOtsNode(convertNode(link.getEnd()));
}
CrossSectionLink result;
Coordinate[] coordinates;
List<OSMNode> nodes = link.getSplineList();
int coordinateCount = 2 + nodes.size();
coordinates = new Coordinate[coordinateCount];
OTSNode start = link.getStart().getOtsNode();
coordinates[0] = new Coordinate(start.getPoint().x, start.getPoint().y, 0);
for (int i = 0; i < nodes.size(); i++)
{
coordinates[i + 1] = new Coordinate(nodes.get(i).getLongitude(), nodes.get(i).getLatitude());
}
OTSNode end = link.getEnd().getOtsNode();
coordinates[coordinates.length - 1] = new Coordinate(end.getPoint().x, end.getPoint().y, 0);
OTSLine3D designLine = new OTSLine3D(coordinates);
// XXX How to figure out whether to keep left, right or keep lane?
// XXX How to figure out if this is a lane in one or two directions? For now, two is assumed...
result =
new CrossSectionLink(link.getId(), start, end, LinkType.ALL, designLine, LongitudinalDirectionality.DIR_BOTH,
LaneKeepingPolicy.KEEP_RIGHT);
return result;
}
/**
* This method converts an OSM node to an OTS node.
* @param node OSM Node to be converted
* @return OTS Node
*/
public OTSNode convertNode(final OSMNode node)
{
OSMTag tag = node.getTag("ele");
if (null != tag)
{
try
{
String ele = tag.getValue();
Double elevation = 0d;
if (ele.matches("[0-9]+(km)|m"))
{
String[] ele2 = ele.split("(km)|m");
ele = ele2[0];
if (ele2[1].equals("km"))
{
elevation = Double.parseDouble(ele) * 1000;
}
else if (ele2[1].equals("m"))
{
elevation = Double.parseDouble(ele);
}
else
{
throw new NumberFormatException("Cannot parse elevation value\"" + ele + "\"");
}
}
else if (ele.matches("[0-9]+"))
{
elevation = Double.parseDouble(ele);
}
Coordinate coordWGS84 = new Coordinate(node.getLongitude(), node.getLatitude(), elevation);
try
{
return new OTSNode(Objects.toString(node.getId()), new OTSPoint3D(transform(coordWGS84)));
}
catch (FactoryException | TransformException exception)
{
exception.printStackTrace();
}
}
catch (NumberFormatException exception)
{
exception.printStackTrace();
}
}
// No elevation specified, or we could not parse it; assume elevation is 0
Coordinate coordWGS84 = new Coordinate(node.getLongitude(), node.getLatitude(), 0d);
try
{
return new OTSNode(Objects.toString(node.getId()), new OTSPoint3D(Convert.transform(coordWGS84)));
}
catch (FactoryException | TransformException exception)
{
exception.printStackTrace();
// FIXME: how does the caller deal with a null result? (Answer: not!)
return null;
}
}
/**
* Determine the positions of the various lanes on an OSMLink.
* @param osmLink - The OSM Link on which the conversion is based.
* @param warningListener WarningListener; the warning listener that receives warning events
* @return Map<Double, LaneAttributes>; the lane structure
* @throws NetworkException on failure
*/
private static Map<Double, LaneAttributes> makeStructure(final OSMLink osmLink, final WarningListener warningListener)
throws NetworkException
{
SortedMap<Integer, LaneAttributes> structure = new TreeMap<Integer, LaneAttributes>();
int forwards = osmLink.getForwardLanes();
int backwards = osmLink.getLanes() - osmLink.getForwardLanes();
LaneType laneType;
LaneAttributes laneAttributes;
for (OSMTag tag : osmLink.getTags())
{
if (tag.getKey().equals("waterway"))
{
switch (tag.getValue())
{
case "river":
laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BOAT);
break;
case "canal":
laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BOAT);
break;
default:
laneType = makeLaneType(GTUType.NONE);
break;
}
laneAttributes = new LaneAttributes(laneType, Color.CYAN, LongitudinalDirectionality.DIR_BOTH);
structure.put(0, laneAttributes);
}
}
for (OSMTag tag : osmLink.getTags())
{
if (tag.getKey().equals("highway")
&& (tag.getValue().equals("primary") || tag.getValue().equals("secondary")
|| tag.getValue().equals("tertiary") || tag.getValue().equals("residential")
|| tag.getValue().equals("trunk") || tag.getValue().equals("motorway")
|| tag.getValue().equals("service") || tag.getValue().equals("unclassified")
|| tag.getValue().equals("motorway_link") || tag.getValue().equals("primary_link")
|| tag.getValue().equals("secondary_link") || tag.getValue().equals("tertiary_link")
|| tag.getValue().equals("trunk_link") || tag.getValue().equals("road")
|| tag.getValue().equals("track") || tag.getValue().equals("living_street")))
{
laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.CAR);
if (osmLink.getLanes() == 1 && !osmLink.isOneway())
{
laneAttributes = new LaneAttributes(laneType, Color.LIGHT_GRAY, LongitudinalDirectionality.DIR_BOTH);
structure.put(0, laneAttributes);
}
else
{
for (int i = 0 - backwards; i < forwards; i++)
{
if (i < 0)
{
laneAttributes =
new LaneAttributes(laneType, Color.LIGHT_GRAY, LongitudinalDirectionality.DIR_MINUS);
structure.put(i, laneAttributes);
}
if (i >= 0)
{
laneAttributes =
new LaneAttributes(laneType, Color.LIGHT_GRAY, LongitudinalDirectionality.DIR_PLUS);
structure.put(i, laneAttributes);
}
}
}
}
else if (tag.getKey().equals("highway") && (tag.getValue().equals("path") || tag.getValue().equals("steps")))
{
List<GTUType> types = new ArrayList<GTUType>();
for (OSMTag t2 : osmLink.getTags())
{
if (t2.getKey().equals("bicycle"))
{
types.add(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE);
}
/*
* if (t2.getKey().equals("foot")) {
* types.add(org.opentrafficsim.importexport.osm.PredefinedGTUTypes.pedestrian); }
*/
}
laneType = makeLaneType(types);
types.add(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.PEDESTRIAN);
if (!types.isEmpty())
{
if (osmLink.getLanes() == 1 && !osmLink.isOneway())
{
laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_BOTH);
structure.put(0, laneAttributes);
}
else
{
for (int i = 0 - backwards; i < forwards; i++)
{
if (i < 0)
{
laneAttributes =
new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_MINUS);
structure.put(i, laneAttributes);
}
if (i >= 0)
{
laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_PLUS);
structure.put(i, laneAttributes);
}
}
}
}
types.clear();
}
}
for (OSMTag tag : osmLink.getTags())
{
if (tag.getKey().equals("cycleway"))
{
laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE);
switch (tag.getValue())
{
case "lane": // cycleway:lane is directly adjacent to the highway.
forwards++;
backwards++;
laneAttributes = new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_MINUS);
structure.put(0 - backwards, laneAttributes);
laneAttributes = new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_PLUS);
structure.put(forwards - 1, laneAttributes);
break;
case "track": // cycleway:track is separated by a gap from the highway.
forwards++;
backwards++;
laneAttributes = new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_MINUS);
structure.put(0 - backwards, laneAttributes);
laneAttributes = new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_PLUS);
structure.put(forwards - 1, laneAttributes);
break;
case "shared_lane": // cycleway:shared_lane is embedded into the highway.
List<GTUType> types = new ArrayList<GTUType>();
types.add(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE);
types.add(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.CAR);
laneType = makeLaneType(types);
laneAttributes = new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_MINUS);
structure.put(0 - backwards, laneAttributes);
laneAttributes = new LaneAttributes(laneType, Color.ORANGE, LongitudinalDirectionality.DIR_PLUS);
structure.put(forwards - 1, laneAttributes);
break;
default:
break;
}
}
}
for (OSMTag tag : osmLink.getTags())
{
if (tag.getKey().equals("sidewalk"))
{
laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.PEDESTRIAN);
switch (tag.getValue())
{
case "both":
forwards++;
backwards++;
laneAttributes = new LaneAttributes(laneType, Color.YELLOW, LongitudinalDirectionality.DIR_MINUS);
structure.put(0 - backwards, laneAttributes);
laneAttributes = new LaneAttributes(laneType, Color.YELLOW, LongitudinalDirectionality.DIR_PLUS);
structure.put(forwards - 1, laneAttributes);
break;
case "left":
backwards++;
laneAttributes = new LaneAttributes(laneType, Color.YELLOW, LongitudinalDirectionality.DIR_BOTH);
structure.put(0 - backwards, laneAttributes);
break;
case "right":
forwards++;
laneAttributes = new LaneAttributes(laneType, Color.YELLOW, LongitudinalDirectionality.DIR_BOTH);
structure.put(forwards - 1, laneAttributes);
break;
default:
break;
}
}
}
for (OSMTag tag : osmLink.getTags())
{
if (tag.getKey().equals("highway")
&& (tag.getValue().equals("cycleway") || tag.getValue().equals("footway")
|| tag.getValue().equals("pedestrian") || tag.getValue().equals("steps")))
{
if (tag.getValue().equals("footway") || tag.getValue().equals("pedestrian") || tag.getValue().equals("steps"))
{
laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.PEDESTRIAN);
if (osmLink.getLanes() == 1 && !osmLink.isOneway())
{
laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_BOTH);
structure.put(0, laneAttributes);
}
else
{
for (int i = 0 - backwards; i < forwards; i++)
{
if (i < 0)
{
laneAttributes =
new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_MINUS);
structure.put(i, laneAttributes);
}
if (i >= 0)
{
laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_PLUS);
structure.put(i, laneAttributes);
}
}
}
}
if (tag.getValue().equals("cycleway"))
{
laneType = makeLaneType(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE);
if (osmLink.getLanes() == 1 && !osmLink.isOneway())
{
laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_BOTH);
structure.put(0, laneAttributes);
}
for (int i = 0 - backwards; i < forwards; i++)
{
if (i < 0)
{
laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_MINUS);
structure.put(i, laneAttributes);
}
if (i >= 0)
{
laneAttributes = new LaneAttributes(laneType, Color.GREEN, LongitudinalDirectionality.DIR_PLUS);
structure.put(i, laneAttributes);
}
}
}
}
}
return calculateOffsets(structure, osmLink, forwards, backwards, warningListener);
}
/**
* Calculates the actual offsets of the individual lanes.
* @param structure - Sorted Map of Lane Positions and Attributes
* @param osmLink - The osmLink on which the conversion is based.
* @param forwards - Number of forwards oriented lanes.
* @param backwards - Number of backwards oriented lanes.
* @param warningListener WarningListener; the warning listener that receives warning events
* @return Map containing the lane structure with offsets.
* @throws NetworkException on failure
*/
private static Map<Double, LaneAttributes> calculateOffsets(final SortedMap<Integer, LaneAttributes> structure,
final OSMLink osmLink, final Integer forwards, final Integer backwards, final WarningListener warningListener)
throws NetworkException
{
HashMap<Double, LaneAttributes> structurewithOffset = new HashMap<Double, LaneAttributes>();
LaneAttributes laneAttributes;
double currentOffset = 0.0D;
if (structure.isEmpty())
{
warningListener.warning(new WarningEvent(osmLink, "Empty Structure at Link " + osmLink.getId()));
}
if (structure.lastKey() >= 0)
{
for (int i = 0; i < forwards; i++)
{
laneAttributes = structure.get(i);
if (null == laneAttributes)
{
break;
}
double useWidth = laneWidth(laneAttributes, osmLink, warningListener);
laneAttributes.setWidth(useWidth);
structurewithOffset.put(currentOffset, laneAttributes);
currentOffset += useWidth;
}
}
if (structure.firstKey() < 0)
{
currentOffset = 0.0d;
for (int i = -1; i >= (0 - backwards); i--)
{
laneAttributes = structure.get(i);
if (null == laneAttributes)
{
break;
}
LaneAttributes previousLaneAttributes = null;
for (int k = i + 1; k <= 0; k++)
{
previousLaneAttributes = structure.get(k);
if (null != previousLaneAttributes)
{
break;
}
}
if (null == previousLaneAttributes)
{
throw new NetworkException("reverse lane without main lane?");
}
double useWidth = laneWidth(laneAttributes, osmLink, warningListener);
laneAttributes.setWidth(useWidth);
currentOffset -= previousLaneAttributes.getWidth().getSI();
structurewithOffset.put(currentOffset, laneAttributes);
}
}
return structurewithOffset;
}
/**
* Figure out a reasonable width for a lane.
* @param laneAttributes LaneAttributes; the attributes of the lane
* @param link OSMLink; the link that owns the lane
* @param warningListener WarningListener; the warning listener that receives warning events
* @return double; the width (in meters) of the lane
*/
static double laneWidth(final LaneAttributes laneAttributes, final OSMLink link, final WarningListener warningListener)
{
Double defaultLaneWidth = 3.05d; // TODO This is the German standard car lane width
boolean widthOverride = false;
for (OSMTag tag : link.getTags())
{
if (tag.getKey().equals("width"))
{
String w = tag.getValue().replace(",", ".");
w = w.replace(" ", "");
w = w.replace("m", "");
w = w.replace("Meter", "");
try
{
defaultLaneWidth = Double.parseDouble(w) / link.getLanes();
}
catch (NumberFormatException nfe)
{
System.err.println("Bad lanewidth: \"" + tag.getValue() + "\"");
}
widthOverride = true;
}
}
LaneType laneType = laneAttributes.getLaneType();
if (laneType.isCompatible(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.CAR))
{
return defaultLaneWidth;
}
else if (laneType.isCompatible(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BIKE))
{
return 0.8d; // TODO German default bikepath width
}
else if (laneType.isCompatible(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.PEDESTRIAN))
{
return 0.95d; // TODO German default footpath width
}
else if (laneType.isCompatible(org.opentrafficsim.road.network.factory.osm.PredefinedGTUTypes.BOAT))
{
for (OSMTag tag : link.getTags())
{
if (tag.getKey().equals("waterway"))
{
switch (tag.getValue())
{
case "riverbank":
return 1d;
default:
return defaultLaneWidth;
}
}
else
{
return 5d;
}
}
}
if (!widthOverride)
{
warningListener.warning(new WarningEvent(link, "No width given; using default laneWidth for Link " + link.getId()));
}
return defaultLaneWidth;
}
/**
* This method creates lanes out of an OSM link LaneTypes are not yet extensive and can be further increased through Tags
* provided by OSM. The standard lane width of 3.05 is an estimation based on the European width limitation for vehicles
* (2.55m) + 25cm each side.
* @param osmlink Link OSMLink; the OSM link to make lanes for
* @param simulator OTSDEVSSimulatorInterface; the simulator that will animate the generates lanes (if it happens to be an
* instance of OTSAnimatorInterface)
* @param warningListener WarningListener; the warning listener that will receive warning events
* @return List<Lane>
* @throws NetworkException on network inconsistency
* @throws NamingException on naming problems (in the animator)
* @throws OTSGeometryException when lane contour or center line cannot be instantiated
*/
public List<Lane> makeLanes(final OSMLink osmlink, final OTSDEVSSimulatorInterface simulator,
final WarningListener warningListener) throws NetworkException, NamingException, OTSGeometryException
{
CrossSectionLink otslink = convertLink(osmlink);
List<Lane> lanes = new ArrayList<Lane>();
Map<Double, LaneAttributes> structure = makeStructure(osmlink, warningListener);
int laneNum = 0;
for (Double offset : structure.keySet())
{
laneNum++;
LaneAttributes laneAttributes = structure.get(offset);
if (laneAttributes == null)
{
break;
}
Color color = Color.LIGHT_GRAY;
LaneType laneType = laneAttributes.getLaneType();
Length latPos = new Length(offset, LengthUnit.METER);
Map<GTUType, LongitudinalDirectionality> directionality = new HashMap<>();
directionality.put(GTUType.ALL, laneAttributes.getDirectionality());
Map<GTUType, Speed> speedLimit = new HashMap<>();
speedLimit.put(GTUType.ALL, new Speed(100, SpeedUnit.KM_PER_HOUR));
Lane newLane = null;
// FIXME the following code assumes right-hand-side driving.
if (osmlink.hasTag("hasPreceding") && offset >= 0 || osmlink.hasTag("hasFollowing") && offset < 0)
{
color = Color.RED;
// FIXME overtaking conditions per country and/or type of road?
newLane =
new Lane(otslink, "lane." + laneNum, latPos, latPos, laneAttributes.getWidth(),
laneAttributes.getWidth(), laneType, directionality, speedLimit,
new OvertakingConditions.LeftAndRight());
SinkSensor sensor = new SinkSensor(newLane, new Length(0.25, LengthUnit.METER), simulator);
newLane.addSensor(sensor, GTUType.ALL);
}
else if (osmlink.hasTag("hasPreceding") && offset < 0 || osmlink.hasTag("hasFollowing") && offset >= 0)
{
color = Color.BLUE;
// FIXME overtaking conditions per country and/or type of road?
newLane =
new Lane(otslink, "lane." + laneNum, latPos, latPos, laneAttributes.getWidth(),
laneAttributes.getWidth(), laneType, directionality, speedLimit,
new OvertakingConditions.LeftAndRight());
}
else
{
color = laneAttributes.getColor();
// FIXME overtaking conditions per country and/or type of road?
newLane =
new Lane(otslink, "lane." + laneNum, latPos, latPos, laneAttributes.getWidth(),
laneAttributes.getWidth(), laneType, directionality, speedLimit,
new OvertakingConditions.LeftAndRight());
}
if (simulator instanceof OTSAnimatorInterface)
{
try
{
new LaneAnimation(newLane, simulator, color, false);
}
catch (RemoteException exception)
{
exception.printStackTrace();
}
}
lanes.add(newLane);
}
return lanes;
}
/**
* This method creates a LaneType which supports all GTUTypes that have been specified in the GTUType List "GTUs".
* @param gtuTypes List<GTUType<String>>; list of GTUTypes
* @return LaneType permeable for all of the specific GTUTypes
*/
public static LaneType makeLaneType(final List<GTUType> gtuTypes)
{
StringBuilder name = new StringBuilder();
for (GTUType gtu : gtuTypes)
{
if (name.length() > 0)
{
name.append("|");
}
name.append(gtu.getId());
}
LaneType result = new LaneType(name.toString(), gtuTypes);
return result;
}
/**
* This method creates a LaneType which supports the specified GTUType.
* @param gtuType GTUType; the type of GTU that can travel on the new LaneType
* @return LaneType
*/
public static LaneType makeLaneType(final GTUType gtuType)
{
List<GTUType> gtuTypes = new ArrayList<GTUType>(1);
gtuTypes.add(gtuType);
return makeLaneType(gtuTypes);
// String name = gtuType.getId();
// LaneType result = new LaneType(name);
// result.addPermeability(gtuType);
// return result;
}
/**
* Identify Links that are sources or sinks.
* @param nodes List of Nodes
* @param links List of Links
* @return List of Links which are candidates for becoming sinks/sources.
*/
private static ArrayList<OSMLink> findBoundaryLinks(final List<OSMNode> nodes, final List<OSMLink> links)
{
// TODO: test performance (memory- and time-wise) when the counters are replaced by ArrayList<OSMLink> which
// would obviate the need to do full searches over all Links to find OSMNodes that are source or sink.
// Reset the counters (should not be necessary unless this method is called more than once)
for (OSMNode node : nodes)
{
node.linksOriginating = 0;
node.linksTerminating = 0;
}
for (OSMLink link : links)
{
link.getStart().linksOriginating++;
link.getEnd().linksTerminating++;
}
ArrayList<OSMNode> foundEndNodes = new ArrayList<OSMNode>();
for (OSMNode node : nodes)
{
if (0 == node.linksOriginating && node.linksTerminating > 0 || 0 == node.linksTerminating
&& node.linksOriginating > 0)
{
foundEndNodes.add(node);
}
}
ArrayList<OSMLink> result = new ArrayList<OSMLink>();
for (OSMLink link : links)
{
if (foundEndNodes.contains(link.getStart()) || foundEndNodes.contains(link.getEnd()))
{
result.add(link);
}
}
return result;
}
/**
* @param net The OSM network which is to be searched for Sinks and Sources.
* @param progressListener ProgressListener; the progress listener that will receive progress events
* @return Network with all possible sinks and sources tagged.
*/
public static OSMNetwork findSinksandSources(final OSMNetwork net, final ProgressListener progressListener)
{
progressListener.progress(new ProgressEvent(net, "Counting number of links at each node"));
List<OSMNode> nodes = new ArrayList<OSMNode>();
nodes.addAll(net.getNodes().values());
ArrayList<OSMLink> foundEndpoints = findBoundaryLinks(nodes, net.getLinks());
progressListener.progress(new ProgressEvent(net, "Adding tags to non-sinks and non-sources"));
int progress = 0;
final int progressReportStep = 5000;
// As tags are immutable we make ONE for following and ONE for preceding
final OSMTag hasFollowing = new OSMTag("hasFollowing", "");
final OSMTag hasPreceding = new OSMTag("hasPreceding", "");
for (OSMLink l : net.getLinks())
{
if (foundEndpoints.contains(l))
{
if (net.hasFollowingLink(l))
{
l.addTag(hasFollowing);
}
else if (net.hasPrecedingLink(l))
{
l.addTag(hasPreceding);
}
}
if (0 == ++progress % progressReportStep)
{
progressListener.progress(new ProgressEvent(net, String.format(Locale.US, "%d of %d links processed (%.1f%%)",
progress, net.getLinks().size(), 100.0 * progress / net.getLinks().size())));
}
}
progressListener.progress(new ProgressEvent(net, "Found " + foundEndpoints.size() + " Sinks and Sources."));
return net;
}
/** {@inheritDoc} */
@Override
public final String toString()
{
return "Convert []";
}
}
/**
* <p>
* Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* <p>
* $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
* initial version Mar 3, 2015 <br>
* @author <a>Moritz Bergmann</a>
*/
class LaneAttributes implements Serializable
{
/** */
private static final long serialVersionUID = 20150303L;
/** Type of the lane (immutable). */
private final LaneType laneType;
/** Drawing color of the lane (immutable). */
private final Color color;
/** LongitudinalDirectionality of the lane (immutable). */
private final LongitudinalDirectionality directionality;
/** Width of the lane. */
private Length width;
/**
* @param lt - LaneType
* @param c - Color
* @param d - LongitudinalDIrectionality
*/
public LaneAttributes(final LaneType lt, final Color c, final LongitudinalDirectionality d)
{
if (lt == null)
{
this.laneType = Convert.makeLaneType(GTUType.NONE);
}
else
{
this.laneType = lt;
}
this.color = c;
this.directionality = d;
}
/**
* @param laneType - LaneType
* @param color - Color
* @param directionality - LongitudinalDIrectionality
* @param width - width
*/
public LaneAttributes(final LaneType laneType, final Color color, final LongitudinalDirectionality directionality,
final Double width)
{
if (laneType == null)
{
this.laneType = Convert.makeLaneType(GTUType.NONE);
}
else
{
this.laneType = laneType;
}
this.color = color;
this.directionality = directionality;
this.setWidth(width);
}
/**
* @return LaneType
*/
public LaneType getLaneType()
{
return this.laneType;
}
/**
* @return Color
*/
public Color getColor()
{
return this.color;
}
/**
* @return LongitudinalDirectionality
*/
public LongitudinalDirectionality getDirectionality()
{
return this.directionality;
}
/**
* @return width.
*/
public Length getWidth()
{
return this.width;
}
/**
* @param width set width.
*/
public void setWidth(final Double width)
{
Length w = new Length(width, LengthUnit.METER);
this.width = w;
}
/** {@inheritDoc} */
public String toString()
{
return "Lane Attributes: " + this.laneType + "; " + this.color + "; " + this.directionality + "; " + this.width;
}
}