Skip to content

Commit d21902f

Browse files
authored
Add flag to enable RelateNG (#1073)
1 parent b78fe01 commit d21902f

File tree

3 files changed

+230
-62
lines changed

3 files changed

+230
-62
lines changed

USING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ module org.foo.baz {
123123
## JTS System Properties
124124

125125
* `-Djts.overlay=ng` enables the use of OverlayNG in `Geometry` overlay methods. (*Note: in a future release this will become the default behaviour*)
126+
* `-Djts.relate=ng` enables the use of RelateNG in `Geometry` topological predicate methods. (*Note: in a future release this will become the default behaviour*)
126127

127128
## JTS Tools
128129

modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java

Lines changed: 12 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -713,10 +713,7 @@ public boolean disjoint(Geometry g) {
713713
* Returns <code>false</code> if both <code>Geometry</code>s are points
714714
*/
715715
public boolean touches(Geometry g) {
716-
// short-circuit test
717-
if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal()))
718-
return false;
719-
return relate(g).isTouches(getDimension(), g.getDimension());
716+
return GeometryRelate.touches(this, g);
720717
}
721718

722719
/**
@@ -771,18 +768,8 @@ public boolean intersects(Geometry g) {
771768
if (g.isRectangle()) {
772769
return RectangleIntersects.intersects((Polygon) g, this);
773770
}
774-
if (isGeometryCollection() || g.isGeometryCollection()) {
775-
for (int i = 0 ; i < getNumGeometries() ; i++) {
776-
for (int j = 0 ; j < g.getNumGeometries() ; j++) {
777-
if (getGeometryN(i).intersects(g.getGeometryN(j))) {
778-
return true;
779-
}
780-
}
781-
}
782-
return false;
783-
}
784-
// general case
785-
return relate(g).isIntersects();
771+
772+
return GeometryRelate.intersects(this, g);
786773
}
787774

788775
/**
@@ -845,7 +832,7 @@ public boolean crosses(Geometry g) {
845832
* @see Geometry#coveredBy
846833
*/
847834
public boolean within(Geometry g) {
848-
return g.contains(this);
835+
return GeometryRelate.within(this, g);
849836
}
850837

851838
/**
@@ -876,25 +863,13 @@ public boolean within(Geometry g) {
876863
* @see Geometry#covers
877864
*/
878865
public boolean contains(Geometry g) {
879-
// optimization - lower dimension cannot contain areas
880-
if (g.getDimension() == 2 && getDimension() < 2) {
881-
return false;
882-
}
883-
// optimization - P cannot contain a non-zero-length L
884-
// Note that a point can contain a zero-length lineal geometry,
885-
// since the line has no boundary due to Mod-2 Boundary Rule
886-
if (g.getDimension() == 1 && getDimension() < 1 && g.getLength() > 0.0) {
887-
return false;
888-
}
889-
// optimization - envelope test
890-
if (! getEnvelopeInternal().contains(g.getEnvelopeInternal()))
891-
return false;
866+
892867
// optimization for rectangle arguments
893868
if (isRectangle()) {
894869
return RectangleContains.contains((Polygon) this, g);
895870
}
896871
// general case
897-
return relate(g).isContains();
872+
return GeometryRelate.contains(this, g);
898873
}
899874

900875
/**
@@ -919,10 +894,7 @@ public boolean contains(Geometry g) {
919894
*@return <code>true</code> if the two <code>Geometry</code>s overlap.
920895
*/
921896
public boolean overlaps(Geometry g) {
922-
// short-circuit test
923-
if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal()))
924-
return false;
925-
return relate(g).isOverlaps(getDimension(), g.getDimension());
897+
return GeometryRelate.overlaps(this, g);
926898
}
927899

928900
/**
@@ -960,24 +932,7 @@ public boolean overlaps(Geometry g) {
960932
* @see Geometry#coveredBy
961933
*/
962934
public boolean covers(Geometry g) {
963-
// optimization - lower dimension cannot cover areas
964-
if (g.getDimension() == 2 && getDimension() < 2) {
965-
return false;
966-
}
967-
// optimization - P cannot cover a non-zero-length L
968-
// Note that a point can cover a zero-length lineal geometry
969-
if (g.getDimension() == 1 && getDimension() < 1 && g.getLength() > 0.0) {
970-
return false;
971-
}
972-
// optimization - envelope test
973-
if (! getEnvelopeInternal().covers(g.getEnvelopeInternal()))
974-
return false;
975-
// optimization for rectangle arguments
976-
if (isRectangle()) {
977-
// since we have already tested that the test envelope is covered
978-
return true;
979-
}
980-
return relate(g).isCovers();
935+
return GeometryRelate.covers(this, g);
981936
}
982937

983938
/**
@@ -1010,7 +965,7 @@ public boolean covers(Geometry g) {
1010965
* @see Geometry#covers
1011966
*/
1012967
public boolean coveredBy(Geometry g) {
1013-
return g.covers(this);
968+
return GeometryRelate.coveredBy(this, g);
1014969
}
1015970

