Skip to content

Commit

Permalink
Add RelateNG envelope interaction/covers optimizations (#1055)
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-jts committed Jun 1, 2024
1 parent c1c8310 commit a9baaba
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ public boolean isTrue(Geometry g) {
});
}

public static Geometry touches(Geometry a, final Geometry mask)
{
return select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return mask.touches(g);
}
});
}

public static Geometry disjoint(Geometry a, Geometry mask)
{
List selected = new ArrayList();
Expand All @@ -81,6 +90,16 @@ public static Geometry disjoint(Geometry a, Geometry mask)
}
return a.getFactory().buildGeometry(selected);
}

public static Geometry relatePattern(Geometry a, final Geometry mask, String pattern)
{
return select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return mask.relate(g, pattern);
}
});
}

public static Geometry valid(Geometry a)
{
List selected = new ArrayList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

package org.locationtech.jtstest.function;

import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.operation.relateng.IntersectionMatrixPattern;
import org.locationtech.jts.operation.relateng.RelateNG;
Expand All @@ -31,9 +32,12 @@ public boolean isTrue(Geometry g) {
public static Geometry intersectsPrep(Geometry a, final Geometry mask)
{
RelateNG relateNG = RelateNG.prepare(mask);
Envelope maskEnv = mask.getEnvelopeInternal();
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return relateNG.evaluate(g, RelatePredicate.intersects());
if (maskEnv.disjoint(g.getEnvelopeInternal()))
return false;
return relateNG.evaluate(g, RelatePredicate.intersects());
}
});
}
Expand All @@ -59,13 +63,38 @@ public boolean isTrue(Geometry g) {
public static Geometry coversPrep(Geometry a, final Geometry mask)
{
RelateNG relateNG = RelateNG.prepare(mask);
Envelope maskEnv = mask.getEnvelopeInternal();
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
if (maskEnv.disjoint(g.getEnvelopeInternal()))
return false;
return relateNG.evaluate(g, RelatePredicate.covers());
}
});
}

public static Geometry touches(Geometry a, final Geometry mask)
{
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return RelateNG.relate(mask, g, RelatePredicate.touches());
}
});
}

public static Geometry touchesPrep(Geometry a, final Geometry mask)
{
RelateNG relateNG = RelateNG.prepare(mask);
Envelope maskEnv = mask.getEnvelopeInternal();
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
if (maskEnv.disjoint(g.getEnvelopeInternal()))
return false;
return relateNG.evaluate(g, RelatePredicate.touches());
}
});
}

public static Geometry adjacent(Geometry a, final Geometry mask)
{
return SelectionFunctions.select(a, new GeometryPredicate() {
Expand All @@ -84,6 +113,26 @@ public boolean isTrue(Geometry g) {
}
});
}

public static Geometry relatePattern(Geometry a, final Geometry mask, String pattern)
{
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return RelateNG.relate(mask, g, RelatePredicate.matches(pattern));
}
});
}

