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 681836128c5c95151e7e887aa8f98cf005f68413
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Jun 24 20:59:29 2026 +0200

    Add a way to create an `EngineeringCRS` for a grid geometry which contains 
only an extent.
---
 .../apache/sis/coverage/grid/GridCRSBuilder.java   | 36 ++++++++++++++++------
 .../org/apache/sis/coverage/grid/GridExtent.java   | 33 +++++++++++++++++++-
 .../org/apache/sis/coverage/grid/GridGeometry.java |  8 +++--
 .../apache/sis/referencing/NamedIdentifier.java    |  6 ++--
 4 files changed, 67 insertions(+), 16 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
index 224c90b68a..38682be486 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
@@ -186,9 +186,9 @@ final class GridCRSBuilder extends 
ReferencingFactoryContainer {
     private GridGeometry fullGrid;
 
     /**
-     * The cell part (center or corner) to map.
+     * The cell part (center or corner) to map, or {@code null} if this 
information is not needed.
      */
-    private final PixelInCell anchor;
+    private PixelInCell anchor;
 
     /**
      * A helper tool for separating the "<abbr>CRS</abbr> to grid" transform 
for each component.
@@ -213,11 +213,8 @@ final class GridCRSBuilder extends 
ReferencingFactoryContainer {
 
     /**
      * Creates a new helper class for building a grid coordinate reference 
system.
-     *
-     * @param  anchor  the cell part to map (center or corner).
      */
-    GridCRSBuilder(final PixelInCell anchor) {
-        this.anchor = anchor;
+    GridCRSBuilder() {
         properties  = new HashMap<>(8);
         if (LOCALE != null) {
             properties.put(DefiningConversion.LOCALE_KEY, LOCALE);
@@ -237,15 +234,17 @@ final class GridCRSBuilder extends 
ReferencingFactoryContainer {
      * May return a compound <abbr>CRS</abbr> if the grid geometry has, for 
example, a temporal component.
      *
      * @param  grid     grid geometry of the coverage.
+     * @param  anchor   the cell part to map (center or corner).
      * @param  derived  whether to force {@link DerivedCRS} instances.
      * @param  name     name of the derived or engineering <abbr>CRS</abbr> to 
create.
      * @return a derived, engineering or compound <abbr>CRS</abbr> for cell 
indices associated to the grid extent.
      * @throws InvalidGeodeticParameterException if characteristics of the 
grid geometry disallow this operation.
      * @throws FactoryException if another error occurred during the use of a 
referencing factory.
      */
-    final CoordinateReferenceSystem forCoverage(final GridGeometry grid, final 
boolean derived, final Identifier name)
+    final CoordinateReferenceSystem forCoverage(final GridGeometry grid, final 
PixelInCell anchor, final boolean derived, final Identifier name)
             throws FactoryException
     {
+        this.anchor = anchor;
         properties.put(DefiningConversion.NORMALIZED_KEY, Boolean.FALSE);
         properties.put(ObjectDomain.SCOPE_KEY, SCOPE);
         grid.getGeographicExtent().ifPresent((domain) -> {
@@ -263,10 +262,27 @@ final class GridCRSBuilder extends 
ReferencingFactoryContainer {
          * We cannot create a derived CRS. Fallback on an engineering CRS with 
no
          * relationship to any other CRS.
          */
-        final int dimension = grid.getDimension();
+        return forExtent(name, grid.getDimension(), 
grid.isDefined(GridGeometry.EXTENT) ? grid.getExtent() : null);
+    }
+
+    /**
+     * Creates an engineering <abbr>CRS</abbr> for grid coordinates with no 
relationship to real world coordinates.
+     * The {@code name} argument is important because it is the only way to 
determine whether a coordinate operation
+     * is possible between two engineering <abbr>CRS</abbr>s. It is 
recommended to use an identifier which is unique
+     * for the grid. It may be, for example, derived from the resource 
identifier.
+     *
+     * @param  name       name of the engineering datum.
+     * @param  dimension  number of dimensions.
+     * @param  extent     extent, or {@code null} if none.
+     * @return an engineering <abbr>CRS</abbr> for cell indices associated to 
the grid extent.
+     * @throws FactoryException if an error occurred during the use of a 
referencing factory.
+     */
+    final EngineeringCRS forExtent(final Identifier name, final int dimension, 
final GridExtent extent)
+            throws FactoryException
+    {
         final DimensionNameType[] dimensionNames;
-        if (grid.isDefined(GridGeometry.EXTENT)) {
-            dimensionNames = Arrays.copyOf(grid.getExtent().getAxisTypes(), 
dimension);
+        if (extent != null) {
+            dimensionNames = Arrays.copyOf(extent.getAxisTypes(), dimension);
         } else {
             dimensionNames = new DimensionNameType[dimension];
         }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
index 45b5d7e4bf..6f181ebc24 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
@@ -36,11 +36,13 @@ import org.opengis.util.FactoryException;
 import org.opengis.util.InternationalString;
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
+import org.opengis.metadata.Identifier;
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.EngineeringCRS;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
@@ -53,6 +55,7 @@ import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.collection.WeakValueHashMap;
+import org.apache.sis.util.collection.BackingStoreException;
 import org.apache.sis.util.internal.shared.Numerics;
 import org.apache.sis.util.internal.shared.Strings;
 import org.apache.sis.util.internal.shared.DoubleDouble;
@@ -1320,7 +1323,7 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
         final GeneralEnvelope envelope = toEnvelope(cornerToCRS, false, 
cornerToCRS, null);
         try {
             final Matrix derivative = derivativeAtPOI(cornerToCRS, 
PixelInCell.CELL_CORNER);
-            final var builder = new GridCRSBuilder(PixelInCell.CELL_CORNER);
+            final var builder = new GridCRSBuilder();
             builder.forExtentAlone(derivative, 
getAxisTypes()).ifPresent(envelope::setCoordinateReferenceSystem);
         } catch (FactoryException | TransformException e) {
             Logging.ignorableException(LOGGER, GridExtent.class, "toEnvelope", 
e);
@@ -2351,6 +2354,34 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
         }
     }
 
+    /**
+     * Creates a coordinate reference system for cell indices in this extent.
+     * The {@code name} argument will be the name of the engineering datum.
+     * Coordinate operations between two <abbr>CRS</abbr>s created by this 
method
+     * will be possible only if they were created with the same {@code name} 
argument.
+     * It is recommended to use an identifier which is unique for the grid. It 
may be, for example, derived
+     * from the {@linkplain 
org.apache.sis.storage.GridCoverageResource#getIdentifier() resource 
identifier}.
+     *
+     * <p>This engineering <abbr>CRS</abbr> may be used when no "grid to 
<abbr>CRS</abbr>" information is available.
+     * Otherwise, {@link GridGeometry#createGridCRS(Identifier, PixelInCell)} 
should be preferred.</p>
+     *
+     * @param  name  name of the engineering datum.
+     * @return an engineering <abbr>CRS</abbr> for cell indices associated to 
this grid extent.
+     *
+     * @see GridGeometry#createGridCRS(Identifier, PixelInCell)
+     *
+     * @since 1.7
+     */
+    public EngineeringCRS createGridCRS(final Identifier name) {
+        ArgumentChecks.ensureNonNull("name", name);
+        try {
+            return new GridCRSBuilder().forExtent(name, getDimension(), this);
+        } catch (FactoryException e) {
+            // Should never happen because `GridCRSBuilder` uses known 
implementations.
+            throw new BackingStoreException(e);
+        }
+    }
+
     /**
      * Compares the specified object with this grid extent for equality.
      * This method delegates to {@code equals(object, ComparisonMode.STRICT)}.
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 10a3778369..3edd2a913b 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
@@ -753,7 +753,8 @@ public class GridGeometry implements LenientComparable, 
Serializable {
     }
 
     /**
-     * Creates a grid geometry with only an envelope.
+     * Creates a grid geometry with only an envelope and its coordinate 
reference system.
+     * The grid geometry has no "grid to <abbr>CRS</abbr>" transform.
      *
      * @param  envelope  the envelope together with CRS of the "real world" 
coordinates.
      * @throws IllegalArgumentException if the envelope has no CRS and only 
NaN coordinate values.
@@ -1882,7 +1883,7 @@ public class GridGeometry implements LenientComparable, 
Serializable {
         final var id = new 
org.apache.sis.referencing.ImmutableIdentifier(null, null, name);
         try {
             // Note: the `true` boolean argument can be removed after the 
removal of this method.
-            final CoordinateReferenceSystem crs = new 
GridCRSBuilder(anchor).forCoverage(this, true, id);
+            final CoordinateReferenceSystem crs = new 
GridCRSBuilder().forCoverage(this, anchor, true, id);
             return (DerivedCRS) 
org.apache.sis.referencing.CRS.getSingleComponents(crs).get(0);
         } catch (FactoryException e) {
             throw new BackingStoreException(e);
@@ -1920,13 +1921,14 @@ public class GridGeometry implements LenientComparable, 
Serializable {
      * @throws FactoryException if another error occurred during the use of a 
referencing factory.
      *
      * @see #createTransformTo(GridGeometry, PixelInCell)
+     * @see GridExtent#createGridCRS(Identifier)
      *
      * @since 1.7
      */
     public CoordinateReferenceSystem createGridCRS(final Identifier name, 
final PixelInCell anchor) throws FactoryException {
         ArgumentChecks.ensureNonNull("name", name);
         ArgumentChecks.ensureNonNull("anchor", anchor);
-        return new GridCRSBuilder(anchor).forCoverage(this, false, name);
+        return new GridCRSBuilder().forCoverage(this, anchor, false, name);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/NamedIdentifier.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/NamedIdentifier.java
index 6fc54e9b88..8979a5a1f4 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/NamedIdentifier.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/NamedIdentifier.java
@@ -39,7 +39,7 @@ import org.apache.sis.util.iso.DefaultNameFactory;
 
 
 /**
- * An identification of a CRS object which is both a {@link Identifier} and a 
{@link GenericName}.
+ * An identification of a <abbr>CRS</abbr> object which is both an {@link 
Identifier} and a {@link GenericName}.
  * This class implements both interfaces in order to allow usage of the same 
instance either as an object
  * {@linkplain AbstractIdentifiedObject#getName() name} or {@linkplain 
AbstractIdentifiedObject#getAlias() alias}.
  * This flexibility make easier to uses object's names in two different models:
@@ -200,7 +200,7 @@ public class NamedIdentifier extends ImmutableIdentifier 
implements GenericName
      * @throws InvalidParameterValueException if a property has an invalid 
value.
      * @throws IllegalArgumentException if a property is invalid for some 
other reason.
      */
-    public NamedIdentifier(final Map<String,?> properties) throws 
IllegalArgumentException {
+    public NamedIdentifier(final Map<String, ?> properties) throws 
IllegalArgumentException {
         super(properties);
         name = (GenericName) properties.get("name");
         isNameSupplied = (name != null);
@@ -563,6 +563,8 @@ public class NamedIdentifier extends ImmutableIdentifier 
implements GenericName
 
     /**
      * Returns a hash code value for this object.
+     *
+     * @return a hash code value for this object.
      */
     @Override
     public int hashCode() {

Reply via email to