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

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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 1a6b0d3841 Check whether an envelope literal is finite before to 
accept as an optimization.
1a6b0d3841 is described below

commit 1a6b0d38419c367183e5be6587c5d98f29398225
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Oct 31 12:45:16 2026 +0100

    Check whether an envelope literal is finite before to accept as an 
optimization.
---
 .../apache/sis/filter/BinaryGeometryFilter.java    |  9 ++++++-
 .../main/org/apache/sis/filter/DistanceFilter.java |  6 ++---
 .../org/apache/sis/filter/sqlmm/TwoGeometries.java | 12 +++++++---
 .../sis/geometry/wrapper/GeometryWrapper.java      | 28 ++++++++++++++++++++++
 .../geometry/wrapper/SpatialOperationContext.java  | 10 ++------
 .../apache/sis/geometry/wrapper/esri/Wrapper.java  |  4 ++--
 .../sis/geometry/wrapper/j2d/PointWrapper.java     | 10 ++++++++
 .../apache/sis/geometry/wrapper/jts/Wrapper.java   | 17 +++++++++++++
 .../org/apache/sis/geometry/AbstractEnvelope.java  |  4 ++--
 9 files changed, 81 insertions(+), 19 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/BinaryGeometryFilter.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/BinaryGeometryFilter.java
index 1dd43d7fa3..f8d32fab1a 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/BinaryGeometryFilter.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/BinaryGeometryFilter.java
@@ -215,12 +215,19 @@ abstract class BinaryGeometryFilter<R> extends Node 
implements SpatialOperator<R
              * If we are optimizing for a feature type, and if the other 
expression is a property value,
              * then try to fetch the CRS of the property values. If we can 
transform the literal to that
              * CRS, do it now in order to avoid doing this transformation for 
