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 42a8abce5b Mave `Envelopes.transform(CoordinateOperation, …)` more 
tolerant to the case where the envelope CRS does not match the coordinate 
operation source CRS.
42a8abce5b is described below

commit 42a8abce5bc00ba7a6899eea142fe755d71503aa
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Jul 21 17:46:55 2026 +0200

    Mave `Envelopes.transform(CoordinateOperation, …)` more tolerant to the case
    where the envelope CRS does not match the coordinate operation source CRS.
---
 .../main/org/apache/sis/geometry/Envelopes.java    | 39 ++++++++++++----------
 .../sis/referencing/datum/DatumOrEnsemble.java     |  2 +-
 .../operation/CoordinateOperationContext.java      |  6 ++--
 .../operation/CoordinateOperationFinder.java       | 23 +++++++------
 .../operation/provider/DatumShiftMethod.java       |  6 +++-
 5 files changed, 43 insertions(+), 33 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/Envelopes.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/Envelopes.java
index 96f352fd07..dff81ab9eb 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/Envelopes.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/Envelopes.java
@@ -49,6 +49,7 @@ import 
org.apache.sis.referencing.operation.CoordinateOperationContext;
 import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
 import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
 import org.apache.sis.referencing.operation.transform.WraparoundTransform;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.internal.shared.CoordinateOperations;
 import org.apache.sis.referencing.internal.shared.DirectPositionView;
 import org.apache.sis.referencing.internal.shared.TemporalAccessor;
@@ -102,7 +103,7 @@ import static 
org.apache.sis.util.StringBuilders.trimFractionalPart;
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Johann Sorel (Geomatys)
- * @version 1.6
+ * @version 1.7
  *
  * @see org.apache.sis.metadata.iso.extent.Extents
  * @see CRS
