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.