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 6988814b79 Avoid an `IllegalGridGeometryException` when a pyramid 
contains no "grid to CRS" transform.
6988814b79 is described below

commit 6988814b79345969c34cd2681d5d59a9565035c8
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Jun 24 18:57:23 2026 +0200

    Avoid an `IllegalGridGeometryException` when a pyramid contains no "grid to 
CRS" transform.
---
 .../apache/sis/coverage/grid/GridDerivation.java   | 61 ++++++++++++++++------
 .../org/apache/sis/coverage/grid/GridGeometry.java | 16 ++----
 .../sis/coverage/grid/ResampledGridCoverage.java   |  2 +-
 .../sis/coverage/grid/GridDerivationTest.java      | 19 +++++++
 4 files changed, 70 insertions(+), 28 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
index 2f1f394eda..d330971eff 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
@@ -87,7 +87,7 @@ import org.opengis.coverage.PointOutsideCoverageException;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Alexis Manin (Geomatys)
- * @version 1.6
+ * @version 1.7
  *
  * @see GridGeometry#derive()
  * @see GridGeometry#selectDimensions(int[])
@@ -543,27 +543,58 @@ public class GridDerivation {
         if (base.equals(areaOfInterest, ComparisonMode.BY_CONTRACT)) {
             return this;
         }
-        if (areaOfInterest.isEnvelopeOnly()) {
-            return subgrid(areaOfInterest.envelope, (double[]) null);
-        }
         final double[] scales;
-        if (areaOfInterest.isExtentOnly()) {
+        if (areaOfInterest.extent == null && areaOfInterest.envelope == null 
&& base.gridToCRS == null) {
+            /*
+             * Case when `areaOfInterest` specifies only a resolution 
(typically in a pyramid)
+             * and we cannot use that resolution by a concatenation of "grid 
to CRS" transforms.
+             * The given resolution is relative to the base grid which was 
used for deriving `areaOfInterest`.
+             * We don't really know if it was the same base as `this.base`, 
but there is good chances that the
+             * fact that `base.gridToCRS` is null is the reason why 
`areaOfInterest` has no information other
+             * than resolution. On the assumption that both bases are the 
same, we need to divide the specified
+             * resolution by `base.resolution` in order to get subsampling 
factors.
+             */
+            scales = areaOfInterest.getResolution(true);
+            final double[] resolution = base.resolution;
+            if (resolution != null) {
+                for (int i = scales.length; --i >= 0;) {
+                    scales[i] /= resolution[i];
+                }
+            }
+        } else if (areaOfInterest.gridToCRS == null && (areaOfInterest.extent 
== null) != (areaOfInterest.envelope == null)) {
+            /*
+             * Case of some kind of bounding box (in grid units or CRS units) 
without "grid to CRS" transform.
+             * In principle, `areaOfInterest.resolution` should be null since 
`areaOfInterest.gridToCRS` is null.
+             * Nevertheless, it may be non-null if the given `areaOfInterest` 
has been derived from another grid,
+             * in which case the resolution requested by the user was saved 
even if no `gridToCRS` was available.
+             */
+            if (areaOfInterest.extent == null) {
+                double[] resolution = areaOfInterest.resolution;
+                if (resolution != null) {
+                    resolution = resolution.clone();
+                }
+                return subgrid(areaOfInterest.envelope, resolution);
+            }
             if (baseExtent != null) {
                 baseExtent = baseExtent.intersect(areaOfInterest.extent);
                 subGridSetter = "subgrid";
             }
-            scales = areaOfInterest.resolution;
             /*
-             * In principle, the above `resolution` should be null because it 
could not be derived from `gridToCRS`,
-             * because the latter is null (otherwise `isExtentOnly()` would 
have been false). However, an exception
-             * to this rule happens if the given `areaOfInterest` has been 
computed by another `GridDerivation`,
-             * in which case the resolution requested by the user was saved 
even if no `gridToCRS` was available.
-             * That resolution is relative to the base grid of the other 
`GridDerivation` instead of this one,
-             * but we ignore that details because the `resolution` field is 
only indicative, especially since
-             * the subsampling offsets are lost. If the exact resolution and 
subsampling offsets where known,
-             * they would have been stored in `gridToCRS`.
+             * The resolution is relative to the base grid which was used for 
deriving `areaOfInterest`.
+             * We don't really know if it was the same base as `this.base`, 
but it is surely not the case if
+             * `base.gridToCRS` is non-null, otherwise `areaOfInterest` would 
have inherited that transform.
+             * Conservatively do NOT divide the specified resolution by 
`base.resolution`.
+             *
+             * This approach is quite approximate. But the `resolution` field 
is only indicative anyway,
+             * especially since the subsampling offsets are lost. If the exact 
resolution and subsampling
+             * offsets where known, they should have been stored in 
`gridToCRS`.
              */
+            scales = areaOfInterest.resolution;
         } else try {
+            /*
+             * Case of grid geometries for which we require the presence of a 
"grid to CRS" transform.
+             * An `IllegalGridGeometryException` will be thrown if a transform 
is missing.
+             */
             final MathTransform nowraparound;
             final var finder = new CoordinateOperationFinder(areaOfInterest, 
base);
             finder.verifyPresenceOfCRS(false);
@@ -697,7 +728,7 @@ public class GridDerivation {
      * @param  resolution      the desired resolution in the same units and 
order than the axes of the given envelope,
      *                         or {@code null} or an empty array if no 
subsampling is desired. The array length should
      *                         be equal to the {@code areaOfInterest} 
dimension, but this is not mandatory
-     *                         (zero or missing values mean no sub-sampling, 
extraneous values are ignored).
+     *                         (zero or NaN values mean no sub-sampling, 
extraneous values are ignored).
      * @return {@code this} for method call chaining.
      * @throws DisjointExtentException if the given area of interest does not 
intersect the grid extent.
      * @throws IncompleteGridGeometryException if the base grid geometry has 
no extent, no "grid to CRS" transform,
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
index 2dbea346be..10a3778369 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
@@ -1604,18 +1604,10 @@ public class GridGeometry implements LenientComparable, 
Serializable {
     }
 
     /**
-     * Returns {@code true} if this grid geometry contains only a grid extent 
and no other information.
-     * Note: if {@link #gridToCRS} is {@code null}, then {@link #cornerToCRS} 
and {@link #resolution}
-     * should be null as well.
-     */
-    final boolean isExtentOnly() {
-        return gridToCRS == null && envelope == null && extent != null;
-    }
-
-    /**
-     * Returns {@code true} if this grid geometry contains only an envelope 
and no other information.
-     * Note: if {@link #gridToCRS} is {@code null}, then {@link #cornerToCRS} 
and {@link #resolution}
-     * should be null as well.
+     * Returns {@code true} if this grid geometry contains only an envelope 
with no extent or transform.
+     * Note: if {@link #gridToCRS} is {@code null}, then {@link #cornerToCRS} 
shall also be {@code null}.
+     * The {@link #resolution} array is usually also null, but may be non-null 
if this grid geometry was
+     * {@linkplain #GridGeometry(GridGeometry, GridExtent, MathTransform) 
derived from another grid}.
      */
     final boolean isEnvelopeOnly() {
         return gridToCRS == null && extent == null && envelope != null;
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ResampledGridCoverage.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ResampledGridCoverage.java
index 67a4710758..237401e492 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ResampledGridCoverage.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ResampledGridCoverage.java
@@ -292,7 +292,7 @@ final class ResampledGridCoverage extends 
DerivedGridCoverage {
             throws FactoryException, TransformException
     {
         final GridGeometry sourceGG = source.getGridGeometry();
-        final CoordinateOperationFinder changeOfCRS = new 
CoordinateOperationFinder(sourceGG, target);
+        final var changeOfCRS = new CoordinateOperationFinder(sourceGG, 
target);
         changeOfCRS.verifyPresenceOfCRS(true);
         /*
          * Compute the transform from source pixels to target CRS (to be 
completed to target pixels later).
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
index 6abb65f224..3e3405a4ed 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
@@ -617,6 +617,25 @@ public final class GridDerivationTest extends TestCase {
         assertArrayEquals(new double[] {3, 5}, result.getResolution(false));
     }
 
+    /**
+     * Tests {@link GridDerivation#subgrid(GridExtent, long...)} with a null 
"grid to CRS" transform
+     * but a non-null resolution.
+     */
+    @Test
+    public void testSubgridWithoutTransformButWithResolution() {
+        GridGeometry aoi  = new GridGeometry(null, PixelInCell.CELL_CORNER, 
MathTransforms.scale(128, 128), null);
+        GridGeometry base = new GridGeometry(new GridExtent(2048, 798), null, 
null, null, aoi.resolution, 0);
+        assertArrayEquals(new double[] {128, 128}, base.getResolution(false));
+        assertSame(base, base.derive().subgrid(aoi).build());
+
+        aoi = new GridGeometry(null, PixelInCell.CELL_CORNER, 
MathTransforms.scale(256, 256), null);
+        GridGeometry derived = base.derive().subgrid(aoi).build();
+        assertArrayEquals(new double[] {256, 256}, 
derived.getResolution(false));
+        assertExtentEquals(new long[2], new long[] {1023, 398}, 
derived.getExtent());
+        assertNull(derived.gridToCRS);
+        assertNull(derived.envelope);
+    }
+
     /**
      * Tests {@link GridDerivation#slice(DirectPosition)}.
      */

Reply via email to