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
}
}