Skip to content

Commit df0d9a0

Browse files
committed
GEOWAVE-398 & GEOWAVE-401. DWITHIN corrected to handle the unit conversion properly from distance (meters, km, etc.) to decimal degrees (EPSG:4326 long/lat). There is still a degree of error difficult to compensate for when dealing polygons (e.g a country). Future work will adopt a more accurate approach consistent with other LocationTech projects
1 parent 08b66af commit df0d9a0

File tree

7 files changed

+283
-8
lines changed

7 files changed

+283
-8
lines changed

core/store/src/main/java/mil/nga/giat/geowave/core/store/adapter/statistics/AbstractDataStatistics.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,9 @@ public DataStatistics<T> duplicate() {
9797
newStats.fromBinary(toBinary());
9898
return newStats;
9999
}
100+
101+
@Override
102+
public String toString() {
103+
return "AbstractDataStatistics [dataAdapterId=" + dataAdapterId.getString() + ", statisticsId=" + statisticsId.getString() + "]";
104+
}
100105
}

extensions/adapters/vector/src/main/java/mil/nga/giat/geowave/adapter/vector/plugin/ExtractGeometryFilterVisitor.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
package mil.nga.giat.geowave.adapter.vector.plugin;
22

3+
import java.awt.geom.Point2D;
4+
import java.util.Arrays;
5+
import java.util.Collections;
6+
7+
import javax.measure.unit.SI;
8+
import javax.measure.unit.Unit;
9+
310
import mil.nga.giat.geowave.core.geotime.GeometryUtils;
411

12+
import org.apache.commons.lang3.tuple.Pair;
513
import org.apache.log4j.Logger;
614
import org.geotools.filter.visitor.NullFilterVisitor;
715
import org.geotools.geometry.jts.ReferencedEnvelope;
16+
import org.geotools.ows.bindings.UnitBinding;
17+
import org.geotools.referencing.GeodeticCalculator;
818
import org.opengis.filter.And;
919
import org.opengis.filter.ExcludeFilter;
1020
import org.opengis.filter.Filter;
@@ -44,9 +54,11 @@
4454
import org.opengis.referencing.crs.CoordinateReferenceSystem;
4555
import org.opengis.referencing.operation.TransformException;
4656

57+
import com.vividsolutions.jts.geom.Coordinate;
4758
import com.vividsolutions.jts.geom.Envelope;
4859
import com.vividsolutions.jts.geom.Geometry;
4960
import com.vividsolutions.jts.geom.GeometryFactory;
61+
import com.vividsolutions.jts.geom.Point;
5062