public static Geometry relatePatternPrep(Geometry a, final Geometry mask, String pattern)
{
RelateNG relateNG = RelateNG.prepare(mask);
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return relateNG.evaluate(g, RelatePredicate.matches(pattern));
}
});
}

}


Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,26 @@ public IMPatternMatcher(String imPattern) {
public void init(Envelope envA, Envelope envB) {
super.init(dimA, dimB);
//-- if pattern specifies any non-E/non-E interaction, envelopes must not be disjoint
boolean requiresInteraction = requiresInteraction(patternMatrix);
boolean requiresInteraction = requireInteraction(patternMatrix);
boolean isDisjoint = envA.disjoint(envB);
setValueIf(false, requiresInteraction && isDisjoint);
}

private static boolean requiresInteraction(IntersectionMatrix im) {
@Override
public boolean requireInteraction() {
return requireInteraction(patternMatrix);
}

private static boolean requireInteraction(IntersectionMatrix im) {
boolean requiresInteraction =
requiresInteraction(im.get(Location.INTERIOR, Location.INTERIOR))
|| requiresInteraction(im.get(Location.INTERIOR, Location.BOUNDARY))
|| requiresInteraction(im.get(Location.BOUNDARY, Location.INTERIOR))
|| requiresInteraction(im.get(Location.BOUNDARY, Location.BOUNDARY));
isInteraction(im.get(Location.INTERIOR, Location.INTERIOR))
|| isInteraction(im.get(Location.INTERIOR, Location.BOUNDARY))
|| isInteraction(im.get(Location.BOUNDARY, Location.INTERIOR))
|| isInteraction(im.get(Location.BOUNDARY, Location.BOUNDARY));
return requiresInteraction;
}

private static boolean requiresInteraction(int imDim) {
private static boolean isInteraction(int imDim) {
return imDim == Dimension.TRUE || imDim >= Dimension.P;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ public boolean isDimChanged(int locA, int locB, int dimension) {
*/
protected abstract boolean isDetermined();

/**
* Tests whether the exterior of the specified input geometry
* is intersected by any part of the other input.
*
* @param isA the input geometry
* @return true if the input geometry exterior is intersected
*/
protected boolean intersectsExteriorOf(boolean isA) {
if (isA) {
return isIntersects(Location.EXTERIOR, Location.INTERIOR)
Expand Down Expand Up @@ -112,6 +119,12 @@ public void finish() {
setValue(valueIM());
}

/**
* Gets the value of the predicate according to the current
* intersection matrix state.
*
* @return the current predicate value
*/
protected abstract boolean valueIM();

public String toString() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static String name(boolean isA) {
private Geometry geom;
private boolean isPrepared = false;

private Envelope geomEnv;
private int geomDim = Dimension.FALSE;
private Set<Coordinate> uniquePoints;
private BoundaryNodeRule boundaryNodeRule;
Expand All @@ -69,6 +70,7 @@ public RelateGeometry(Geometry input, BoundaryNodeRule bnRule) {

public RelateGeometry(Geometry input, boolean isPrepared, BoundaryNodeRule bnRule) {
this.geom = input;
this.geomEnv = input.getEnvelopeInternal();
this.isPrepared = isPrepared;
this.boundaryNodeRule = bnRule;
//-- cache geometry metadata
Expand Down Expand Up @@ -160,7 +162,7 @@ public boolean isPrepared() {
}

public Envelope getEnvelope() {
return geom.getEnvelopeInternal();
return geomEnv;
}

public int getDimension() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ public RelateMatrixPredicate() {

public String name() { return "relateMatrix"; }

@Override
public boolean requireInteraction() {
//-- ensure entire matrix is computed
return false;
}

@Override
public boolean isDetermined() {
//-- ensure entire matrix is computed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,11 @@ public boolean evaluate(Geometry b, String imPattern) {
* @return true if the predicate is satisfied
*/
public boolean evaluate(Geometry b, TopologyPredicate predicate) {

//-- fast envelope checks
if (! hasRequiredEnvelopeInteraction(b, predicate)) {
return false;
}

RelateGeometry geomB = new RelateGeometry(b, boundaryNodeRule);

if (geomA.isEmpty() && geomB.isEmpty()) {
Expand Down Expand Up @@ -267,6 +271,29 @@ public boolean evaluate(Geometry b, TopologyPredicate predicate) {
return topoComputer.getResult();
}

private boolean hasRequiredEnvelopeInteraction(Geometry b, TopologyPredicate predicate) {
Envelope envB = b.getEnvelopeInternal();
boolean isInteracts = false;
if (predicate.requireCovers(GEOM_A)) {
if (! geomA.getEnvelope().covers(envB)) {
return false;
}
isInteracts = true;
}
else if (predicate.requireCovers(GEOM_B)) {
if (! envB.covers(geomA.getEnvelope())) {
return false;
}
isInteracts = true;
}
if (! isInteracts
&& predicate.requireInteraction()
&& ! geomA.getEnvelope().intersects(envB)) {
return false;
}
return true;
}

private boolean finishValue(TopologyPredicate predicate) {
predicate.finish();
return predicate.value();
Expand Down Expand Up @@ -323,8 +350,8 @@ private void computeAtPoints(RelateGeometry geom, boolean isA,
* for the intersects predicate (since these are checked
* during segment/segment intersection checking anyway).
* Checking points against areas is necessary, since the input
* linework may be entirely disjoint if one input lies wholly
* inside an area.
* linework is disjoint if one input lies wholly inside an area,
* so segment intersection checking is not sufficient.
*/
boolean checkDisjointPoints = geomTarget.hasDimension(Dimension.A)
|| topoComputer.isExteriorCheckRequired(isA);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ public static TopologyPredicate intersects() {
public String name() { return "intersects"; }

@Override
public boolean requiresSelfNoding() {
public boolean requireSelfNoding() {
//-- self-noding is not required to check for a simple interaction
return false;
}

@Override
public boolean requiresExteriorCheck(boolean isSourceA) {
public boolean requireExteriorCheck(boolean isSourceA) {
//-- intersects only requires testing interaction
return false;
}
Expand Down Expand Up @@ -104,13 +104,19 @@ public static TopologyPredicate disjoint() {
public String name() { return "disjoint"; }

@Override
public boolean requiresSelfNoding() {
public boolean requireSelfNoding() {
//-- self-noding is not required to check for a simple interaction
return false;
}


@Override
public boolean requireInteraction() {
//-- ensure entire matrix is computed
return false;
}

@Override
public boolean requiresExteriorCheck(boolean isSourceA) {
public boolean requireExteriorCheck(boolean isSourceA) {
//-- disjoint only requires testing interaction
return false;
}
Expand Down Expand Up @@ -164,7 +170,12 @@ public static TopologyPredicate contains() {
public String name() { return "contains"; }

@Override
public boolean requiresExteriorCheck(boolean isSourceA) {
public boolean requireCovers(boolean isSourceA) {
return isSourceA == RelateGeometry.GEOM_A;
}

@Override
public boolean requireExteriorCheck(boolean isSourceA) {
//-- only need to check B against Exterior of A
return isSourceA == RelateGeometry.GEOM_B;
}
Expand Down Expand Up @@ -222,7 +233,12 @@ public static TopologyPredicate within() {
public String name() { return "within"; }

@Override
public boolean requiresExteriorCheck(boolean isSourceA) {
public boolean requireCovers(boolean isSourceA) {
return isSourceA == RelateGeometry.GEOM_B;
}

@Override
public boolean requireExteriorCheck(boolean isSourceA) {
//-- only need to check A against Exterior of B
return isSourceA == RelateGeometry.GEOM_A;
}
Expand Down Expand Up @@ -286,7 +302,12 @@ public static TopologyPredicate covers() {
public String name() { return "covers"; }

@Override
public boolean requiresExteriorCheck(boolean isSourceA) {
public boolean requireCovers(boolean isSourceA) {
return isSourceA == RelateGeometry.GEOM_A;
}

@Override
public boolean requireExteriorCheck(boolean isSourceA) {
//-- only need to check B against Exterior of A
return isSourceA == RelateGeometry.GEOM_B;
}
Expand Down Expand Up @@ -345,7 +366,12 @@ public static TopologyPredicate coveredBy() {
public String name() { return "coveredBy"; }

@Override
public boolean requiresExteriorCheck(boolean isSourceA) {
public boolean requireCovers(boolean isSourceA) {
return isSourceA == RelateGeometry.GEOM_B;
}

@Override
public boolean requireExteriorCheck(boolean isSourceA) {
//-- only need to check A against Exterior of B
return isSourceA == RelateGeometry.GEOM_A;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ public boolean isSelfNodingRequired() {
//TODO: change to testing for lines or GC with > 1 polygon
if (geomA.isPointsOrPolygons()) return false;
if (geomB.isPointsOrPolygons()) return false;
return predicate.requiresSelfNoding();
return predicate.requireSelfNoding();
}

public boolean isExteriorCheckRequired(boolean isA) {
return predicate.requiresExteriorCheck(isA);
return predicate.requireExteriorCheck(isA);
}

private void updateDim(int locA, int locB, int dimension) {
Expand Down
Loading

0 comments on commit a9baaba

Please sign in to comment.