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 <[email protected]>

Move to src dir

Signed-off-by: Martin Davis <[email protected]>

More code

Signed-off-by: Martin Davis <[email protected]>

Get it running

Signed-off-by: Martin Davis <[email protected]>

Fix conflict

Add more short-circuits

Signed-off-by: Martin Davis <[email protected]>

Refactoring, add collinear intersection

Signed-off-by: Martin Davis <[email protected]>

Add license, more predicates

Signed-off-by: Martin Davis <[email protected]>

Renaming

Signed-off-by: Martin Davis <[email protected]>

Renaming, fix L/L short-circuit

Signed-off-by: Martin Davis <[email protected]>

WIP - more detailed intersection info

Signed-off-by: Martin Davis <[email protected]>

Add geometry dimensionality checks

Signed-off-by: Martin Davis <[email protected]>

Add LinearBoundary

Signed-off-by: Martin Davis <[email protected]>

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

Fix SegmentString method usage

Align with master

Align with master
  • Loading branch information
dr-jts committed Jan 19, 2024
1 parent 7b1395b commit 5e1e44c
Show file tree
Hide file tree
Showing 25 changed files with 3,917 additions and 0 deletions.
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
@@ -0,0 +1,207 @@
/*
* Copyright (c) 2022 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.jts.operation.relateng;

import org.locationtech.jts.algorithm.RobustLineIntersector;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.noding.SegmentIntersector;
import org.locationtech.jts.noding.SegmentString;
import org.locationtech.jts.noding.SegmentStringUtil;

public class EdgeSegmentIntersector implements SegmentIntersector
{
private RobustLineIntersector li = new RobustLineIntersector();
private TopologyBuilder topoBuilder;

public EdgeSegmentIntersector(TopologyBuilder topoBuilder) {
this.topoBuilder = topoBuilder;
}

@Override
public boolean isDone() {
return topoBuilder.isResultKnown();
}

public void processIntersections(SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1) {
if (isAB(ss0, ss1)) {
if (isA(ss0)) {
processIntersectionAB(ss0, segIndex0, ss1, segIndex1);
}
else {
processIntersectionAB(ss1, segIndex1, ss0, segIndex0);
}
}
else {
processSelfIntersection(ss0, segIndex0, ss1, segIndex1);
}
}

private void processSelfIntersection(SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1) {
// don't intersect a segment with itself
if (ss0 == ss1 && segIndex0 == segIndex1) return;

//TODO: skip intersections between adjacent segments?

Coordinate p00 = ss0.getCoordinate(segIndex0);
Coordinate p01 = ss0.getCoordinate(segIndex0 + 1);
Coordinate p10 = ss1.getCoordinate(segIndex1);
Coordinate p11 = ss1.getCoordinate(segIndex1 + 1);

li.computeIntersection(p00, p01, p10, p11);

if (! li.hasIntersection())
return;

boolean isA = isA(ss0);
if (li.getIntersectionNum() == 2) {
//-- intersection is collinear
topoBuilder.addSelfIntersectionCollinear(isA, p00, p01, p10, p11, li.getIntersection(0), li.getIntersection(1));
}
else if (li.isProper()) {
//-- intersection is proper
topoBuilder.addSelfIntersectionProper(isA, p00, p01, p10, p11, li.getIntersection(0));
}
else {
//-- non-proper intersection (at least one segment intersects at endpoint)
addSelfIntersectionNonProper(isA, ss0, segIndex0, ss1, segIndex1, li.getIntersection(0));
}
}

private boolean isAB(SegmentString ss0, SegmentString ss1) {
RelateGeometry geom0 = (RelateGeometry) ss0.getData();
RelateGeometry geom1 = (RelateGeometry) ss1.getData();
return geom0 != geom1;
}

private boolean isA(SegmentString ss0) {
return topoBuilder.isA((RelateGeometry) ss0.getData());
}

private void processIntersectionAB(SegmentString ssA, int segIndexA, SegmentString ssB, int segIndexB) {

Coordinate a0 = ssA.getCoordinate(segIndexA);
Coordinate a1 = ssA.getCoordinate(segIndexA + 1);
Coordinate b0 = ssB.getCoordinate(segIndexB);
Coordinate b1 = ssB.getCoordinate(segIndexB + 1);

li.computeIntersection(a0, a1, b0, b1);

if (! li.hasIntersection())
return;

if (li.getIntersectionNum() == 2) {
//-- intersection is collinear
topoBuilder.addIntersectionCollinear(a0, a1, b0, b1, li.getIntersection(0), li.getIntersection(1));
}
else if (li.isProper()) {
//-- intersection is proper
topoBuilder.addIntersectionProper(a0, a1, b0, b1, li.getIntersection(0));
}
else {
//-- non-proper intersection (at least one segment intersects at endpoint)
addIntersectionNonProper(ssA, segIndexA, ssB, segIndexB, li.getIntersection(0));
}
}

private void addSelfIntersectionNonProper(boolean isA, SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1, Coordinate intPt) {
//-- this handles A/L, L/A, and L/L
addIntersectionEdges(isA, ss0, segIndex0, intPt);
addIntersectionEdges(isA, ss1, segIndex1, intPt);
}

private void addIntersectionNonProper(SegmentString ssA, int segIndexA, SegmentString ssB, int segIndexB, Coordinate intPt) {
if (topoBuilder.isAreaArea()) {
addNonProperAreaArea(ssA, segIndexA, ssB, segIndexB, intPt);
return;
}
//-- this handles A/L, L/A, and L/L
addIntersectionEdges(RelateGeometry.GEOM_A, ssA, segIndexA, intPt);
addIntersectionEdges(RelateGeometry.GEOM_B, ssB, segIndexB, intPt);

//TODO: more logic required?
//TODO: move to topoBuilder
topoBuilder.addEdgeIntersectionNode(intPt);
}

private void addIntersectionEdges(boolean isA, SegmentString ss, int segIndex, Coordinate intPt) {
Coordinate nextVertex = nextVertex(ss, segIndex, intPt);
topoBuilder.addEdge(isA, intPt, nextVertex, RelateEdge.IS_FORWARD);
Coordinate prevVertex = prevVertex(ss, segIndex, intPt);
topoBuilder.addEdge(isA, intPt, prevVertex, RelateEdge.IS_REVERSE);
}

/**
*
* @param ss
* @param segIndex
* @param pt
* @return the previous vertex, or null if none exists
*/
private static Coordinate prevVertex(SegmentString ss, int segIndex, Coordinate pt) {
Coordinate segStart = ss.getCoordinate(segIndex);
if (! segStart.equals2D(pt))
return segStart;
//-- pt is at segment start, so get previous vertex
if (segIndex > 0)
return ss.getCoordinate(segIndex - 1);
if (ss.isClosed())
return ss.prevInRing(segIndex);
return null;
}