1016971
/**
@@ -1037,7 +992,7 @@ public boolean coveredBy(Geometry g) {
1037992
* @see IntersectionMatrix
1038993
*/
1039994
public boolean relate(Geometry g, String intersectionPattern) {
1040-
return relate(g).matches(intersectionPattern);
995+
return GeometryRelate.relate(this, g, intersectionPattern);
1041996
}
1042997

1043998
/**
@@ -1048,9 +1003,7 @@ public boolean relate(Geometry g, String intersectionPattern) {
10481003
* boundaries and exteriors of the two <code>Geometry</code>s
10491004
*/
10501005
public IntersectionMatrix relate(Geometry g) {
1051-
checkNotGeometryCollection(this);
1052-
checkNotGeometryCollection(g);
1053-
return RelateOp.relate(this, g);
1006+
return GeometryRelate.relate(this, g);
10541007
}
10551008

10561009
/**
@@ -1101,10 +1054,7 @@ public boolean equals(Geometry g) {
11011054
*/
11021055
public boolean equalsTopo(Geometry g)
11031056
{
1104-
// short-circuit test
1105-
if (! getEnvelopeInternal().equals(g.getEnvelopeInternal()))
1106-
return false;
1107-
return relate(g).isEquals(getDimension(), g.getDimension());
1057+
return GeometryRelate.equalsTopo(this, g);
11081058
}
11091059

