Skip to content

Commit

Permalink
Handle point location on GC adjacent edges
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-jts committed Apr 9, 2024
1 parent 8cfdddb commit 9e39a46
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.jts.operation.relateng;

import java.util.ArrayList;
import java.util.List;

import org.locationtech.jts.algorithm.Orientation;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Dimension;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineSegment;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.Location;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.util.Assert;

public class AdjacentEdgeLocator {

private List<Coordinate[]> ringList;;

public AdjacentEdgeLocator(Geometry geom) {
init(geom);
}

public int locate(Coordinate p) {
NodeSections sections = new NodeSections(p);
for (Coordinate[] ring : ringList) {
addIfOnSegment(p, ring, sections);
}
RelateNode node = sections.createNode();
//node.finish(false, false);
return node.hasExteriorEdge(true) ? Location.BOUNDARY : Location.INTERIOR;
}

private void addIfOnSegment(Coordinate p, Coordinate[] ring, NodeSections sections) {
for (int i = 0; i < ring.length - 1; i++) {
Coordinate p0 = ring[i];
Coordinate pnext = ring[i + 1];

if (p.equals2D(p0)) {
int iprev = i > 0 ? i - 1 : ring.length - 2;
Coordinate pprev = ring[iprev];
sections.addNodeSection(createSection(p, pprev, pnext));
}
else if (p.equals2D(pnext)) {
//-- segment to point is assigned to next segment
continue;
}
else {
LineSegment seg = new LineSegment(p0, pnext);
if (Orientation.COLLINEAR == seg.orientationIndex(p)) {
sections.addNodeSection(createSection(p, p0, pnext));
}
}
}
}

private NodeSection createSection(Coordinate p, Coordinate prev, Coordinate next) {
if (prev.distance(p) == 0 || next.distance(p) == 0) {
System.out.println("Found zero-length section segment");
};
NodeSection ns = new NodeSection(true, Dimension.A, 1, 0, false, prev, p, next);
return ns;
}

private void init(Geometry geom) {
if (geom.isEmpty())
return;
ringList = new ArrayList<Coordinate[]>();
addRings(geom);
}

private void addRings(Geometry geom) {
if (geom instanceof Polygon) {
Polygon poly = (Polygon) geom;
LinearRing shell = poly.getExteriorRing();
addRing(shell, true);
for (int i = 0; i < poly.getNumInteriorRing(); i++) {
LinearRing hole = poly.getInteriorRingN(i);
addRing(hole, false);
}
}
else if (geom instanceof GeometryCollection) {
for (int i = 0; i < geom.getNumGeometries(); i++) {
addRings(geom.getGeometryN(i));
}
}
}

private void addRing(LinearRing ring, boolean requireCW) {
Coordinate[] pts = RelateGeometry.orient(ring.getCoordinates(), requireCW);
ringList.add(pts);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Martin Davis.
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ private void extractRingToSegmentString(boolean isA, LinearRing ring, int ringId
segStrings.add(ss);
}

private Coordinate[] orient(Coordinate[] pts, boolean orientCW) {
public static Coordinate[] orient(Coordinate[] pts, boolean orientCW) {
boolean isFlipped = orientCW == Orientation.isCCW(pts);
if (isFlipped) {
pts = pts.clone();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Dimension;
import org.locationtech.jts.geom.Location;
import org.locationtech.jts.geom.Position;
import org.locationtech.jts.io.WKTWriter;
import org.locationtech.jts.util.Assert;
Expand Down Expand Up @@ -219,4 +220,14 @@ public String toString() {
}
return buf.toString();
}

public boolean hasExteriorEdge(boolean isA) {
for (RelateEdge e : edges) {
if (Location.EXTERIOR == e.location(isA, Position.LEFT)
|| Location.EXTERIOR == e.location(isA, Position.RIGHT)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public static int dimension(int dimLoc, int exteriorDim) {

private Geometry geom;
private BoundaryNodeRule boundaryRule;
private AdjacentEdgeLocator adjEdgeLocator;

private int elementDim = Dimension.FALSE;
private int numLineBoundaries = 0; // the number of sub-elements whose boundaries the point lies in
Expand Down Expand Up @@ -185,26 +186,26 @@ private void computeLocation(Coordinate p, boolean isNode, Geometry geom)
return;

if (geom instanceof Point) {
updateLocationInfo(locateOnPoint(p, (Point) geom), Dimension.P);
updateLocationDim(locateOnPoint(p, (Point) geom), Dimension.P);
}
else if (geom instanceof LineString) {
updateLocationInfo(locateOnLineString(p, isNode, (LineString) geom), Dimension.L);
updateLocationDim(locateOnLineString(p, isNode, (LineString) geom), Dimension.L);
}
else if (geom instanceof Polygon) {
updateLocationInfo(locateInPolygon(p, isNode, (Polygon) geom), Dimension.A);
updateLocationDim(locateInPolygon(p, isNode, (Polygon) geom), Dimension.A);
}
else if (geom instanceof MultiLineString) {
MultiLineString ml = (MultiLineString) geom;
for (int i = 0; i < ml.getNumGeometries(); i++) {
LineString l = (LineString) ml.getGeometryN(i);
updateLocationInfo(locateOnLineString(p, isNode, l), Dimension.L);
updateLocationDim(locateOnLineString(p, isNode, l), Dimension.L);
}
}
else if (geom instanceof MultiPolygon) {
MultiPolygon mpoly = (MultiPolygon) geom;
for (int i = 0; i < mpoly.getNumGeometries(); i++) {
Polygon poly = (Polygon) mpoly.getGeometryN(i);
updateLocationInfo(locateInPolygon(p, isNode, poly), Dimension.A);
updateLocationDim(locateInPolygon(p, isNode, poly), Dimension.A);
}
}
else if (geom instanceof GeometryCollection) {
Expand All @@ -214,10 +215,17 @@ else if (geom instanceof GeometryCollection) {
if (g2 != geom)
computeLocation(p, isNode, g2);
}
if (numAreaBoundaries > 1) {
if (adjEdgeLocator == null) {
adjEdgeLocator = new AdjacentEdgeLocator(geom);
}
int loc = adjEdgeLocator.locate(p);
updateLocationDim(loc, Dimension.A);
}
}
}

private void updateLocationInfo(int loc, int dimension)
private void updateLocationDim(int loc, int dimension)
{
if (loc == Location.EXTERIOR) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.locationtech.jts.operation.relateng;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Location;

import junit.textui.TestRunner;
import test.jts.GeometryTestCase;

public class AdjacentEdgeLocatorTest extends GeometryTestCase {

public static void main(String args[]) {
TestRunner.run(AdjacentEdgeLocatorTest.class);
}

public AdjacentEdgeLocatorTest(String name) {
super(name);
}

public void testAdjacent2() {
checkLocation(
"GEOMETRYCOLLECTION (POLYGON ((1 9, 5 9, 5 1, 1 1, 1 9)), POLYGON ((9 9, 9 1, 5 1, 5 9, 9 9)))",
5, 5, Location.INTERIOR
);
}

public void testNonAdjacent() {
checkLocation(
"GEOMETRYCOLLECTION (POLYGON ((1 9, 4 9, 5 1, 1 1, 1 9)), POLYGON ((9 9, 9 1, 5 1, 5 9, 9 9)))",
5, 5, Location.BOUNDARY
);
}

private void checkLocation(String wkt, int x, int y, int expectedLoc) {
Geometry geom = read(wkt);
AdjacentEdgeLocator ael = new AdjacentEdgeLocator(geom);
int loc = ael.locate(new Coordinate(x, y));
assertEquals(expectedLoc, loc);
}
}
25 changes: 23 additions & 2 deletions modules/tests/src/test/resources/testxml/misc/TestRelateGC.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
</case>

<case>
<desc>P/GC-A - point on common boundaries of adjacent polygons</desc>
<desc>P/GC-A - point on common boundaries of 2 adjacent polygons</desc>
<a>
POINT (4 3)
</a>
Expand All @@ -127,6 +127,27 @@
<test><op name="within" arg1="A" arg2="B"> true </op></test>
</case>

<case>
<desc>P/GC-A - point on common node of 3 adjacent polygons</desc>
<a>
POINT (5 4)
</a>
<b>
GEOMETRYCOLLECTION (POLYGON ((1 6, 5 4, 4 1, 1 6)), POLYGON ((4 1, 5 4, 9 6, 4 1)), POLYGON ((1 6, 9 6, 5 4, 1 6)))
</b>
<test><op name="relate" arg3="0FFFFF212" arg1="A" arg2="B"> true </op></test>
<test><op name="contains" arg1="A" arg2="B"> false </op></test>
<test><op name="coveredBy" arg1="A" arg2="B"> true </op></test>
<test><op name="covers" arg1="A" arg2="B"> false </op></test>
<test><op name="crosses" arg1="A" arg2="B"> false </op></test>
<test><op name="disjoint" arg1="A" arg2="B"> false </op></test>
<test><op name="equalsTopo" arg1="A" arg2="B"> false </op></test>
<test><op name="intersects" arg1="A" arg2="B"> true </op></test>
<test><op name="overlaps" arg1="A" arg2="B"> false </op></test>
<test><op name="touches" arg1="A" arg2="B"> false </op></test>
<test><op name="within" arg1="A" arg2="B"> true </op></test>
</case>

<case>
<desc>L/GC-A - line on common boundaries of adjacent polygons</desc>
<a>
Expand All @@ -135,7 +156,7 @@
<b>
GEOMETRYCOLLECTION (POLYGON ((1 1, 1 6, 4 6, 4 1, 1 1)), POLYGON ((9 1, 4 1, 4 6, 9 6, 9 1)))
</b>
<test><op name="relate" arg3="1FFFFF212" arg1="A" arg2="B"> true </op></test>
<test><op name="relate" arg3="1FF0FF212" arg1="A" arg2="B"> true </op></test>
<test><op name="contains" arg1="A" arg2="B"> false </op></test>
<test><op name="coveredBy" arg1="A" arg2="B"> true </op></test>
<test><op name="covers" arg1="A" arg2="B"> false </op></test>
Expand Down

0 comments on commit 9e39a46

Please sign in to comment.