Unverified Commit 342fea6f authored by Juon Kawakami's avatar Juon Kawakami 🥗
Browse files

init

parent 54f6cedf
package maps.convert.osm2gml;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import rescuecore2.misc.gui.ShapeDebugFrame;
import rescuecore2.misc.geometry.Line2D;
/**
A ShapeInfo that knows how to draw DirectedEdges.
*/
public class DirectedEdgeShapeInfo extends ShapeDebugFrame.Line2DShapeInfo {
private Collection<DirectedEdge> edges;
/**
Create a new DirectedEdgeShapeInfo.
@param edge The edge to draw.
@param name The name of the edge.
@param colour The colour to draw the edge.
@param thick Whether to draw the edge thick or not.
*/
public DirectedEdgeShapeInfo(DirectedEdge edge, String name, Color colour, boolean thick) {
this(Collections.singleton(edge), name, colour, thick);
}
/**
Create a new DirectedEdgeShapeInfo.
@param edges The edges to draw.
@param name The name of the edge.
@param colour The colour to draw the edge.
@param thick Whether to draw the edge thick or not.
*/
public DirectedEdgeShapeInfo(Collection<DirectedEdge> edges, String name, Color colour, boolean thick) {
super(makeLines(edges), name, colour, thick, true);
this.edges = edges;
}
@Override
public Object getObject() {
return edges;
}
private static Collection<Line2D> makeLines(Collection<DirectedEdge> edges) {
if (edges == null) {
return null;
}
Collection<Line2D> result = new ArrayList<Line2D>();
for (DirectedEdge next : edges) {
result.add(next.getLine());
}
return result;
}
}
package maps.convert.osm2gml;
import rescuecore2.misc.geometry.Line2D;
/**
An edge. An edge is a line between two nodes.
*/
public class Edge extends ManagedObject {
private Node start;
private Node end;
private Line2D line;
/**
Construct a new Edge.
@param id The ID of this object.
@param start The start node.
@param end The end node.
*/
public Edge(long id, Node start, Node end) {
super(id);
this.start = start;
this.end = end;
line = new Line2D(start.getCoordinates(), end.getCoordinates());
}
/**
Get the start node.
@return The start node.
*/
public Node getStart() {
return start;
}
/**
Get the end node.
@return The end node.
*/
public Node getEnd() {
return end;
}
/**
Get the line represented by this edge.
@return The line.
*/
public Line2D getLine() {
return line;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("Edge ");
result.append(getID());
result.append(" from ");
result.append(start);
result.append(" to ");
result.append(end);
return result.toString();
}
}
package maps.convert.osm2gml;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import rescuecore2.misc.gui.ShapeDebugFrame;
import rescuecore2.misc.geometry.Line2D;
/**
A ShapeInfo that knows how to draw Edges.
*/
public class EdgeShapeInfo extends ShapeDebugFrame.Line2DShapeInfo {
private Collection<Edge> edges;
/**
Create a new EdgeShapeInfo.
@param edge The edge to draw.
@param name The name of the edge.
@param colour The colour to draw the edge.
@param thick Whether to draw the edge thick or not.
@param arrow Whether to draw the edge's direction or not.
*/
public EdgeShapeInfo(Edge edge, String name, Color colour, boolean thick, boolean arrow) {
this(Collections.singleton(edge), name, colour, thick, arrow);
}
/**
Create a new EdgeShapeInfo.
@param edges The edges to draw.
@param name The name of the edge.
@param colour The colour to draw the edge.
@param thick Whether to draw the edge thick or not.
@param arrow Whether to draw the edge's direction or not.
*/
public EdgeShapeInfo(Collection<Edge> edges, String name, Color colour, boolean thick, boolean arrow) {
super(makeLines(edges), name, colour, thick, arrow);
this.edges = edges;
}
@Override
public Object getObject() {
return edges;
}
private static Collection<Line2D> makeLines(Collection<Edge> edges) {
if (edges == null) {
return null;
}
Collection<Line2D> result = new ArrayList<Line2D>();
for (Edge next : edges) {
result.add(next.getLine());
}
return result;
}
}
package maps.convert.osm2gml;
import maps.gml.GMLMap;
import maps.gml.GMLNode;
import maps.gml.GMLEdge;
import maps.gml.GMLDirectedEdge;
import maps.gml.GMLShape;
import maps.convert.ConvertStep;
import maps.ScaleConversion;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
/**
This step creates the final GML objects.
*/
public class MakeObjectsStep extends ConvertStep {
private TemporaryMap map;
private GMLMap gmlMap;
/**
Construct a MakeObjectsStep.
@param map The TemporaryMap to read.
@param gmlMap The GMLMap to populate.
*/
public MakeObjectsStep(TemporaryMap map, GMLMap gmlMap) {
super();
this.map = map;
this.gmlMap = gmlMap;
}
@Override
public String getDescription() {
return "Generating GML objects";
}
@Override
protected void step() {
double xMin = Double.POSITIVE_INFINITY;
double yMin = Double.POSITIVE_INFINITY;
for (Node next : map.getAllNodes()) {
xMin = Math.min(xMin, next.getX());
yMin = Math.min(yMin, next.getY());
}
double sizeOf1m = ConvertTools.sizeOf1Metre(map.getOSMMap());
double scale = 1.0 / sizeOf1m;
ScaleConversion conversion = new ScaleConversion(xMin, yMin, scale, scale);
Collection<Node> nodes = map.getAllNodes();
Collection<Edge> edges = map.getAllEdges();
setProgressLimit(nodes.size() + edges.size() + (map.getAllObjects().size() * 2));
Map<Node, GMLNode> nodeMap = new HashMap<Node, GMLNode>();
Map<Edge, GMLEdge> edgeMap = new HashMap<Edge, GMLEdge>();
Map<TemporaryObject, GMLShape> shapeMap = new HashMap<TemporaryObject, GMLShape>();
for (Node n : nodes) {
GMLNode node = gmlMap.createNode(conversion.convertX(n.getX()), conversion.convertY(n.getY()));
nodeMap.put(n, node);
bumpProgress();
}
for (Edge e : edges) {
GMLNode first = nodeMap.get(e.getStart());
GMLNode second = nodeMap.get(e.getEnd());
GMLEdge edge = gmlMap.createEdge(first, second);
edgeMap.put(e, edge);
bumpProgress();
}
for (TemporaryBuilding b : map.getBuildings()) {
shapeMap.put(b, gmlMap.createBuilding(makeEdges(b, edgeMap)));
bumpProgress();
}
for (TemporaryRoad r : map.getRoads()) {
shapeMap.put(r, gmlMap.createRoad(makeEdges(r, edgeMap)));
bumpProgress();
}
for (TemporaryIntersection i : map.getIntersections()) {
shapeMap.put(i, gmlMap.createRoad(makeEdges(i, edgeMap)));
bumpProgress();
}
// Generate neighbour information
for (TemporaryObject o : map.getAllObjects()) {
GMLShape s = shapeMap.get(o);
for (DirectedEdge e : o.getEdges()) {
TemporaryObject neighbour = o.getNeighbour(e);
if (neighbour != null) {
s.setNeighbour(edgeMap.get(e.getEdge()), shapeMap.get(neighbour).getID());
}
}
bumpProgress();
}
setStatus("Created " + gmlMap.getRoads().size() + " roads and " + gmlMap.getBuildings().size() + " buildings");
}
private List<GMLDirectedEdge> makeEdges(TemporaryObject o, Map<Edge, GMLEdge> edgeMap) {
List<DirectedEdge> oldEdges = o.getEdges();
List<GMLDirectedEdge> result = new ArrayList<GMLDirectedEdge>(oldEdges.size());
for (DirectedEdge dEdge : oldEdges) {
result.add(new GMLDirectedEdge(edgeMap.get(dEdge.getEdge()), dEdge.isForward()));
}
return result;
}
}
package maps.convert.osm2gml;
import maps.convert.ConvertStep;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import rescuecore2.misc.geometry.Point2D;
/**
This step creates TemporaryObjects from the OSM data.
*/
public class MakeTempObjectsStep extends ConvertStep {
private TemporaryMap map;
/**
Construct a MakeTempObjectsStep.
@param map The TemporaryMap to populate.
*/
public MakeTempObjectsStep(TemporaryMap map) {
super();
this.map = map;
}
@Override
public String getDescription() {
return "Generating temporary objects";
}
@Override
protected void step() {
Collection<OSMRoadInfo> roads = map.getOSMRoadInfo();
Collection<OSMIntersectionInfo> intersections = map.getOSMIntersectionInfo();
Collection<OSMBuildingInfo> buildings = map.getOSMBuildingInfo();
setProgressLimit(roads.size() + intersections.size() + buildings.size());
generateRoadObjects(roads);
generateIntersectionObjects(intersections);
generateBuildingObjects(buildings);
setStatus("Created " + map.getRoads().size() + " roads, " + map.getIntersections().size() + " intersections, " + map.getBuildings().size() + " buildings");
}
private void generateRoadObjects(Collection<OSMRoadInfo> roads) {
for (OSMRoadInfo road : roads) {
if (road.getArea() != null) {
List<DirectedEdge> edges = generateEdges(road);
if (edges.size() > 2) {
map.addRoad(new TemporaryRoad(edges));
}
}
bumpProgress();
}
}
private void generateIntersectionObjects(Collection<OSMIntersectionInfo> intersections) {
for (OSMIntersectionInfo intersection : intersections) {
if (intersection.getArea() != null) {
List<DirectedEdge> edges = generateEdges(intersection);
if (edges.size() > 2) {
map.addIntersection(new TemporaryIntersection(edges));
}
}
bumpProgress();
}
}
private void generateBuildingObjects(Collection<OSMBuildingInfo> buildings) {
for (OSMBuildingInfo building : buildings) {
if (building.getArea() != null) {
List<DirectedEdge> edges = generateEdges(building);
if (edges.size() > 2) {
map.addBuilding(new TemporaryBuilding(edges, building.getBuildingID()));
}
}
bumpProgress();
}
}
private List<DirectedEdge> generateEdges(OSMShape s) {
List<DirectedEdge> result = new ArrayList<DirectedEdge>();
Iterator<Point2D> it = s.getVertices().iterator();
Node first = map.getNode(it.next());
Node previous = first;
while (it.hasNext()) {
Node n = map.getNode(it.next());
if (!n.equals(previous)) {
result.add(map.getDirectedEdge(previous, n));
previous = n;
}
}
if (!previous.equals(first)) {
result.add(map.getDirectedEdge(previous, first));
}
return result;
}
}
package maps.convert.osm2gml;
/**
A managed map object.
*/
public abstract class ManagedObject {
private long id;
/**
Construct a managed object.
@param id The id of the object.
*/
protected ManagedObject(long id) {
this.id = id;
}
/**
Get this object's ID.
@return The object ID.
*/
public long getID() {
return id;
}
@Override
public int hashCode() {
return (int)id;
}
@Override
public boolean equals(Object o) {
if (o instanceof ManagedObject) {
return this.id == ((ManagedObject)o).id;
}
return false;
}
}
package maps.convert.osm2gml;
import java.util.Map;
import java.util.List;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import java.util.Collections;
import java.awt.Color;
import maps.convert.ConvertStep;
/**
This class merges adjacent shapes of the same type.
*/
public class MergeShapesStep extends ConvertStep {
private TemporaryMap map;
/**
Construct a MergeShapesStep.
@param map The TemporaryMap to use.
*/
public MergeShapesStep(TemporaryMap map) {
this.map = map;
}
@Override
public String getDescription() {
return "Merging adjacent shapes";
}
@Override
protected void step() {
debug.setBackground(ConvertTools.getAllDebugShapes(map));
Collection<TemporaryBuilding> buildings = map.getBuildings();
Collection<TemporaryRoad> roads = map.getRoads();
setProgressLimit(buildings.size() + roads.size());
// Merge any buildings with the same ID that got split earlier.
int buildingCount = 0;
int roadCount = 0;
for (TemporaryBuilding next : buildings) {
if (tryToMerge(next)) {
++buildingCount;
}
bumpProgress();
}
// Try merging adjacent roads
for (TemporaryRoad next : roads) {
if (tryToMerge(next)) {
++roadCount;
}
bumpProgress();
}
setStatus("Merged " + buildingCount + " building shapes and " + roadCount + " road shapes");
}
private boolean tryToMerge(TemporaryBuilding b) {
if (!map.getBuildings().contains(b)) {
return false;
}
Collection<TemporaryBuilding> others = map.getBuildings();
for (TemporaryBuilding other : others) {
if (b == other) {
continue;
}
if (other.getBuildingID() == b.getBuildingID()) {
List<DirectedEdge> boundary = mergeShapes(b, other);
if (boundary == null) {
continue;
}
TemporaryBuilding newBuilding = new TemporaryBuilding(boundary, b.getBuildingID());
map.addBuilding(newBuilding);
map.removeBuilding(b);
map.removeBuilding(other);
debug.show("Merged buildings", new TemporaryObjectInfo(b, "First", Color.BLACK, Color.GREEN),
new TemporaryObjectInfo(other, "Second", Color.BLACK, Color.WHITE),
new TemporaryObjectInfo(newBuilding, "New building", Color.BLUE, null));
return true;
}
}
return false;
}
private boolean tryToMerge(TemporaryRoad r) {
if (!map.getRoads().contains(r)) {
return false;
}
Collection<TemporaryRoad> others = map.getRoads();
for (TemporaryRoad other : others) {
if (r == other) {
continue;
}
List<DirectedEdge> boundary = mergeShapes(r, other);
if (boundary == null) {
continue;
}
// Check for convexity
if (ConvertTools.isConvex(boundary)) {
TemporaryRoad newRoad = new TemporaryRoad(boundary);
map.addRoad(newRoad);
map.removeRoad(r);
map.removeRoad(other);
debug.show("Merged roads", new TemporaryObjectInfo(r, "First", Color.BLACK, Color.GREEN),
new TemporaryObjectInfo(other, "Second", Color.BLACK, Color.WHITE),
new TemporaryObjectInfo(newRoad, "New road", Color.BLUE, null));
return true;
}
}
return false;
}
private List<DirectedEdge> mergeShapes(TemporaryObject first, TemporaryObject second) {
Map<Edge, DirectedEdge> edges1 = new HashMap<Edge, DirectedEdge>();
Map<Edge, DirectedEdge> edges2 = new HashMap<Edge, DirectedEdge>();
for (DirectedEdge e : first.getEdges()) {
edges1.put(e.getEdge(), e);
}
for (DirectedEdge e : second.getEdges()) {
edges2.put(e.getEdge(), e);
}
if (Collections.disjoint(edges1.keySet(), edges2.keySet())) {
return null;
}
Set<DirectedEdge> boundary = new HashSet<DirectedEdge>();
for (Map.Entry<Edge, DirectedEdge> next : edges1.entrySet()) {
if (!edges2.containsKey(next.getKey())) {
boundary.add(next.getValue());
}
}
for (Map.Entry<Edge, DirectedEdge> next : edges2.entrySet()) {
if (!edges1.containsKey(next.getKey())) {
boundary.add(next.getValue());
}
}
// Walk the boundary
DirectedEdge start = boundary.iterator().next();
List<DirectedEdge> result = new ArrayList<DirectedEdge>();
result.add(start);
while (!boundary.isEmpty()) {
start = findNextEdge(start, boundary);
boundary.remove(start);
result.add(start);
}
return result;
}
private DirectedEdge findNextEdge(DirectedEdge from, Set<DirectedEdge> candidates) {
Node n = from.getEndNode();
for (DirectedEdge next : candidates) {
if (next.getStartNode().equals(n)) {
return next;
}
}
throw new IllegalArgumentException("No candidate edge starting from " + n);
}
}
package maps.convert.osm2gml;
import rescuecore2.misc.geometry.Point2D;
/**
A node object.
*/
public class Node extends ManagedObject {
private Point2D coordinates;
/**
Construct a new node.
@param id The ID of this node.
@param x The x coordinate of this node.
@param y The y coordinate of this node.
*/
public Node(long id, double x, double y) {
this(id, new Point2D(x, y));
}
/**
Construct a new node.
@param id The ID of this node.
@param coordinates The coordinates of this node.
*/
public Node(long id, Point2D coordinates) {
super(id);
this.coordinates = coordinates;
}
/**
Get the coordinates of this node.
@return The node coordinates.
*/
public Point2D getCoordinates() {
return coordinates;
}
/**
Get the X coordinate.
@return The X coordinate.
*/
public double getX() {
return coordinates.getX();
}
/**
Get the Y coordinate.
@return The Y coordinate.
*/
public double getY() {
return coordinates.getY();
}
@Override
public String toString() {
return "Node " + getID() + " at " + coordinates;
}
}
package maps.convert.osm2gml;
import maps.osm.OSMBuilding;
import maps.osm.OSMMap;
import maps.osm.OSMNode;
import rescuecore2.misc.geometry.Point2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
/**
Information about an OSM building.
*/
public class OSMBuildingInfo implements OSMShape {
private List<Point2D> vertices;
private Area area;
private long buildingID;
/**
Construct a new OSMBuildingInfo.
@param building The building.
@param map The map.
*/
public OSMBuildingInfo(OSMBuilding building, OSMMap map) {
buildingID = building.getID();
vertices = new ArrayList<Point2D>();
for (Long next : building.getNodeIDs()) {
OSMNode node = map.getNode(next);
vertices.add(new Point2D(node.getLongitude(), node.getLatitude()));
}
// Compute the area
Path2D.Double path = new Path2D.Double();
Iterator<Point2D> it = vertices.iterator();
Point2D point = it.next();
path.moveTo(point.getX(), point.getY());
while (it.hasNext()) {
point = it.next();
path.lineTo(point.getX(), point.getY());
}
path.closePath();
area = new Area(path.createTransformedShape(null));
}
@Override
public List<Point2D> getVertices() {
return vertices;
}
@Override
public Area getArea() {
return area;
}
/**
Get the ID of the building.
@return The building ID.
*/
public long getBuildingID() {
return buildingID;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("BuildingInfo [");
for (Iterator<Point2D> it = vertices.iterator(); it.hasNext();) {
result.append(it.next().toString());
if (it.hasNext()) {
result.append(", ");
}
}
result.append("]");
return result.toString();
}
}
package maps.convert.osm2gml;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collections;
import java.util.Comparator;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import rescuecore2.misc.geometry.Line2D;
import rescuecore2.misc.geometry.Point2D;
import rescuecore2.misc.geometry.Vector2D;
import rescuecore2.misc.geometry.GeometryTools2D;
//import rescuecore2.misc.gui.ShapeDebugFrame;
//import java.awt.Color;
import maps.osm.OSMNode;
/**
Information about an OSM intersection.
*/
public class OSMIntersectionInfo implements OSMShape {
// private static ShapeDebugFrame debug = new ShapeDebugFrame();
private OSMNode centre;
private List<RoadAspect> roads;
private List<Point2D> vertices;
private Area area;
/**
Create an IntersectionInfo.
@param centre The OSMNode at the centre of the intersection.
*/
public OSMIntersectionInfo(OSMNode centre) {
this.centre = centre;
roads = new ArrayList<RoadAspect>();
}
/**
Add an incoming road.
@param road The incoming road.
*/
public void addRoadSegment(OSMRoadInfo road) {
if (road.getFrom() == centre && road.getTo() == centre) {
System.out.println("Degenerate road found");
}
else {
roads.add(new RoadAspect(road, centre));
}
}
/**
Process this intersection and determine the vertices and area it covers.
@param sizeOf1m The size of 1m in latitude/longitude.
*/
public void process(double sizeOf1m) {
vertices = new ArrayList<Point2D>();
if (roads.size() > 1) {
processRoads(sizeOf1m);
}
else {
processSingleRoad(sizeOf1m);
area = null;
}
}
/**
Get the OSMNode at the centre of this intersection.
@return The OSMNode at the centre.
*/
public OSMNode getCentre() {
return centre;
}
@Override
public Area getArea() {
return area;
}
@Override
public List<Point2D> getVertices() {
return vertices;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("IntersectionInfo (centre ");
result.append(centre);
result.append(") [");
for (Iterator<Point2D> it = vertices.iterator(); it.hasNext();) {
result.append(it.next().toString());
if (it.hasNext()) {
result.append(", ");
}
}
result.append("]");
if (area == null) {
result.append(" (degenerate)");
}
return result.toString();
}
private void processRoads(double sizeOf1m) {
// Sort incoming roads counterclockwise about the centre
Point2D centrePoint = new Point2D(centre.getLongitude(), centre.getLatitude());
CounterClockwiseSort sort = new CounterClockwiseSort(centrePoint);
Collections.sort(roads, sort);
// Go through each pair of adjacent incoming roads and compute the two intersection points
Iterator<RoadAspect> it = roads.iterator();
RoadAspect first = it.next();
RoadAspect previous = first;
while (it.hasNext()) {
RoadAspect next = it.next();
Point2D p = findIncomingRoadIntersection(previous, next, centrePoint, sizeOf1m);
vertices.add(p);
previous = next;
}
Point2D p = findIncomingRoadIntersection(previous, first, centrePoint, sizeOf1m);
vertices.add(p);
// If there are multiple vertices then compute the area
if (vertices.size() > 2) {
Iterator<Point2D> ix = vertices.iterator();
Point2D point = ix.next();
Path2D.Double path = new Path2D.Double();
path.moveTo(point.getX(), point.getY());
while (ix.hasNext()) {
point = ix.next();
path.lineTo(point.getX(), point.getY());
}
path.closePath();
area = new Area(path.createTransformedShape(null));
}
else {
area = null;
}
}
/**
Process two incoming roads and find the intersection of the right edge of the first road and the left edge of the second road.
@param first The road to check the right edge of.
@param second The road the check the left edge of.
@param centrePoint The centre of the intersection.
@return The intersection of the two roads.
*/
private Point2D findIncomingRoadIntersection(RoadAspect first, RoadAspect second, Point2D centrePoint, double sizeOf1m) {
OSMNode firstNode = first.getFarNode();
OSMNode secondNode = second.getFarNode();
// Find the intersection of the incoming road edges
Point2D firstPoint = new Point2D(firstNode.getLongitude(), firstNode.getLatitude());
Point2D secondPoint = new Point2D(secondNode.getLongitude(), secondNode.getLatitude());
Vector2D firstVector = centrePoint.minus(firstPoint);
Vector2D secondVector = centrePoint.minus(secondPoint);
Vector2D firstNormal = firstVector.getNormal().normalised().scale(-Constants.ROAD_WIDTH * sizeOf1m / 2);
Vector2D secondNormal = secondVector.getNormal().normalised().scale(Constants.ROAD_WIDTH * sizeOf1m / 2);
Point2D start1Point = firstPoint.plus(firstNormal);
Point2D start2Point = secondPoint.plus(secondNormal);
Line2D line1 = new Line2D(start1Point, firstVector);
Line2D line2 = new Line2D(start2Point, secondVector);
Point2D intersection = GeometryTools2D.getIntersectionPoint(line1, line2);
if (intersection == null) {
// Lines are parallel
// This means the normals are parallel, so we can just add a normal to the centre point to generate an intersection point
intersection = centrePoint.plus(firstNormal);
}
first.setRightEnd(intersection);
second.setLeftEnd(intersection);
/*
List<ShapeDebugFrame.ShapeInfo> shapes = new ArrayList<ShapeDebugFrame.ShapeInfo>();
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(firstPoint, centrePoint), "First road", Color.BLUE, false, false));
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(firstPoint, firstNormal), "First road offset", Color.YELLOW, false, false));
shapes.add(new ShapeDebugFrame.Point2DShapeInfo(start1Point, "Left start", Color.BLUE, true));
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(line1, "Left edge", Color.BLUE, true, false));
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(secondPoint, centrePoint), "Second road", Color.WHITE, false, false));
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(secondPoint, secondNormal), "Second road offset", Color.CYAN, false, false));
shapes.add(new ShapeDebugFrame.Point2DShapeInfo(start2Point, "Right start", Color.WHITE, true));
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(line2, "Right edge", Color.WHITE, true, false));
shapes.add(new ShapeDebugFrame.Point2DShapeInfo(intersection, "Intersection", Color.ORANGE, true));
debug.show("Intersection", shapes);
*/
return intersection;
}
/**
This "intersection" has a single incoming road. Set the incoming road's left and right edges.
*/
private void processSingleRoad(double sizeOf1m) {
Point2D centrePoint = new Point2D(centre.getLongitude(), centre.getLatitude());
RoadAspect road = roads.iterator().next();
OSMNode node = road.getFarNode();
Point2D nodePoint = new Point2D(node.getLongitude(), node.getLatitude());
Vector2D nodeVector = centrePoint.minus(nodePoint);
Vector2D nodeNormal = nodeVector.getNormal().normalised().scale(-Constants.ROAD_WIDTH * sizeOf1m / 2);
Vector2D nodeNormal2 = nodeNormal.scale(-1);
Point2D start1Point = nodePoint.plus(nodeNormal);
Point2D start2Point = nodePoint.plus(nodeNormal2);
Line2D line1 = new Line2D(start1Point, nodeVector);
Line2D line2 = new Line2D(start2Point, nodeVector);
Point2D end1 = line1.getPoint(1);
Point2D end2 = line2.getPoint(1);
road.setRightEnd(end1);
road.setLeftEnd(end2);
/*
List<ShapeDebugFrame.ShapeInfo> shapes = new ArrayList<ShapeDebugFrame.ShapeInfo>();
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(nodePoint, centrePoint), "Single road", Color.BLUE, false));
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(nodePoint, nodeNormal), "Offset 1", Color.YELLOW, false));
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(nodePoint, nodeNormal2), "Offset 2", Color.CYAN, false));
shapes.add(new ShapeDebugFrame.Point2DShapeInfo(start1Point, "Left start", Color.BLUE, true));
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(line1, "Left edge", Color.BLUE, true));
shapes.add(new ShapeDebugFrame.Point2DShapeInfo(start2Point, "Right start", Color.WHITE, true));
shapes.add(new ShapeDebugFrame.Line2DShapeInfo(line2, "Right edge", Color.WHITE, true));
shapes.add(new ShapeDebugFrame.Point2DShapeInfo(end1, "Endpoint 1", Color.ORANGE, true));
shapes.add(new ShapeDebugFrame.Point2DShapeInfo(end2, "Endpoint 2", Color.PINK, true));
debug.show(shapes);
*/
}
private static class RoadAspect {
private boolean forward;
private OSMRoadInfo road;
RoadAspect(OSMRoadInfo road, OSMNode intersection) {
this.road = road;
forward = intersection == road.getTo();
}
OSMRoadInfo getRoad() {
return road;
}
OSMNode getFarNode() {
return forward ? road.getFrom() : road.getTo();
}
void setLeftEnd(Point2D p) {
if (forward) {
road.setToLeft(p);
}
else {
road.setFromRight(p);
}
}
void setRightEnd(Point2D p) {
if (forward) {
road.setToRight(p);
}
else {
road.setFromLeft(p);
}
}
}
private static class CounterClockwiseSort implements Comparator<RoadAspect> {
private Point2D centre;
/**
Construct a CounterClockwiseSort with a reference point.
@param centre The reference point.
*/
public CounterClockwiseSort(Point2D centre) {
this.centre = centre;
}
@Override
public int compare(RoadAspect first, RoadAspect second) {
double d1 = score(first);
double d2 = score(second);
if (d1 < d2) {
return 1;
}
else if (d1 > d2) {
return -1;
}
else {
return 0;
}
}
/**
Compute the score for a RoadAspect - the amount of clockwiseness from 12 o'clock.
@param aspect The RoadAspect.
@return The amount of clockwiseness. This will be in the range [0..4) with 0 representing 12 o'clock, 1 representing 3 o'clock and so on.
*/
public double score(RoadAspect aspect) {
OSMNode node = aspect.getFarNode();
Point2D point = new Point2D(node.getLongitude(), node.getLatitude());
Vector2D v = point.minus(centre);
double sin = v.getX() / v.getLength();
double cos = v.getY() / v.getLength();
if (Double.isNaN(sin) || Double.isNaN(cos)) {
System.out.println(v);
System.out.println(v.getLength());
}
return convert(sin, cos);
}
// CHECKSTYLE:OFF:MagicNumber
private double convert(double sin, double cos) {
if (sin >= 0 && cos >= 0) {
return sin;
}
if (sin >= 0 && cos < 0) {
return 2 - sin;
}
if (sin < 0 && cos < 0) {
return 2 - sin;
}
if (sin < 0 && cos >= 0) {
return 4 + sin;
}
throw new IllegalArgumentException("This should be impossible! What's going on? sin=" + sin + ", cos=" + cos);
}
// CHECKSTYLE:ON:MagicNumber
}
}
package maps.convert.osm2gml;
import rescuecore2.misc.geometry.Point2D;
import maps.osm.OSMNode;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.util.List;
import java.util.ArrayList;
/**
Information about an OSM road.
*/
public class OSMRoadInfo implements OSMShape {
private OSMNode from;
private OSMNode to;
private Point2D fromLeft;
private Point2D toLeft;
private Point2D fromRight;
private Point2D toRight;
private Area area;
/**
Create an OSMRoadInfo between two nodes.
@param from The first OSMNode.
@param to The second OSMNode.
*/
public OSMRoadInfo(OSMNode from, OSMNode to) {
this.from = from;
this.to = to;
area = null;
}
/**
Get the "from" node.
@return The "from" node.
*/
public OSMNode getFrom() {
return from;
}
/**
Get the "to" node.
@return The "to" node.
*/
public OSMNode getTo() {
return to;
}
/**
Set the point that is at the left side of this road at the "from" end.
@param p The from-left corner point.
*/
public void setFromLeft(Point2D p) {
fromLeft = p;
area = null;
}
/**
Set the point that is at the right side of this road at the "from" end.
@param p The from-right corner point.
*/
public void setFromRight(Point2D p) {
fromRight = p;
area = null;
}
/**
Set the point that is at the left side of this road at the "to" end.
@param p The to-left corner point.
*/
public void setToLeft(Point2D p) {
toLeft = p;
area = null;
}
/**
Set the point that is at the right side of this road at the "to" end.
@param p The to-right corner point.
*/
public void setToRight(Point2D p) {
toRight = p;
area = null;
}
/**
Get the point that is at the left side of this road at the "from" end.
@return The from-left corner point.
*/
public Point2D getFromLeft() {
return fromLeft;
}
/**
Get the point that is at the right side of this road at the "from" end.
@return The from-right corner point.
*/
public Point2D getFromRight() {
return fromRight;
}
/**
Get the point that is at the left side of this road at the "to" end.
@return The to-left corner point.
*/
public Point2D getToLeft() {
return toLeft;
}
/**
Get the point that is at the right side of this road at the "to" end.
@return The to-right corner point.
*/
public Point2D getToRight() {
return toRight;
}
@Override
public Area getArea() {
if (area == null) {
if (fromLeft == null || fromRight == null || toLeft == null || toRight == null) {
return null;
}
Path2D.Double path = new Path2D.Double();
path.moveTo(fromLeft.getX(), fromLeft.getY());
path.lineTo(fromRight.getX(), fromRight.getY());
path.lineTo(toRight.getX(), toRight.getY());
path.lineTo(toLeft.getX(), toLeft.getY());
path.closePath();
area = new Area(path.createTransformedShape(null));
}
return area;
}
@Override
public List<Point2D> getVertices() {
List<Point2D> result = new ArrayList<Point2D>();
result.add(fromLeft);
result.add(fromRight);
result.add(toRight);
result.add(toLeft);
return result;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("RoadInfo [");
result.append(fromLeft);
result.append(", ");
result.append(fromRight);
result.append(", ");
result.append(toRight);
result.append(", ");
result.append(toLeft);
result.append("]");
return result.toString();
}
}
package maps.convert.osm2gml;
import java.util.List;
import java.awt.geom.Area;
import rescuecore2.misc.geometry.Point2D;
/**
Interface for OSM object shapes.
*/
public interface OSMShape {
/**
Get the vertices of this shape in clockwise order.
@return The vertices of this shape.
*/
List<Point2D> getVertices();
/**
Get the area covered by this shape.
@return The area of this shape.
*/
Area getArea();
}
package maps.convert.osm2gml;
import maps.gml.GMLMap;
import maps.convert.ConvertStep;
/**
This step removes extra nodes and edges.
*/
public class PruneStep extends ConvertStep {
// private GMLMap gmlMap;
/**
Construct a PruneStep.
@param gmlMap The GMLMap to use.
*/
public PruneStep(GMLMap gmlMap) {
super();
// this.gmlMap = gmlMap;
}
@Override
public String getDescription() {
return "Pruning nodes and edges";
}
@Override
protected void step() {
/*
setProgressLimit(gmlMap.getEdges().size() + gmlMap.getNodes().size());
int edgeCount = 0;
int nodeCount = 0;
// Any edge that is not part of a face can be pruned
setStatus("Pruning edges");
for (GMLEdge next : gmlMap.getEdges()) {
if (gmlMap.getAttachedFaces(next).isEmpty()) {
gmlMap.removeEdge(next);
++edgeCount;
}
bumpProgress();
}
// Any node that is not part of an edge can be pruned
setStatus("Pruning nodes");
for (GMLNode next : gmlMap.getNodes()) {
if (gmlMap.getAttachedEdges(next).isEmpty()) {
gmlMap.removeNode(next);
++nodeCount;
}
bumpProgress();
}
setStatus("Removed " + edgeCount + " edges and " + nodeCount + " nodes");
*/
}
}
package maps.convert.osm2gml;
import java.awt.Color;
import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import maps.convert.ConvertStep;
import rescuecore2.log.Logger;
/**
This step removes shapes that are duplicates or contained entirely inside another shape.
*/
public class RemoveShapesStep extends ConvertStep {
private TemporaryMap map;
/**
Construct a RemoveFacesStep.
@param map The TemporaryMap to use.
*/
public RemoveShapesStep(TemporaryMap map) {
super();
this.map = map;
}
@Override
public String getDescription() {
return "Removing extraneous shapes";
}
@Override
protected void step() {
debug.setBackground(ConvertTools.getAllDebugShapes(map));
debug.setAutozoomEnabled(false);
Collection<TemporaryObject> allObjects = map.getAllObjects();
setProgressLimit(allObjects.size() * 2);
Set<TemporaryObject> removed = new HashSet<TemporaryObject>();
setStatus("Removing duplicate shapes");
int duplicateCount = 0;
int interiorCount = 0;
Logger.debug("Removing building duplicates");
duplicateCount += removeDuplicates(map.getBuildings(), removed, allObjects);
Logger.debug("Removing intersection duplicates");
duplicateCount += removeDuplicates(map.getIntersections(), removed, allObjects);
Logger.debug("Removing road duplicates");
duplicateCount += removeDuplicates(map.getRoads(), removed, allObjects);
Logger.debug("Removing interior faces");
setStatus("Removing interior faces");
interiorCount += removeInterior(map.getRoads(), removed, allObjects);
interiorCount += removeInterior(map.getIntersections(), removed, allObjects);
interiorCount += removeInterior(map.getBuildings(), removed, allObjects);
setStatus("Removed " + removed.size() + " faces: " + duplicateCount + " duplicates and " + interiorCount + " interior");
debug.clearBackground();
debug.activate();
debug.show("Result", ConvertTools.getAllDebugShapes(map));
}
/**
Remove all shapes that are duplicates of a test set.
@param test The set of objects to test against.
@param removed The set of removed objects.
@param toCheck The set of shapes to check.
@return The number of objects removed.
*/
private int removeDuplicates(Collection<? extends TemporaryObject> test, Set<TemporaryObject> removed, Collection<TemporaryObject> toCheck) {
int count = 0;
Logger.debug(test.size() + " test objects, " + toCheck.size() + " to check, " + removed.size() + " already removed");
for (TemporaryObject first : test) {
bumpProgress();
if (removed.contains(first)) {
continue;
}
Logger.debug("Next test object: " + first);
for (TemporaryObject second : toCheck) {
if (removed.contains(second)) {
continue;
}
if (first == second) {
continue;
}
Logger.debug("Next check object: " + second);
if (first.isDuplicate(second)) {
map.removeTemporaryObject(second);
removed.add(second);
++count;
Logger.debug("Removed duplicate object: " + second + " is same as " + first);
}
debug.show("Checking for duplicates",
new TemporaryObjectInfo(first, "First", Color.WHITE, Constants.TRANSPARENT_LIME),
new TemporaryObjectInfo(second, "Second", Color.WHITE, Constants.TRANSPARENT_BLUE));
}
}
return count;
}
/**
Remove any shapes that are entirely inside another shapes.
@param toCheck The set of objects to check.
@param removed The set of removed objects.
@param allObjects All objects.
@return The number of removed objects.
*/
private int removeInterior(Collection<? extends TemporaryObject> toCheck, Set<TemporaryObject> removed, Collection<TemporaryObject> allObjects) {
int count = 0;
for (TemporaryObject first : toCheck) {
bumpProgress();
if (removed.contains(first)) {
continue;
}
for (TemporaryObject second : allObjects) {
if (removed.contains(second)) {
continue;
}
if (first == second) {
continue;
}
if (first.isEntirelyInside(second)) {
map.removeTemporaryObject(first);
removed.add(first);
++count;
}
}
}
return count;
}
}
package maps.convert.osm2gml;
import maps.osm.OSMMap;
import maps.osm.OSMNode;
import maps.osm.OSMRoad;
import maps.osm.OSMBuilding;
import maps.convert.ConvertStep;
import java.util.Map;
import java.util.List;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;
/**
This step scans the OpenStreetMap data and generates information about roads, intersections and buildings.
*/
public class ScanOSMStep extends ConvertStep {
private TemporaryMap map;
private Map<OSMNode, OSMIntersectionInfo> nodeToIntersection;
private List<OSMIntersectionInfo> intersections;
private List<OSMRoadInfo> roads;
private List<OSMBuildingInfo> buildings;
/**
Construct a ScanOSMStep.
@param map The OSMMap to scan.
*/
public ScanOSMStep(TemporaryMap map) {
this.map = map;
}
@Override
public String getDescription() {
return "Scanning OpenStreetMap data";
}
@Override
protected void step() {
nodeToIntersection = new HashMap<OSMNode, OSMIntersectionInfo>();
intersections = new ArrayList<OSMIntersectionInfo>();
roads = new ArrayList<OSMRoadInfo>();
buildings = new ArrayList<OSMBuildingInfo>();
OSMMap osm = map.getOSMMap();
setProgressLimit(osm.getRoads().size() + osm.getBuildings().size());
setStatus("Scanning roads and buildings");
scanRoads();
scanBuildings();
double sizeOf1m = ConvertTools.sizeOf1Metre(osm);
setStatus("Generating intersections");
setProgressLimit(intersections.size());
setProgress(0);
for (OSMIntersectionInfo next : intersections) {
next.process(sizeOf1m);
bumpProgress();
}
setStatus("Created " + roads.size() + " roads, " + intersections.size() + " intersections, " + buildings.size() + " buildings");
map.setOSMInfo(intersections, roads, buildings);
}
private void scanRoads() {
OSMMap osm = map.getOSMMap();
for (OSMRoad road : osm.getRoads()) {
Iterator<Long> it = road.getNodeIDs().iterator();
OSMNode start = osm.getNode(it.next());
while (it.hasNext()) {
OSMNode end = osm.getNode(it.next());
if (start == end) {
System.out.println("Degenerate road: " + road.getID());
continue;
}
OSMIntersectionInfo from = nodeToIntersection.get(start);
OSMIntersectionInfo to = nodeToIntersection.get(end);
if (from == null) {
from = new OSMIntersectionInfo(start);
nodeToIntersection.put(start, from);
intersections.add(from);
}
if (to == null) {
to = new OSMIntersectionInfo(end);
nodeToIntersection.put(end, to);
intersections.add(to);
}
OSMRoadInfo roadInfo = new OSMRoadInfo(start, end);
from.addRoadSegment(roadInfo);
to.addRoadSegment(roadInfo);
start = end;
roads.add(roadInfo);
}
bumpProgress();
}
}
private void scanBuildings() {
OSMMap osm = map.getOSMMap();
for (OSMBuilding building : osm.getBuildings()) {
buildings.add(new OSMBuildingInfo(building, osm));
bumpProgress();
}
}
}
package maps.convert.osm2gml;
import java.util.List;
import java.util.Deque;
import java.util.ArrayDeque;
import java.util.Set;
import java.util.HashSet;
import rescuecore2.misc.geometry.Point2D;
import rescuecore2.misc.geometry.Line2D;
import rescuecore2.misc.geometry.GeometryTools2D;
//import rescuecore2.log.Logger;
import maps.convert.ConvertStep;
/**
This step splits any edges that intersect.
*/
public class SplitIntersectingEdgesStep extends ConvertStep {
private TemporaryMap map;
private int splitCount;
private int inspectedCount;
private Deque<Edge> toCheck;
private Set<Edge> seen;
/**
Construct a SplitIntersectingEdgesStep.
@param map The TemporaryMap to use.
*/
public SplitIntersectingEdgesStep(TemporaryMap map) {
this.map = map;
}
@Override
public String getDescription() {
return "Splitting intersecting edges";
}
@Override
protected void step() {
debug.setBackground(ConvertTools.getAllDebugShapes(map));
toCheck = new ArrayDeque<Edge>(map.getAllEdges());
seen = new HashSet<Edge>();
setProgressLimit(toCheck.size());
splitCount = 0;
inspectedCount = 0;
while (!toCheck.isEmpty()) {
Edge next = toCheck.pop();
check(next);
++inspectedCount;
setProgressLimit(toCheck.size() + inspectedCount);
bumpProgress();
}
setStatus("Inspected " + inspectedCount + " edges and split " + splitCount);
}
private void check(Edge e) {
if (!map.getAllEdges().contains(e)) {
// Logger.debug("Skipped edge " + e);
// debug.show("Skipped edge", new EdgeShapeInfo(e, "Skipped edge", Color.BLUE, true, false));
return;
}
if (seen.contains(e)) {
return;
}
seen.add(e);
Line2D l1 = e.getLine();
Set<Edge> edges = new HashSet<Edge>(map.getAllEdges());
for (Edge test : edges) {
if (test.equals(e)) {
continue;
}
Line2D l2 = test.getLine();
if (GeometryTools2D.parallel(l1, l2)) {
if (processParallelLines(e, test)) {
break;
}
}
else {
if (checkForIntersection(e, test)) {
break;
}
}
}
}
/**
@return True if e1 was split.
*/
private boolean processParallelLines(Edge e1, Edge e2) {
// Possible cases:
// Shorter line entirely inside longer
// Shorter line overlaps longer at longer start
// Shorter line overlaps longer at longer end
// Shorter line start point is same as longer start and end point is inside
// Shorter line start point is same as longer end and end point is inside
// Shorter line end point is same as longer start and start point is inside
// Shorter line end point is same as longer end and start point is inside
Edge shorterEdge = e1;
Edge longerEdge = e2;
if (e1.getLine().getDirection().getLength() > e2.getLine().getDirection().getLength()) {
shorterEdge = e2;
longerEdge = e1;
}
Line2D shorter = shorterEdge.getLine();
Line2D longer = longerEdge.getLine();
boolean shortStartLongStart = shorterEdge.getStart() == longerEdge.getStart();
boolean shortStartLongEnd = shorterEdge.getStart() == longerEdge.getEnd();
boolean shortEndLongStart = shorterEdge.getEnd() == longerEdge.getStart();
boolean shortEndLongEnd = shorterEdge.getEnd() == longerEdge.getEnd();
boolean startInside = !shortStartLongStart && !shortStartLongEnd && GeometryTools2D.contains(longer, shorter.getOrigin());
boolean endInside = !shortEndLongStart && !shortEndLongEnd && GeometryTools2D.contains(longer, shorter.getEndPoint());
/*
if (startInside || endInside) {
++overlapCount;
}
*/
if (startInside && endInside) {
processInternalEdge(shorterEdge, longerEdge);
return longerEdge == e1;
}
else if (startInside && !endInside) {
// Either full overlap or coincident end point
if (shortEndLongStart) {
processCoincidentNode(shorterEdge, longerEdge, shorterEdge.getEnd());
return longerEdge == e1;
}
else if (shortEndLongEnd) {
processCoincidentNode(shorterEdge, longerEdge, shorterEdge.getEnd());
return longerEdge == e1;
}
else {
// Full overlap
processOverlap(shorterEdge, longerEdge);
return true;
}
}
else if (endInside && !startInside) {
// Either full overlap or coincident end point
if (shortStartLongStart) {
processCoincidentNode(shorterEdge, longerEdge, shorterEdge.getStart());
return longerEdge == e1;
}
else if (shortStartLongEnd) {
processCoincidentNode(shorterEdge, longerEdge, shorterEdge.getStart());
return longerEdge == e1;
}
else {
// Full overlap
processOverlap(shorterEdge, longerEdge);
return true;
}
}
return false;
}
/**
@return true if first is split.
*/
private boolean checkForIntersection(Edge first, Edge second) {
Point2D intersection = GeometryTools2D.getSegmentIntersectionPoint(first.getLine(), second.getLine());
// System.out.println(intersection);
if (intersection == null) {
// Maybe the intersection is within the map's "nearby" tolerance?
intersection = GeometryTools2D.getIntersectionPoint(first.getLine(), second.getLine());
/*
debug.show("Split intersection",
new ShapeDebugFrame.Line2DShapeInfo(first, "Line 1", Color.ORANGE, true, false),
new ShapeDebugFrame.Line2DShapeInfo(second, "Line 2", Color.BLUE, true, false),
new ShapeDebugFrame.Point2DShapeInfo(intersection, "Near-miss intersection", Color.BLACK, false),
new ShapeDebugFrame.Line2DShapeInfo(firstObject.getLines(), "Object 1", Color.GREEN, false, false),
new ShapeDebugFrame.Line2DShapeInfo(secondObject.getLines(), "Object 2", Color.GRAY, false, false)
);
*/
// Was this a near miss?
if (map.isNear(intersection, first.getStart().getCoordinates()) || map.isNear(intersection, first.getEnd().getCoordinates())) {
// Check that the intersection is actually somewhere on the second segment
double d = second.getLine().getIntersection(first.getLine());
if (d < 0 || d > 1) {
// Nope. Ignore it.
return false;
}
}
else if (map.isNear(intersection, second.getStart().getCoordinates()) || map.isNear(intersection, second.getEnd().getCoordinates())) {
// Check that the intersection is actually somewhere on the first line segment
double d = first.getLine().getIntersection(second.getLine());
if (d < 0 || d > 1) {
// Nope. Ignore it.
return false;
}
}
else {
// Not a near miss.
return false;
}
}
Node n = map.getNode(intersection);
// Split the two edges into 4 (maybe)
// Was the first edge split?
boolean splitFirst = !n.equals(first.getStart()) && !n.equals(first.getEnd());
boolean splitSecond = !n.equals(second.getStart()) && !n.equals(second.getEnd());
Set<Edge> newEdges = new HashSet<Edge>();
if (splitFirst) {
List<Edge> e = map.splitEdge(first, n);
toCheck.addAll(e);
newEdges.addAll(e);
++splitCount;
/*
Logger.debug("Split edge " + first);
debug.show("Split first line",
new ShapeDebugFrame.Line2DShapeInfo(first.getLine(), "Line 1", Color.ORANGE, true, false),
new EdgeShapeInfo(e, "New edges", Color.WHITE, false, true),
new ShapeDebugFrame.Point2DShapeInfo(intersection, "Intersection", Color.BLACK, true)
);
*/
}
if (splitSecond) {
List<Edge> e = map.splitEdge(second, n);
toCheck.addAll(e);
newEdges.addAll(e);
++splitCount;
/*
Logger.debug("Split edge " + second);
debug.show("Split second line",
new ShapeDebugFrame.Line2DShapeInfo(second.getLine(), "Line 2", Color.BLUE, true, false),
new EdgeShapeInfo(e, "New edges", Color.WHITE, false, true),
new ShapeDebugFrame.Point2DShapeInfo(intersection, "Intersection", Color.BLACK, true)
);
*/
}
// if (splitFirst || splitSecond) {
/*
Logger.debug("First line: " + first + " -> " + first.getLine());
Logger.debug("Second line: " + second + " -> " + second.getLine());
Logger.debug("Intersection: " + intersection);
Logger.debug("New edges");
for (Edge next : newEdges) {
Logger.debug(" " + next + " -> " + next.getLine());
}
*/
// debug.show("Split intersection",
// new ShapeDebugFrame.Line2DShapeInfo(first.getLine(), "Line 1", Color.ORANGE, true, false),
// new ShapeDebugFrame.Line2DShapeInfo(second.getLine(), "Line 2", Color.BLUE, true, false),
// new EdgeShapeInfo(newEdges, "New edges", Color.WHITE, false, true),
// new ShapeDebugFrame.Point2DShapeInfo(intersection, "Intersection", Color.BLACK, true)
// );
// }
return splitFirst;
}
private void processInternalEdge(Edge shorter, Edge longer) {
// Split longer into (up to) three chunks
double t1 = GeometryTools2D.positionOnLine(longer.getLine(), shorter.getLine().getOrigin());
double t2 = GeometryTools2D.positionOnLine(longer.getLine(), shorter.getLine().getEndPoint());
Node first;
Node second;
if (t1 < t2) {
first = shorter.getStart();
second = shorter.getEnd();
}
else {
first = shorter.getEnd();
second = shorter.getStart();
}
toCheck.addAll(map.splitEdge(longer, first, second));
++splitCount;
}
private void processCoincidentNode(Edge shorter, Edge longer, Node coincidentPoint) {
// Split the long edge at the non-coincident point
Node cutPoint = coincidentPoint.equals(shorter.getStart()) ? shorter.getEnd() : shorter.getStart();
toCheck.addAll(map.splitEdge(longer, cutPoint));
++splitCount;
}
private void processOverlap(Edge shorter, Edge longer) {
Node shortSplit = GeometryTools2D.contains(shorter.getLine(), longer.getLine().getOrigin()) ? longer.getStart() : longer.getEnd();
Node longSplit = GeometryTools2D.contains(longer.getLine(), shorter.getLine().getOrigin()) ? shorter.getStart() : shorter.getEnd();
toCheck.addAll(map.splitEdge(shorter, shortSplit));
toCheck.addAll(map.splitEdge(longer, longSplit));
++splitCount;
}
}
package maps.convert.osm2gml;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import maps.convert.ConvertStep;
import rescuecore2.log.Logger;
/**
This step splits any shapes that overlap.
*/
public class SplitShapesStep extends ConvertStep {
private TemporaryMap map;
/**
Construct a SplitFacesStep.
@param map The map to use.
*/
public SplitShapesStep(TemporaryMap map) {
this.map = map;
}
@Override
public String getDescription() {
return "Splitting overlapping shapes";
}
@Override
protected void step() {
Collection<TemporaryObject> all = map.getAllObjects();
setProgressLimit(all.size());
int count = 0;
debug.setBackground(ConvertTools.getAllDebugShapes(map));
for (TemporaryObject shape : all) {
count += splitShapeIfRequired(shape);
bumpProgress();
}
setStatus("Added " + count + " new shapes");
}
private int splitShapeIfRequired(TemporaryObject shape) {
Set<DirectedEdge> edgesRemaining = new HashSet<DirectedEdge>(shape.getEdges());
boolean firstShape = true;
int newShapeCount = 0;
// debug.show("Splitting shapes", new TemporaryObjectInfo(shape, "Shape", Constants.BLACK, Constants.TRANSPARENT_ORANGE));
// Logger.debug("Splitting shape " + shape);
// Logger.debug("Edges: ");
// for (DirectedEdge e : edgesRemaining) {
// Logger.debug(" " + e);
// }
while (!edgesRemaining.isEmpty()) {
// Logger.debug(edgesRemaining.size() + " edges remaining");
DirectedEdge dEdge = edgesRemaining.iterator().next();
edgesRemaining.remove(dEdge);
Node start = dEdge.getStartNode();
Node end = dEdge.getEndNode();
List<DirectedEdge> result = new ArrayList<DirectedEdge>();
result.add(dEdge);
// Now walk around
Logger.debug("Starting walk from " + dEdge);
Logger.debug("Start: " + start);
Logger.debug("End: " + end);
while (!end.equals(start)) {
Set<Edge> candidates = map.getAttachedEdges(end);
// Logger.debug("From edge: " + dEdge);
// Logger.debug("Candidates: ");
// for (Edge e : candidates) {
// Logger.debug(" " + e);
// }
Edge turn = ConvertTools.findLeftTurn(dEdge, candidates);
Logger.debug("Best turn: " + turn);
DirectedEdge newDEdge = new DirectedEdge(turn, end);
// debug.show("Splitting shapes",
// new TemporaryObjectInfo(shape, "Shape", Constants.BLACK, Constants.TRANSPARENT_ORANGE),
// new ShapeDebugFrame.Line2DShapeInfo(dEdge.getLine(), "Edge", Constants.BLUE, true, true),
// new ShapeDebugFrame.Line2DShapeInfo(newDEdge.getLine(), "Turn", Constants.RED, true, true));
dEdge = newDEdge;
end = dEdge.getEndNode();
edgesRemaining.remove(dEdge);
result.add(dEdge);
Logger.debug("Added " + dEdge);
Logger.debug("New end: " + end);
}
if (!firstShape || !edgesRemaining.isEmpty()) {
// Didn't cover all edges so new shapes are needed.
if (firstShape) {
map.removeTemporaryObject(shape);
firstShape = false;
}
else {
++newShapeCount;
}
TemporaryObject newObject = null;
if (shape instanceof TemporaryRoad) {
newObject = new TemporaryRoad(result);
}
if (shape instanceof TemporaryIntersection) {
newObject = new TemporaryIntersection(result);
}
if (shape instanceof TemporaryBuilding) {
newObject = new TemporaryBuilding(result, ((TemporaryBuilding)shape).getBuildingID());
}
map.addTemporaryObject(newObject);
// debug.show("Splitting shapes",
// new TemporaryObjectInfo(shape, "Original shape", Constants.BLACK, null),
// new TemporaryObjectInfo(newObject, "New shape", Constants.RED, Constants.TRANSPARENT_RED));
}
}
return newShapeCount;
}
}
package maps.convert.osm2gml;
import java.util.List;
/**
A temporary building during conversion.
*/
public class TemporaryBuilding extends TemporaryObject {
private long id;
/**
Construct a new TemporaryBuilding.
@param edges The edges of the building in counter-clockwise order.
@param id The ID of the OSM building that generated this data.
*/
public TemporaryBuilding(List<DirectedEdge> edges, long id) {
super(edges);
this.id = id;
}
/**
Get the ID of the original OSM building.
@return The OSM building ID.
*/
public long getBuildingID() {
return id;
}
}
package maps.convert.osm2gml;
import java.util.List;
/**
A temporary intersection during conversion.
*/
public class TemporaryIntersection extends TemporaryObject {
/**
Construct a new TemporaryIntersection.
@param edges The edges of the intersection in counter-clockwise order.
*/
public TemporaryIntersection(List<DirectedEdge> edges) {
super(edges);
}
}
package maps.convert.osm2gml;
import java.util.Set;
import java.util.Map;
import java.util.HashSet;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import maps.osm.OSMMap;
import rescuecore2.misc.geometry.Point2D;
import rescuecore2.misc.collections.LazyMap;
//import rescuecore2.log.Logger;
/**
This class holds all temporary information during map conversion.
*/
public class TemporaryMap {
/**
The threshold for determining if nodes are co-located in metres.
*/
private static final double NEARBY_THRESHOLD_M = 1;
private double threshold;
private Set<Node> nodes;
private Set<Edge> edges;
private Map<Node, Set<Edge>> edgesAtNode;
private Map<Edge, Set<TemporaryObject>> objectsAtEdge;
private Set<TemporaryRoad> tempRoads;
private Set<TemporaryIntersection> tempIntersections;
private Set<TemporaryBuilding> tempBuildings;
private Set<TemporaryObject> allObjects;
private OSMMap osmMap;
private Collection<OSMIntersectionInfo> osmIntersections;
private Collection<OSMRoadInfo> osmRoads;
private Collection<OSMBuildingInfo> osmBuildings;
private int nextID;
/**
Construct a TemporaryMap.
@param osmMap The OpenStreetMap data this map is generated from.
*/
public TemporaryMap(OSMMap osmMap) {
this.osmMap = osmMap;
nextID = 0;
nodes = new HashSet<Node>();
edges = new HashSet<Edge>();
threshold = ConvertTools.nearbyThreshold(osmMap, NEARBY_THRESHOLD_M);
tempRoads = new HashSet<TemporaryRoad>();
tempIntersections = new HashSet<TemporaryIntersection>();
tempBuildings = new HashSet<TemporaryBuilding>();
allObjects = new HashSet<TemporaryObject>();
edgesAtNode = new LazyMap<Node, Set<Edge>>() {
@Override
public Set<Edge> createValue() {
return new HashSet<Edge>();
}
};
objectsAtEdge = new LazyMap<Edge, Set<TemporaryObject>>() {
@Override
public Set<TemporaryObject> createValue() {
return new HashSet<TemporaryObject>();
}
};
}
/**
Get the OSMMap.
@return The OSMMap.
*/
public OSMMap getOSMMap() {
return osmMap;
}
/**
Set the OSMMap information.
@param intersections The set of intersections.
@param roads The set of roads.
@param buildings The set of buildings.
*/
public void setOSMInfo(Collection<OSMIntersectionInfo> intersections, Collection<OSMRoadInfo> roads, Collection<OSMBuildingInfo> buildings) {
osmIntersections = new HashSet<OSMIntersectionInfo>(intersections);
osmRoads = new HashSet<OSMRoadInfo>(roads);
osmBuildings = new HashSet<OSMBuildingInfo>(buildings);
}
/**
Get the OSM intersection info.
@return The OSM intersection info.
*/
public Collection<OSMIntersectionInfo> getOSMIntersectionInfo() {
return Collections.unmodifiableCollection(osmIntersections);
}
/**
Get the OSM road info.
@return The OSM road info.
*/
public Collection<OSMRoadInfo> getOSMRoadInfo() {
return Collections.unmodifiableCollection(osmRoads);
}
/**
Get the OSM building info.
@return The OSM building info.
*/
public Collection<OSMBuildingInfo> getOSMBuildingInfo() {
return Collections.unmodifiableCollection(osmBuildings);
}
/**
Add a road.
@param road The road to add.
*/
public void addRoad(TemporaryRoad road) {
tempRoads.add(road);
addObject(road);
}
/**
Remove a road.
@param road The road to remove.
*/
public void removeRoad(TemporaryRoad road) {
tempRoads.remove(road);
removeObject(road);
}
/**
Add an intersection.
@param intersection The intersection to add.
*/
public void addIntersection(TemporaryIntersection intersection) {
tempIntersections.add(intersection);
addObject(intersection);
}
/**
Remove an intersection.
@param intersection The intersection to remove.
*/
public void removeIntersection(TemporaryIntersection intersection) {
tempIntersections.remove(intersection);
removeObject(intersection);
}
/**
Add a building.
@param building The building to add.
*/
public void addBuilding(TemporaryBuilding building) {
tempBuildings.add(building);
addObject(building);
}
/**
Remove a building.
@param building The building to remove.
*/
public void removeBuilding(TemporaryBuilding building) {
tempBuildings.remove(building);
removeObject(building);
}
/**
Add an object.
@param object The object to add.
*/
public void addTemporaryObject(TemporaryObject object) {
if (object instanceof TemporaryRoad) {
addRoad((TemporaryRoad)object);
}
if (object instanceof TemporaryIntersection) {
addIntersection((TemporaryIntersection)object);
}
if (object instanceof TemporaryBuilding) {
addBuilding((TemporaryBuilding)object);
}
}
/**
Remove an object.
@param object The object to remove.
*/
public void removeTemporaryObject(TemporaryObject object) {
if (object instanceof TemporaryRoad) {
removeRoad((TemporaryRoad)object);
}
if (object instanceof TemporaryIntersection) {
removeIntersection((TemporaryIntersection)object);
}
if (object instanceof TemporaryBuilding) {
removeBuilding((TemporaryBuilding)object);
}
}
/**
Get all roads in the map.
@return All roads.
*/
public Collection<TemporaryRoad> getRoads() {
return new HashSet<TemporaryRoad>(tempRoads);
}
/**
Get all intersections in the map.
@return All intersections.
*/
public Collection<TemporaryIntersection> getIntersections() {
return new HashSet<TemporaryIntersection>(tempIntersections);
}
/**
Get all buildings in the map.
@return All buildings.
*/
public Collection<TemporaryBuilding> getBuildings() {
return new HashSet<TemporaryBuilding>(tempBuildings);
}
/**
Get all objects in the map.
@return All objects.
*/
public Collection<TemporaryObject> getAllObjects() {
return new HashSet<TemporaryObject>(allObjects);
}
/**
Get all nodes in the map.
@return All nodes.
*/
public Collection<Node> getAllNodes() {
return new HashSet<Node>(nodes);
}
/**
Get all edges in the map.
@return All edges.
*/
public Collection<Edge> getAllEdges() {
return new HashSet<Edge>(edges);
}
/**
Get all objects attached to an Edge.
@param e The Edge.
@return All attached TemporaryObjects.
*/
public Set<TemporaryObject> getAttachedObjects(Edge e) {
return new HashSet<TemporaryObject>(objectsAtEdge.get(e));
}
/**
Get all edges attached to a Node.
@param n The Node.
@return All attached edges.
*/
public Set<Edge> getAttachedEdges(Node n) {
return new HashSet<Edge>(edgesAtNode.get(n));
}
/**
Set the threshold for deciding if two points are the same. The {@link #isNear(Point2D, Point2D)} method uses this value to check if a new point needs to be registered.
@param t The new threshold.
*/
public void setNearbyThreshold(double t) {
threshold = t;
}
/**
Get the threshold for deciding if two points are the same. The {@link #isNear(Point2D, Point2D)} method uses this value to check if a new point needs to be registered.
@return The nearby threshold.
*/
public double getNearbyThreshold() {
return threshold;
}
/**
Find out if two points are nearby.
@param point1 The first point.
@param point2 The second point.
@return True iff the two points are within the nearby threshold.
*/
public boolean isNear(Point2D point1, Point2D point2) {
return isNear(point1.getX(), point1.getY(), point2.getX(), point2.getY());
}
/**
Find out if two points are nearby.
@param x1 The x coordinate of the first point.
@param y1 The y coordinate of the first point.
@param x2 The x coordinate of the second point.
@param y2 The y coordinate of the second point.
@return True iff the two points are within the nearby threshold.
*/
public boolean isNear(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return (dx >= -threshold
&& dx <= threshold
&& dy >= -threshold
&& dy <= threshold);
}
/**
Get a Node near a point. If a Node already exists nearby then it will be returned, otherwise a new Node will be created.
@param p The node coordinates.
@return A Node.
*/
public Node getNode(Point2D p) {
return getNode(p.getX(), p.getY());
}
/**
Get a Node near a point. If a Node already exists nearby then it will be returned, otherwise a new Node will be created.
@param x The X coordinate.
@param y The Y coordinate.
@return A Node.
*/
public Node getNode(double x, double y) {
for (Node next : nodes) {
if (isNear(x, y, next.getX(), next.getY())) {
return next;
}
}
return createNode(x, y);
}
/**
Get an Edge between two nodes. This will return either a new Edge or a shared instance if one already exists.
@param from The from node.
@param to The to node.
@return An Edge.
*/
public Edge getEdge(Node from, Node to) {
for (Edge next : edges) {
if (next.getStart().equals(from) && next.getEnd().equals(to)
|| next.getStart().equals(to) && next.getEnd().equals(from)) {
return next;
}
}
return createEdge(from, to);
}
/**
Get a DirectedEdge between two nodes.
@param from The from node.
@param to The to node.
@return A new DirectedEdge.
*/
public DirectedEdge getDirectedEdge(Node from, Node to) {
Edge e = getEdge(from, to);
return new DirectedEdge(e, from);
}
/**
Replace an existing edge with a set of new edges.
@param edge The old edge.
@param newEdges The new edges.
*/
public void replaceEdge(Edge edge, Collection<Edge> newEdges) {
for (TemporaryObject next : getAttachedObjects(edge)) {
next.replaceEdge(edge, newEdges);
for (Edge nextEdge : newEdges) {
objectsAtEdge.get(nextEdge).add(next);
}
}
removeEdge(edge);
}
/**
Split an edge into chunks.
@param edge The edge to split.
@param splitPoints The points to split the line. These must be in order along the line.
@return The replacement edges.
*/
public List<Edge> splitEdge(Edge edge, Node... splitPoints) {
List<Edge> replacements = new ArrayList<Edge>();
Edge current = edge;
for (Node n : splitPoints) {
if (n.equals(current.getStart()) || n.equals(current.getEnd())) {
// Don't bother if the split point is at the origin or endpoint
continue;
}
replacements.add(getEdge(current.getStart(), n));
current = getEdge(n, current.getEnd());
}
if (!edge.equals(current)) {
replacements.add(current);
}
if (!replacements.isEmpty()) {
replaceEdge(edge, replacements);
}
return replacements;
}
private Node createNode(double x, double y) {
Node result = new Node(nextID++, x, y);
nodes.add(result);
return result;
}
private Edge createEdge(Node from, Node to) {
Edge result = new Edge(nextID++, from, to);
edges.add(result);
edgesAtNode.get(from).add(result);
edgesAtNode.get(to).add(result);
// Logger.debug("Created edge " + result);
return result;
}
private void addObject(TemporaryObject object) {
allObjects.add(object);
for (DirectedEdge next : object.getEdges()) {
objectsAtEdge.get(next.getEdge()).add(object);
}
}
private void removeNode(Node n) {
nodes.remove(n);
edgesAtNode.remove(n);
}
private void removeEdge(Edge e) {
edges.remove(e);
edgesAtNode.get(e.getStart()).remove(e);
edgesAtNode.get(e.getEnd()).remove(e);
objectsAtEdge.remove(e);
// Logger.debug("Removed edge " + e);
}
private void removeObject(TemporaryObject object) {
allObjects.remove(object);
for (DirectedEdge next : object.getEdges()) {
objectsAtEdge.get(next.getEdge()).remove(object);
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment