OSMLink.java
package org.opentrafficsim.road.network.factory.osm;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.opentrafficsim.road.network.factory.osm.events.WarningEvent;
import org.opentrafficsim.road.network.factory.osm.events.WarningListener;
/**
* OpenStreetMap Link.
* <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-07-26 01:01:13 +0200 (Sun, 26 Jul 2015) $, @version $Revision: 1155 $, by $Author: averbraeck $,
* initial version 31 dec. 2014 <br>
* @author <a>Moritz Bergmann</a>
*/
public class OSMLink implements Serializable
{
/** */
private static final long serialVersionUID = 20141231L;
/** The Link ID. It is generated out of the Start ID and the End ID and (if present) the name tag. */
private final String id;
/** The start Node of the OSMLink. */
private final OSMNode start;
/** The end Node of the OSMLink. */
private final OSMNode end;
/** The List of nodes that are used only to define shape (for mapping purposes). */
private List<OSMNode> intermediateNodes;
/** The length of the OSMLink. */
private final double length;
/** The tags that this OSMLink inherits from it's way. */
private List<OSMTag> tags;
/** The number of lanes on this OSMLink. */
private byte lanes;
/** The number of lanes going forward (the <i>design</i> direction). */
private byte forwardLanes;
/** Is this OSMLink one way? */
private boolean oneway;
/**
* Construct a new OSMLink.
* @param fromNode OSMNode; the OSMNode where this OSMLinks begins
* @param toNode OSMNode; the OSMNode where this OSMLink ends
* @param tags List<OSMTab>; the OSMTags (inherited from the OSMWay that causes this OSMLink to be constructed)
* @param length double; the length of the new OSMLink
* @param warningListener WarningListener; the warning listener that will receive warning events
*/
public OSMLink(final OSMNode fromNode, final OSMNode toNode, final List<OSMTag> tags, final double length,
final WarningListener warningListener)
{
if (fromNode == toNode)
{
throw new Error("Start and end of link are the same Node: " + fromNode);
}
String name = "";
for (OSMTag tag : tags)
{
if (tag.getKey().equals("name"))
{
name = ": " + tag.getValue();
}
}
this.id = Objects.toString(fromNode.getId()) + Objects.toString(toNode.getId()) + name;
this.start = fromNode;
this.end = toNode;
this.length = length;
this.lanes = 1;
this.forwardLanes = 1;
boolean forwardDefined = false;
List<OSMTag> linkTags = new ArrayList<OSMTag>(tags);
for (OSMTag tag : tags)
{
if (tag.getKey().equals("oneway") && tag.getValue().equals("yes"))
{
this.setOneway(true);
linkTags.remove(tag);
this.forwardLanes = this.lanes;
}
if (tag.getKey().equals("highway") && tag.getValue().equals("motorway_link"))
{
this.setOneway(true);
this.lanes = 1;
this.forwardLanes = this.lanes;
}
if (tag.getKey().equals("highway") && tag.getValue().equals("motorway"))
{
this.setOneway(true);
this.forwardLanes = this.lanes;
}
if (tag.getKey().equals("highway")
&& (tag.getValue().equals("cycleway") || tag.getValue().equals("footway")
|| tag.getValue().equals("pedestrian") || tag.getValue().equals("steps")))
{
this.lanes = 1;
}
}
for (OSMTag tag2 : new ArrayList<OSMTag>(linkTags))
{
if (tag2.getKey().equals("lanes"))
{
if (isByte(tag2.getValue()))
{
this.lanes = Byte.parseByte(tag2.getValue());
linkTags.remove(tag2);
if (this.oneway)
{
this.forwardLanes = this.lanes;
forwardDefined = true;
}
}
else
{
String warning = "Illegal value for the tag 'lanes' at link " + this.id;
warningListener.warning(new WarningEvent(this, warning));
}
}
if (tag2.getKey().equals("lanes:forward"))
{
if (isByte(tag2.getValue()))
{
this.forwardLanes = Byte.parseByte(tag2.getValue());
linkTags.remove(tag2);
forwardDefined = true;
}
else
{
String warning = "Illegal value for the tag 'lanes:forward' at link " + this.id;
warningListener.warning(new WarningEvent(this, warning));
}
}
}
this.tags = linkTags;
if (!forwardDefined && this.lanes > 1)
{
this.forwardLanes = (byte) (this.lanes / 2);
String warning = "No forward lanes defined at link " + this.id;
warningListener.warning(new WarningEvent(this, warning));
}
this.intermediateNodes = new ArrayList<OSMNode>();
}
/**
* Construct a new OSMLink with specified number of lanes and forward lanes.
* @param startNode OSMNode; the start OSMNode of the new OSMLink
* @param endNode OSMNode; the end OSMNode of the new OSMLink
* @param tags List of Tags inherited from way
* @param length length of link
* @param lanes byte; the total number of lanes
* @param flanes byte; the number of forward lanes
*/
public OSMLink(final OSMNode startNode, final OSMNode endNode, final List<OSMTag> tags, final double length,
final byte lanes, final byte flanes)
{
if (startNode == endNode)
{
throw new Error("start and end of link are the same Node: " + startNode);
}
this.id = Objects.toString(startNode.getId()) + Objects.toString(endNode.getId());
this.start = startNode;
this.end = endNode;
this.tags = tags;
this.length = length;
this.lanes = lanes;
this.forwardLanes = flanes;
this.intermediateNodes = new ArrayList<OSMNode>();
}
/**
* @return id
*/
public final String getId()
{
return this.id;
}
/**
* @return start.
*/
public final OSMNode getStart()
{
return this.start;
}
/**
* @return end.
*/
public final OSMNode getEnd()
{
return this.end;
}
/**
* Retrieve the tags of this OSMLink.
* @return List<OSMTab>; the returned object is a copy; modifications of the returned object do not affect this
* OSMLink
*/
public final List<OSMTag> getTags()
{
return new ArrayList<OSMTag>(this.tags); // Create and return a copy (the tags themselves are immutable).
}
/**
* Indicate if this OSMLink is one way.
* @return boolean; true if this OSMLink is one way; false if this OSMLink is not one way
*/
public final boolean isOneway()
{
return this.oneway;
}
/**
* Set the one way status of this OSMLink.
* @param isOneWay the new value for the one way status of this OSMLink
*/
public final void setOneway(final boolean isOneWay)
{
this.oneway = isOneWay;
}
/**
* Retrieve the total number of lanes on this OSMLink.
* @return byte; the total number of lanes on this OSMLink
*/
public final byte getLanes()
{
return this.lanes;
}
/**
* Retrieve the total number of forward lanes on this OSMLink; forward lanes are lanes that may only be traveled from
* startNode towards endNode.
* @return byte; the number of forward lanes on this OSMLink
*/
public final byte getForwardLanes()
{
return this.forwardLanes;
}
/**
* Add an OSMTag to this Link.
* @param tag Tag; the OSMTag that must be added
*/
public final void addTag(final OSMTag tag)
{
this.tags.add(tag);
}
/**
* Retrieve the length of this OSMLink.
* @return double; the length of this OSMLink in meters
*/
public final double getLength()
{
return this.length;
}
/**
* Retrieve the list of OSMNodes that define the shape of this OSMLink.
* @return List<OSMNode>; the list of OSMNodes that define the shape of this OSMLink
*/
public final List<OSMNode> getSplineList()
{
return this.intermediateNodes;
}
/**
* Append a Node to the list of OSMNodes of this OSMLink that define the shape of this OSMLink.
* @param shapeNode OSMNode; the OSMNode to add to the list of OSMNodes that define the shape of this OSMLink
*/
public final void addSpline(final OSMNode shapeNode)
{
this.intermediateNodes.add(shapeNode);
}
/**
* Returns true if the link has an OSMTag with the specified key.
* @param key String; the key of the sought OSMTag
* @return boolean; true if this OSMLink has (one or more) OSMTag(s) with the specified key
*/
public final boolean hasTag(final String key)
{
for (OSMTag t : this.tags)
{
if (t.getKey().equals(key))
{
return true;
}
}
return false;
}
/**
* Determine if a string represents a number that can stored in a byte.
* @param s String; the string
* @return is the given String a byte.
*/
private boolean isByte(final String s)
{
try
{
Byte.parseByte(s);
}
catch (NumberFormatException e)
{
return false;
}
return true;
}
/** {@inheritDoc} */
public final String toString()
{
return String.format("Link %s from %d to %d", getId(), getStart().getId(), getEnd().getId());
}
/**
* Report if this OSMLink has all tags in a supplied set.
* @param tagsToCheck List<OSMTag>; the supplied set of tags
* @return boolean; true if this Link has all the supplied tags; false otherwise
*/
public final boolean containsAllTags(final List<OSMTag> tagsToCheck)
{
return this.tags.containsAll(tagsToCheck);
}
}