@@ -577,11 +578,12 @@ nextPoint:  for (int pointIndex = 0;;) {                
// Break condition at th
      * or when it cross the ±180° longitude.</p>
      *
      * <h4>Usage note</h4>
-     * If the envelope CRS is non-null, then the caller should ensure that the 
operation source CRS
-     * is the same as the envelope CRS. In case of mismatch, this method 
transforms the envelope
-     * to the operation source CRS before to apply the operation. This extra 
step may cause a lost
-     * of accuracy. In order to prevent this method from performing such 
pre-transformation (if not desired),
-     * callers can ensure that the envelope CRS is {@code null} before to call 
this method.
+     * If the given envelope has a non-null <abbr>CRS</abbr>, then the caller 
should ensure that the
+     * {@linkplain Envelope#getCoordinateReferenceSystem() envelope 
<abbr>CRS</abbr>} is equivalent
+     * to the {@linkplain CoordinateOperation#getSourceCRS() operation source 
<abbr>CRS</abbr>}.
+     * In case of mismatch, this method may add an operation step from the 
former to the latter.
+     * IF such extra step is not desired or for more predictable results,
+     * callers can force the envelope <abbr>CRS</abbr> to {@code null} before 
to call this method.
      *
      * @param  operation  the operation to use.
      * @param  envelope   envelope to transform, or {@code null}. This 
envelope will not be modified.
@@ -592,7 +594,7 @@ nextPoint:  for (int pointIndex = 0;;) {                // 
Break condition at th
      *
      * @since 0.5
      */
-    public static GeneralEnvelope transform(final CoordinateOperation 
operation, Envelope envelope)
+    public static GeneralEnvelope transform(final CoordinateOperation 
operation, final Envelope envelope)
             throws TransformException
     {
         ArgumentChecks.ensureNonNull("operation", operation);
@@ -600,31 +602,34 @@ nextPoint:  for (int pointIndex = 0;;) {                
// Break condition at th
             return null;
         }
         boolean isOperationComplete = true;
-        final CoordinateReferenceSystem sourceCRS = operation.getSourceCRS();
+        MathTransform mt = operation.getMathTransform();
+        CoordinateReferenceSystem sourceCRS = operation.getSourceCRS();
         if (sourceCRS != null) {
             final CoordinateReferenceSystem crs = 
envelope.getCoordinateReferenceSystem();
             if (crs != null && !CRS.equivalent(crs, sourceCRS)) {
                 /*
                  * Argument-check: the envelope CRS seems inconsistent with 
the given operation.
-                 * However, we need to push the check a little bit further, 
since 3D-GeographicCRS
-                 * are considered not equal to CompoundCRS[2D-GeographicCRS + 
ellipsoidal height].
+                 * But this is not sure since `CRS.equivalent(…)` sometime 
returns false negative.
                  * Checking for identity MathTransform is a more powerfull 
(but more costly) check.
-                 * Since we have the MathTransform, perform an opportunist 
envelope transform if it
-                 * happen to be required.
+                 * The very tolerant accuracy is an indication that datum 
shift can be ignored.
                  */
-                final MathTransform mt;
+                final MathTransform prefix;
                 try {
-                    mt = CRS.findOperation(crs, sourceCRS, 
null).getMathTransform();
+                    final var context = new CoordinateOperationContext();
+                    context.setDesiredAccuracy(Double.POSITIVE_INFINITY);
+                    prefix = CRS.findOperation(new 
DefaultCoordinateMetadata(crs, null),
+                                               new 
DefaultCoordinateMetadata(sourceCRS, null),
+                                               context).getMathTransform();
                 } catch (FactoryException e) {
                     throw new 
TransformException(Errors.format(Errors.Keys.CanNotTransformEnvelope), e);
                 }
-                if (!mt.isIdentity()) {
+                if (!prefix.isIdentity()) {
+                    mt = MathTransforms.concatenate(prefix, mt);
+                    sourceCRS = crs;
                     isOperationComplete = false;
-                    envelope = transform(mt, envelope);
                 }
             }
         }
-        final MathTransform mt = operation.getMathTransform();
         final double[] centerPt = new double[mt.getTargetDimensions()];
         final GeneralEnvelope transformed = transform(mt, envelope, centerPt, 
null);
         /*
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DatumOrEnsemble.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DatumOrEnsemble.java
index 2821c8960d..863a15f1ed 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DatumOrEnsemble.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DatumOrEnsemble.java
@@ -111,7 +111,7 @@ public final class DatumOrEnsemble {
     }
 
     /**
-     * Returns the datum or ensemble of a coordinate operation from 
<var>source</var> to <var>target</var>.
+     * Returns the datum or datum ensemble of a coordinate operation from 
<var>source</var> to <var>target</var>.
      * If the two given coordinate reference systems are associated to equal 
datum (ignoring metadata),
      * then this method returns the <var>target</var> datum. Otherwise, this 
method returns
      * the largest ensemble which fully contains the datum or datum ensemble 
of the other <abbr>CRS</abbr>.
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationContext.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationContext.java
index aab9161cbd..2fc0570dff 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationContext.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationContext.java
@@ -251,10 +251,10 @@ public class CoordinateOperationContext implements 
Localized {
      * Returns the desired accuracy in metres.
      * A value of 0 means to search for the most accurate operation.
      *
-     * <p>When searching for the most accurate operation, SIS considers only 
the operations specified by the authority.
+     * <p>When searching for the most accurate operation, <abbr>SIS</abbr> 
considers only the operations specified by the authority.
      * For example, the <cite>Molodensky</cite> method is a better datum shift 
approximation than <cite>Abridged Molodensky</cite>.
-     * But if all coordinate operations defined by the authority use the 
Abridged Molodensky method, then SIS will ignore
-     * the Molodensky one.</p>
+     * But if all coordinate operations defined by the authority use the 
Abridged Molodensky method,
+     * then the Molodensky operation will not be used despite being 
theoretically more accurate.</p>
      *
      * @return the desired accuracy in metres.
      */
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
index baa510d453..8b95682a0f 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
@@ -67,6 +67,7 @@ import 
org.apache.sis.referencing.operation.provider.DatumShiftMethod;
 import org.apache.sis.referencing.operation.provider.GeocentricAffine;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.Classes;
 import org.apache.sis.util.collection.BackingStoreException;
 import org.apache.sis.util.internal.shared.Constants;
 import org.apache.sis.util.internal.shared.DoubleDouble;
@@ -330,7 +331,9 @@ public class CoordinateOperationFinder extends 
CoordinateOperationRegistry {
         // │                Any single CRS  ↔  CRS of the same type            
     │
         // 
└────────────────────────────────────────────────────────────────────────┘
         if (sourceCRS instanceof SingleCRS && targetCRS instanceof SingleCRS) {
-            return createOperationStepFallback((SingleCRS) sourceCRS, 
(SingleCRS) targetCRS);
+            if (Classes.implementSameInterfaces(sourceCRS.getClass(), 
targetCRS.getClass(), SingleCRS.class)) {
+                return createOperationStepFallback((SingleCRS) sourceCRS, 
(SingleCRS) targetCRS);
+            }
         }
         // 
┌────────────────────────────────────────────────────────────────────────┐
         // │                        Compound  ↔  various CRS                   
     │
@@ -837,18 +840,13 @@ public class CoordinateOperationFinder extends 
CoordinateOperationRegistry {
 
     /**
      * Creates an operation between two coordinate reference systems having no 
specialized method.
-     * The two given <abbr>CRS</abbr> should be of the same type. This is not 
verified directly by
-     * this method. However, because the <abbr>CRS</abbr> type is determined 
by the datum type
+     * The two given <abbr>CRS</abbr> should be of the same type, but this is 
verified indirectly:
+     * because the <abbr>CRS</abbr> type is determined by the datum type
      * (sometimes completed by the <abbr>CS</abbr> type), having equivalent 
datum and compatible
      * <abbr>CS</abbr> should be a sufficient criterion for saying that the 
<abbr>CRS</abbr> are
      * of the same type.
      *
-     * <p>This method should be invoked as in last resort only.</p>
-     *
-     * <h4>Implementation type</h4>
-     * The method body is a pattern repeated in most {@code 
createOperationStep(…)} methods of this class.
-     * Except that in other methods, the logic is interleaved with more 
complex checks for datum changes.
-     * Understanding the code of this method can help to understand the code 
of other methods.
+     * <p>This method should be invoked in last resort only.</p>
      *
      * @param  sourceCRS  input coordinate reference system.
      * @param  targetCRS  output coordinate reference system.
@@ -868,10 +866,13 @@ public class CoordinateOperationFinder extends 
CoordinateOperationRegistry {
             typeOfChange = AXIS_CHANGES;
         } else {
             finalDatum = DatumOrEnsemble.ofTarget(sourceCRS, targetCRS);
-            if (finalDatum.isEmpty()) {
+            if (finalDatum.isPresent()) {
+                typeOfChange = SAME_DATUM_ENSEMBLE;
+            } else if (desiredAccuracy == Double.POSITIVE_INFINITY) {
+                typeOfChange = UNSPECIFIED_DATUM_CHANGE;
+            } else {
                 throw new 
OperationNotFoundException(datumChangeNotFound(sourceDatum, targetDatum));
             }
-            typeOfChange = SAME_DATUM_ENSEMBLE;
         }
         final CoordinateSystem sourceCS = sourceCRS.getCoordinateSystem();
         final CoordinateSystem targetCS = targetCRS.getCoordinateSystem();
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/DatumShiftMethod.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/DatumShiftMethod.java
index 37c500e2ff..f48509ae6b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/DatumShiftMethod.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/DatumShiftMethod.java
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.referencing.operation.provider;
 
+import org.apache.sis.referencing.internal.PositionalAccuracyConstant;
+
 
 /**
  * The method to use for applying a datum shift.
@@ -61,7 +63,9 @@ public enum DatumShiftMethod {
      * @see <a href="https://issues.apache.org/jira/browse/SIS-369";>SIS-369</a>
      */
     public static DatumShiftMethod forAccuracy(final double desired) {
-        if (desired >= 5) {
+        if (desired >= PositionalAccuracyConstant.UNKNOWN_ACCURACY) {
+            return NONE;
+        } else if (desired >= 5) {
             return MOLODENSKY;
         } else {
             return GEOCENTRIC_DOMAIN;

Reply via email to