Skip to content

Commit

Permalink
Initial commit for RelateNG
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Move to src dir

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

More code

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Get it running

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Fix conflict

Add more short-circuits

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Refactoring, add collinear intersection

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Add license, more predicates

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Renaming

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Renaming, fix L/L short-circuit

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

WIP - more detailed intersection info

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Add geometry dimensionality checks

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Add LinearBoundary

Signed-off-by: Martin Davis <mtnclimb@gmail.com>

Add header, fix imports

Rename IMPredicate methods

Refactoring

Refactoring

remove dead code

Refactor predicate model

Refactor predicate model

refactoring

Add point support

Simplify builder logic

Fix proper intersection logic

renaming

code reorg

Refactoring

refactoring

Fix order of EdgeIntersector comparison

Add AreaArea crossing test

Enhance PolygonNodeTopology to handle collinear

Add predicates

Javadoc

Add node edge handling

cleanup

fix imports

Add node evaluation

Various improvements

Various improvements

Fix touches bug

Renaming, fixes

Renaming, fixes

Improve tests

Improve perf test

Add PredicateTracer

Add short-circuit

Fix some bugs

Renaming

Refactoring

refactoring

Avoid check for empty element

Fix area-vertex evaluation

Remove unused import

Renaming

Renames

Renames

Refactor constants

Renaming, refactoring

refactoring

refactoring

rename TopologyPredicateValue

renaming

use constant

renaming

initial commit for self-noding

Add RelateNG functions

Refactor addAreaEdge

Add AB geometry edge intersection test

formatting

Refactoring to simplify

Remove single-call method

Refactoring

Renaming

various improvements

typo in comment

various improvements

refactoring

Chg addEdge method sig

Fix unit test

Improve tracing output

Finish self-intersection handling

Expose constants

javadoc, refactoring

Switch to HPRtree

Refactoring

formatting

Refactor predicate logic functions

Improve predicate logic shortcut methods

simplify code

add method

improve msg

add tests

Add relate function

Various fixes

change evaluation order

Rework point topology evaluation

refactoring

refactoring

javadoc, renaming

remove dead code

Improve relate predicate code

improve internal API

rename TopologyPredicate.value

add perf tests

refactoring

Add BoundaryNodeRule support
  • Loading branch information
dr-jts committed Jan 19, 2024
1 parent a790382 commit a782580
Show file tree
Hide file tree
Showing 30 changed files with 4,071 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,23 @@
import java.util.List;

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.prep.PreparedGeometry;
import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
import org.locationtech.jts.operation.distance.IndexedFacetDistance;



public class SelectionFunctions
{

public static Geometry intersectsPrep(Geometry a, final Geometry mask)
{
PreparedGeometry prep = PreparedGeometryFactory.prepare(mask);
return select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return prep.intersects(g);
}
});
}

public static Geometry intersects(Geometry a, final Geometry mask)
{
return select(a, new GeometryPredicate() {
Expand All @@ -35,7 +46,17 @@ public static Geometry covers(Geometry a, final Geometry mask)
{
return select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return g.covers(mask);
return mask.covers(g);
}
});
}

public static Geometry coversPrep(Geometry a, final Geometry mask)
{
PreparedGeometry prep = PreparedGeometryFactory.prepare(mask);
return select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return prep.covers(g);
}
});
}
Expand Down Expand Up @@ -168,7 +189,7 @@ public boolean isTrue(Geometry g) {
});
}

private static Geometry select(Geometry geom, GeometryPredicate pred)
public static Geometry select(Geometry geom, GeometryPredicate pred)
{
List selected = new ArrayList();
for (int i = 0; i < geom.getNumGeometries(); i++ ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2024 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/

package org.locationtech.jtstest.function;

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.operation.relateng.RelateNG;
import org.locationtech.jts.operation.relateng.TopologyPredicateFactory;

public class SelectionNGFunctions
{
public static Geometry intersectsNG(Geometry a, final Geometry mask)
{
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return RelateNG.evaluate(TopologyPredicateFactory.intersects(), mask, g);
}
});
}

public static Geometry intersectsNGPrep(Geometry a, final Geometry mask)
{
RelateNG relateNG = new RelateNG(mask, true);
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return relateNG.evaluate(TopologyPredicateFactory.intersects(), g);
}
});
}

public static Geometry coversNG(Geometry a, final Geometry mask)
{
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return RelateNG.evaluate(TopologyPredicateFactory.covers(), mask, g);
}
});
}

