Network.java
package org.opentrafficsim.importexport.osm;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* <p>
* Copyright (c) 2013-2014 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/node/13">OpenTrafficSim License</a>.
* <p>
* @version 31 dec. 2014 <br>
* @author <a>Moritz Bergmann</a>
*/
public class Network
{
/** The Nodes of the Network. */
private HashMap<Long, Node> nodes;
/** The Ways of the Network. */
private HashMap<Long, Way> ways;
/** The Relations of the Network. */
private HashMap<Long, Relation> relations;
/** The name of the Network. */
private String name;
/** The Links of the Network. */
private List<Link> links;
/**
* Construct a new Network.
* @param name String; the name of the new Network
*/
public Network(final String name)
{
this.setName(name);
this.nodes = new HashMap<Long, Node>();
this.ways = new HashMap<Long, Way>();
this.relations = new HashMap<Long, Relation>();
this.links = new ArrayList<Link>();
}
/**
* Construct a new Network out of another Network.
* @param net
*/
public Network(final Network net)
{
this.setName(net.getName());
this.nodes = new HashMap<Long, Node>(net.getNodes());
this.ways = new HashMap<Long, Way>(net.getWays());
this.relations = new HashMap<Long, Relation>(net.getRelations());
this.links = new ArrayList<Link>(net.getLinks());
}
/**
* Retrieve a list of Nodes that form a Way from this Network.
* @param wayid long; the id of the Way
* @return List<Long*gt;; the list of NodeIds of way with the specified id
* @throws IOException when no Way with the specified id exists in this Network
*/
public final List<Long> getNodesFromWay(final Long wayid) throws IOException
{
if (this.ways.get(wayid) == null)
{
throw new IOException("Way with the ID: " + wayid + "was not found");
}
else
{
return this.ways.get(wayid).getNodes();
}
}
/**
* Retrieve a Node from this Network.
* @param nodeid long; the id of the Node
* @return node Node; the node with the specified id
* @throws IOException when no Node with the specified id exist in this Network
*/
public final Node getNode(final long nodeid) throws IOException
{
if (this.nodes.get(nodeid) == null)
{
throw new IOException("Node with the ID: " + nodeid + "was not found");
}
else
{
return this.nodes.get(nodeid);
}
}
/**
* @return HashMap of all Nodes in this Network
*/
public final HashMap<Long, Node> getNodes()
{
return this.nodes;
}
/**
* Retrieve a Relation from this Network.
* @param relid long; the id of the Relation
* @return Relation
* @throws IOException when no Relation with the specified id exists in this Network
*/
public final Relation getRelation(final long relid) throws IOException
{
if (this.relations.get(relid) == null)
{
throw new IOException("Relation with the ID: " + relid + "was not found");
}
else
{
return this.relations.get(relid);
}
}
/**
* @return HashMap of all relations in the network
*/
public final HashMap<Long, Relation> getRelations()
{
return this.relations;
}
/**
* Retrieve a Way from this Network.
* @param wayid long; the id of a Way
* @return Way
* @throws IOException when no Way with the specified id exist in this network
*/
public final Way getWay(final long wayid) throws IOException
{
if (this.ways.get(wayid) == null)
{
throw new IOException("Way with the ID: " + wayid + "was not found");
}
else
{
return this.ways.get(wayid);
}
}
/**
* @return name
*/
public final String getName()
{
return this.name;
}
/**
* Set/update the name of this Network.
* @param name String; the new name
*/
public final void setName(final String name)
{
this.name = name;
}
/**
* Set/replace the Nodes of this Network.<br>
* The provided list is <b>not copied</b>; the caller should not modify the list after setting it.
* @param newnodes HashMap<Long, Node>; the (new) Nodes for this Network
*/
public final void setNodes(final HashMap<Long, Node> newnodes)
{
this.nodes = newnodes;
}
/**
* Add a Node to this Network.
* @param node Node; the node to add to this Network
*/
public final void addNode(final Node node)
{
this.nodes.put(node.getID(), node);
}
/**
* Delete a Node from this Network.
* @param node Node; the Node to delete
*/
public final void delNode(final Node node)
{
this.nodes.remove(node.getID());
}
/**
* Delete a Node from this Network.
* @param nodeid Long; the id of the Node to delete
*/
public final void delNode(final Long nodeid)
{
this.nodes.remove(nodeid);
}
/**
* Add a Way to this Network.
* @param way Way; the Way to add
*/
public final void addWay(final Way way)
{
this.ways.put(way.getID(), way);
}
/**
* Add a Relation to this Network.
* @param rel Relation; the relation to add
*/
public final void addRelation(final Relation rel)
{
this.relations.put(rel.getID(), rel);
}
/**
* @return ways
*/
public final HashMap<Long, Way> getWays()
{
return this.ways;
}
/**
* @return links
*/
public final List<Link> getLinks()
{
return this.links;
}
/**
* Creates links out of the ways in this network.
* @throws IOException on read errors
*/
public final void makeLinks() throws IOException
{
List<Link> links2 = new ArrayList<Link>();
double length;
Node n1;
Node n2;
double x1;
double x2;
double y1;
double y2;
for (Long wayid : this.ways.keySet())
{
List<Long> waynodes = this.getWay(wayid).getNodes();
for (int i = 0; i < (waynodes.size() - 1); i++)
{
n1 = this.getNode(waynodes.get(i).longValue());
n2 = this.getNode(waynodes.get(i + 1).longValue());
// Workaround for bug in OSM near Mettmann Germany
if (n1 == n2)
{
continue;
}
x1 = n1.getLatitude();
x2 = n2.getLatitude();
y1 = n1.getLongitude();
y2 = n2.getLongitude();
length = distanceLongLat(x1, y1, x2, y2);
Link e = new Link(n1, n2, this.getWay(wayid).getTags(), length);
links2.add(e);
}
}
this.links = links2;
}
/**
* @param xx1 Latitude of point 1
* @param yy1 Longitude of point 1
* @param xx2 Latitude of point 2
* @param yy2 Longitude of point 2
* @return distance in meter between the (x1,y1) and (x2,y2) This method utilizes great circle calculation
*/
private static double distanceLongLat(final double xx1, final double yy1, final double xx2, final double yy2)
{
double y1 = yy1;
double y2 = yy2;
if (y1 < 0)
{
y1 = 360 + yy1;
}
if (y2 < 0)
{
y2 = 360 + yy2;
}
double x1 = 90 - xx1;
double x2 = 90 - xx2;
x1 = x1 * (2 * Math.PI / 360);
x2 = x2 * (2 * Math.PI / 360);
y1 = y1 * (2 * Math.PI / 360);
y2 = y2 * (2 * Math.PI / 360);
double distance;
distance =
Math.acos(Math.sin(x1) * Math.sin(x2) * Math.cos(y1 - y2) + Math.cos(x1) * Math.cos(x2)) * 6371 * 1000;
return distance;
}
/**
* This function checks for and removes redundancies between the networks links.
*/
public final void removeRedundancy()
{
boolean again = false;
do
{
again = this.redundancyCheck();
}
while (again);
}
/**
* This function checks the networks links for redundancy.
* @return boolean; true if the Network was modified (further reduction may be possible by calling this method
* again)
*/
private boolean redundancyCheck()
{
List<Link> checkedLinks = new ArrayList<Link>();
List<Link> removedLinks = new ArrayList<Link>();
boolean redundancy = false;
for (Link l1 : this.links)
{
if (removedLinks.contains(l1))
{
continue;
}
String name1 = "1";
for (Tag t1 : l1.getTags())
{
if (t1.getKey().equals("name"))
{
name1 = t1.getValue();
}
}
for (Link l2 : this.links)
{
if (removedLinks.contains(l2))
{
continue;
}
String name2 = "2";
for (Tag t2 : l2.getTags())
{
if (t2.getKey().equals("name"))
{
name2 = t2.getValue();
}
}
if (l1.getEnd().equals(l2.getStart()) && name1.equalsIgnoreCase(name2)
&& l1.getEnd().getTags().isEmpty() && l1.getTags().containsAll(l2.getTags())
&& l1.getLanes() == l2.getLanes() && l1.getForwardLanes() == l2.getForwardLanes()
&& l1.getStart() != l2.getEnd())
{
if (removedLinks.contains(l1))
{
for (int i = 0; i < this.links.size(); i++)
{
Link l = this.links.get(i);
if (l == l1)
{
System.out.println("found link " + l + " at position " + i);
}
}
throw new Error("about to add " + l1 + " to removeLinks which already contains that link");
}
redundancy = true;
Link lnew =
new Link(l1.getStart(), l2.getEnd(), l1.getTags(), l1.getLength() + l2.getLength(),
l1.getLanes(), l1.getForwardLanes());
if (!l1.getSplineList().isEmpty())
{
for (Node n1 : l1.getSplineList())
{
lnew.addSpline(n1);
}
}
if (!l2.getSplineList().isEmpty())
{
for (Node n2 : l2.getSplineList())
{
lnew.addSpline(n2);
}
}
// System.out.println("removing " + l1 + " and " + l2);
// System.out.println("adding " + lnew);
checkedLinks.add(lnew);
removedLinks.add(l1);
removedLinks.add(l2);
break; // don't merge any other links with l1; do that on the next iteration.
}
}
}
// System.out.println("Combining " + removedLinks.size() + " to " + checkedLinks.size());
// if (removedLinks.size() < 40)
// {
// for (Link l : removedLinks)
// System.out.println("Removing link " + l);
// }
this.links.removeAll(removedLinks);
this.links.addAll(checkedLinks);
//System.out.println("there are now " + this.links.size() + " links");
return redundancy;
}
/**
* Finds the link which follows the given link. If it exists.
* @param l
* @return Link
*/
public final Link findFollowingLink(final Link l)
{
if (!this.links.contains(l))
{
throw new Error("This link does not exist in this network: " + l);
}
for (Link l2: this.links)
{
if (l.getEnd().equals(l2.getStart()))
{
return l2;
}
}
return null;
}
/**
* Finds the link which precedes the given link. If it exists.
* @param l
* @return Link
*/
public final Link findPrecedingLink(final Link l)
{
if (!this.links.contains(l))
{
throw new Error("This link does not exist in this network: " + l);
}
for (Link l2: this.links)
{
if (l.getStart().equals(l2.getEnd()))
{
return l2;
}
}
return null;
}
/**
* Returns true if the given link has a preceding link.
* @param l
* @return boolean
*/
public final boolean hasPrecedingLink(final Link l)
{
if (!this.links.contains(l))
{
throw new Error("This link does not exist in this network: " + l);
}
for (Link l2: this.links)
{
if (l.getStart().equals(l2.getEnd()))
{
return true;
}
}
return false;
}
/**
* Returns true if the given link has a following link.
* @param l
* @return boolean
*/
public final boolean hasFollowingLink(final Link l)
{
if (!this.links.contains(l))
{
throw new Error("This link does not exist in this network: " + l);
}
for (Link l2: this.links)
{
if (l.getEnd().equals(l2.getStart()))
{
return true;
}
}
return false;
}
}