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 e34709ee6ae825876576749a713ac52e5af968e6
Author: jsorel <[email protected]>
AuthorDate: Fri Jul 31 10:49:50 2026 +0200

    feat(DGGS): add code operation and coverage test
---
 .../main/module-info.java                          |   7 +
 .../sis/storage/coverage/CoverageUtilities.java    |  30 ----
 .../apache/sis/storage/coverage/FeatureExt.java    |  50 +-----
 .../apache/sis/storage/coverage/TileMatrices.java  |  37 -----
 .../rs/internal/shared/ArrayCodedCoverage.java     |   8 +-
 .../rs/internal/shared/GridAsCodedResource.java    |   3 +-
 .../rs/internal/shared/ResampledCodedCoverage.java |   3 +-
 .../sis/referencing/rs/CodeOperationTest.java      | 175 ++++++++++++++++++++
 .../rs/ArrayReferencedGridCoverageTest.java        | 184 +++++++++++++++++++++
 9 files changed, 372 insertions(+), 125 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/module-info.java 
b/incubator/src/org.apache.sis.referencing.dggs/main/module-info.java
index 7daddf9b10..97651953a3 100644
--- a/incubator/src/org.apache.sis.referencing.dggs/main/module-info.java
+++ b/incubator/src/org.apache.sis.referencing.dggs/main/module-info.java
@@ -28,6 +28,13 @@ module org.apache.sis.referencing.dggs {
     requires transitive org.apache.sis.feature;
     requires transitive org.apache.sis.geometry;
 
+    exports org.apache.sis.referencing.dggs;
+    exports org.apache.sis.referencing.dggs.s2;
+    exports org.apache.sis.referencing.rs;
+    exports org.apache.sis.storage.coverage;
+    exports org.apache.sis.storage.dggs;
+    exports org.apache.sis.storage.rs;
+    exports org.apache.sis.storage.util;
 
     uses 
org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystemFactory;
     provides 
org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystemFactory
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/CoverageUtilities.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/CoverageUtilities.java
index 7e8d6d3efa..6818adbe79 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/CoverageUtilities.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/CoverageUtilities.java
@@ -16,42 +16,12 @@
  */
 package org.apache.sis.storage.coverage;
 
-import java.awt.image.BufferedImage;
-import java.awt.image.RenderedImage;
-import java.util.AbstractMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import org.apache.sis.coverage.Category;
-import org.apache.sis.coverage.SampleDimension;
-import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.geometry.Envelopes;
-import org.apache.sis.geometry.GeneralDirectPosition;
-import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.image.PixelIterator;
-import org.apache.sis.measure.NumberRange;
-import org.apache.sis.referencing.CRS;
-import org.apache.sis.referencing.CommonCRS;
-import org.apache.sis.referencing.operation.matrix.Matrices;
-import org.apache.sis.referencing.operation.matrix.MatrixSIS;
-import 
org.apache.sis.referencing.operation.matrix.NoninvertibleMatrixException;
-import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
-import org.opengis.geometry.DirectPosition;
-import org.opengis.geometry.Envelope;
-import org.opengis.coordinate.MismatchedDimensionException;
-import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.crs.GeographicCRS;
 import org.apache.sis.coverage.grid.PixelInCell;
 import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.MathTransform1D;
-import org.opengis.referencing.operation.Matrix;
-import org.opengis.referencing.operation.TransformException;
-import org.opengis.util.FactoryException;
 
 
 /**
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/FeatureExt.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/FeatureExt.java
index 865edc963e..08e6748a49 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/FeatureExt.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/FeatureExt.java
@@ -16,66 +16,23 @@
  */
 package org.apache.sis.storage.coverage;
 
-import java.lang.reflect.Array;
-import java.net.URI;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Optional;
-import java.util.function.BiFunction;
-import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
-import org.apache.sis.coverage.grid.GridCoverage;
-import static org.apache.sis.feature.AbstractIdentifiedType.NAME_KEY;
-import org.apache.sis.feature.DefaultAttributeType;
-import org.apache.sis.feature.DefaultFeatureType;
-import org.apache.sis.feature.Features;
-import org.apache.sis.feature.builder.FeatureTypeBuilder;
 import org.apache.sis.feature.internal.shared.AttributeConvention;
 import org.apache.sis.geometry.wrapper.Geometries;
-import org.apache.sis.geometry.wrapper.GeometryWrapper;
-import org.apache.sis.parameter.Parameters;
-import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.FeatureSet;
-import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
-import org.apache.sis.util.ObjectConverters;
-import org.apache.sis.util.collection.BackingStoreException;
-import org.apache.sis.util.iso.DefaultNameFactory;
-import org.apache.sis.util.iso.DefaultNameSpace;
 import org.locationtech.jts.geom.Geometry;
-import org.opengis.feature.Attribute;
 import org.opengis.feature.AttributeType;
 import org.opengis.feature.Feature;
-import org.opengis.feature.FeatureAssociation;
-import org.opengis.feature.FeatureAssociationRole;
 import org.opengis.feature.FeatureType;
 import org.opengis.feature.IdentifiedType;
 import org.opengis.feature.Operation;
-import org.opengis.feature.Property;
 import org.opengis.feature.PropertyNotFoundException;
 import org.opengis.feature.PropertyType;
-import org.opengis.filter.ResourceId;
-import org.opengis.geometry.Envelope;
-import org.opengis.parameter.GeneralParameterDescriptor;
-import org.opengis.parameter.GeneralParameterValue;
-import org.opengis.parameter.ParameterDescriptor;
-import org.opengis.parameter.ParameterDescriptorGroup;
-import org.opengis.parameter.ParameterValue;
-import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.util.FactoryException;
-import org.opengis.util.GenericName;
 
 /**
  * NOTE : merge with Apache SIS 'org.apache.sis.feature.Features' class.
@@ -86,11 +43,6 @@ public final class FeatureExt {
 
     public static final Logger LOGGER = 
Logger.getLogger("org.apache.sis.storage.coverage");
 
-    /**
-     * TODO remove when AttributeConvention.CRS will exist
-     */
-    public static final String CRS = "sis:crs";
-
     /**
      * A test to know if a given property is an SIS convention or not. Return 
true if
      * the property is NOT marked as an SIS convention, false otherwise.
@@ -127,7 +79,7 @@ public final class FeatureExt {
      * @return CoordinateReferenceSystem or null
      */
     public static CoordinateReferenceSystem getCRS(PropertyType type){
-        return getCharacteristicValue(type, CRS, null);
+        return getCharacteristicValue(type, AttributeConvention.CRS, null);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/TileMatrices.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/TileMatrices.java
deleted file mode 100644
index 16d151108b..0000000000
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/coverage/TileMatrices.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.sis.storage.coverage;
-
-import org.apache.sis.coverage.grid.GridExtent;
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-public final class TileMatrices {
-
-    private TileMatrices(){}
-
-    public static long countCells(GridExtent extent) {
-        long nb = 1;
-        for (int i = 0; i < extent.getDimension(); i++) {
-            nb *= extent.getSize(i);
-        }
-        return nb;
-    }
-
-}
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/ArrayCodedCoverage.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/ArrayCodedCoverage.java
index 298338faca..37cc3c0e55 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/ArrayCodedCoverage.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/ArrayCodedCoverage.java
@@ -27,11 +27,9 @@ import org.apache.sis.geometries.math.DataType;
 import org.apache.sis.geometries.math.NDArrays;
 import org.apache.sis.geometries.math.SampleSystem;
 import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.coverage.TileMatrices;
 import org.apache.sis.storage.rs.CodeIterator;
 import org.apache.sis.storage.rs.CodedGeometry;
 import org.apache.sis.storage.rs.WritableCodeIterator;
-import org.opengis.feature.FeatureType;
 import org.opengis.util.FactoryException;
 import org.opengis.util.GenericName;
 
@@ -47,7 +45,7 @@ public final class ArrayCodedCoverage extends 
AbstractCodedCoverage{
     public ArrayCodedCoverage(final GenericName name, CodedGeometry 
gridGeometry, SampleDimension ... sampleDimensions) throws FactoryException {
         super(name, gridGeometry, List.of(sampleDimensions));
 
-        final int nbCell = 
Math.toIntExact(TileMatrices.countCells(gridGeometry.getExtent()));
+        final int nbCell = 
Math.toIntExact(gridGeometry.getExtent().getLatticePointCount());
 
         samples = new ArrayList<>();
         final double[] nans = new double[sampleDimensions.length];
@@ -64,7 +62,7 @@ public final class ArrayCodedCoverage extends 
AbstractCodedCoverage{
         super(name, gridGeometry, toSds(samples));
 
         this.samples = samples;
-        final long nbCell = TileMatrices.countCells(extent);
+        final long nbCell = extent.getLatticePointCount();
         for (Array ta : samples) {
             if (ta.getLength() != nbCell) {
                 throw new IllegalArgumentException("Number of samples do not 
match number of cells");
@@ -127,7 +125,7 @@ public final class ArrayCodedCoverage extends 
AbstractCodedCoverage{
         private final Cursor[] cursors;
 
         public Iterator() {
-            nbCell = TileMatrices.countCells(extent);
+            nbCell = extent.getLatticePointCount();
 
             cursors = new Cursor[samples.size()];
             for (int i = 0; i < cursors.length; i++) {
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/GridAsCodedResource.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/GridAsCodedResource.java
index 26fa926386..f9f110e00c 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/GridAsCodedResource.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/GridAsCodedResource.java
@@ -44,7 +44,6 @@ import org.apache.sis.storage.NoSuchDataException;
 import org.apache.sis.storage.RasterLoadingStrategy;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
-import org.apache.sis.storage.coverage.TileMatrices;
 import org.apache.sis.referencing.rs.Code;
 import org.apache.sis.referencing.rs.ReferenceSystems;
 import org.apache.sis.storage.rs.CodedCoverage;
@@ -167,7 +166,7 @@ public final class GridAsCodedResource extends 
AbstractResource implements Coded
 
         final GridExtent extent = resultGeometry.getExtent();
         final CodeTransform transform = resultGeometry.getGridToRS();
-        final int nbZone = Math.toIntExact(TileMatrices.countCells(extent));
+        final int nbZone = Math.toIntExact(extent.getLatticePointCount());
 
         final List<Array> samples = new ArrayList<>();
         final double[] nans = new double[sampleDimensions.size()];
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/ResampledCodedCoverage.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/ResampledCodedCoverage.java
index 40e2227af0..de74e5bff3 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/ResampledCodedCoverage.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/ResampledCodedCoverage.java
@@ -20,7 +20,6 @@ import java.util.Arrays;
 import org.apache.sis.referencing.rs.Code;
 import org.apache.sis.referencing.rs.CodeOperation;
 import org.apache.sis.referencing.rs.ReferenceSystems;
-import org.apache.sis.storage.coverage.TileMatrices;
 import org.apache.sis.storage.rs.CodeIterator;
 import org.apache.sis.storage.rs.CodeTransform;
 import org.apache.sis.storage.rs.CodedCoverage;
@@ -75,7 +74,7 @@ public final class ResampledCodedCoverage extends 
AbstractCodedCoverage {
 
         BandedIterator() {
             sourceIterator = base.createIterator();
-            nbCell = TileMatrices.countCells(extent);
+            nbCell = extent.getLatticePointCount();
             sourceBanded = sourceIterator instanceof CodeIterator;
         }
 
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/test/org/apache/sis/referencing/rs/CodeOperationTest.java
 
b/incubator/src/org.apache.sis.referencing.dggs/test/org/apache/sis/referencing/rs/CodeOperationTest.java
new file mode 100644
index 0000000000..61bd30f77b
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/test/org/apache/sis/referencing/rs/CodeOperationTest.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.rs;
+
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.referencing.gazetteer.GazetteerException;
+import org.apache.sis.referencing.gazetteer.GeohashReferenceSystem;
+import org.apache.sis.referencing.gazetteer.MilitaryGridReferenceSystem;
+import org.apache.sis.referencing.rs.internal.shared.CodeOperations;
+import org.opengis.referencing.ReferenceSystem;
+import org.opengis.referencing.crs.TemporalCRS;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public class CodeOperationTest {
+
+    private final MilitaryGridReferenceSystem MGRS = new 
MilitaryGridReferenceSystem();
+    private final TemporalCRS TIME_JAVA = CommonCRS.Temporal.JAVA.crs();
+    private final TemporalCRS TIME_JULIAN = CommonCRS.Temporal.JULIAN.crs();
+    private final GeohashReferenceSystem GEOHASH;
+
+    public CodeOperationTest() throws GazetteerException {
+        GEOHASH = new 
GeohashReferenceSystem(GeohashReferenceSystem.Format.BASE32, 
CommonCRS.WGS84.normalizedGeographic());
+    }
+
+    @Test
+    public void MGRStoGeoHash84() throws FactoryException, TransformException {
+
+        final CodeOperation operation = ReferenceSystems.findOperation(MGRS, 
GEOHASH, null);
+        assertTrue(operation instanceof CodeOperations.Concatenate);
+        final CodeOperations.Concatenate co = (CodeOperations.Concatenate) 
operation;
+        assertTrue(co.getOperation1() instanceof CodeOperations.RbiToCrs);
+        assertTrue(co.getOperation2() instanceof CodeOperations.CrsToRbi);
+        final CodeOperations.RbiToCrs op1 = (CodeOperations.RbiToCrs) 
co.getOperation1();
+        final CodeOperations.CrsToRbi op2 = (CodeOperations.CrsToRbi) 
co.getOperation2();
+        assertEquals(op1.getSourceRS(), MGRS);
+        assertEquals(op1.getTargetRS(), 
CommonCRS.WGS84.normalizedGeographic());
+        assertEquals(op2.getSourceRS(), 
CommonCRS.WGS84.normalizedGeographic());
+        assertEquals(op2.getTargetRS(), GEOHASH);
+
+        //test conversion
+        final Code code = operation.transform(new Code(MGRS, "4Q FJ 12 67"), 
null);
+        assertEquals("87z9y8fhdbff", code.getOrdinate(0));
+
+    }
+
+    @Test
+    public void MGRS3DtoGeoHash() throws FactoryException {
+
+        {
+            final ReferenceSystem mrgs3d = 
ReferenceSystems.createCompound(MGRS, TIME_JAVA);
+            final CodeOperation operation = 
ReferenceSystems.findOperation(mrgs3d, GEOHASH, null);
+
+            assertTrue(operation instanceof CodeOperations.Concatenate);
+            final CodeOperations.Concatenate concat1 = 
(CodeOperations.Concatenate) operation;
+            assertTrue(concat1.getOperation1() instanceof 
CodeOperations.Reorder);
+            assertTrue(concat1.getOperation2() instanceof 
CodeOperations.Concatenate);
+            final CodeOperations.Reorder op1 = (CodeOperations.Reorder) 
concat1.getOperation1();
+            final CodeOperations.Concatenate op2 = 
(CodeOperations.Concatenate) concat1.getOperation2();
+
+            assertArrayEquals(new int[]{0},op1.getTargetMapping());
+
+            assertTrue(op2.getOperation1() instanceof CodeOperations.RbiToCrs);
+            assertTrue(op2.getOperation2() instanceof CodeOperations.CrsToRbi);
+            final CodeOperations.RbiToCrs op11 = (CodeOperations.RbiToCrs) 
op2.getOperation1();
+            final CodeOperations.CrsToRbi op12 = (CodeOperations.CrsToRbi) 
op2.getOperation2();
+            assertEquals(op11.getSourceRS(), MGRS);
+            assertEquals(op11.getTargetRS(), 
CommonCRS.WGS84.normalizedGeographic());
+            assertEquals(op12.getSourceRS(), 
CommonCRS.WGS84.normalizedGeographic());
+            assertEquals(op12.getTargetRS(), GEOHASH);
+
+            try {
+                operation.inverse();
+                fail("Operation can not be inverted");
+            } catch (NoninvertibleTransformException e) {
+                //ok
+            }
+        }
+
+        {
+            final ReferenceSystem mrgs3d = 
ReferenceSystems.createCompound(TIME_JAVA, MGRS);
+            final CodeOperation operation = 
ReferenceSystems.findOperation(mrgs3d, GEOHASH, null);
+
+            assertTrue(operation instanceof CodeOperations.Concatenate);
+            final CodeOperations.Concatenate concat1 = 
(CodeOperations.Concatenate) operation;
+            assertTrue(concat1.getOperation1() instanceof 
CodeOperations.Reorder);
+            assertTrue(concat1.getOperation2() instanceof 
CodeOperations.Concatenate);
+            final CodeOperations.Reorder op1 = (CodeOperations.Reorder) 
concat1.getOperation1();
+            final CodeOperations.Concatenate op2 = 
(CodeOperations.Concatenate) concat1.getOperation2();
+
+            assertArrayEquals(new int[]{1},op1.getTargetMapping());
+
+            assertTrue(op2.getOperation1() instanceof CodeOperations.RbiToCrs);
+            assertTrue(op2.getOperation2() instanceof CodeOperations.CrsToRbi);
+            final CodeOperations.RbiToCrs op11 = (CodeOperations.RbiToCrs) 
op2.getOperation1();
+            final CodeOperations.CrsToRbi op12 = (CodeOperations.CrsToRbi) 
op2.getOperation2();
+            assertEquals(op11.getSourceRS(), MGRS);
+            assertEquals(op11.getTargetRS(), 
CommonCRS.WGS84.normalizedGeographic());
+            assertEquals(op12.getSourceRS(), 
CommonCRS.WGS84.normalizedGeographic());
+            assertEquals(op12.getTargetRS(), GEOHASH);
+
+            try {
+                operation.inverse();
+                fail("Operation can not be inverted");
+            } catch(NoninvertibleTransformException e) {
+                //ok
+            }
+        }
+
+        {
+            try {
+                final ReferenceSystem mrgs3d = 
ReferenceSystems.createCompound(TIME_JAVA, MGRS);
+                final CodeOperation operation = 
ReferenceSystems.findOperation(GEOHASH, mrgs3d, null);
+                fail("Operation should not be possible");
+            } catch (FactoryException e) {
+                //ok
+            }
+        }
+    }
+
+    @Test
+    public void MGRS3DtoGeoHash3D() throws FactoryException {
+
+        {
+            final ReferenceSystem mrgs3d = 
ReferenceSystems.createCompound(MGRS, TIME_JAVA);
+            final ReferenceSystem geohash3d = 
ReferenceSystems.createCompound(GEOHASH, TIME_JULIAN);
+            final CodeOperation operation = 
ReferenceSystems.findOperation(mrgs3d, geohash3d, null);
+
+            assertTrue(operation instanceof CodeOperations.Compound);
+            final CodeOperations.Compound compound = (CodeOperations.Compound) 
operation;
+            assertTrue(compound.getOperation1() instanceof 
CodeOperations.Concatenate);
+            assertTrue(compound.getOperation2() instanceof 
CodeOperations.CrsToCrs);
+            final CodeOperations.Concatenate op1 = 
(CodeOperations.Concatenate) compound.getOperation1();
+            final CodeOperations.CrsToCrs op2 = (CodeOperations.CrsToCrs) 
compound.getOperation2();
+
+            assertTrue(op1 instanceof CodeOperations.Concatenate);
+            final CodeOperations.Concatenate co = (CodeOperations.Concatenate) 
op1;
+            assertTrue(co.getOperation1() instanceof CodeOperations.RbiToCrs);
+            assertTrue(co.getOperation2() instanceof CodeOperations.CrsToRbi);
+            final CodeOperations.RbiToCrs op11 = (CodeOperations.RbiToCrs) 
co.getOperation1();
+            final CodeOperations.CrsToRbi op12 = (CodeOperations.CrsToRbi) 
co.getOperation2();
+            assertEquals(op11.getSourceRS(), MGRS);
+            assertEquals(op11.getTargetRS(), 
CommonCRS.WGS84.normalizedGeographic());
+            assertEquals(op12.getSourceRS(), 
CommonCRS.WGS84.normalizedGeographic());
+            assertEquals(op12.getTargetRS(), GEOHASH);
+
+            assertEquals(op2.getSourceRS(), TIME_JAVA);
+            assertEquals(op2.getTargetRS(), TIME_JULIAN);
+
+        }
+
+    }
+}
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/test/org/apache/sis/storage/rs/ArrayReferencedGridCoverageTest.java
 
b/incubator/src/org.apache.sis.referencing.dggs/test/org/apache/sis/storage/rs/ArrayReferencedGridCoverageTest.java
new file mode 100644
index 0000000000..54d9735884
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/test/org/apache/sis/storage/rs/ArrayReferencedGridCoverageTest.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage.rs;
+
+import org.apache.sis.referencing.rs.Code;
+import org.apache.sis.referencing.rs.ReferenceSystems;
+import java.nio.ByteBuffer;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.coverage.grid.BufferedGridCoverage;
+import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.coverage.grid.GridOrientation;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.image.DataType;
+import org.apache.sis.image.internal.shared.RasterFactory;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.MemoryGridCoverageResource;
+import org.apache.sis.util.iso.Names;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.referencing.dggs.Zone;
+import org.apache.sis.referencing.dggs.s2.S2Dggrs;
+import org.apache.sis.storage.rs.internal.shared.CodeTransforms;
+import org.opengis.referencing.ReferenceSystem;
+import org.opengis.util.GenericName;
+
+import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public class ArrayReferencedGridCoverageTest {
+
+    @Test
+    public void readTest() throws Exception {
+
+        final DiscreteGlobalGridReferenceSystem dggrs = new S2Dggrs();
+        final GridCoverageResource source = create4D();
+        final CodedResource resource = 
CodedCoverages.viewAsDggrs(Names.createLocalName(null, null, "coverage4d"), 
source, dggrs);
+
+        final CodedGeometry query;
+        { //prepare the query
+            final List<Object> rootZoneIds = 
dggrs.getGridSystem().getHierarchy().getGrids().get(1).getZones().map(Zone::getIdentifier).toList();
+
+            final GeneralEnvelope verticalEnv = new 
GeneralEnvelope(CommonCRS.Vertical.ELLIPSOIDAL.crs());
+            verticalEnv.setRange(0, 100, 300);
+            final GeneralEnvelope temporalEnv = new 
GeneralEnvelope(CommonCRS.Temporal.JAVA.crs());
+            temporalEnv.setRange(0,
+                    Instant.parse("2000-06-10T10:00:00Z").toEpochMilli(),
+                    Instant.parse("2000-06-10T12:00:00Z").toEpochMilli());
+            final GridGeometry vertivalGrid = new GridGeometry(new 
GridExtent(null, 0, 2, true), verticalEnv, GridOrientation.HOMOTHETY);
+            final GridGeometry temporalGrid = new GridGeometry(new 
GridExtent(null, 0, 2, true), temporalEnv, GridOrientation.HOMOTHETY);
+
+            final ReferenceSystem rs = ReferenceSystems.createCompound(dggrs, 
verticalEnv.getCoordinateReferenceSystem(), 
temporalEnv.getCoordinateReferenceSystem());
+            final GridExtent extent = new GridExtent(null, new long[]{0,0,0}, 
new long[]{rootZoneIds.size(), 3, 3}, false);
+            final CodeTransform gridToRS = CodeTransforms.compound(
+                    CodeTransforms.toTransform(dggrs, rootZoneIds),
+                    CodeTransforms.toTransform(vertivalGrid),
+                    CodeTransforms.toTransform(temporalGrid)
+                );
+
+            query = new CodedGeometry(rs, extent, gridToRS, null);
+        }
+
+
+        final CodedCoverage coverage = resource.read(query);
+        final CodeTransform gridToRS = coverage.getGeometry().getGridToRS();
+        final CodeIterator iterator = coverage.createIterator();
+
+        while (iterator.next()) {
+            final int[] gridPosition = iterator.getPosition();
+            Code code = gridToRS.toCode(gridPosition);
+            assertNotNull(code);
+            double[] cell = iterator.getCell((double[])null);
+            double v = cell[0];
+
+            switch (gridPosition[2]) { //time axis
+                case 0 :
+                    switch (gridPosition[1]) { //vertical axis
+                        case 0: assertEquals(1.0, v, 0.0); break;
+                        case 1: assertEquals(2.0, v, 0.0); break;
+                        case 2: assertEquals(3.0, v, 0.0); break;
+                        default : fail();
+                    } break;
+                case 1 :
+                    switch (gridPosition[1]) { //vertical axis
+                        case 0: assertEquals(4.0, v, 0.0); break;
+                        case 1: assertEquals(5.0, v, 0.0); break;
+                        case 2: assertEquals(6.0, v, 0.0); break;
+                        default : fail();
+                    } break;
+                case 2 :
+                    switch (gridPosition[1]) { //vertical axis
+                        case 0: assertEquals(7.0, v, 0.0); break;
+                        case 1: assertEquals(8.0, v, 0.0); break;
+                        case 2: assertEquals(9.0, v, 0.0); break;
+                        default : fail();
+                    } break;
+                default : fail();
+            }
+        }
+    }
+
+    private static GridCoverageResource create4D() throws Exception {
+        final GeneralEnvelope dataEnv = new GeneralEnvelope(CRS.compound(
+                CommonCRS.WGS84.normalizedGeographic(),
+                CommonCRS.Vertical.ELLIPSOIDAL.crs(),
+                CommonCRS.Temporal.JAVA.crs()
+        ));
+        dataEnv.setRange(0, -180, 180);
+        dataEnv.setRange(1, -90, 90);
+        dataEnv.setRange(2, 100, 300);
+        dataEnv.setRange(3,
+                Instant.parse("2000-06-10T10:00:00Z").toEpochMilli(),
+                Instant.parse("2000-06-10T12:00:00Z").toEpochMilli());
+        final GridGeometry dataGrid = new GridGeometry(
+                new GridExtent(null, new long[4], new long[] { 2, 2, 3, 3 }, 
false),
+                dataEnv,
+                GridOrientation.DISPLAY
+        );
+
+        final ByteBuffer dataBuffer = ByteBuffer.wrap(new byte[] {
+                // v0 t0
+                1, 1,
+                1, 1,
+                // v1 t0
+                2, 2,
+                2, 2,
+                // v2 t0
+                3, 3,
+                3, 3,
+                // v0 t1
+                4, 4,
+                4, 4,
+                // v1 t1
+                5, 5,
+                5, 5,
+                // v2 t1
+                6, 6,
+                6, 6,
+                // v0 t2
+                7, 7,
+                7, 7,
+                // v1 t2
+                8, 8,
+                8, 8,
+                // v2 t2
+                9, 9,
+                9, 9
+        });
+
+        final BufferedGridCoverage coverage = new BufferedGridCoverage(
+                dataGrid,
+                (List)List.of(new 
SampleDimension.Builder().setName("test").build()),
+                RasterFactory.wrap(DataType.BYTE, dataBuffer)
+        );
+        return new MemoryGridCoverageResource(null, null, coverage, null) {
+            @Override
+            public Optional<GenericName> getIdentifier() {
+                return Optional.of(Names.createLocalName(null, null, "test"));
+            }
+        };
+    }
+}

Reply via email to