public static Geometry coversNGPrep(Geometry a, final Geometry mask)
{
RelateNG relateNG = new RelateNG(mask, true);
return SelectionFunctions.select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return relateNG.evaluate(TopologyPredicateFactory.covers(), g);
}
});
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2023 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jtstest.function;

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.operation.relateng.RelateNG;
import org.locationtech.jts.operation.relateng.TopologyPredicateFactory;

public class SpatialPredicateNGFunctions {
public static boolean contains(Geometry a, Geometry b) {
return RelateNG.evaluate(TopologyPredicateFactory.contains(), a, b);
}
public static boolean covers(Geometry a, Geometry b) {
return RelateNG.evaluate(TopologyPredicateFactory.covers(), a, b);
}
public static boolean coveredBy(Geometry a, Geometry b) {
return RelateNG.evaluate(TopologyPredicateFactory.coveredBy(), a, b);
}
public static boolean disjoint(Geometry a, Geometry b) {
return RelateNG.evaluate(TopologyPredicateFactory.disjoint(), a, b);
}
public static boolean equals(Geometry a, Geometry b) {
return RelateNG.evaluate(TopologyPredicateFactory.equalsTopo(), a, b);
}
public static boolean intersects(Geometry a, Geometry b) {
return RelateNG.evaluate(TopologyPredicateFactory.intersects(), a, b);
}
public static boolean overlaps(Geometry a, Geometry b) {
return RelateNG.evaluate(TopologyPredicateFactory.overlaps(), a, b);
}
public static boolean touches(Geometry a, Geometry b) {
return RelateNG.evaluate(TopologyPredicateFactory.touches(), a, b);
}
public static boolean within(Geometry a, Geometry b) {
return RelateNG.evaluate(TopologyPredicateFactory.within(), a, b);
}
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).toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@
import org.locationtech.jtstest.function.PrecisionFunctions;
import org.locationtech.jtstest.function.PreparedGeometryFunctions;
import org.locationtech.jtstest.function.SelectionFunctions;
import org.locationtech.jtstest.function.SelectionNGFunctions;
import org.locationtech.jtstest.function.SimplificationFunctions;
import org.locationtech.jtstest.function.SnappingFunctions;
import org.locationtech.jtstest.function.SortingFunctions;
import org.locationtech.jtstest.function.SpatialIndexFunctions;
import org.locationtech.jtstest.function.SpatialPredicateFunctions;
import org.locationtech.jtstest.function.SpatialPredicateNGFunctions;
import org.locationtech.jtstest.function.TriangleFunctions;
import org.locationtech.jtstest.function.TriangulatePolyFunctions;
import org.locationtech.jtstest.function.TriangulationFunctions;
Expand Down Expand Up @@ -102,6 +104,7 @@ public static GeometryFunctionRegistry createTestBuilderRegistry()
funcRegistry.add(PrecisionFunctions.class);
funcRegistry.add(PreparedGeometryFunctions.class);
funcRegistry.add(SelectionFunctions.class);
funcRegistry.add(SelectionNGFunctions.class);
funcRegistry.add(SimplificationFunctions.class);
funcRegistry.add(AffineTransformationFunctions.class);
funcRegistry.add(DiffFunctions.class);
Expand All @@ -112,6 +115,7 @@ public static GeometryFunctionRegistry createTestBuilderRegistry()
funcRegistry.add(CreateRandomShapeFunctions.class);
funcRegistry.add(SpatialIndexFunctions.class);
funcRegistry.add(SpatialPredicateFunctions.class);
funcRegistry.add(SpatialPredicateNGFunctions.class);
funcRegistry.add(JTSFunctions.class);
//funcRegistry.add(MemoryFunctions.class);
funcRegistry.add(OffsetCurveFunctions.class);
Expand Down
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,7 +26,7 @@ public class PolygonNodeTopology
/**
* Check if the segments at a node between two rings (or one ring) cross.
* The node is topologically valid if the rings do not cross.
* This function assumes that the segments are not collinear.
* If any segments are collinear, the test returns false.
*
* @param nodePt the node location
* @param a0 the previous segment endpoint in a ring
Expand All @@ -42,14 +42,24 @@ public static boolean isCrossing(Coordinate nodePt, Coordinate a0, Coordinate a1
aLo = a1;
aHi = a0;
}
/**
* Find positions of b0 and b1.
* If they are the same they do not cross the other edge
*/
/*
boolean isBetween0 = isBetween(nodePt, b0, aLo, aHi);
boolean isBetween1 = isBetween(nodePt, b1, aLo, aHi);
return isBetween0 != isBetween1;
*/

