Skip to content

Commit

Permalink
Preserve line order in OverlayNG (#665)
Browse files Browse the repository at this point in the history
* Fix OverlayNG edge ordering to preserve order of input lines

Signed-off-by: Martin Davis <mtnclimb@gmail.com>
  • Loading branch information
dr-jts committed Jan 16, 2021
1 parent 26af9f3 commit ec691b3
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
package org.locationtech.jts.operation.overlayng;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.locationtech.jts.util.Assert;
import org.locationtech.jts.util.Debug;

/**
* Performs merging on the noded edges of the input geometries.
Expand All @@ -39,25 +37,22 @@
* no other coincident edge, or if all coincident edges have the same direction).
* This ensures that the overlay output line direction will be as consistent
* as possible with input lines.
* <p>
* The merger also preserves the order of the edges in the input.
* This means that for polygon-line overlay
* the result lines will be in the same order as in the input
* (possibly with multiple result lines for a single input line).
*
* @author mdavis
*
*/
class EdgeMerger {

public static List<Edge> merge(List<Edge> edges) {
EdgeMerger merger = new EdgeMerger(edges);
return merger.merge();
}
// use a list to collect the final edges, to preserve order
List<Edge> mergedEdges = new ArrayList<Edge>();
Map<EdgeKey, Edge> edgeMap = new HashMap<EdgeKey, Edge>();

private Collection<Edge> edges;
private Map<EdgeKey, Edge> edgeMap = new HashMap<EdgeKey, Edge>();

public EdgeMerger(List<Edge> edges) {
this.edges = edges;
}

public ArrayList<Edge> merge() {
for (Edge edge : edges) {
EdgeKey edgeKey = EdgeKey.create(edge);
Edge baseEdge = edgeMap.get(edgeKey);
Expand All @@ -66,6 +61,7 @@ public ArrayList<Edge> merge() {
edgeMap.put(edgeKey, edge);
//Debug.println("edge added: " + edge);
//Debug.println(edge.toLineString());
mergedEdges.add(edge);
}
else {
// found an existing edge
Expand All @@ -80,7 +76,7 @@ public ArrayList<Edge> merge() {
//Debug.println(edge.toLineString());
}
}
return new ArrayList<Edge>(edgeMap.values());
return mergedEdges;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ private void addResultLinesRings() {
* by the start edge direction. This implies
* that if all edges are reversed, the created line
* will be reversed to match.
* This ensures the orientation of linework is faithful to the input
* in the case of polygon-line overlay.
* However, this does not provide a consistent orientation
* in the case of line-line intersection(where A and B might have different orientations).
* (Other more complex strategies would be possible.
* E.g. using the direction of the majority of segments,
* or preferring the direction of the A edges.)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,14 @@ public void testPolygonFlatCollapseIntersection() {
checkEqual(expected, actual);
}

public void testPolygonLineIntersectionOrder() {
Geometry a = read("POLYGON ((1 1, 1 9, 9 9, 9 7, 3 7, 3 3, 9 3, 9 1, 1 1))");
Geometry b = read("MULTILINESTRING ((2 10, 2 0), (4 10, 4 0))");
Geometry expected = read("MULTILINESTRING ((2 9, 2 1), (4 9, 4 7), (4 3, 4 1))");
Geometry actual = intersection(a, b, 1);
checkEqualExact(expected, actual);
}

//============================================================


Expand Down
17 changes: 16 additions & 1 deletion modules/core/src/test/java/test/jts/GeometryTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ protected GeometryTestCase(String name, CoordinateSequenceFactory coordinateSequ

/**
* Checks that the normalized values of the expected and actual
* geometries are exactly equals.
* geometries are exactly equal.
*
* @param expected the expected value
* @param actual the actual value
Expand All @@ -72,6 +72,21 @@ protected void checkEqual(Geometry expected, Geometry actual) {
assertTrue(equal);
}

/**
* Checks that the values of the expected and actual
* geometries are exactly equal.
*
* @param expected the expected value
* @param actual the actual value
*/
protected void checkEqualExact(Geometry expected, Geometry actual) {
boolean equal = actual.equalsExact(expected);
if (! equal) {
System.out.format(CHECK_EQUAL_FAIL, expected, actual );
}
assertTrue(equal);
}

protected void checkEqual(Geometry expected, Geometry actual, double tolerance) {
Geometry actualNorm = actual.norm();
Geometry expectedNorm = expected.norm();
Expand Down

0 comments on commit ec691b3

Please sign in to comment.