This is an automated email from the ASF dual-hosted git repository. amanin pushed a commit to branch refactor/sql-store in repository https://gitbox.apache.org/repos/asf/sis.git
commit 944474a6f6fd74a272739887b0172ffda8d41adc Author: Alexis Manin <[email protected]> AuthorDate: Thu Oct 10 12:55:51 2019 +0200 WIP(Feature): CRS matching for filters. --- .../java/org/apache/sis/filter/CRSMatching.java | 16 ++- .../java/org/apache/sis/filter/ST_Intersects.java | 110 +++++++++++++++------ 2 files changed, 89 insertions(+), 37 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/CRSMatching.java b/core/sis-feature/src/main/java/org/apache/sis/filter/CRSMatching.java index 78c4d9f..8f8a265 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/filter/CRSMatching.java +++ b/core/sis-feature/src/main/java/org/apache/sis/filter/CRSMatching.java @@ -9,6 +9,7 @@ import org.opengis.util.FactoryException; import org.apache.sis.referencing.CRS; import org.apache.sis.util.NullArgumentException; import org.apache.sis.util.Utilities; +import org.apache.sis.util.collection.BackingStoreException; /** * TODO: improve CRS conversion/suggestion by infering a geographic region of interest. @@ -27,7 +28,7 @@ public interface CRSMatching { } else { return new CRSMatching() { @Override - public Match right(CoordinateReferenceSystem other) throws FactoryException { + public Match right(CoordinateReferenceSystem other) { if (other == null) throw new NullArgumentException("No match can be established with previous CRS because input one is null"); final CoordinateReferenceSystem commonCrs = CRS.suggestCommonTarget(null, source, other); return new DefaultMatch(commonCrs, source, other); @@ -35,7 +36,8 @@ public interface CRSMatching { }; } } - Match right(final CoordinateReferenceSystem other) throws FactoryException; + + Match right(final CoordinateReferenceSystem other); interface Match { Optional<CoordinateReferenceSystem> getCommonCRS(); @@ -67,10 +69,14 @@ public interface CRSMatching { final Optional<CoordinateOperation> fromLeft; final Optional<CoordinateOperation> fromRight; - public DefaultMatch(CoordinateReferenceSystem commonCRS, CoordinateReferenceSystem left, CoordinateReferenceSystem right) throws FactoryException { + public DefaultMatch(CoordinateReferenceSystem commonCRS, CoordinateReferenceSystem left, CoordinateReferenceSystem right) { this.commonCRS = commonCRS; - fromLeft = createOp(left, commonCRS); - fromRight = createOp(right, commonCRS); + try { + fromLeft = createOp(left, commonCRS); + fromRight = createOp(right, commonCRS); + } catch (FactoryException e) { + throw new BackingStoreException(e); + } } @Override diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Intersects.java b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Intersects.java index 407d291..f5ddf46 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Intersects.java +++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Intersects.java @@ -1,5 +1,7 @@ package org.apache.sis.filter; +import java.util.AbstractMap; +import java.util.Map; import java.util.function.Predicate; import org.opengis.filter.FilterVisitor; @@ -7,8 +9,14 @@ import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.Literal; import org.opengis.filter.spatial.Intersects; import org.opengis.geometry.Geometry; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.operation.CoordinateOperation; +import org.opengis.referencing.operation.TransformException; +import org.opengis.util.FactoryException; import org.apache.sis.internal.feature.GeometryWrapper; +import org.apache.sis.internal.feature.jts.JTS; +import org.apache.sis.util.collection.BackingStoreException; import org.locationtech.jts.geom.prep.PreparedGeometry; import org.locationtech.jts.geom.prep.PreparedGeometryFactory; @@ -38,11 +46,12 @@ public class ST_Intersects implements Intersects { ensureNonNull("Left operand", left); ensureNonNull("Right operand", right); if (left instanceof Literal && right instanceof Literal) { - intersects = constantOp((Literal) left, (Literal) right); + final boolean constantResult = nonOptimizedIntersects(null); + intersects = it -> constantResult; } else if (left instanceof Literal) { - intersects = intersect(right, (Literal) left); + intersects = intersect((Literal) left, right); } else if (right instanceof Literal) { - intersects = intersect(left, (Literal) right); + intersects = intersect((Literal) right, left); } else intersects = this::nonOptimizedIntersects; } @@ -50,46 +59,83 @@ public class ST_Intersects implements Intersects { final Object leftEval = left.evaluate(candidate); final Object rightEval = right.evaluate(candidate); if (leftEval == null || rightEval == null) return false; - return toJTS(leftEval).intersects(toJTS(rightEval)); + + final Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> leftEntry = toJTS(leftEval); + final Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> rightEntry = toJTS(rightEval); + final CRSMatching.Match match = CRSMatching.left(leftEntry.getValue()).right(rightEntry.getValue()); + + final org.locationtech.jts.geom.Geometry leftGeom = match.fromLeft() + .map(op -> transformSilently(leftEntry.getKey(), op)) + .orElse(leftEntry.getKey()); + final org.locationtech.jts.geom.Geometry rightGeom = match.fromRight() + .map(op -> transformSilently(rightEntry.getKey(), op)) + .orElse(rightEntry.getKey()); + + return leftGeom.intersects(rightGeom); } - private static org.locationtech.jts.geom.Geometry toJTS(Object value) { - if (value instanceof GeometryWrapper) value = ((GeometryWrapper) value).geometry; - if (value instanceof org.locationtech.jts.geom.Geometry) return (org.locationtech.jts.geom.Geometry) value; - throw new UnsupportedOperationException("Unsupported geometry type: "+value.getClass().getCanonicalName()); + private static org.locationtech.jts.geom.Geometry transformSilently(org.locationtech.jts.geom.Geometry target, CoordinateOperation op) { + try { + return JTS.transform(target, op); + } catch (TransformException e) { + throw new BackingStoreException(e); + } } - private Predicate constantOp(Literal left, Literal right) { - final boolean result = left.getValue() != null - && right.getValue() != null - && toJTS(left.getValue()).intersects(toJTS(right.getValue())); - return it -> result; + private static Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> toJTS(Object value) { + CoordinateReferenceSystem crs = null; + if (value instanceof Geometry) crs = ((Geometry) value).getCoordinateReferenceSystem(); + if (value instanceof GeometryWrapper) value = ((GeometryWrapper) value).geometry; + if (value instanceof org.locationtech.jts.geom.Geometry) { + final org.locationtech.jts.geom.Geometry geom = (org.locationtech.jts.geom.Geometry) value; + if (crs == null) { + try { + crs = JTS.getCoordinateReferenceSystem(geom); + } catch (FactoryException e) { + throw new BackingStoreException("Cannot extract CRS from operand", e); + } + } + return new AbstractMap.SimpleImmutableEntry<>(geom, crs); + } + throw new UnsupportedOperationException("Unsupported geometry type: "+value.getClass().getCanonicalName()); } - private static Predicate intersect(Expression left, Literal right) { - Object value = right.getValue(); + private static Predicate intersect(Literal left, Expression right) { + Object value = left.getValue(); ensureNonNull("Literal value", value); // TODO: make more consistent strategy once Geometry API is stable. - if (value instanceof GeometryWrapper) value = ((GeometryWrapper) value).geometry; - if (value instanceof org.locationtech.jts.geom.Geometry) { - final PreparedGeometry pg = new PreparedGeometryFactory().create((org.locationtech.jts.geom.Geometry) value); + try { + final Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> leftEntry = toJTS(value); + final CRSMatching crsMatching = CRSMatching.left(leftEntry.getValue()); + final PreparedGeometry optimizedGeom = new PreparedGeometryFactory().create((org.locationtech.jts.geom.Geometry) value); return it -> { - Object val = left.evaluate(it); + Object val = right.evaluate(it); if (val == null) return false; - return pg.intersects(toJTS(val)); + final Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> rightEntry = toJTS(val); + final CRSMatching.Match match = crsMatching.right(rightEntry.getValue()); + final org.locationtech.jts.geom.Geometry rightGeom = match.fromRight() + .map(op -> transformSilently(rightEntry.getKey(), op)) + .orElse(rightEntry.getKey()); + return match.fromLeft() + .map(op -> transformSilently(leftEntry.getKey(), op)) + .map(geom -> geom.intersects(rightGeom)) + .orElseGet(() -> optimizedGeom.intersects(rightGeom)); }; - } else if (value instanceof Geometry) { - final Geometry geom = (Geometry) value; - return it -> { - final Geometry newVal = left.evaluate(it, Geometry.class); - if (newVal == null) { - final Object testVal = left.evaluate(it); - if (testVal == null) return false; - throw new UnsupportedOperationException("Unsupported geometry type: "+testVal.getClass().getCanonicalName()); - } - return geom.intersects(newVal); - }; - } else throw new UnsupportedOperationException("Unsupported geometry type: "+value.getClass().getCanonicalName()); + } catch (UnsupportedOperationException e) { + if (value instanceof Geometry) { + final Geometry geom = (Geometry) value; + return it -> { + final Geometry newVal = left.evaluate(it, Geometry.class); + if (newVal == null) { + final Object testVal = left.evaluate(it); + if (testVal == null) return false; + throw new UnsupportedOperationException("Unsupported geometry type: "+testVal.getClass().getCanonicalName()); + } + return geom.intersects(newVal); + }; + } + } + throw new UnsupportedOperationException("Unsupported geometry type: "+value.getClass().getCanonicalName()); } @Override
