This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 8f0d46beeff512ad684071e93334d27a8c4297f5
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Wed Sep 15 13:56:51 2021 +0200

    Add basic Java2D support for a few filter/expression operations.
---
 .../apache/sis/internal/feature/Geometries.java    |   4 +-
 .../apache/sis/internal/feature/esri/Factory.java  |   1 -
 .../apache/sis/internal/feature/esri/Wrapper.java  |   2 +-
 .../apache/sis/internal/feature/j2d/Factory.java   |  64 +++++++
 .../sis/internal/feature/j2d/PointWrapper.java     | 115 +++++++++++-
 .../apache/sis/internal/feature/j2d/Wrapper.java   | 199 ++++++++++++++++++++-
 .../sis/internal/filter/GeometryConverter.java     |   2 +-
 .../BinarySpatialFilterUsingJava2D_Test.java       | 102 +++++++++++
 .../internal/filter/sqlmm/RegistryTestCase.java    |   2 +-
 .../filter/sqlmm/RegistryUsingESRI_Test.java       |  12 +-
 ...SRI_Test.java => RegistryUsingJava2D_Test.java} |  28 +--
 .../apache/sis/test/suite/FeatureTestSuite.java    |   2 +
 12 files changed, 506 insertions(+), 27 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
index fd28438..d650f9a 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
@@ -379,9 +379,7 @@ public abstract class Geometries<G> implements Serializable 
{
      * @return geometry built from the given components.
      * @throws ClassCastException if the given object is not an array or a 
collection of supported geometry components.
      */
-    public GeometryWrapper<G> createFromComponents(GeometryType type, Object 
components) {
-        throw new UnsupportedOperationException();
-    }
+    public abstract GeometryWrapper<G> createFromComponents(GeometryType type, 
Object components);
 
     /**
      * Creates a polyline made of points describing a rectangle whose start 
point is the lower left corner.
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/esri/Factory.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/esri/Factory.java
index 7fcc179..6437cb9 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/esri/Factory.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/esri/Factory.java
@@ -191,7 +191,6 @@ public final class Factory extends Geometries<Geometry> {
      * @throws ClassCastException if the given object is not an array or a 
collection of supported geometry components.
      */
     @Override
-    @SuppressWarnings("fallthrough")
     public GeometryWrapper<Geometry> createFromComponents(final GeometryType 
type, final Object components) {
         /*
          * No exhaustive `if (x instanceof y)` checks in this method.
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/esri/Wrapper.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/esri/Wrapper.java
index 1981004..60dc2b7 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/esri/Wrapper.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/esri/Wrapper.java
@@ -212,7 +212,7 @@ add:    for (Geometry next = geometry;;) {
     }
 
     /**
-     * All predicates recognized by {@link #predicate(SpatialOperatorName, 
Geometry)}.
+     * All predicates recognized by {@link 
#predicateSameCRS(SpatialOperatorName, GeometryWrapper)}.
      * Array indices are {@link SpatialOperatorName#ordinal()} values.
      */
     @SuppressWarnings({"unchecked","rawtypes"})
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Factory.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Factory.java
index 08055f5..6434c64 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Factory.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Factory.java
@@ -16,6 +16,9 @@
  */
 package org.apache.sis.internal.feature.j2d;
 
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Collection;
 import java.awt.Shape;
 import java.awt.geom.Line2D;
 import java.awt.geom.Path2D;
@@ -24,8 +27,10 @@ import java.nio.ByteBuffer;
 import java.io.ObjectStreamException;
 import org.apache.sis.setup.GeometryLibrary;
 import org.apache.sis.internal.feature.Geometries;
+import org.apache.sis.internal.feature.GeometryType;
 import org.apache.sis.internal.feature.GeometryWrapper;
 import org.apache.sis.internal.referencing.j2d.ShapeUtilities;
+import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.math.Vector;
 import org.apache.sis.util.UnsupportedImplementationException;
 
@@ -247,10 +252,69 @@ public final class Factory extends Geometries<Shape> {
         for (final Shape geometry : shapes) {
             path.append(geometry, false);
         }
+        // path.trimToSize();       // TODO: uncomment with JDK10.
         return new Wrapper(path);
     }
 
     /**
+     * Creates a geometry from components.
+     * The expected {@code components} type depend on the target geometry type:
+     * <ul>
+     *   <li>If {@code type} is a multi-geometry, then the components shall be 
an array of {@link Shape} elements.</li>
+     *   <li>Otherwise the components shall be an array or collection of 
{@link Point2D} instances.</li>
+     * </ul>
+     *
+     * @param  type        type of geometry to create.
+     * @param  components  the components. Valid classes depend on the type of 
geometry to create.
+     * @return geometry built from the given components.
+     * @throws ClassCastException if the given object is not an array or a 
collection of supported geometry components.
+     */
+    @Override
+    @SuppressWarnings("fallthrough")
+    public GeometryWrapper<Shape> createFromComponents(final GeometryType 
type, final Object components) {
+        /*
+         * No exhaustive `if (x instanceof y)` checks in this method.
+         * `ClassCastException` shall be handled by the caller.
+         */
+        final Collection<?> data = (components instanceof Collection<?>)
+                ? (Collection<?>) components : Arrays.asList((Object[]) 
components);
+        /*
+         * Java2D API does not distinguish between single geometry and 
geometry collection.
+         * So if the number of components is 1, there is no reason to create a 
new geometry object.
+         */
+        Shape geometry = (Shape) CollectionsExt.singletonOrNull(data);
+        if (geometry == null) {
+            boolean isFloat = true;
+            for (final Object component : data) {
+                isFloat = ShapeUtilities.isFloat(component);
+                if (!isFloat) break;
+            }
+            final Path2D path = createPath(isFloat, 20);
+            if (type.isCollection()) {
+                for (final Object component : data) {
+                    path.append((Shape) component, false);
+                }
+            } else {
+                final Iterator<?> it = data.iterator();
+                if (it.hasNext()) {
+                    Point2D p = (Point2D) it.next();
+                    path.moveTo(p.getX(), p.getY());
+                    while (it.hasNext()) {
+                        p = (Point2D) it.next();
+                        path.lineTo(p.getX(), p.getY());
+                    }
+                    if (type == GeometryType.POLYGON) {
+                        path.closePath();
+                    }
+                }
+            }
+            // path.trimToSize();       // TODO: uncomment with JDK10.
+            geometry = path;
+        }
+        return new Wrapper(geometry);
+    }
+
+    /**
      * Well Known Text (WKT) parsing not supported with Java2D.
      */
     @Override
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/PointWrapper.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/PointWrapper.java
index d8b8d1a..7c238a7 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/PointWrapper.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/PointWrapper.java
@@ -17,8 +17,12 @@
 package org.apache.sis.internal.feature.j2d;
 
 import java.awt.Shape;
+import java.awt.Point;
+import java.awt.Rectangle;
 import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
 import java.util.Iterator;
+import java.util.function.BiPredicate;
 import org.opengis.geometry.DirectPosition;
 import org.apache.sis.geometry.DirectPosition2D;
 import org.apache.sis.geometry.GeneralEnvelope;
@@ -27,6 +31,7 @@ import org.apache.sis.internal.feature.GeometryWithCRS;
 import org.apache.sis.internal.feature.GeometryWrapper;
 import org.apache.sis.internal.filter.sqlmm.SQLMM;
 import org.apache.sis.util.Debug;
+import org.opengis.filter.SpatialOperatorName;
 
 
 /**
@@ -42,7 +47,7 @@ final class PointWrapper extends GeometryWithCRS<Shape> {
     /**
      * The wrapped implementation.
      */
-    private final Point2D point;
+    final Point2D point;
 
     /**
      * Creates a new wrapper around the given point.
@@ -120,6 +125,39 @@ final class PointWrapper extends GeometryWithCRS<Shape> {
     }
 
     /**
+     * Applies a filter predicate between this geometry and another geometry.
+     * This method assumes that the two geometries are in the same CRS (this 
is not verified).
+     */
+    @Override
+    protected boolean predicateSameCRS(final SpatialOperatorName type, final 
GeometryWrapper<Shape> other) {
+        final int ordinal = type.ordinal();
+        if (ordinal >= 0 && ordinal < PREDICATES.length) {
+            final BiPredicate<PointWrapper,Object> op = PREDICATES[ordinal];
+            if (op != null) {
+                return op.test(this, other);
+            }
+        }
+        return super.predicateSameCRS(type, other);
+    }
+
+    /**
+     * All predicates recognized by {@link 
#predicateSameCRS(SpatialOperatorName, GeometryWrapper)}.
+     * Array indices are {@link SpatialOperatorName#ordinal()} values.
+     */
+    @SuppressWarnings({"unchecked","rawtypes"})
+    private static final BiPredicate<PointWrapper,Object>[] PREDICATES =
+            new BiPredicate[SpatialOperatorName.OVERLAPS.ordinal() + 1];
+    static {
+        PREDICATES[SpatialOperatorName.BBOX      .ordinal()] = // Fallback on 
intersects.
+        PREDICATES[SpatialOperatorName.OVERLAPS  .ordinal()] = // Fallback on 
intersects.
+        PREDICATES[SpatialOperatorName.INTERSECTS.ordinal()] = 
PointWrapper::intersect;
+        PREDICATES[SpatialOperatorName.WITHIN    .ordinal()] = 
PointWrapper::within;
+        PREDICATES[SpatialOperatorName.CONTAINS  .ordinal()] = // Fallback on 
equals.
+        PREDICATES[SpatialOperatorName.EQUALS    .ordinal()] = 
PointWrapper::equal;
+        PREDICATES[SpatialOperatorName.DISJOINT  .ordinal()] = (w,o) -> 
!w.intersect(o);
+    }
+
+    /**
      * Applies a SQLMM operation on this geometry.
      *
      * @param  operation  the SQLMM operation to apply.
@@ -128,14 +166,87 @@ final class PointWrapper extends GeometryWithCRS<Shape> {
      * @return result of the specified operation.
      */
     @Override
+    @SuppressWarnings("fallthrough")
     protected Object operationSameCRS(final SQLMM operation, final 
GeometryWrapper<Shape> other, final Object argument) {
         switch (operation) {
-            case ST_Centroid: return point.clone();
+            case ST_Dimension:
+            case ST_CoordDim:   return 2;
+            case ST_Is3D:
+            case ST_IsMeasured: return Boolean.FALSE;
+            case ST_Centroid:   return point.clone();
+            case ST_Envelope:   return getEnvelope();
+            case ST_Boundary: {
+                if (point instanceof Point) {
+                    final Point p = (Point) point;
+                    final Rectangle r = new Rectangle();
+                    r.x = p.x;
+                    r.y = p.y;
+                    return r;
+                } else if (point instanceof Point2D.Float) {
+                    final Point2D.Float p = (Point2D.Float) point;
+                    final Rectangle2D.Float r = new Rectangle2D.Float();
+                    r.x = p.x;
+                    r.y = p.y;
+                    return r;
+                } else {
+                    final Rectangle2D.Double r = new Rectangle2D.Double();
+                    r.x = point.getX();
+                    r.y = point.getY();
+                    return r;
+                }
+            }
+            case ST_Overlaps:   // Falback on "within".
+            case ST_Within:     return  within(other);
+            case ST_Intersects: return  intersect(other);
+            case ST_Disjoint:   return !intersect(other);
+            case ST_Contains:   // Fallback on "equals".
+            case ST_Equals:     return  equal(other);
             default: return super.operationSameCRS(operation, other, argument);
         }
     }
 
     /**
+     * Estimates whether the wrapped geometry is equal to the geometry of the 
given wrapper.
+     *
+     * @param  wrapper  instance of {@link PointWrapper}.
+     */
+    private boolean equal(final Object wrapper) {       // "s" omitted for 
avoiding confusion with super.equals(…).
+        if (wrapper instanceof PointWrapper) {
+            final Point2D p = ((PointWrapper) wrapper).point;
+            return Double.doubleToLongBits(point.getX()) == 
Double.doubleToLongBits(p.getX())
+                && Double.doubleToLongBits(point.getY()) == 
Double.doubleToLongBits(p.getY());
+        }
+        return false;
+    }
+
+    /**
+     * Estimates whether the wrapped geometry is contained by the geometry of 
the given wrapper.
+     * This method may conservatively returns {@code false} if an accurate 
computation would be
+     * too expansive.
+     *
+     * @param  wrapper  instance of {@link Wrapper}.
+     */
+    private boolean within(final Object wrapper) {
+        return (wrapper instanceof Wrapper) && (((Wrapper) 
wrapper).geometry).contains(point);
+    }
+
+    /**
+     * Estimates whether the wrapped geometry intersects the geometry of the 
given wrapper.
+     * This method may conservatively returns {@code true} if an accurate 
computation would
+     * be too expansive.
+     *
+     * @param  wrapper  instance of {@link Wrapper} or {@link PointWrapper}.
+     * @throws ClassCastException if the given object is not a recognized 
wrapper.
+     */
+    private boolean intersect(final Object wrapper) {   // "s" omitted for 
avoiding confusion with super.intersects(…).
+        if (wrapper instanceof PointWrapper) {
+            return point.equals(((PointWrapper) wrapper).point);
+        } else {
+            return within(wrapper);
+        }
+    }
+
+    /**
      * Builds a WKT representation of the wrapped point.
      */
     @Override
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Wrapper.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Wrapper.java
index 4178386..a37af34 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Wrapper.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Wrapper.java
@@ -18,11 +18,14 @@ package org.apache.sis.internal.feature.j2d;
 
 import java.util.List;
 import java.util.Iterator;
+import java.util.function.BiPredicate;
 import java.awt.Shape;
+import java.awt.geom.Area;
 import java.awt.geom.Path2D;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.geom.RectangularShape;
+import java.awt.geom.PathIterator;
 import org.opengis.geometry.DirectPosition;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.geometry.DirectPosition2D;
@@ -31,9 +34,13 @@ import org.apache.sis.internal.feature.GeometryWithCRS;
 import org.apache.sis.internal.feature.GeometryWrapper;
 import org.apache.sis.internal.filter.sqlmm.SQLMM;
 import org.apache.sis.internal.referencing.j2d.ShapeUtilities;
+import org.apache.sis.internal.jdk9.JDK9;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Debug;
 
+// Branch-dependent imports
+import org.opengis.filter.SpatialOperatorName;
+
 
 /**
  * The wrapper of Java2D geometries.
@@ -49,7 +56,7 @@ final class Wrapper extends GeometryWithCRS<Shape> {
     /**
      * The wrapped implementation.
      */
-    private final Shape geometry;
+    final Shape geometry;
 
     /**
      * Creates a new wrapper around the given geometry.
@@ -75,6 +82,14 @@ final class Wrapper extends GeometryWithCRS<Shape> {
     }
 
     /**
+     * Returns the geometry as an {@link Area} object, creating it on the fly 
if necessary.
+     * The returned area shall not be modified because it may be the {@link 
#geometry} instance.
+     */
+    private Area area() {
+        return (geometry instanceof Area) ? (Area) geometry : new 
Area(geometry);
+    }
+
+    /**
      * Returns the Java2D envelope as an Apache SIS implementation.
      *
      * @return the envelope of the geometry.
@@ -185,6 +200,39 @@ add:    for (;;) {
     }
 
     /**
+     * Applies a filter predicate between this geometry and another geometry.
+     * This method assumes that the two geometries are in the same CRS (this 
is not verified).
+     */
+    @Override
+    protected boolean predicateSameCRS(final SpatialOperatorName type, final 
GeometryWrapper<Shape> other) {
+        final int ordinal = type.ordinal();
+        if (ordinal >= 0 && ordinal < PREDICATES.length) {
+            final BiPredicate<Wrapper,Object> op = PREDICATES[ordinal];
+            if (op != null) {
+                return op.test(this, other);
+            }
+        }
+        return super.predicateSameCRS(type, other);
+    }
+
+    /**
+     * All predicates recognized by {@link 
#predicateSameCRS(SpatialOperatorName, GeometryWrapper)}.
+     * Array indices are {@link SpatialOperatorName#ordinal()} values.
+     */
+    @SuppressWarnings({"unchecked","rawtypes"})
+    private static final BiPredicate<Wrapper,Object>[] PREDICATES =
+            new BiPredicate[SpatialOperatorName.OVERLAPS.ordinal() + 1];
+    static {
+        PREDICATES[SpatialOperatorName.OVERLAPS  .ordinal()] = // Fallback on 
intersects.
+        PREDICATES[SpatialOperatorName.INTERSECTS.ordinal()] = 
Wrapper::intersect;
+        PREDICATES[SpatialOperatorName.CONTAINS  .ordinal()] = 
Wrapper::contain;
+        PREDICATES[SpatialOperatorName.WITHIN    .ordinal()] = Wrapper::within;
+        PREDICATES[SpatialOperatorName.BBOX      .ordinal()] = Wrapper::bbox;
+        PREDICATES[SpatialOperatorName.EQUALS    .ordinal()] = Wrapper::equal;
+        PREDICATES[SpatialOperatorName.DISJOINT  .ordinal()] = (w,o) -> 
!w.intersect(o);
+    }
+
+    /**
      * Applies a SQLMM operation on this geometry.
      *
      * @param  operation  the SQLMM operation to apply.
@@ -194,14 +242,163 @@ add:    for (;;) {
      */
     @Override
     protected Object operationSameCRS(final SQLMM operation, final 
GeometryWrapper<Shape> other, final Object argument) {
+        final Shape result;
         switch (operation) {
+            case ST_Dimension:
+            case ST_CoordDim:   return 2;
+            case ST_Is3D:
+            case ST_IsMeasured: return Boolean.FALSE;
+            case ST_IsEmpty: {
+                if (geometry instanceof RectangularShape) {
+                    return ((RectangularShape) geometry).isEmpty();
+                } else {
+                    return geometry.getPathIterator(null).isDone();
+                }
+            }
+            case ST_Overlaps:   // Our approximate algorithm can not 
distinguish with intersects.
+            case ST_Intersects: return  intersect(other);
+            case ST_Disjoint:   return !intersect(other);
+            case ST_Contains:   return  contain  (other);
+            case ST_Within:     return  within   (other);
+            case ST_Equals:     return  equal    (other);
+            case ST_Envelope:   return getEnvelope();
+            case ST_Boundary:   result = geometry.getBounds2D(); break;
             case ST_Centroid: {
                 final RectangularShape frame = (geometry instanceof 
RectangularShape)
                                 ? (RectangularShape) geometry : 
geometry.getBounds2D();
                 return new Point2D.Double(frame.getCenterX(), 
frame.getCenterY());
             }
+            case ST_Intersection: {
+                final Area area = new Area(geometry);
+                area.intersect(((Wrapper) other).area());
+                result = area;
+                break;
+            }
+            case ST_Union: {
+                final Area area = new Area(geometry);
+                area.add(((Wrapper) other).area());
+                result = area;
+                break;
+            }
+            case ST_Difference: {
+                final Area area = new Area(geometry);
+                area.subtract(((Wrapper) other).area());
+                result = area;
+                break;
+            }
+            case ST_SymDifference: {
+                final Area area = new Area(geometry);
+                area.exclusiveOr(((Wrapper) other).area());
+                result = area;
+                break;
+            }
             default: return super.operationSameCRS(operation, other, argument);
         }
+        // No metadata to copy in current version.
+        return result;
+    }
+
+    /**
+     * Estimates whether the wrapped geometry is equal to the geometry of the 
given wrapper.
+     *
+     * @param  wrapper  instance of {@link Wrapper}.
+     */
+    private boolean equal(final Object wrapper) {       // "s" omitted for 
avoiding confusion with super.equals(…).
+        if (wrapper instanceof Wrapper) {
+            final Shape other = ((Wrapper) wrapper).geometry;
+            final PathIterator it1 = geometry.getPathIterator(null);
+            final PathIterator it2 = other.getPathIterator(null);
+            if (it1.getWindingRule() == it2.getWindingRule()) {
+                final double[] p1 = new double[6];
+                final double[] p2 = new double[6];
+                while (!it1.isDone()) {
+                    if (it2.isDone()) return false;
+                    final int c = it1.currentSegment(p1);
+                    if (c != it2.currentSegment(p2)) {
+                        return false;
+                    }
+                    it1.next();
+                    it2.next();
+                    final int n;
+                    switch (c) {
+                        case PathIterator.SEG_CLOSE: continue;
+                        case PathIterator.SEG_MOVETO:
+                        case PathIterator.SEG_LINETO: n=2; break;
+                        case PathIterator.SEG_QUADTO: n=4; break;
+                        default: n=6; break;
+                    }
+                    if (!JDK9.equals(p1, 0, n, p2, 0, n)) {
+                        return false;
+                    }
+                }
+                return it2.isDone();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Estimates whether the wrapped geometry is contained by the geometry of 
the given wrapper.
+     * This method may conservatively returns {@code false} if an accurate 
computation would be
+     * too expansive.
+     *
+     * @param  wrapper  instance of {@link Wrapper}.
+     */
+    private boolean within(final Object wrapper) {
+        if (wrapper instanceof Wrapper) {
+            final Shape other = ((Wrapper) wrapper).geometry;
+            return other.contains(geometry.getBounds2D());
+        }
+        return false;
+    }
+
+    /**
+     * Estimates whether the wrapped geometry contains the geometry of the 
given wrapper.
+     * This method may conservatively returns {@code false} if an accurate 
computation would
+     * be too expansive.
+     *
+     * @param  wrapper  instance of {@link Wrapper} or {@link PointWrapper}.
+     * @throws ClassCastException if the given object is not a recognized 
wrapper.
+     */
+    private boolean contain(final Object wrapper) {     // "s" omitted for 
avoiding confusion with super.contains(…).
+        if (wrapper instanceof PointWrapper) {
+            return geometry.contains(((PointWrapper) wrapper).point);
+        }
+        final Shape other = ((Wrapper) wrapper).geometry;
+        return geometry.contains(other.getBounds2D());
+    }
+
+    /**
+     * Estimates whether the wrapped geometry intersects the geometry of the 
given wrapper.
+     * This method may conservatively returns {@code true} if an accurate 
computation would
+     * be too expansive.
+     *
+     * @param  wrapper  instance of {@link Wrapper} or {@link PointWrapper}.
+     * @throws ClassCastException if the given object is not a recognized 
wrapper.
+     */
+    private boolean intersect(final Object wrapper) {   // "s" omitted for 
avoiding confusion with super.intersects(…).
+        if (wrapper instanceof PointWrapper) {
+            return geometry.contains(((PointWrapper) wrapper).point);
+        }
+        final Shape other = ((Wrapper) wrapper).geometry;
+        return geometry.intersects(other.getBounds2D()) && 
other.intersects(geometry.getBounds2D());
+    }
+
+    /**
+     * Estimates whether the wrapped geometry intersects the geometry of the 
given wrapper, testing only
+     * the bounding box of the {@code wrapper} argument. This method may be 
more accurate than required
+     * by OGC Filter Encoding specification in that this geometry is not 
simplified to a bounding box.
+     * But Java2D implementations sometime use bounding box approximation, so 
the result may be the same.
+     *
+     * @param  wrapper  instance of {@link Wrapper} or {@link PointWrapper}.
+     * @throws ClassCastException if the given object is not a recognized 
wrapper.
+     */
+    private boolean bbox(final Object wrapper) {
+        if (wrapper instanceof PointWrapper) {
+            return geometry.contains(((PointWrapper) wrapper).point);
+        }
+        final Shape other = ((Wrapper) wrapper).geometry;
+        return geometry.intersects(other.getBounds2D());
     }
 
     /**
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java
index ba048a7..cb7da80 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java
@@ -138,7 +138,7 @@ final class GeometryConverter<R,G> extends Node implements 
Optimization.OnExpres
      *       If we can not do that, check how to propagate the wrap-around 
policy from some context.
      *
      * @param  input  the geometry to evaluate with this expression.
-     * @return the geometry boundary.
+     * @return the geometry wrapper, or {@code null} if the evaluated value is 
null.
      * @throws InvalidFilterValueException if the expression result is not an 
instance of a supported type.
      */
     @Override
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterUsingJava2D_Test.java
 
b/core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterUsingJava2D_Test.java
new file mode 100644
index 0000000..5fccd95
--- /dev/null
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterUsingJava2D_Test.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.filter;
+
+import java.awt.Shape;
+import org.junit.Ignore;
+import org.junit.Test;
+
+
+/**
+ * Tests {@link BinarySpatialFilter} implementations using ESRI library.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public final strictfp class BinarySpatialFilterUsingJava2D_Test extends 
BinarySpatialFilterTestCase<Shape> {
+    /**
+     * Creates a new test.
+     */
+    public BinarySpatialFilterUsingJava2D_Test() {
+        super(Shape.class);
+    }
+
+    /**
+     * Test ignored for now (not yet mapped to a Java2D operation).
+     */
+    @Test
+    @Override
+    @Ignore("Not yet mapped to a Java2D operation.")
+    public void testTouches() {
+    }
+
+    /**
+     * Test ignored for now (not yet mapped to a Java2D operation).
+     */
+    @Test
+    @Override
+    @Ignore("Not yet mapped to a Java2D operation.")
+    public void testCrosses() {
+    }
+
+    /**
+     * Test ignored for now (not yet mapped to a Java2D operation).
+     */
+    @Test
+    @Override
+    @Ignore("Not yet mapped to a Java2D operation.")
+    public void testOverlaps() {
+    }
+
+    /**
+     * Test ignored for now (not yet mapped to a Java2D operation).
+     */
+    @Test
+    @Override
+    @Ignore("Not yet mapped to a Java2D operation.")
+    public void testDWithin() {
+    }
+
+    /**
+     * Test ignored for now (not yet mapped to a Java2D operation).
+     */
+    @Test
+    @Override
+    @Ignore("Not yet mapped to a Java2D operation.")
+    public void testBeyond() {
+    }
+
+    /**
+     * Test ignored for now (not yet mapped to a Java2D operation).
+     */
+    @Test
+    @Override
+    @Ignore("Not yet mapped to a Java2D operation.")
+    public void testWithReprojection() {
+    }
+
+    /**
+     * Test ignored for now because {@link java.awt.geom.Path2D} does not 
implement {@code equals(Object)}.
+     */
+    @Test
+    @Ignore
+    @Override
+    public void testSerialization() {
+    }
+}
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryTestCase.java
 
b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryTestCase.java
index 4c158ed..d9c3f4f 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryTestCase.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryTestCase.java
@@ -356,7 +356,7 @@ public abstract strictfp class RegistryTestCase<G> extends 
TestCase {
         // Border should intersect. Also use Feature instead of Literal as a 
source.
         final Feature feature = 
createFeatureWithGeometry(library.polygonClass);
         final ValueReference<Feature,?> ref = factory.property(P_NAME, 
library.polygonClass);
-        point = library.createPoint(1.2, 0.2);
+        point = library.createPoint(0.2, 0.3);
         function = factory.function("ST_Intersects", ref, 
factory.literal(point));
         assertEquals(Boolean.TRUE, function.apply(feature));
 
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingESRI_Test.java
 
b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingESRI_Test.java
index 149d100..8c6a236 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingESRI_Test.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingESRI_Test.java
@@ -40,12 +40,6 @@ public final strictfp class RegistryUsingESRI_Test extends 
RegistryTestCase<Geom
 
     @Test
     @Override
-    @Ignore("Current implementation ignores the distance parameter.")
-    public void testSimplify() {
-    }
-
-    @Test
-    @Override
     @Ignore("Reprojection not yet implemented.")
     public void testTransform() {
     }
@@ -70,6 +64,12 @@ public final strictfp class RegistryUsingESRI_Test extends 
RegistryTestCase<Geom
 
     @Test
     @Override
+    @Ignore("Current implementation ignores the distance parameter.")
+    public void testSimplify() {
+    }
+
+    @Test
+    @Override
     @Ignore("Operation not yet implemented.")
     public void testSimplifyPreserveTopology() {
     }
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingESRI_Test.java
 
b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingJava2D_Test.java
similarity index 81%
copy from 
core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingESRI_Test.java
copy to 
core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingJava2D_Test.java
index 149d100..2f9b1f7 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingESRI_Test.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/RegistryUsingJava2D_Test.java
@@ -16,13 +16,13 @@
  */
 package org.apache.sis.internal.filter.sqlmm;
 
-import com.esri.core.geometry.Geometry;
+import java.awt.Shape;
 import org.junit.Ignore;
 import org.junit.Test;
 
 
 /**
- * Tests {@link SpatialFunction} implementations using JTS library.
+ * Tests {@link SpatialFunction} implementations using Java2D.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -30,18 +30,12 @@ import org.junit.Test;
  * @since   1.1
  * @module
  */
-public final strictfp class RegistryUsingESRI_Test extends 
RegistryTestCase<Geometry> {
+public final strictfp class RegistryUsingJava2D_Test extends 
RegistryTestCase<Shape> {
     /**
      * Creates a new test.
      */
-    public RegistryUsingESRI_Test() {
-        super(Geometry.class, false);
-    }
-
-    @Test
-    @Override
-    @Ignore("Current implementation ignores the distance parameter.")
-    public void testSimplify() {
+    public RegistryUsingJava2D_Test() {
+        super(Shape.class, false);
     }
 
     @Test
@@ -71,6 +65,18 @@ public final strictfp class RegistryUsingESRI_Test extends 
RegistryTestCase<Geom
     @Test
     @Override
     @Ignore("Operation not yet implemented.")
+    public void testBuffer() {
+    }
+
+    @Test
+    @Override
+    @Ignore("Operation not yet implemented.")
+    public void testSimplify() {
+    }
+
+    @Test
+    @Override
+    @Ignore("Operation not yet implemented.")
     public void testSimplifyPreserveTopology() {
     }
 }
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
 
b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
index 55be51d..dfe80ee 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
@@ -60,12 +60,14 @@ import org.junit.runners.Suite;
     org.apache.sis.filter.TemporalFilterTest.class,
     org.apache.sis.filter.BinarySpatialFilterUsingJTS_Test.class,
     org.apache.sis.filter.BinarySpatialFilterUsingESRI_Test.class,
+    org.apache.sis.filter.BinarySpatialFilterUsingJava2D_Test.class,
     org.apache.sis.internal.feature.AttributeConventionTest.class,
     org.apache.sis.internal.feature.GeometryTypeTest.class,
     org.apache.sis.internal.filter.FunctionNamesTest.class,
     org.apache.sis.internal.filter.sqlmm.SQLMMTest.class,
     org.apache.sis.internal.filter.sqlmm.RegistryUsingJTS_Test.class,
     org.apache.sis.internal.filter.sqlmm.RegistryUsingESRI_Test.class,
+    org.apache.sis.internal.filter.sqlmm.RegistryUsingJava2D_Test.class,
     org.apache.sis.internal.feature.j2d.ShapePropertiesTest.class,
     org.apache.sis.internal.feature.j2d.FlatShapeTest.class,
     org.apache.sis.internal.feature.j2d.FactoryTest.class,

Reply via email to