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

commit cc658331f72db20c8b46a795022bbb646771214b
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Jul 8 14:21:58 2026 +0200

    Consolidate the use of default resolution in a new 
`GridGeometry.defaultToGridCRS(Identifier)` method.
    The goal is to handle missing "grid to CRS" transform in a way which is 
consistent between TIFF and HEIF.
---
 .../apache/sis/coverage/grid/GridDerivation.java   | 12 +++---
 .../org/apache/sis/coverage/grid/GridGeometry.java | 45 ++++++++++++++++++++++
 .../grid/IncompleteGridGeometryException.java      |  8 +++-
 .../sis/storage/geotiff/ImageFileDirectory.java    | 28 ++------------
 .../apache/sis/storage/geoheif/ImageResource.java  |  6 ++-
 .../org/apache/sis/storage/geoheif/Pyramid.java    | 25 +-----------
 6 files changed, 67 insertions(+), 57 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 eb36c478f8..9babcebf44 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
@@ -547,12 +547,12 @@ public class GridDerivation {
         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.
+             * and we cannot use that resolution in a concatenation of "grid 
to CRS" transforms.
+             *
+             * Assume that the given resolution is relative to `base`. We 
don't really know if this is true,
+             * but the fact that `base.gridToCRS` is null is probably the 
reason why `areaOfInterest` has no
+             * information other than the 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;
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 81eaa83f18..92660fc262 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
@@ -1589,6 +1589,7 @@ public class GridGeometry implements LenientComparable, 
Serializable {
      * @see #getExtent()
      * @see #getResolution(boolean)
      * @see #getGridToCRS(PixelInCell)
+     * @see #defaultToGridCRS(Identifier)
      */
     public boolean isDefined(final int bitmask) {
         if ((bitmask & ~(CRS | ENVELOPE | EXTENT | GRID_TO_CRS | ORIGIN | 
RESOLUTION | GEOGRAPHIC_EXTENT | TEMPORAL_EXTENT)) != 0) {
@@ -1839,6 +1840,50 @@ public class GridGeometry implements LenientComparable, 
Serializable {
         return this;
     }
 
+    /**
+     * Returns this grid geometry with missing properties defaulting to the 
properties of a grid <abbr>CRS</abbr>.
+     * This method usually returns {@code this} unchanged, except in the 
following rare circumstance:
+     * if this grid geometry has no {@linkplain #getResolution(boolean) 
resolution}, no "grid to <abbr>CRS</abbr>"
+     * transform and no <abbr>CRS</abbr>, then this method returns a new grid 
geometry with the resolution set to 1,
+     * <i>i.e.</i> the resolution is defined as one unit of grid cell.
+     * With such default, the <abbr>CRS</abbr> become implicitly the 
<abbr>CRS</abbr> of the grid.
+     * If {@code datum} is non-null, then this implicit assumption is made 
explicit
+     * by setting the <abbr>CRS</abbr> to the value returned by {@link 
GridExtent#createGridCRS(Identified)} and
+     * by setting "grid to <abbr>CRS</abbr>" to the identity transform.
+     * If {@code datum} is null, then the <abbr>CRS</abbr> and "grid to 
<abbr>CRS</abbr>" properties stay undefined.
+     *
+     * <h4>Usage in context of image pyramids</h4>
+     * The "grid to <abbr>CRS</abbr>" information is sometime missing, for 
example because a file
+     * is an ordinary <abbr>TIFF</abbr> file instead of GeoTIFF, or because of 
encoding error.
+     * Missing "grid to <abbr>CRS</abbr>" information usually causes missing 
grid resolution,
+     * since the latter is derived from the former.
+     * However, the {@inkplain org.apache.sis.storage.tiling tiling} package 
needs a resolution
+     * at each pyramid level, even if the exact "grid to <abbr>CRS</abbr>" 
transform is unknown.
+     * This method can be used for setting a default resolution to the base 
level
+     * (the level with the finest resolution) before to derive the grid 
geometry of other levels.
+     *
+     * @param  datum  name of the engineering datum, or {@code null} for not 
creating a <abbr>CRS</abbr>.
+     * @return a grid geometry with a resolution if possible.
+     *
+     * @since 1.7
+     */
+    public GridGeometry defaultToGridCRS(final Identifier datum) {
+        if (resolution != null || gridToCRS != null || 
getCoordinateReferenceSystem(envelope) != null) {
+            return this;
+        }
+        final double[] newResolution = new double[getDimension()];
+        Arrays.fill(newResolution, 1);
+        MathTransform tr = null;
+        ImmutableEnvelope env = envelope;
+        if (env == null && datum != null && extent != null) {
+            final GeneralEnvelope t = extent.toEnvelope(false);
+            t.setCoordinateReferenceSystem(extent.createGridCRS(datum));
+            env = new ImmutableEnvelope(t);
+            tr = MathTransforms.identity(env.getDimension());
+        }
+        return new GridGeometry(extent, tr, tr, env, newResolution, 0);
+    }
+
     /**
      * Creates a one-, two- or three-dimensional coordinate reference system 
for cell indices in the grid.
      * This method returns a CRS which is derived from the "real world" CRS or 
a subset of it.
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/IncompleteGridGeometryException.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/IncompleteGridGeometryException.java
index d99bd7d7b4..66a0c46d60 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/IncompleteGridGeometryException.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/IncompleteGridGeometryException.java
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.coverage.grid;
 
+import org.opengis.metadata.Identifier;
+
 
 /**
  * Thrown by {@link GridGeometry} when a grid geometry cannot provide the 
requested information.
@@ -36,7 +38,11 @@ package org.apache.sis.coverage.grid;
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @version 1.0
- * @since   1.0
+ *
+ * @see GridGeometry#isDefined(int)
+ * @see GridGeometrydefaultToGridCRS(Identifier)
+ *
+ * @since 1.0
  */
 public class IncompleteGridGeometryException extends IllegalStateException {
     /**
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
index a5647855a6..b3063a1a72 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
@@ -1498,10 +1498,10 @@ final class ImageFileDirectory extends DataCube {
                 if (source != null) {
                     domain = reader.store.customizer.customize(source, domain);
                 }
-                gridGeometry = domain;
                 if (overviews != null) {
-                    ensureResolutionExists();
+                    domain = domain.defaultToGridCRS(null);
                 }
+                gridGeometry = domain;
             }
             return domain;
         }
@@ -2037,31 +2037,11 @@ final class ImageFileDirectory extends DataCube {
         if (!images.isEmpty()) {
             overviews = new Overviews(images);
             if (gridGeometry != null) {
-                ensureResolutionExists();
+                gridGeometry = gridGeometry.defaultToGridCRS(null);
             }
         }
     }
 
-    /**
-     * Ensures that the grid geometry declares a resolution.
-     * This method should be invoked only when {@link #gridGeometry} 
<strong>and</strong>
-     * {@link #overviews} are non-null, i.e. when we determined that a pyramid 
exists.
-     * The pyramid system of Apache <abbr>SIS</abbr> needs a resolution in all 
levels.
-     * Therefore, if the base level has no resolution, we need to add an 
arbitrary one.
-     * A resolution is missing when there is no "grid to <abbr>CRS</abbr>" 
transform.
-     * Arbitrary resolution values are okay when there is no <abbr>CRS</abbr>.
-     * The arbitrary value is 1, which means that resolutions are in units of 
pixels
-     * of the base level.
-     */
-    private void ensureResolutionExists() throws DataStoreException {
-        if (!gridGeometry.isDefined(GridGeometry.RESOLUTION) && 
!gridGeometry.isDefined(GridGeometry.CRS)) try {
-            gridGeometry = new GridGeometry(gridGeometry, 
gridGeometry.getExtent(),
-                            
MathTransforms.identity(gridGeometry.getDimension()));
-        } catch (TransformException e) {
-            throw new DataStoreReferencingException(e);     // Should never 
happen.
-        }
-    }
-
     /**
      * A list of Image File Directories (<abbr>FID</abbr>) where the first 
element is the image at the coarsest
      * resolution and next elements are images at finer resolutions. The 
element at the finest resolution is the
@@ -2157,7 +2137,7 @@ final class ImageFileDirectory extends DataCube {
                                     case 1:  size = image.imageHeight; break;
                                     default: scales[i] = 1; continue;
                                 }
-                                scales[i] = fullExtent.getSize(i, false) / 
size;
+                                scales[i] = 
Numerics.divide(fullExtent.getSize(i), size);
                                 high[i] = size - 1;
                             }
                             image.gridGeometry = new GridGeometry(
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
index 2e205ae0a0..0a293ed803 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
@@ -37,6 +37,7 @@ import org.opengis.metadata.Metadata;
 import org.opengis.util.GenericName;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
@@ -194,9 +195,10 @@ final class ImageResource extends 
TiledGridCoverageResource implements StoreReso
             final GridExtent baseExtent  = base.getExtent();
             final var factors = new double[baseExtent.getDimension()];
             for (int i = 0; i < factors.length; i++) {
-                factors[i] = 1 / Numerics.divide(levelExtent.getSize(i), 
baseExtent.getSize(i));
+                factors[i] = Numerics.divide(baseExtent.getSize(i), 
levelExtent.getSize(i));
             }
-            gridGeometry = new GridGeometry(base, levelExtent, 
MathTransforms.scale(factors));
+            final LinearTransform toLevel = MathTransforms.scale(factors);
+            gridGeometry = toLevel.isIdentity() ? base : new 
GridGeometry(base, levelExtent, toLevel);
         }
     }
 
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Pyramid.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Pyramid.java
index 234dcbb2e9..8596c14d7c 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Pyramid.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Pyramid.java
@@ -88,32 +88,9 @@ final class Pyramid extends TiledGridCoverageResource 
implements TiledGridCovera
         tileSizeY = pyramid.tileSizeY;
         this.levels = levels;
         Arrays.sort(levels, LEVEL_COMPARATOR);
-        /*
-         * Select the finest level for which the "grid to CRS" transform, and 
therefore the resolution,
-         * is defined. If none, select the very fist level, which should have 
the finest resolution.
-         */
-        GridGeometry base = null;
-        boolean updateBase = true;
-        for (final ImageResource level : levels) {
-            final GridGeometry grid = level.getGridGeometry();
-            if (grid.isDefined(GridGeometry.GRID_TO_CRS)) {
-                updateBase = false;   // Base is already complete.
-                base = grid;
-                break;
-            } else if (base == null) {
-                base = grid;
-            }
-        }
-        /*
-         * Now compute the "grid to CRS" transform for each pyramid level.
-         * It may include the base level itself if the transform was not 
specified anywhere.
-         */
+        final GridGeometry base = 
levels[0].getGridGeometry().defaultToGridCRS(null);
         for (ImageResource level : levels) {
             level.setPyramidLevelOf(base);
-            if (updateBase) {
-                updateBase = false;
-                base = level.getGridGeometry();
-            }
         }
     }
 

Reply via email to