all feature instances.
+             *
+             * The transformed geometry may have infinite bounds (for example 
world-wide box in a Mercator projection),
+             * which is okay for operations such as disjoin, touches, within, 
overlaps, crosses, intersects or contains
+             * when those operations are applied on Apache SIS envelopes, but 
uncertain with more complex geometries.
+             * Infinite bounds may also be problematic for operations 
involving a distance such as beyond and d-within.
+             * In such cases, do not optimize and let the feature geometries 
be transformed at evaluation time.
+             * Maybe a different CRS will be selected, or maybe PostGIS (for 
example) will use a different strategy.
              */
             final GeometryWrapper geometry = wrapper.apply(null);
             if (geometry != null) try {
                 final CoordinateReferenceSystem crs = 
optimization.findExpectedCRS(other).orElse(null);
                 final GeometryWrapper transformed = geometry.transform(crs, 
true);
-                if (geometry != transformed) {
+                if (geometry != transformed && transformed.isFinite2D()) {
                     literal = Optimization.literal(transformed);
                     if (literal == effective1) effective1 = literal;
                     else effective2 = literal;
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DistanceFilter.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DistanceFilter.java
index c71d0c641e..e68de7ff07 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DistanceFilter.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DistanceFilter.java
@@ -36,11 +36,11 @@ import org.opengis.filter.DistanceOperatorName;
 
 /**
  * Spatial operations between two geometries and using a distance.
- * The nature of the operation depends on the subclass.
+ * The nature of the operation depends on {@link #getOperatorType()}.
  *
- * <h2>API design note</h2>
+ * <p><b><abbr>API</abbr> design note:</b>
  * This class has 3 parameters, but the third one is not an expression.
- * It still a "binary" operator if we count only the expressions.
+ * It still a "binary" operator if we count only the expressions.</p>
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/TwoGeometries.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/TwoGeometries.java
index fee6aabe35..a31bbac647 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/TwoGeometries.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/TwoGeometries.java
@@ -31,7 +31,7 @@ import org.opengis.filter.Literal;
 
 
 /**
- * SQLMM spatial functions taking two geometry operands.
+ * <abbr>SQLMM</abbr> spatial functions taking two geometry operands.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -81,7 +81,13 @@ class TwoGeometries<R> extends SpatialFunction<R> {
             if (literal != null) try {
                 final CoordinateReferenceSystem crs = 
optimization.findExpectedCRS(unwrap(geometry1)).orElse(null);
                 final GeometryWrapper transformed = literal.transform(crs, 
true);
-                if (transformed != literal) {
+                if (transformed != literal && transformed.isFinite2D()) {
+                    /*
+                     * While infinite bounds are okay when computing 
intersections between Apache SIS envelopes,
+                     * they may be problematic for more complex geometries or 
for operations computing distances.
+                     * Skipping the optimization in such cases (above 
condition) is conservative: transforms will
+                     * be applied at evaluation time, maybe by delegating to a 
storage engine such as PostGIS.
+                     */
                     @SuppressWarnings({"unchecked","rawtypes"})
                     final Expression<R,?>[] effective = 
getParameters().toArray(Expression[]::new);
                     effective[1] = Optimization.literal(transformed);
@@ -138,7 +144,7 @@ class TwoGeometries<R> extends SpatialFunction<R> {
 
 
     /**
-     * SQLMM spatial functions taking a single geometry operand with one 
argument.
+     * <abbr>SQLMM</abbr> spatial functions taking a single geometry operand 
with one argument.
      *
      * @param  <R>  the type of resources (e.g. {@code Feature}) used as 
inputs.
      */
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/GeometryWrapper.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/GeometryWrapper.java
index dfa4641402..68a09fd6ff 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/GeometryWrapper.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/GeometryWrapper.java
@@ -229,6 +229,18 @@ public abstract class GeometryWrapper extends 
AbstractGeometry implements Geomet
     @Debug
     public abstract double[] getAllCoordinates();
 
+    /**
+     * Returns whether the coordinates in the two first dimensions are finite.
+     * The <abbr>Z</abbr> and <abbr>M</abbr> coordinates are ignored.
+     * For performance reasons, this method checks only the envelope.
+     * Note that a finite envelope may be empty.
+     *
+     * @return whether the two-dimensional coordinates are non-NaN and finite.
+     */
+    public boolean isFinite2D() {
+        return getEnvelope().subEnvelope(0, 
Geometries.BIDIMENSIONAL).isFinite();
+    }
+
     /**
      * Appends a sequence of points or polylines after this geometry.
      * Each previous polyline will be a separated path in the new polyline 
instance.
@@ -495,6 +507,22 @@ public abstract class GeometryWrapper extends 
AbstractGeometry implements Geomet
         throw new 
UnconvertibleObjectException(Errors.format(Errors.Keys.CanNotConvertFromType_2, 
geometry.getClass(), type));
     }
 
+    /**
+     * Transforms this geometry using the given coordinate operation.
+     * The caller is responsible to ensure that the source <abbr>CRS</abbr> of 
the given {@code operation}
+     * is equivalent to {@link #getCoordinateReferenceSystem()}. This is not 
verified by this method.
+     *
+     * @param  operation  the coordinate operation to apply.
+     * @return the transformed geometry (may be the same geometry instance, 
but never {@code null}).
+     * @throws UnsupportedOperationException if this operation is not 
supported for current implementation.
+     * @throws FactoryException if a problem happened while mapping a 
<abbr>CRS</abbr> from/to a <abbr>SRID</abbr>.
+     * @throws TransformException if the geometry cannot be transformed.
+     * @throws BackingStoreException if the operation failed because of 
another checked exception.
+     */
+    final GeometryWrapper transform(final CoordinateOperation operation) 
throws FactoryException, TransformException {
+        return transform(operation.getMathTransform(), 
operation.getTargetCRS());
+    }
+
     /**
      * Transforms this geometry using the given transform.
      * If the transform is identity, then the geometry is returned unchanged 
(with the same <abbr>CRS</abbr>).
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java
index aa4e108deb..184477b7fc 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java
@@ -32,8 +32,6 @@ import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.OperationMethod;
-import org.opengis.referencing.operation.CoordinateOperation;
-import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.ImmutableIdentifier;
 import org.apache.sis.referencing.CRS;
@@ -158,9 +156,7 @@ public final class SpatialOperationContext implements 
Serializable {
         if (computationCRS != null) {
             final CoordinateReferenceSystem sourceCRS = 
to2D(geometry.getCoordinateReferenceSystem());
             if (sourceCRS != null && !CRS.equivalent(computationCRS, 
sourceCRS)) {
-                final CoordinateOperation operation = 
CRS.findOperation(sourceCRS, computationCRS, areaOfInterest);
-                final MathTransform transform = operation.getMathTransform();
-                geometry = geometry.transform(transform, 
operation.getTargetCRS());
+                geometry = geometry.transform(CRS.findOperation(sourceCRS, 
computationCRS, areaOfInterest));
             }
         }
         return geometry;
@@ -259,9 +255,7 @@ select:     if (commonCRS == null) {
                 if (i != skipIndex) {
                     final CoordinateReferenceSystem sourceCRS = allCRS[i];
                     if (!CRS.equivalent(commonCRS, sourceCRS)) {
-                        final CoordinateOperation operation = 
CRS.findOperation(sourceCRS, commonCRS, areaOfInterest);
-                        final MathTransform transform = 
operation.getMathTransform();
-                        geometries[i] = geometries[i].transform(transform, 
operation.getTargetCRS());
+                        geometries[i] = 
geometries[i].transform(CRS.findOperation(sourceCRS, commonCRS, 
areaOfInterest));
                     }
                 }
             }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/esri/Wrapper.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/esri/Wrapper.java
index f956791357..4b97c98ea0 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/esri/Wrapper.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/esri/Wrapper.java
@@ -48,7 +48,7 @@ import org.opengis.filter.SpatialOperatorName;
 
 
 /**
- * The wrapper of ERSI geometries.
+ * The wrapper of <abbr>ERSI</abbr> geometries.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -84,7 +84,7 @@ final class Wrapper extends GeometryWrapper {
     }
 
     /**
-     * Returns the ESRI envelope as an Apache SIS implementation.
+     * Returns the <abbr>ESRI</abbr> envelope as an Apache <abbr>SIS</abbr> 
implementation.
      *
      * @return the envelope.
      */
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/j2d/PointWrapper.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/j2d/PointWrapper.java
index da3ce14fc3..c494372ef7 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/j2d/PointWrapper.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/j2d/PointWrapper.java
@@ -70,6 +70,16 @@ final class PointWrapper extends GeometryWrapper {
         return point;
     }
 
+    /**
+     * Returns whether the coordinates of this geometry are finite.
+     *
+     * @return whether the coordinates are non-NaN and finite.
+     */
+    @Override
+    public boolean isFinite2D() {
+        return Double.isFinite(point.getX()) && Double.isFinite(point.getY());
+    }
+
     /**
      * Returns an empty envelope centered on this point.
      */
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
index c3b24feff0..1f3e98f04c 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
@@ -248,6 +248,23 @@ final class Wrapper extends GeometryWrapper {
         return coordinates;
     }
 
+    /**
+     * Returns whether the coordinates in the two first dimensions are finite.
+     * The <abbr>Z</abbr> and <abbr>M</abbr> coordinates are ignored.
+     * For performance reasons, this method checks only the envelope.
+     *
+     * @return whether the two-dimensional coordinates are non-NaN and finite.
+     */
+    @Override
+    public boolean isFinite2D() {
+        final Envelope bounds = geometry.getEnvelopeInternal();
+        return !bounds.isNull()
+                && Double.isFinite(bounds.getMinX())
+                && Double.isFinite(bounds.getMinY())
+                && Double.isFinite(bounds.getMaxX())
+                && Double.isFinite(bounds.getMaxY());
+    }
+
     /**
      * Merges a sequence of points or paths after the wrapped geometry.
      *
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractEnvelope.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractEnvelope.java
index fdc2b9c697..d796b119c0 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractEnvelope.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractEnvelope.java
@@ -677,8 +677,8 @@ public abstract class AbstractEnvelope extends 
FormattableObject implements Enve
 
     /**
      * Determines whether or not this envelope contains only finite values.
-     * More specifically this method returns {@code false} if at least one
-     * coordinate value is NaN or infinity, and {@code true} otherwise.
+     * This method returns {@code false} if at least one coordinate value
+     * is <abbr>NaN</abbr> or infinity, and {@code true} otherwise.
      * Note that a finite envelope may be {@linkplain #isEmpty() empty}.
      *
      * @return {@code true} if this envelope contains only finite coordinate 
values.

Reply via email to