11101060
/**
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* Copyright (c) 2020 Martin Davis.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
7+
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
8+
* and the Eclipse Distribution License is available at
9+
*
10+
* http://www.eclipse.org/org/documents/edl-v10.php.
11+
*/
12+
package org.locationtech.jts.geom;
13+
14+
import org.locationtech.jts.operation.relate.RelateOp;
15+
import org.locationtech.jts.operation.relateng.RelateNG;
16+
import org.locationtech.jts.operation.relateng.RelatePredicate;
17+
18+
/**
19+
* Internal class which encapsulates the runtime switch to use RelateNG.
20+
* <p>
21+
* This class allows the {@link Geometry} predicate methods to be
22+
* switched between the original {@link RelateOp} algorithm
23+
* and the modern {@link RelateNG} codebase
24+
* via a system property <code>jts.relate</code>.
25+
* <ul>
26+
* <li><code>jts.relate=old</code> - (default) use original RelateOp algorithm
27+
* <li><code>jts.relate=ng</code> - use RelateNG
28+
* </ul>
29+
*
30+
* @author mdavis
31+
*
32+
*/
33+
class GeometryRelate
34+
{
35+
public static String RELATE_PROPERTY_NAME = "jts.relate";
36+
37+
public static String RELATE_PROPERTY_VALUE_NG = "ng";
38+
public static String RELATE_PROPERTY_VALUE_OLD = "old";
39+
40+
/**
41+
* Currently the old relate implementation is the default
42+
*/
43+
public static boolean RELATE_NG_DEFAULT = false;
44+
45+
private static boolean isRelateNG = RELATE_NG_DEFAULT;
46+
47+
static {
48+
setRelateImpl(System.getProperty(RELATE_PROPERTY_NAME));
49+
}
50+
51+
/**
52+
* This function is provided primarily for unit testing.
53+
* It is not recommended to use it dynamically, since
54+
* that may result in inconsistent overlay behaviour.
55+
*
56+
* @param relateImplCode the code for the overlay method (may be null)
57+
*/
58+
static void setRelateImpl(String relateImplCode) {
59+
if (relateImplCode == null)
60+
return;
61+
// set flag explicitly since current value may not be default
62+
isRelateNG = RELATE_NG_DEFAULT;
63+
64+
if (RELATE_PROPERTY_VALUE_NG.equalsIgnoreCase(relateImplCode) )
65+
isRelateNG = true;
66+
}
67+
68+
static boolean intersects(Geometry a, Geometry b)
69+
{
70+
if (isRelateNG) {
71+
return RelateNG.relate(a, b, RelatePredicate.intersects());
72+
}
73+
if (a.isGeometryCollection() || b.isGeometryCollection()) {
74+
for (int i = 0 ; i < a.getNumGeometries() ; i++) {
75+
for (int j = 0 ; j < b.getNumGeometries() ; j++) {
76+
if (a.getGeometryN(i).intersects(b.getGeometryN(j))) {
77+
return true;
78+
}
79+
}
80+
}
81+
return false;
82+
}
83+
return RelateOp.relate(a, b).isIntersects();
84+
}
85+
86+
static boolean contains(Geometry a, Geometry b)
87+
{
88+
if (isRelateNG) {
89+
return RelateNG.relate(a, b, RelatePredicate.contains());
90+
}
91+
// optimization - lower dimension cannot contain areas
92+
if (b.getDimension() == 2 && a.getDimension() < 2) {
93+
return false;
94+
}
95+
// optimization - P cannot contain a non-zero-length L
96+
// Note that a point can contain a zero-length lineal geometry,
97+
// since the line has no boundary due to Mod-2 Boundary Rule
98+
if (b.getDimension() == 1 && a.getDimension() < 1 && b.getLength() > 0.0) {
99+
return false;
100+
}
101+
// optimization - envelope test
102+
if (! a.getEnvelopeInternal().contains(b.getEnvelopeInternal()))
103+
return false;
104+
return RelateOp.relate(a, b).isContains();
105+
}
106+
107+
static boolean covers(Geometry a, Geometry b)
108+
{
109+
if (isRelateNG) {
110+
return RelateNG.relate(a, b, RelatePredicate.covers());
111+
}
112+
// optimization - lower dimension cannot cover areas
113+
if (b.getDimension() == 2 && a.getDimension() < 2) {
114+
return false;
115+
}
116+
// optimization - P cannot cover a non-zero-length L
117+
// Note that a point can cover a zero-length lineal geometry
118+
if (b.getDimension() == 1 && a.getDimension() < 1 && b.getLength() > 0.0) {
119+
return false;
120+
}
121+
// optimization - envelope test
122+
if (! a.getEnvelopeInternal().covers(b.getEnvelopeInternal()))
123+
return false;
124+
// optimization for rectangle arguments
125+
if (a.isRectangle()) {
126+
// since we have already tested that the test envelope is covered
127+
return true;
128+
}
129+
return RelateOp.relate(a, b).isCovers();
130+
}
131+
132+
static boolean coveredBy(Geometry a, Geometry b)
133+
{
134+
if (isRelateNG) {
135+
return RelateNG.relate(a, b, RelatePredicate.coveredBy());
136+
}
137+
return covers(b, a);
138+
}
139+
140+
static boolean crosses(Geometry a, Geometry b)
141+
{
142+
if (isRelateNG) {
143+
return RelateNG.relate(a, b, RelatePredicate.crosses());
144+
}
145+
// short-circuit test
146+
if (! a.getEnvelopeInternal().intersects(b.getEnvelopeInternal()))
147+
return false;
148+
return RelateOp.relate(a, b).isCrosses(a.getDimension(), b.getDimension());
149+
}
150+
151+
static boolean disjoint(Geometry a, Geometry b)
152+
{
153+
if (isRelateNG) {
154+
return RelateNG.relate(a, b, RelatePredicate.disjoint());
155+
}
156+
return ! intersects(a, b);
157+
}
158+
159+
static boolean equalsTopo(Geometry a, Geometry b)
160+
{
161+
if (isRelateNG) {
162+
return RelateNG.relate(a, b, RelatePredicate.equalsTopo());
163+
}
164+
if (! a.getEnvelopeInternal().equals(b.getEnvelopeInternal()))
165+
return false;
166+
return RelateOp.relate(a, b).isEquals(a.getDimension(), b.getDimension());
167+
}
168+
169+
static boolean overlaps(Geometry a, Geometry b)
170+
{
171+
if (isRelateNG) {
172+
return RelateNG.relate(a, b, RelatePredicate.overlaps());
173+
}
174+
if (! a.getEnvelopeInternal().intersects(b.getEnvelopeInternal()))
175+
return false;
176+
return RelateOp.relate(a, b).isOverlaps(a.getDimension(), b.getDimension());
177+
}
178+
179+
static boolean touches(Geometry a, Geometry b)
180+
{
181+
if (isRelateNG) {
182+
return RelateNG.relate(a, b, RelatePredicate.touches());
183+
}
184+
if (! a.getEnvelopeInternal().intersects(b.getEnvelopeInternal()))
185+
return false;
186+
return RelateOp.relate(a, b).isTouches(a.getDimension(), b.getDimension());
187+
}
188+
189+
static boolean within(Geometry a, Geometry b)
190+
{
191+
if (isRelateNG) {
192+
return RelateNG.relate(a, b, RelatePredicate.within());
193+
}
194+
return contains(b, a);
195+
}
196+
197+
static IntersectionMatrix relate(Geometry a, Geometry b)
198+
{
199+
if (isRelateNG) {
200+
return RelateNG.relate(a, b);
201+
}
202+
Geometry.checkNotGeometryCollection(a);
203+
Geometry.checkNotGeometryCollection(b);
204+
return RelateOp.relate(a, b);
205+
}
206+
207+
static boolean relate(Geometry a, Geometry b, String intersectionPattern)
208+
{
209+
if (isRelateNG) {
210+
return RelateNG.relate(a, b, intersectionPattern);
211+
}
212+
Geometry.checkNotGeometryCollection(a);
213+
Geometry.checkNotGeometryCollection(b);
214+
return RelateOp.relate(a, b).matches(intersectionPattern);
215+
}
216+
217+
}

0 commit comments

Comments
 (0)