5163
/**
5264
* This class can be used to get Geometry from an OpenGIS filter object. GeoWave
@@ -307,13 +319,16 @@ public Object visit(
307319
if (geom == null) {
308320
return infinity();
309321
}
310-
geom = geom.buffer(filter.getDistance());
322+
Pair<Geometry, Double> geometryAndDegrees = mil.nga.giat.geowave.adapter.vector.utils.GeometryUtils.convertToCRS(
323+
geom,
324+
filter.getDistanceUnits(),
325+
filter.getDistance());
311326

312327
if (bbox != null) {
313-
return geom.union(bbox);
328+
return geometryAndDegrees.getLeft().union(bbox);
314329
}
315330
else {
316-
return geom;
331+
return geometryAndDegrees.getLeft();
317332
}
318333
}
319334

extensions/adapters/vector/src/main/java/mil/nga/giat/geowave/adapter/vector/query/cql/FilterToCQLTool.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@ public static String toCQL(
1414
new StringBuilder());
1515

1616
return output.toString();
17-
1817
}
1918
}

extensions/adapters/vector/src/main/java/mil/nga/giat/geowave/adapter/vector/query/cql/FilterToECQLExtension.java

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
import java.util.Iterator;
1212
import java.util.List;
1313

14+
import org.apache.commons.lang3.tuple.Pair;
15+
import org.geotools.filter.LiteralExpressionImpl;
16+
import org.geotools.filter.spatial.IntersectsImpl;
1417
import org.geotools.filter.text.commons.ExpressionToText;
1518
import org.geotools.filter.text.commons.FilterToTextUtil;
1619
import org.opengis.filter.And;
@@ -34,6 +37,7 @@
3437
import org.opengis.filter.expression.Expression;
3538
import org.opengis.filter.expression.Function;
3639
import org.opengis.filter.expression.Literal;
40+
import org.opengis.filter.expression.PropertyName;
3741
import org.opengis.filter.identity.Identifier;
3842
import org.opengis.filter.spatial.BBOX;
3943
import org.opengis.filter.spatial.Beyond;
@@ -61,6 +65,8 @@
6165
import org.opengis.filter.temporal.TEquals;
6266
import org.opengis.filter.temporal.TOverlaps;
6367

68+
import com.vividsolutions.jts.geom.Geometry;
69+
6470
public class FilterToECQLExtension implements
6571
FilterVisitor
6672
{
@@ -481,12 +487,43 @@ public Object visit(
481487
extraData);
482488
}
483489

490+
/**
491+
* DWithin spatial operator will find out if a feature in a datalayer is
492+
* within X meters of a point, line, or polygon.
493+
*/
484494
@Override
485495
public Object visit(
486496
DWithin filter,
487497
Object extraData ) {
488-
return FilterToTextUtil.buildDWithin(
489-
filter,
498+
IntersectsImpl newWithImpl = null;
499+
if ((filter.getExpression1() instanceof PropertyName) && (filter.getExpression2() instanceof Literal)) {
500+
Pair<Geometry, Double> geometryAndDegrees = mil.nga.giat.geowave.adapter.vector.utils.GeometryUtils.convertToCRS(
501+
filter.getExpression2().evaluate(
502+
extraData,
503+
Geometry.class),
504+
filter.getDistanceUnits(),
505+
filter.getDistance());
506+
newWithImpl = new IntersectsImpl(
507+
filter.getExpression1(),
508+
new LiteralExpressionImpl(
509+
geometryAndDegrees.getLeft()));
510+
511+
}
512+
else if ((filter.getExpression2() instanceof PropertyName) && (filter.getExpression1() instanceof Literal)) {
513+
Pair<Geometry, Double> geometryAndDegrees = mil.nga.giat.geowave.adapter.vector.utils.GeometryUtils.convertToCRS(
514+
filter.getExpression1().evaluate(
515+
extraData,
516+
Geometry.class),
517+
filter.getDistanceUnits(),
518+
filter.getDistance());
519+
newWithImpl = new IntersectsImpl(
520+
new LiteralExpressionImpl(
521+
geometryAndDegrees.getLeft()),
522+
filter.getExpression2());
523+
}
524+
return FilterToTextUtil.buildBinarySpatialOperator(
525+
"INTERSECTS",
526+
newWithImpl,
490527
extraData);
491528
}
492529

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package mil.nga.giat.geowave.adapter.vector.utils;
2+
3+
import java.awt.geom.Point2D;
4+
import java.util.Arrays;
5+
import java.util.Collections;
6+
7+
import javax.measure.unit.SI;
8+
import javax.measure.unit.Unit;
9+
10+
import org.apache.commons.lang3.tuple.Pair;
11+
import org.apache.log4j.Logger;
12+
import org.geotools.ows.bindings.UnitBinding;
13+
import org.geotools.referencing.GeodeticCalculator;
14+
15+
import com.vividsolutions.jts.geom.Coordinate;
16+
import com.vividsolutions.jts.geom.Envelope;
17+
import com.vividsolutions.jts.geom.Geometry;
18+
import com.vividsolutions.jts.geom.GeometryFactory;
19+
import com.vividsolutions.jts.geom.Point;
20+
21+
public class GeometryUtils
22+
{
23+
24+
private static Logger LOGGER = Logger.getLogger(GeometryUtils.class);
25+
26+
public static final Pair<Geometry, Double> convertToCRS(
27+
Geometry geometry,
28+
String distanceUnits,
29+
double distance ) {
30+
Unit<?> unit;
31+
try {
32+
unit = (Unit<?>) new UnitBinding().parse(
33+
null,
34+
distanceUnits);
35+
}
36+
catch (Exception e) {
37+
unit = SI.METER;
38+
LOGGER.warn(
39+
"Cannot lookup unit of measure " + distanceUnits,
40+
e);
41+
}
42+
final double meterDistance = unit.getConverterTo(
43+
SI.METER).convert(
44+
distance);
45+
final double degrees = distanceToDegrees(
46+
geometry,
47+
meterDistance);
48+
return Pair.of(
49+
geometry.buffer(degrees),
50+
degrees);
51+
52+
}
53+
54+
/**
55+
* Convert meters to decimal degrees based on widest point
56+
*/
57+
private static double distanceToDegrees(
58+
Geometry geometry,
59+
double meters ) {
60+
final GeometryFactory factory = geometry.getFactory();
61+
return (geometry instanceof Point) ? geometry.distance(farthestPoint(
62+
(Point) geometry,
63+
meters)) : distanceToDegrees(
64+
geometry.getEnvelopeInternal(),
65+
factory == null ? new GeometryFactory() : factory,
66+
meters);
67+
}
68+
69+
private static double distanceToDegrees(
70+
Envelope env,
71+
GeometryFactory factory,
72+
double meters ) {
73+
return Collections.max(Arrays.asList(
74+
distanceToDegrees(
75+
factory.createPoint(new Coordinate(
76+
env.getMaxX(),
77+
env.getMaxY())),
78+
meters),
79+
distanceToDegrees(
80+
factory.createPoint(new Coordinate(
81+
env.getMaxX(),
82+
env.getMinY())),
83+
meters),
84+
distanceToDegrees(
85+
factory.createPoint(new Coordinate(
86+
env.getMinX(),
87+
env.getMinY())),
88+
meters),
89+
distanceToDegrees(
90+
factory.createPoint(new Coordinate(
91+
env.getMinX(),
92+
env.getMaxY())),
93+
meters)));
94+
}
95+
96+
/** farther point in longitudinal axis given a latitude */
97+
98+
private static Point farthestPoint(
99+
Point point,
100+
double meters ) {
101+
final GeodeticCalculator calc = new GeodeticCalculator();
102+
calc.setStartingGeographicPoint(
103+
point.getX(),
104+
point.getY());
105+
calc.setDirection(
106+
90,
107+
meters);
108+
Point2D dest2D = calc.getDestinationGeographicPoint();
109+
return point.getFactory().createPoint(
110+
new Coordinate(
111+
dest2D.getX(),
112+
dest2D.getY()));
113+
}
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package mil.nga.giat.geowave.adapter.vector.plugin;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNotNull;
5+
6+
import java.text.ParseException;
7+
8+
import org.geotools.data.Query;
9+
import org.geotools.filter.text.cql2.CQL;
10+
import org.geotools.filter.text.cql2.CQLException;
11+
import org.geotools.geometry.jts.JTS;
12+
import org.junit.Test;
13+
import org.opengis.filter.Filter;
14+
import org.opengis.referencing.operation.TransformException;
15+
16+
import com.vividsolutions.jts.geom.Coordinate;
17+
import com.vividsolutions.jts.geom.Geometry;
18+
19+
public class ExtractGeometryFilterVisitorTest
20+
{
21+
final ExtractGeometryFilterVisitor visitorWithDescriptor = (ExtractGeometryFilterVisitor) ExtractGeometryFilterVisitor.GEOMETRY_VISITOR;
22+
23+
@Test
24+
public void testAfter()
25+
throws CQLException,
26+
TransformException,
27+
ParseException {
28+
29+
Filter filter = CQL.toFilter("DWITHIN(geom, POINT(-122.7668 0.4979), 233.7, meters)");
30+
Query query = new Query(
31+
"type",
32+
filter);
33+
34+
Geometry geometry = (Geometry) query.getFilter().accept(
35+
visitorWithDescriptor,
36+
null);
37+
assertNotNull(geometry);
38+
for (Coordinate coord : geometry.getCoordinates()) {
39+
40+
assertEquals(
41+
233.7,
42+
JTS.orthodromicDistance(
43+
coord,
44+
new Coordinate(
45+
-122.7668,
46+
0.4979),
47+
GeoWaveGTDataStore.DEFAULT_CRS),
48+
2);
49+
}
50+
}
51+
52+
}

0 commit comments

Comments
 (0)