/**
*
* @param ss
* @param segIndex
* @param pt
* @return the next vertex, or null if none exists
*/
private static Coordinate nextVertex(SegmentString ss, int segIndex, Coordinate pt) {
Coordinate segEnd = ss.getCoordinate(segIndex + 1);
if (! segEnd.equals2D(pt))
return segEnd;
//-- pt is at seg end, so get next vertex
if (segIndex < ss.size() - 2)
return ss.getCoordinate(segIndex + 2);
if (ss.isClosed())
return ss.nextInRing(segIndex);
//-- segstring is not closed, so there is no next segment
return null;
}

private void addNonProperAreaArea(SegmentString ssA, int segIndexA, SegmentString ssB, int segIndexB,
Coordinate intPt) {
Coordinate[] adjPtsA = adjacentRingVertices(ssA, segIndexA, intPt);
Coordinate[] adjPtsB = adjacentRingVertices(ssB, segIndexB, intPt);
topoBuilder.addAreaAreaIntersection(false, adjPtsA[0], adjPtsA[1], adjPtsB[0], adjPtsB[1], intPt);
}

private static Coordinate[] adjacentRingVertices(SegmentString ssRing, int segIndex, Coordinate intPt) {
Coordinate p0 = ssRing.getCoordinate(segIndex);
Coordinate p1 = ssRing.getCoordinate(segIndex + 1);
Coordinate[] adjPts = new Coordinate[2];
if (intPt.equals2D(p0)) {
adjPts[0] = ssRing.prevInRing(segIndex);
adjPts[1] = p1;
}
else if (intPt.equals2D(p1)) {
adjPts[0] = p0;
adjPts[1] = ssRing.nextInRing(segIndex + 1);
}
else {
//-- intersection is in interior of segment
adjPts[0] = p0;
adjPts[1] = p1;
}
return adjPts;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.locationtech.jts.operation.relateng;

import org.locationtech.jts.index.chain.MonotoneChain;
import org.locationtech.jts.index.chain.MonotoneChainOverlapAction;
import org.locationtech.jts.noding.SegmentIntersector;
import org.locationtech.jts.noding.SegmentString;

public class EdgeSegmentOverlapAction
extends MonotoneChainOverlapAction
{
private SegmentIntersector si = null;

public EdgeSegmentOverlapAction(SegmentIntersector si)
{
this.si = si;
}

public void overlap(MonotoneChain mc1, int start1, MonotoneChain mc2, int start2)
{
SegmentString ss1 = (SegmentString) mc1.getContext();
SegmentString ss2 = (SegmentString) mc2.getContext();
si.processIntersections(ss1, start1, ss2, start2);
}

}
Loading

0 comments on commit 5e1e44c

Please sign in to comment.