diff --git a/modules/plugin/jdbc/jdbc-oracle/src/main/java/org/geotools/data/oracle/OracleFilterToSQL.java b/modules/plugin/jdbc/jdbc-oracle/src/main/java/org/geotools/data/oracle/OracleFilterToSQL.java index e4341383401..9dff5c031b3 100644 --- a/modules/plugin/jdbc/jdbc-oracle/src/main/java/org/geotools/data/oracle/OracleFilterToSQL.java +++ b/modules/plugin/jdbc/jdbc-oracle/src/main/java/org/geotools/data/oracle/OracleFilterToSQL.java @@ -308,7 +308,8 @@ protected void doSDORelate(Filter filter, Expression e1, Expression e2, boolean protected void doSDODistance(BinarySpatialOperator filter, Expression e1, Expression e2, Object extraData) throws IOException { double distance = ((DistanceBufferOperator) filter).getDistance(); - String unit = ((DistanceBufferOperator) filter).getDistanceUnits(); + String unit = getSDOUnitFromOGCUnit(((DistanceBufferOperator) filter).getDistanceUnits()); + String within = filter instanceof DWithin ? "TRUE" : "FALSE"; out.write("SDO_WITHIN_DISTANCE("); @@ -322,5 +323,28 @@ protected void doSDODistance(BinarySpatialOperator filter, else out.write(",'distance=" + distance + "') = '" + within + "' "); } - + + /** + * The mapping between OGC filter units and Oracle Units. + * The full list of Oracle Units can be obtained by issuing + * "select * from MDSYS.SDO_DIST_UNITS WHERE SDO_UNIT IS NOT NULL order by SDO_UNIT;" + */ + private static final Map UNITS_MAP = new HashMap() { + { + put("metre", "m"); + put("meters", "m"); + put("kilometers", "km"); + put("mi", "Mile"); + put("miles", "Mile"); + put("NM", "naut_mile"); + put("feet", "foot"); + put("ft", "foot"); + put("in", "inch"); + } + }; + + private static String getSDOUnitFromOGCUnit(String ogcUnit) { + Object sdoUnit = UNITS_MAP.get(ogcUnit); + return sdoUnit != null ? sdoUnit.toString() : ogcUnit; + } } diff --git a/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/OracleSpatialFiltersTest.java b/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/OracleSpatialFiltersTest.java index 797b4aeb67f..8c677e96100 100644 --- a/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/OracleSpatialFiltersTest.java +++ b/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/OracleSpatialFiltersTest.java @@ -16,12 +16,24 @@ */ package org.geotools.data.oracle; +import org.geotools.data.Query; +import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.factory.CommonFactoryFinder; import org.geotools.feature.FeatureCollection; import org.geotools.jdbc.JDBCDataStoreAPITestSetup; import org.geotools.jdbc.JDBCSpatialFiltersTest; import org.opengis.filter.FilterFactory; +import org.opengis.filter.FilterFactory2; +import org.opengis.filter.expression.Literal; +import org.opengis.filter.expression.PropertyName; import org.opengis.filter.spatial.BBOX; +import org.opengis.filter.spatial.DWithin; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryCollection; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.Point; /** * @@ -44,4 +56,45 @@ public void testLooseBboxFilter() throws Exception { FeatureCollection features = dataStore.getFeatureSource(tname("road")).getFeatures(bbox); checkSingleResult(features, "r2"); } + + // As reported in GEOS-4384 (http://jira.codehaus.org/browse/GEOS-4384) + public void testSDODWithinOGCUnits() throws Exception { + // express the same distance in different ways and check results + validateOGCUnitUsage(10, "kilometers"); + validateOGCUnitUsage(10, "km"); + validateOGCUnitUsage(10, "kilometer"); + // this one does not work... not sure why + // validateOGCUnitUsage(10000 * 1000, "mm"); + validateOGCUnitUsage(10000, "m"); + validateOGCUnitUsage(10000, "metre"); + validateOGCUnitUsage(10000, "meters"); + validateOGCUnitUsage(10000 / 0.0254, "in"); + validateOGCUnitUsage(10000 / 0.3048, "feet"); + validateOGCUnitUsage(10000 / 0.3048, "foot"); + validateOGCUnitUsage(10000 / 0.3048, "ft"); + validateOGCUnitUsage(10000 / 1609.344, "mi"); + validateOGCUnitUsage(10000 / 1609.344, "mile"); + validateOGCUnitUsage(10000 / 1609.344, "miles"); + validateOGCUnitUsage(10000 / 1852, "NM"); + } + + private void validateOGCUnitUsage(double distance, String unit) throws Exception { + Coordinate coordinate = new Coordinate(3.031, 2.754); + GeometryFactory factory = new GeometryFactory(); + Point point = factory.createPoint(coordinate); + Geometry[] geometries = {point}; + GeometryCollection geometry = new GeometryCollection(geometries, factory ); + + FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null); + + PropertyName geomName = ff.property(aname("geom")); + Literal lit = ff.literal(geometry); + + DWithin dwithinGeomFilter = ((FilterFactory2) ff).dwithin(geomName, lit, distance, unit); + Query query = new Query(tname("road"), dwithinGeomFilter); + SimpleFeatureCollection features = dataStore.getFeatureSource(tname("road")).getFeatures(query); + assertEquals(1, features.size()); + checkSingleResult(features, "r2"); + } + } diff --git a/modules/plugin/jdbc/jdbc-postgis/src/main/java/org/geotools/data/postgis/FilterToSqlHelper.java b/modules/plugin/jdbc/jdbc-postgis/src/main/java/org/geotools/data/postgis/FilterToSqlHelper.java index c9a232fd139..d970860aa1d 100644 --- a/modules/plugin/jdbc/jdbc-postgis/src/main/java/org/geotools/data/postgis/FilterToSqlHelper.java +++ b/modules/plugin/jdbc/jdbc-postgis/src/main/java/org/geotools/data/postgis/FilterToSqlHelper.java @@ -24,7 +24,9 @@ import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.geotools.data.jdbc.FilterToSQL; import org.geotools.factory.CommonFactoryFinder; @@ -90,6 +92,24 @@ class FilterToSqlHelper { protected static final String IO_ERROR = "io problem writing filter"; + /** + * Conversion factor from common units to meter + */ + private static final Map UNITS_MAP = new HashMap() { + { + put("kilometers", 1000.0); + put("kilometer", 1000.0); + put("mm", 0.001); + put("millimeter", 0.001); + put("mi", 1609.344); + put("miles", 1609.344); + put("NM", 1852d); + put("feet", 0.3048); + put("ft", 0.3048); + put("in", 0.0254); + } + }; + private static final Envelope WORLD = new Envelope(-180, 180, -90, 90); FilterToSQL delegate; @@ -193,7 +213,7 @@ void visitDistanceSpatialOperator(DistanceBufferOperator filter, out.write(","); geometry.accept(delegate, extraData); out.write(","); - out.write(Double.toString(filter.getDistance())); + out.write(toMeters(filter.getDistance(), filter.getDistanceUnits())); out.write(")"); } if ((filter instanceof DWithin && swapped) @@ -207,6 +227,19 @@ void visitDistanceSpatialOperator(DistanceBufferOperator filter, } } + private String toMeters(double distance, String unit) { + // only geography uses metric units + if(isCurrentGeography()) { + Double conversion = UNITS_MAP.get(unit); + if(conversion != null) { + return String.valueOf(distance * conversion); + } + } + + // in case unknown unit or not geography, use as-is + return String.valueOf(distance); + } + void visitComparisonSpatialOperator(BinarySpatialOperator filter, PropertyName property, Literal geometry, boolean swapped, Object extraData) throws IOException { diff --git a/modules/plugin/jdbc/jdbc-postgis/src/test/java/org/geotools/data/postgis/PostgisGeographyTest.java b/modules/plugin/jdbc/jdbc-postgis/src/test/java/org/geotools/data/postgis/PostgisGeographyTest.java index f9c9f59f09a..0b32e2a06b0 100644 --- a/modules/plugin/jdbc/jdbc-postgis/src/test/java/org/geotools/data/postgis/PostgisGeographyTest.java +++ b/modules/plugin/jdbc/jdbc-postgis/src/test/java/org/geotools/data/postgis/PostgisGeographyTest.java @@ -16,10 +16,26 @@ */ package org.geotools.data.postgis; +import org.geotools.data.Query; +import org.geotools.data.simple.SimpleFeatureCollection; +import org.geotools.factory.CommonFactoryFinder; +import org.geotools.feature.FeatureCollection; +import org.geotools.feature.FeatureIterator; import org.geotools.jdbc.JDBCDataStore; import org.geotools.jdbc.JDBCGeographyTest; import org.geotools.jdbc.JDBCGeographyTestSetup; +import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; +import org.opengis.filter.FilterFactory2; +import org.opengis.filter.expression.Literal; +import org.opengis.filter.expression.PropertyName; +import org.opengis.filter.spatial.DWithin; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryCollection; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.Point; /** * @@ -45,4 +61,53 @@ public void testSchema() throws Exception { SimpleFeatureType ft = dataStore.getFeatureSource(tname("geopoint")).getSchema(); assertEquals("geography", ft.getGeometryDescriptor().getUserData().get(JDBCDataStore.JDBC_NATIVE_TYPENAME)); } + + // As reported in GEOS-4384 (http://jira.codehaus.org/browse/GEOS-4384) + public void testDWithinOGCUnits() throws Exception { + validateOGCUnitUsage(10000, "m"); + validateOGCUnitUsage(10000, "metre"); + validateOGCUnitUsage(10000, "meters"); + validateOGCUnitUsage(10000 * 1000, "mm"); + validateOGCUnitUsage(10, "kilometers"); + validateOGCUnitUsage(10, "km"); + validateOGCUnitUsage(10, "kilometer"); + validateOGCUnitUsage(10000 / 0.0254, "in"); + validateOGCUnitUsage(10000 / 0.3048, "feet"); + validateOGCUnitUsage(10000 / 0.3048, "foot"); + validateOGCUnitUsage(10000 / 0.3048, "ft"); + validateOGCUnitUsage(10000 / 1609.344, "mi"); + validateOGCUnitUsage(10000 / 1609.344, "mile"); + validateOGCUnitUsage(10000 / 1609.344, "miles"); + validateOGCUnitUsage(10000 / 1852, "NM"); + } + + private void validateOGCUnitUsage(double distance, String unit) throws Exception { + Coordinate coordinate = new Coordinate(-110, 30); + GeometryFactory factory = new GeometryFactory(); + Point point = factory.createPoint(coordinate); + Geometry[] geometries = {point}; + GeometryCollection geometry = new GeometryCollection(geometries, factory ); + + FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null); + + PropertyName geomName = ff.property(aname("geo")); + Literal lit = ff.literal(geometry); + + DWithin dwithinGeomFilter = ((FilterFactory2) ff).dwithin(geomName, lit, distance, unit); + Query query = new Query(tname("geopoint"), dwithinGeomFilter); + SimpleFeatureCollection features = dataStore.getFeatureSource(tname("geopoint")).getFeatures(query); + assertEquals(1, features.size()); + checkSingleResult(features, "Town"); + } + + protected void checkSingleResult(FeatureCollection features, String name) { + assertEquals(1, features.size()); + FeatureIterator fr = features.features(); + assertTrue(fr.hasNext()); + SimpleFeature f = (SimpleFeature) fr.next(); + assertNotNull(f); + assertEquals(name, f.getAttribute(aname("name"))); + assertFalse(fr.hasNext()); + fr.close(); + } }