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