diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/SimplePredicate.java b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/BasicPredicate.java
similarity index 84%
rename from modules/core/src/main/java/org/locationtech/jts/operation/relateng/SimplePredicate.java
rename to modules/core/src/main/java/org/locationtech/jts/operation/relateng/BasicPredicate.java
index 15f91bb7b8..edb63ef10a 100644
--- a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/SimplePredicate.java
+++ b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/BasicPredicate.java
@@ -13,7 +13,7 @@
import org.locationtech.jts.geom.Location;
-public abstract class SimplePredicate implements TopologyPredicate {
+public abstract class BasicPredicate implements TopologyPredicate {
private int value = TopologyPredicateValue.UNKNOWN;
@@ -21,6 +21,16 @@ public boolean isSelfNodingRequired() {
return false;
}
+ @Override
+ public boolean isKnown() {
+ return TopologyPredicateValue.isKnown(value);
+ }
+
+ @Override
+ public boolean value() {
+ return TopologyPredicateValue.toBoolean(value);
+ }
+
/**
* Updates the predicate value to the given state
* if it is currently unknown.
@@ -28,24 +38,16 @@ public boolean isSelfNodingRequired() {
* @param val the predicate value to update
*/
protected void updateValue(boolean val) {
- //-- don't change already-known value
- if (isKnown())
- return;
- value = TopologyPredicateValue.toValue(val);
- }
-
- @Override
- public int valuePartial(int dimA, int dimB) {
- return value;
- }
-
- @Override
- public boolean value(int dimA, int dimB) {
- return TopologyPredicateValue.toBoolean(value);
+ updateValue(TopologyPredicateValue.toValue(val));
}
- public boolean isKnown() {
- return TopologyPredicateValue.isKnown(value);
+ protected void updateValue(int val) {
+ //-- don't change already-known value
+ if (isKnown())
+ return;
+ if (TopologyPredicateValue.isKnown(val)) {
+ value = val;
+ }
}
/**
diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/IMPredicate.java b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/IMPredicate.java
index ed92491dc3..9ba4496f9c 100644
--- a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/IMPredicate.java
+++ b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/IMPredicate.java
@@ -15,20 +15,30 @@
import org.locationtech.jts.geom.IntersectionMatrix;
import org.locationtech.jts.geom.Location;
-public abstract class IMPredicate implements TopologyPredicate {
+public abstract class IMPredicate extends BasicPredicate {
static final int DIM_UNKNOWN = Dimension.DONTCARE;
+ protected int dimA;
+ protected int dimB;
protected IntersectionMatrix intMatrix;
public IMPredicate() {
- //TODO: add initializer for IntersectionMatrix to a single value?
- //intMatrix = new IntersectionMatrix("*********");
intMatrix = new IntersectionMatrix();
//-- E/E is always dim = 2
intMatrix.set(Location.EXTERIOR, Location.EXTERIOR, Dimension.A);
}
+ @Override
+ public void init(int dimA, int dimB) {
+ this.dimA = dimA;
+ this.dimB = dimB;
+ }
+
+ public boolean isSelfNodingRequired() {
+ return true;
+ }
+
/**
* Gets the current state of the IM matrix (which may only be partially complete).
*
@@ -44,9 +54,14 @@ public void updateDim(int locA, int locB, int dimension) {
if (dimension <= intMatrix.get(locA, locB))
return;
- intMatrix.set(locA, locB, dimension);
+ intMatrix.set(locA, locB, dimension);
+ updateValue( valuePartial());
}
+ protected int valuePartial() {
+ return TopologyPredicateValue.UNKNOWN;
+ }
+
protected boolean intersectsExteriorOf(boolean isA) {
if (isA) {
return isIntersects(Location.EXTERIOR, Location.INTERIOR)
@@ -81,7 +96,9 @@ public int getDim(int locA, int locB) {
/**
* Finalizes the matrix by setting UNKNOWN values to appropriate values.
*/
+ @Override
public void finish() {
+ updateValue(valueIM());
//TODO: is this needed?
/*
for (int ia = 0; ia < 3; ia++) {
@@ -93,6 +110,8 @@ public void finish() {
*/
}
+ protected abstract boolean valueIM();
+
public String toString() {
return name() + ": " + intMatrix;
}
diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelateNG.java b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelateNG.java
index 114a833918..5e3720e183 100644
--- a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelateNG.java
+++ b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelateNG.java
@@ -35,10 +35,10 @@
* The algorithm used provides the following:
*
* - Efficient short-circuited evaluation of all predicates
- *
- Optimized evaluation of repeated predicates against a single geometry
- * (via cached spatial indexes)
+ *
- Optimized evaluation of repeated predicates against a single geometry
+ * via cached spatial indexes (AKA prepared mode)
*
- Robust computation (since only point-local topology is required)
- *
- FUTURE Support for {@link BoundaryNodeRule}
+ *
- Support for {@link BoundaryNodeRule}
*
- FUTURE Support for all GeometryCollection inputs, using union semantics
*
- FUTURE Support for a distance tolerance to compute approximate predicates
*
@@ -111,15 +111,14 @@ public boolean evaluate(TopologyPredicate predicate, Geometry inputB) {
int dimA = geomA.getDimension();
int dimB = geomB.getDimension();
- //-- check if predicate is determined by dimension
- int dimValue = predicate.valueDimensions(dimA, dimB);
- if (TopologyPredicateValue.isKnown(dimValue))
- return TopologyPredicateValue.toBoolean(dimValue);
+ //-- check if predicate is determined by dimension or envelope
+ predicate.init(dimA, dimB);
+ if (predicate.isKnown())
+ return predicate.value();
- //-- check if predicate is determined by envelopes
- int envValue = predicate.valueEnvelopes(geomA.getEnvelope(), inputB.getEnvelopeInternal());
- if (TopologyPredicateValue.isKnown(envValue))
- return TopologyPredicateValue.toBoolean(envValue);
+ predicate.init(geomA.getEnvelope(), inputB.getEnvelopeInternal());
+ if (predicate.isKnown())
+ return predicate.value();
TopologyBuilder topoBuilder = new TopologyBuilder(predicate, geomA, geomB);
diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelatePredicate.java b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelatePredicate.java
index 44e0f4941f..8b32f098c5 100644
--- a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelatePredicate.java
+++ b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelatePredicate.java
@@ -25,19 +25,16 @@ public RelatePredicate(String mask) {
public String name() { return "relate"; }
@Override
- public int valuePartial(int dimA, int dimB) {
- return TopologyPredicateValue.UNKNOWN;
- }
-
- @Override
- public boolean value(int dimA, int dimB) {
+ public boolean valueIM() {
if (mask == null)
return false;
boolean val = intMatrix.matches(mask);
+ /*
if (! val) {
- System.out.println( intMatrix + " does not equal mask " + mask);
+ System.out.println("DEBUG: " + intMatrix + " does not equal mask " + mask);
}
+ */
return val;
}
diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyBuilder.java b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyBuilder.java
index e01dbaaed3..c4e7f32a30 100644
--- a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyBuilder.java
+++ b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyBuilder.java
@@ -30,7 +30,6 @@ class TopologyBuilder {
private RelateGeometry geomB;
private int dimA;
private int dimB;
- private int predicateValue;
private Map nodeMap = new HashMap();
public TopologyBuilder(TopologyPredicate predicate, RelateGeometry geomA, RelateGeometry geomB) {
@@ -41,7 +40,6 @@ public TopologyBuilder(TopologyPredicate predicate, RelateGeometry geomA, Relate
this.dimB = geomB.getDimension();
initExteriorDims();
- predicateValue = predicate.valueDimensions(dimA, dimB);
}
/**
@@ -136,7 +134,6 @@ public boolean isSelfNodingRequired() {
*/
private boolean updateDim(int locA, int locB, int dimension) {
predicate.updateDim(locA, locB, dimension);
- predicateValue = predicate.valuePartial(dimA, dimB);
return true;
}
@@ -149,19 +146,18 @@ private boolean updateDim(boolean isAB, int loc1, int loc2, int dimension) {
}
public boolean isResultKnown() {
- return TopologyPredicateValue.isKnown(predicateValue);
+ return predicate.isKnown();
}
public boolean getResult() {
- return TopologyPredicateValue.toBoolean(predicateValue);
+ return predicate.value();
}
/**
- * Finalizes the matrix by setting UNKNOWN values to Dimension.FALSE (empty).
+ * Finalize the evaluation.
*/
public void finish() {
predicate.finish();
- predicateValue = TopologyPredicateValue.toValue(predicate.value(dimA, dimB));
}
private RelateNode getNode(Coordinate intPt) {
diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicate.java b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicate.java
index 46420f2913..4f8f704525 100644
--- a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicate.java
+++ b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicate.java
@@ -15,8 +15,7 @@
/**
* The API for strategy classes implementing
- * short-circuited spatial predicates based
- * on the DE-9IM topology model,
+ * short-circuited spatial predicates based on the DE-9IM topology model,
* evaluated by {@link RelateNG}.
*
* @author Martin Davis
@@ -43,6 +42,30 @@ default boolean isSelfNodingRequired() {
return true;
}
+ /**
+ * Initializes the predicate for a specific geometric case.
+ * This may allow the predicate result to become known
+ * if it can be inferred from the dimensions.
+ *
+ * @param dimA the dimension of geometry A
+ * @param dimB the dimension of geometry B
+ */
+ default void init(int dimA, int dimB) {
+
+ }
+
+ /**
+ * Initializes the predicate for a specific geometric case.
+ * This may allow the predicate result to become known
+ * if it can be inferred from the envelopes.
+ *
+ * @param envA the envelope of geometry A
+ * @param envB the envelope of geometry B
+ */
+ default void init(Envelope envA, Envelope envB) {
+
+ }
+
/**
* Updates the entry in the DE-9IM intersection matrix
* for given {@link Location}s in the input geometries.
@@ -61,25 +84,24 @@ default boolean isSelfNodingRequired() {
void updateDim(int locA, int locB, int dimension);
/**
- * Gets the predicate value if it can be determined
- * solely from the geometry dimensions.
+ * Tests if the predicate value is known.
*
- * @param dimA
- * @param dimB
- * @return
+ * @return true if the result is known
*/
- default int valueDimensions(int dimA, int dimB) {
- return TopologyPredicateValue.UNKNOWN;
- }
-
- default int valueEnvelopes(Envelope envA, Envelope envB) {
- return TopologyPredicateValue.UNKNOWN;
- }
-
- int valuePartial(int dimA, int dimB);
+ boolean isKnown();
+ /**
+ * Indicates that the value of the predicate can be finalized
+ * based on its current state.
+ */
void finish();
- boolean value(int dimA, int dimB);
+ /**
+ * Gets the current value of the predicate result.
+ * The value is only valid if {@link #isKnown()} is true.
+ *
+ * @return the predicate result value
+ */
+ boolean value();
}
diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicateFactory.java b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicateFactory.java
index 958bdc8888..9533a1bcf8 100644
--- a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicateFactory.java
+++ b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicateFactory.java
@@ -18,9 +18,14 @@
public class TopologyPredicateFactory {
public static TopologyPredicate intersects() {
- return new SimplePredicate() {
+ return new BasicPredicate() {
public String name() { return "intersects"; }
+
+ @Override
+ public void init(Envelope envA, Envelope envB) {
+ updateValue(requireIntersects(envA, envB));
+ }
@Override
public void updateDim(int locA, int locB, int dimension) {
@@ -28,11 +33,6 @@ public void updateDim(int locA, int locB, int dimension) {
updateValue(true);
}
}
-
- @Override
- public int valueEnvelopes(Envelope envA, Envelope envB) {
- return requireIntersects(envA, envB);
- }
@Override
public void finish() {
@@ -43,22 +43,22 @@ public void finish() {
}
public static TopologyPredicate disjoint() {
- return new SimplePredicate() {
+ return new BasicPredicate() {
public String name() { return "disjoint"; }
- @Override
+ @Override
+ public void init(Envelope envA, Envelope envB) {
+ updateValue(valueIf(true, envA.disjoint(envB)));
+ }
+
+ @Override
public void updateDim(int locA, int locB, int dimension) {
if (isIntersection(locA, locB)) {
updateValue(false);
}
}
- @Override
- public int valueEnvelopes(Envelope envA, Envelope envB) {
- return valueIf(true, envA.disjoint(envB));
- }
-
@Override
public void finish() {
//-- no intersecting locations have been found
@@ -74,102 +74,105 @@ public static TopologyPredicate contains() {
public String name() { return "contains"; }
@Override
- public int valueDimensions(int dimA, int dimB) {
- return require( isDimsCompatibleWithCovers(dimA, dimB) );
+ public void init(int dimA, int dimB) {
+ super.init(dimA, dimB);
+ updateValue( require( isDimsCompatibleWithCovers(dimA, dimB) ));
}
-
+
@Override
- public int valueEnvelopes(Envelope envA, Envelope envB) {
- return requireCovers(envA, envB);
+ public void init(Envelope envA, Envelope envB) {
+ updateValue(requireCovers(envA, envB));
}
@Override
- public int valuePartial(int dimA, int dimB) {
+ public int valuePartial() {
return valueIf( false, intersectsExteriorOf(RelateGeometry.GEOM_A));
}
@Override
- public boolean value(int dimA, int dimB) {
+ public boolean valueIM() {
return intMatrix.isContains();
}
};
}
- public static TopologyPredicate within() {
- return new IMPredicate() {
-
- public String name() { return "within"; }
-
- @Override
- public int valueDimensions(int dimA, int dimB) {
- return require( isDimsCompatibleWithCovers(dimB, dimA) );
- }
-
- @Override
- public int valueEnvelopes(Envelope envA, Envelope envB) {
- return requireCovers(envB, envA);
- }
-
- @Override
- public int valuePartial(int dimA, int dimB) {
- return valueIf( false, intersectsExteriorOf(RelateGeometry.GEOM_B));
- }
-
- @Override
- public boolean value(int dimA, int dimB) {
- return intMatrix.isWithin();
- }
- };
- }
-
public static TopologyPredicate covers() {
return new IMPredicate() {
public String name() { return "covers"; }
@Override
- public int valueDimensions(int dimA, int dimB) {
- return require(isDimsCompatibleWithCovers(dimA, dimB));
+ public void init(int dimA, int dimB) {
+ super.init(dimA, dimB);
+ updateValue( require( isDimsCompatibleWithCovers(dimA, dimB) ));
}
-
+
@Override
- public int valueEnvelopes(Envelope envA, Envelope envB) {
- return requireCovers(envA, envB);
+ public void init(Envelope envA, Envelope envB) {
+ updateValue(requireCovers(envA, envB));
}
@Override
- public int valuePartial(int dimA, int dimB) {
+ public int valuePartial() {
return valueIf( false, intersectsExteriorOf(RelateGeometry.GEOM_A));
}
@Override
- public boolean value(int dimA, int dimB) {
+ public boolean valueIM() {
return intMatrix.isCovers();
}
};
}
+
+ public static TopologyPredicate within() {
+ return new IMPredicate() {
+
+ public String name() { return "within"; }
+
+ @Override
+ public void init(int dimA, int dimB) {
+ super.init(dimA, dimB);
+ updateValue( require( isDimsCompatibleWithCovers(dimB, dimA) ));
+ }
+
+ @Override
+ public void init(Envelope envA, Envelope envB) {
+ updateValue(requireCovers(envB, envA));
+ }
+
+ @Override
+ public int valuePartial() {
+ return valueIf( false, intersectsExteriorOf(RelateGeometry.GEOM_B));
+ }
+
+ public boolean valueIM() {
+ return intMatrix.isWithin();
+ }
+ };
+ }
public static TopologyPredicate coveredBy() {
return new IMPredicate() {
public String name() { return "coveredBy"; }
@Override
- public int valueDimensions(int dimA, int dimB) {
- return require(isDimsCompatibleWithCovers(dimB, dimA));
+ public void init(int dimA, int dimB) {
+ super.init(dimA, dimB);
+ updateValue( require( isDimsCompatibleWithCovers(dimB, dimA) ));
}
-
+
@Override
- public int valueEnvelopes(Envelope envA, Envelope envB) {
- return requireCovers(envB, envA);
+ public void init(Envelope envA, Envelope envB) {
+ updateValue(requireCovers(envB, envA));
}
@Override
- public int valuePartial(int dimA, int dimB) {
+ public int valuePartial() {
return valueIf( false, intersectsExteriorOf(RelateGeometry.GEOM_B));
}
@Override
- public boolean value(int dimA, int dimB) {
+ public boolean valueIM() {
return intMatrix.isCoveredBy();
}
};
@@ -180,15 +183,15 @@ public static TopologyPredicate crosses() {
public String name() { return "crosses"; }
@Override
- public int valueDimensions(int dimA, int dimB) {
- //-- value is FALSE for P/P and A/A situations
+ public void init(int dimA, int dimB) {
+ super.init(dimA, dimB);
boolean isBothPointsOrAreas = (dimA == Dimension.P && dimB == Dimension.P)
|| (dimA == Dimension.A && dimB == Dimension.A);
- return require(! isBothPointsOrAreas);
+ updateValue( require(! isBothPointsOrAreas));
}
@Override
- public int valuePartial(int dimA, int dimB) {
+ public int valuePartial() {
if (dimA == Dimension.L && dimB == Dimension.L) {
//-- L/L interaction can only be dim = 0
if (getDim(Location.INTERIOR, Location.INTERIOR) > Dimension.P)
@@ -210,7 +213,7 @@ && isIntersects(Location.EXTERIOR, Location.INTERIOR)) {
}
@Override
- public boolean value(int dimA, int dimB) {
+ public boolean valueIM() {
return intMatrix.isCrosses(dimA, dimB);
}
};
@@ -221,17 +224,18 @@ public static TopologyPredicate equalsTopo() {
public String name() { return "equals"; }
@Override
- public int valueDimensions(int dimA, int dimB) {
- return require(dimA == dimB);
+ public void init(int dimA, int dimB) {
+ super.init(dimA, dimB);
+ updateValue( require(dimA == dimB));
}
-
+
@Override
- public int valueEnvelopes(Envelope envA, Envelope envB) {
- return requireEquals(envA, envB);
+ public void init(Envelope envA, Envelope envB) {
+ updateValue( requireEquals(envA, envB));
}
@Override
- public int valuePartial(int dimA, int dimB) {
+ public int valuePartial() {
boolean isEitherExteriorIntersects =
isIntersects(Location.INTERIOR, Location.EXTERIOR)
|| isIntersects(Location.BOUNDARY, Location.EXTERIOR)
@@ -242,7 +246,7 @@ public int valuePartial(int dimA, int dimB) {
}
@Override
- public boolean value(int dimA, int dimB) {
+ public boolean valueIM() {
return intMatrix.isEquals(dimA, dimB);
}
};
@@ -253,12 +257,13 @@ public static TopologyPredicate overlaps() {
public String name() { return "overlaps"; }
@Override
- public int valueDimensions(int dimA, int dimB) {
- return require( dimA == dimB );
+ public void init(int dimA, int dimB) {
+ super.init(dimA, dimB);
+ updateValue( require(dimA == dimB));
}
@Override
- public int valuePartial(int dimA, int dimB) {
+ public int valuePartial() {
if (dimA == Dimension.A || dimA == Dimension.P) {
if (isIntersects(Location.INTERIOR, Location.INTERIOR)
&& isIntersects(Location.INTERIOR, Location.EXTERIOR)
@@ -275,7 +280,7 @@ && isIntersects(Location.EXTERIOR, Location.INTERIOR))
}
@Override
- public boolean value(int dimA, int dimB) {
+ public boolean valueIM() {
return intMatrix.isOverlaps(dimA, dimB);
}
};
@@ -286,21 +291,22 @@ public static TopologyPredicate touches() {
public String name() { return "touches"; }
@Override
- public int valueDimensions(int dimA, int dimB) {
+ public void init(int dimA, int dimB) {
+ super.init(dimA, dimB);
//-- Points have only interiors, so cannot touch
boolean isBothPoints = dimA == 0 && dimB == 0;
- return require(! isBothPoints);
+ updateValue( require(! isBothPoints));
}
@Override
- public int valuePartial(int dimA, int dimB) {
+ public int valuePartial() {
//-- for touches interiors cannot intersect
boolean isInteriorsIntersects = isIntersects(Location.INTERIOR, Location.INTERIOR);
return valueIf(false, isInteriorsIntersects);
}
@Override
- public boolean value(int dimA, int dimB) {
+ public boolean valueIM() {
return intMatrix.isTouches(dimA, dimB);
}
};
diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicateTracer.java b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicateTracer.java
index 7101ba7708..52d82085d4 100644
--- a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicateTracer.java
+++ b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/TopologyPredicateTracer.java
@@ -24,31 +24,32 @@ public class TopologyPredicateTracer implements TopologyPredicate {
public String name() { return pred.name(); }
+ @Override
+ public void init(int dimA, int dimB) {
+ pred.init(dimA, dimB);
+ checkValue("dimensions");
+ }
+
+ @Override
+ public void init(Envelope envA, Envelope envB) {
+ pred.init(envA, envB);
+ checkValue("envelopes");
+ }
+
@Override
public void updateDim(int locA, int locB, int dimension) {
System.out.println("A:" + Location.toLocationSymbol(locA)
+ "/B:" + Location.toLocationSymbol(locB)
+ " -> " + dimension);
pred.updateDim(locA, locB, dimension);
+ checkValue("IM entry");
}
- @Override
- public int valueDimensions(int dimA, int dimB) {
- return pred.valueDimensions(dimA, dimB);
- }
-
- @Override
- public int valueEnvelopes(Envelope envA, Envelope envB) {
- return pred.valueEnvelopes(envA, envB);
- }
-
- @Override
- public int valuePartial(int dimA, int dimB) {
- int val = pred.valuePartial(dimA, dimB);
- if (TopologyPredicateValue.isKnown(val)) {
- System.out.println(name() + " = " + TopologyPredicateValue.toBoolean(val));
+ private void checkValue(String source) {
+ if (pred.isKnown()) {
+ System.out.println(name() + " = " + pred.value()
+ + " based on " + source);
}
- return val;
}
@Override
@@ -57,11 +58,17 @@ public void finish() {
}
@Override
- public boolean value(int dimA, int dimB) {
- return pred.value(dimA, dimB);
+ public boolean isKnown() {
+ return pred.isKnown();
+ }
+
+ @Override
+ public boolean value() {
+ return pred.value();
}
public String toString() {
return pred.toString();
}
+
}