Skip to content

Commit

Permalink
Add BoundaryNodeRule support
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-jts committed Jan 19, 2024
1 parent 6144100 commit 1ce81bb
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static boolean relate(Geometry a, Geometry b, String mask) {
return RelateNG.relate(a, b, mask);
}
public static String relateIM(Geometry a, Geometry b) {
return RelateNG.relate(a, b);
return RelateNG.relate(a, b).toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ public boolean isInBoundary(int boundaryCount)
// the "Mod-2 Rule"
return boundaryCount % 2 == 1;
}

public String toString() {
return "Mod2 Boundary Node Rule";
}
}

/**
Expand Down Expand Up @@ -152,6 +156,10 @@ public boolean isInBoundary(int boundaryCount)
{
return boundaryCount > 0;
}

public String toString() {
return "EndPoint Boundary Node Rule";
}
}

/**
Expand All @@ -171,6 +179,10 @@ public boolean isInBoundary(int boundaryCount)
{
return boundaryCount > 1;
}

public String toString() {
return "MultiValent EndPoint Boundary Node Rule";
}
}

/**
Expand All @@ -189,6 +201,10 @@ public boolean isInBoundary(int boundaryCount)
{
return boundaryCount == 1;
}

public String toString() {
return "MonoValent EndPoint Boundary Node Rule";
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ public class LinearBoundary {

private Map<Coordinate, Integer> vertexDegree = new HashMap<Coordinate, Integer>();
private boolean hasBoundary;
private BoundaryNodeRule boundaryNodeRule;

public LinearBoundary(Geometry geom) {
public LinearBoundary(Geometry geom, BoundaryNodeRule bnRule) {
//assert: dim(geom) == 1
this.boundaryNodeRule = bnRule;
vertexDegree = computeBoundaryPoints(geom);
hasBoundary = checkBoundary(vertexDegree);
}

private boolean checkBoundary(Map<Coordinate, Integer> vertexDegree) {
for (int degree : vertexDegree.values()) {
if (BoundaryNodeRule.MOD2_BOUNDARY_RULE.isInBoundary(degree)) {
if (boundaryNodeRule.isInBoundary(degree)) {
return true;
}
}
Expand All @@ -47,7 +49,7 @@ public boolean isBoundary(Coordinate pt) {
return false;
int degree = vertexDegree.get(pt);
//TODO: add support for settable BoundaryNodeRule
return BoundaryNodeRule.MOD2_BOUNDARY_RULE.isInBoundary(degree);
return boundaryNodeRule.isInBoundary(degree);
}

private static Map<Coordinate, Integer> computeBoundaryPoints(Geometry geom) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.List;
import java.util.Set;

import org.locationtech.jts.algorithm.BoundaryNodeRule;
import org.locationtech.jts.algorithm.Orientation;
import org.locationtech.jts.algorithm.PointLocator;
import org.locationtech.jts.algorithm.locate.IndexedPointInAreaLocator;
Expand Down Expand Up @@ -51,21 +52,32 @@ public class RelateGeometry {
private boolean isPrepared = false;
private List<Coordinate> pts;
private Set<Coordinate> uniquePoints;
private BoundaryNodeRule boundaryNodeRule;

public RelateGeometry(Geometry input) {
this(input, false);
this(input, false, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE);
}

public RelateGeometry(Geometry input, boolean isPrepared) {
this(input, false, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE);
}

public RelateGeometry(Geometry input, BoundaryNodeRule bnRule) {
this(input, false, bnRule);
}

public RelateGeometry(Geometry input, boolean isPrepared, BoundaryNodeRule bnRule) {
this.geom = input;
this.isPrepared = isPrepared;
this.boundaryNodeRule = bnRule;
dim = input.getDimension();

if (dim == 1) {
lineBoundary = new LinearBoundary(input);
lineBoundary = new LinearBoundary(input, boundaryNodeRule);
}
}



public Envelope getEnvelope() {
return geom.getEnvelopeInternal();
}
Expand Down Expand Up @@ -148,7 +160,7 @@ private int LocateOnPoint(Coordinate pt) {
private int locateOnLine(Coordinate pt) {
if (lineLocator == null) {
//TODO: index the lines in prepared mode?
lineLocator = new PointLocator();
lineLocator = new PointLocator(boundaryNodeRule);
}
return lineLocator.locate(pt, geom);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.locationtech.jts.geom.Dimension;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.IntersectionMatrix;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.prep.PreparedGeometry;
Expand Down Expand Up @@ -57,34 +58,56 @@ public static boolean evaluate(TopologyPredicate pred, Geometry a, Geometry b) {
return rng.evaluate(pred, b);
}

public static String relate(Geometry a, Geometry b) {
RelatePredicate rel = new RelatePredicate();
RelateNG.evaluate(rel, a, b);
return rel.getIM().toString();
private static boolean evaluate(RelatePredicate pred, Geometry a, Geometry b, BoundaryNodeRule bnRule) {
RelateNG rng = new RelateNG(a, bnRule);
return rng.evaluate(pred, b);
}

public static boolean relate(Geometry a, Geometry b, String mask) {
RelatePredicate rel = new RelatePredicate(mask);
return RelateNG.evaluate(rel, a, b);
}

public static IntersectionMatrix relate(Geometry a, Geometry b) {
RelatePredicate rel = new RelatePredicate();
RelateNG.evaluate(rel, a, b);
return rel.getIM();
}

public static IntersectionMatrix relate(Geometry a, Geometry b, BoundaryNodeRule bnRule) {
RelatePredicate rel = new RelatePredicate();
RelateNG.evaluate(rel, a, b, bnRule);
return rel.getIM();
}


private RelateGeometry geomA;
private boolean isPrepared = false;

private EdgeSetMutualIntersector edgeMutualInt;
private BoundaryNodeRule boundaryNodeRule;

public RelateNG(Geometry inputA) {
geomA = new RelateGeometry(inputA);
this(inputA, false, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE);
}

public RelateNG(Geometry inputA, BoundaryNodeRule bnRule) {
this(inputA, false, bnRule);
}

public RelateNG(Geometry inputA, boolean isPrepared) {
geomA = new RelateGeometry(inputA, isPrepared);
this(inputA, isPrepared, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE);
}

public RelateNG(Geometry inputA, boolean isPrepared, BoundaryNodeRule bnRule) {
this.boundaryNodeRule = bnRule;
geomA = new RelateGeometry(inputA, isPrepared, boundaryNodeRule);
this.boundaryNodeRule = bnRule;
}

public boolean evaluate(TopologyPredicate predicate, Geometry inputB) {

RelateGeometry geomB = new RelateGeometry(inputB);
RelateGeometry geomB = new RelateGeometry(inputB, boundaryNodeRule);
int dimA = geomA.getDimension();
int dimB = geomB.getDimension();

Expand Down Expand Up @@ -287,4 +310,5 @@ private void computeEdgesMutual(List<SegmentString> edgesB, Envelope envInt, Edg
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public TopologyBuilder(TopologyPredicate predicate, RelateGeometry geomA, Relate
*/
private void initExteriorDims() {
int dimAEff = geomA.getEffectiveDimension();
//int dimABdyEff = geomA.getEffectiveDimension();
int dimBEff = geomB.getEffectiveDimension();
/**
* For P/A case, the Area Int and Bdy intersect the Point exterior.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
package org.locationtech.jts.operation.relateng;

import org.locationtech.jts.algorithm.BoundaryNodeRule;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.IntersectionMatrix;

import junit.textui.TestRunner;
import test.jts.GeometryTestCase;
Expand Down Expand Up @@ -42,7 +44,7 @@ public void testMultiLineStringSelfIntTouchAtEndpoint()
String b = "LINESTRING (60 60, 20 60)";

// under EndPoint, A has a boundary node - A.bdy / B.bdy = 0
runRelateTest(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FF1F00102" );
runRelate(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FF1F00102" );
}

public void testLineStringSelfIntTouchAtEndpoint()
Expand All @@ -51,8 +53,8 @@ public void testLineStringSelfIntTouchAtEndpoint()
String b = "LINESTRING (60 60, 20 60)";

// results for both rules are the same
runRelateTest(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "F01FF0102" );
runRelateTest(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "F01FF0102" );
runRelate(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "F01FF0102" );
runRelate(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "F01FF0102" );
}

public void testMultiLineStringTouchAtEndpoint()
Expand All @@ -63,7 +65,7 @@ public void testMultiLineStringTouchAtEndpoint()
// under Mod2, A has no boundary - A.int / B.bdy = 0
// runRelateTest(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "F01FFF102" );
// under EndPoint, A has a boundary node - A.bdy / B.bdy = 0
runRelateTest(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FF1F00102" );
runRelate(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FF1F00102" );
// under MultiValent, A has a boundary node but B does not - A.bdy / B.bdy = F and A.int
// runRelateTest(a, b, BoundaryNodeRule.MULTIVALENT_ENDPOINT_BOUNDARY_RULE, "0F1FFF1F2" );
}
Expand All @@ -74,11 +76,11 @@ public void testLineRingTouchAtEndpoints()
String b = "LINESTRING (20 20, 20 100)";

// under Mod2, A has no boundary - A.int / B.bdy = 0
// runRelateTest(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "F01FFF102" );
runRelate(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "F01FFF102" );
// under EndPoint, A has a boundary node - A.bdy / B.bdy = 0
// runRelateTest(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FF1F0F102" );
runRelate(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FF1F0F102" );
// under MultiValent, A has a boundary node but B does not - A.bdy / B.bdy = F and A.int
runRelateTest(a, b, BoundaryNodeRule.MULTIVALENT_ENDPOINT_BOUNDARY_RULE, "0F1FFF1F2" );
runRelate(a, b, BoundaryNodeRule.MULTIVALENT_ENDPOINT_BOUNDARY_RULE, "FF10FF1F2" );
}

public void testLineRingTouchAtEndpointAndInterior()
Expand All @@ -87,9 +89,9 @@ public void testLineRingTouchAtEndpointAndInterior()
String b = "LINESTRING (20 20, 40 100)";

// this is the same result as for the above test
runRelateTest(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "F01FFF102" );
runRelate(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "F01FFF102" );
// this result is different - the A node is now on the boundary, so A.bdy/B.ext = 0
runRelateTest(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "F01FF0102" );
runRelate(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "F01FF0102" );
}

public void testPolygonEmptyRing()
Expand All @@ -98,10 +100,10 @@ public void testPolygonEmptyRing()
String b = "LINESTRING (20 100, 20 220, 120 100, 20 100)";

// closed line has no boundary under SFS rule
runRelateTest(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "FFFFFF1F2" );
runRelate(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "FFFFFF1F2" );

// closed line has boundary under ENDPOINT rule
runRelateTest(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FFFFFF102" );
runRelate(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FFFFFF102" );
}

public void testPolygonEmptyMultiLineStringClosed()
Expand All @@ -110,22 +112,20 @@ public void testPolygonEmptyMultiLineStringClosed()
String b = "MULTILINESTRING ((0 0, 0 1), (0 1, 1 1, 1 0, 0 0))";

// closed line has no boundary under SFS rule
runRelateTest(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "FFFFFF1F2" );
runRelate(a, b, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, "FFFFFF1F2" );

// closed line has boundary under ENDPOINT rule
runRelateTest(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FFFFFF102" );
runRelate(a, b, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, "FFFFFF102" );
}

void runRelateTest(String wkt1, String wkt2, BoundaryNodeRule bnRule, String expectedIM)
void runRelate(String wkt1, String wkt2, BoundaryNodeRule bnRule, String expectedIM)
{
/*
Geometry g1 = rdr.read(wkt1);
Geometry g2 = rdr.read(wkt2);
Geometry g1 = read(wkt1);
Geometry g2 = read(wkt2);
IntersectionMatrix im = RelateNG.relate(g1, g2, bnRule);
String imStr = im.toString();
//System.out.println(imStr);
assertTrue("Expected " + expectedIM + ", found " + im, im.matches(expectedIM));
*/
}

}

0 comments on commit 1ce81bb

Please sign in to comment.