/**
* Find positions of b0 and b1.
* The edges cross if the positions are different.
* If any edge is collinear they are reported as not crossing
*/
int compBetween0 = compareBetween(nodePt, b0, aLo, aHi);
if (compBetween0 == 0) return false;
int compBetween1 = compareBetween(nodePt, b1, aLo, aHi);
if (compBetween1 == 0) return false;

return compBetween0 != compBetween1;
}

/**
Expand Down Expand Up @@ -99,6 +109,28 @@ private static boolean isBetween(Coordinate origin, Coordinate p, Coordinate e0,
return ! isGreater1;
}

/**
* Compares whether an edge p is between or outside the edges e0 and e1,
* where the edges all originate at a common origin.
* The "inside" of e0 and e1 is the arc which does not include
* the positive X-axis at the origin.
* If p is collinear with an edge 0 is returned.
*
* @param origin the origin
* @param p the destination point of edge p
* @param e0 the destination point of edge e0
* @param e1 the destination point of edge e1
* @return a negative integer, zero or positive integer as the vector P lies outside, collinear with, or inside the vectors E0 and E1
*/
private static int compareBetween(Coordinate origin, Coordinate p, Coordinate e0, Coordinate e1) {
int comp0 = compareAngle(origin, p, e0);
if (comp0 == 0) return 0;
int comp1 = compareAngle(origin, p, e1);
if (comp1 == 0) return 0;
if (comp0 > 0 && comp1 < 0) return 1;
return -1;
}

/**
* Tests if the angle with the origin of a vector P is greater than that of the
* vector Q.
Expand Down Expand Up @@ -126,6 +158,37 @@ private static boolean isAngleGreater(Coordinate origin, Coordinate p, Coordinat
return orient == Orientation.COUNTERCLOCKWISE;
}

/**
* Compares the angles of two vectors
* relative to the positive X-axis at their origin.
*
* @param origin the origin of the vectors
* @param p the endpoint of the vector P
* @param q the endpoint of the vector Q
* @return a negative integer, zero, or a positive integer as this vector P has angle less than, equal to, or greater than vector Q
*/
public static int compareAngle(Coordinate origin, Coordinate p, Coordinate q) {
int quadrantP = quadrant(origin, p);
int quadrantQ = quadrant(origin, q);

/**
* If the vectors are in different quadrants,
* that determines the ordering
*/
if (quadrantP > quadrantQ) return 1;
if (quadrantP < quadrantQ) return -1;

//--- vectors are in the same quadrant
// Check relative orientation of vectors
// P > Q if it is CCW of Q
int orient = Orientation.index(origin, q, p);
switch (orient) {
case Orientation.COUNTERCLOCKWISE: return 1;
case Orientation.CLOCKWISE: return -1;
default: return 0;
}
}

private static int quadrant(Coordinate origin, Coordinate p) {
double dx = p.getX() - origin.getX();
double dy = p.getY() - origin.getY();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,41 @@ public static Geometry toGeometry(Collection segStrings, GeometryFactory geomFac
return geomFact.createMultiLineString(lines);
}

/**
* Gets the previous vertex in a ring from a vertex index.
*
* @param ringSS a segment string forming a ring
* @param index the vertex index
* @return the previous vertex in the ring
*/
public static Coordinate prevInRing(SegmentString ringSS, int index) {
int prevIndex = index - 1;
if (prevIndex < 0) {
prevIndex = ringSS.size() - 2;
}
return ringSS.getCoordinate( prevIndex );
}

/**
* Gets the next vertex in a ring from a vertex index.
*
* @param ringSS a segment string forming a ring
* @param index the vertex index
* @return the next vertex in the ring
*/
public static Coordinate nextInRing(SegmentString ringSS, int index) {
int nextIndex = index + 1;
if (nextIndex > ringSS.size() - 1) {
nextIndex = 1;
}
return ringSS.getCoordinate( nextIndex );
}

public static boolean isRing(SegmentString ss) {
if (ss.size() <= 1) return false;
return ss.getCoordinate(0).equals2D(ss.getCoordinate(ss.size() - 1));
}

public static String toString(List segStrings)
{
StringBuffer buf = new StringBuffer();
Expand Down
Loading

0 comments on commit a782580

Please sign in to comment.