This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit ca8241da59f6885fc6d5446ee9589890c2d3cebb
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Nov 24 16:28:54 2023 +0100

    Add a `GridGeometry` constructor doing the concatenation of two grid 
geometries.
    Opportunistic migration of JUnit 4 to JUnit 5 for the relevant tests.
---
 .../org/apache/sis/coverage/grid/GridExtent.java   |  29 ++++-
 .../org/apache/sis/coverage/grid/GridGeometry.java |  63 +++++++++-
 .../apache/sis/coverage/grid/GridExtentTest.java   | 113 +++++++++--------
 .../apache/sis/coverage/grid/GridGeometryTest.java | 140 +++++++++++++--------
 .../main/org/apache/sis/util/ArraysExt.java        |  20 ++-
 5 files changed, 260 insertions(+), 105 deletions(-)

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 352247f403..62d68bb38b 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
@@ -91,7 +91,7 @@ import org.opengis.coverage.grid.GridCoordinates;
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Alexis Manin (Geomatys)
  * @author  Johann Sorel (Geomatys)
- * @version 1.4
+ * @version 1.5
  * @since   1.0
  */
 public class GridExtent implements GridEnvelope, LenientComparable, 
Serializable {
@@ -354,6 +354,33 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
         validateCoordinates();
     }
 
+    /**
+     * Creates a new grid extent as the concatenation of the two specified 
grid extent.
+     * The number of dimensions of the new extent is the sum of the number of 
dimensions
+     * of the two specified extents. The dimensions of the lower extent are 
first,
+     * followed by the dimensions of the upper extent.
+     *
+     * @param  lower  the grid extent providing the lowest dimensions.
+     * @param  upper  the grid extent providing the highest dimensions.
+     * @throws IllegalArgumentException if the concatenation results in 
duplicated {@linkplain #getAxisType(int) axis types}.
+     *
+     * @since 1.5
+     */
+    public GridExtent(final GridExtent lower, final GridExtent upper) {
+        final int d1  = lower.getDimension();
+        final int d2  = upper.getDimension();
+        final int dim = d1 + d2;
+        final var axisTypes = new DimensionNameType[dim];
+        if (lower.types != null) System.arraycopy(lower.types, 0, axisTypes,  
0, d1);
+        if (upper.types != null) System.arraycopy(upper.types, 0, axisTypes, 
d1, d2);
+        types = validateAxisTypes(axisTypes);
+        coordinates = allocate(dim);
+        System.arraycopy(lower.coordinates,  0, coordinates,      0, d1);
+        System.arraycopy(upper.coordinates,  0, coordinates,     d1, d2);
+        System.arraycopy(lower.coordinates, d1, coordinates, dim,    d1);
+        System.arraycopy(upper.coordinates, d2, coordinates, dim+d1, d2);
+    }
+
     /**
      * Infers the axis types from the given coordinate reference system.
      * This method is the converse of {@link GridExtentCRS}.
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 3389b1a534..464b9644bd 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
@@ -747,6 +747,66 @@ public class GridGeometry implements LenientComparable, 
Serializable {
         }
     }
 
+    /**
+     * Creates a new grid geometry as the concatenation of the two specified 
grid geometries.
+     * The number of dimensions of the new grid geometry is the sum of the 
number of dimensions
+     * of the two specified grid geometries. The dimensions of the lower grid 
geometry are first,
+     * followed by the dimensions of the upper grid geometry.
+     *
+     * @param  lower  the grid geometry providing the lowest dimensions.
+     * @param  upper  the grid geometry providing the highest dimensions.
+     * @throws IncompleteGridGeometryException if a property presents in one 
grid geometry is absent in the other.
+     * @throws IllegalArgumentException if the concatenation results in 
duplicated
+     *         {@linkplain GridExtent#getAxisType(int) grid axis types}.
+     * @throws FactoryException if the geodetic factory failed to create the 
compound CRS.
+     *
+     * @see #selectDimensions(int...)
+     *
+     * @since 1.5
+     */
+    public GridGeometry(final GridGeometry lower, final GridGeometry upper) 
throws FactoryException {
+        /*
+         * Concatenate the extents first because they perform a check for axis 
duplication.
+         * We set the extent to null only if both `lower` and `upper` have a 
null extent.
+         * Otherwise, invoke `getExtent()` for causing an 
`IncompleteGridGeometryException`
+         * to be thrown if only one grid geometry has an extent.
+         */
+        extent = (lower.extent == null && upper.extent == null) ? null
+               : new GridExtent(lower.getExtent(), upper.getExtent());
+        /*
+         * Concatenate the "grid to CRS" transforms and derived information
+         * (resolutions and non-linearity).
+         */
+        if (lower.gridToCRS == null && upper.gridToCRS == null) {
+            gridToCRS   = null;
+            cornerToCRS = null;
+        } else {
+            gridToCRS   = compound(lower, upper, PixelInCell.CELL_CENTER);
+            cornerToCRS = compound(lower, upper, PixelInCell.CELL_CORNER);
+        }
+        nonLinears = lower.nonLinears | (upper.nonLinears << 
lower.getDimension());
+        resolution = (lower.resolution == null && upper.resolution == null) ? 
null
+                   : ArraysExt.concatenate(lower.getResolution(true), 
upper.getResolution(true));
+        /*
+         * The check for presence/absence of envelope is more complex because 
an envelope
+         * may be considered missing, but still be non-null in order to store 
the CRS.
+         */
+        if (lower.envelope == null && upper.envelope == null) {
+            envelope = null;
+        } else {
+            Envelope e1 = lower.envelope; if (e1 == null) e1 = 
lower.getEnvelope();
+            Envelope e2 = upper.envelope; if (e2 == null) e2 = 
upper.getEnvelope();
+            envelope = ImmutableEnvelope.castOrCopy(Envelopes.compound(e1, 
e2));
+        }
+    }
+
+    /**
+     * Aggregates the dimensions of the "grid to CRS" transforms of the given 
grid geometries.
+     */
+    private static MathTransform compound(final GridGeometry lower, final 
GridGeometry upper, final PixelInCell anchor) {
+        return MathTransforms.compound(lower.getGridToCRS(anchor), 
upper.getGridToCRS(anchor));
+    }
+
     /**
      * Converts a "grid to CRS" transform from "cell corner" convention to 
"cell center" convention.
      * This is a helper method for use of {@link #GridGeometry(GridExtent, 
MathTransform, MathTransform,
@@ -1542,7 +1602,8 @@ public class GridGeometry implements LenientComparable, 
Serializable {
      * The number of dimensions of the sub grid geometry will be {@code 
dimensions.length}.
      *
      * <p>This method performs a <cite>dimensionality reduction</cite>.
-     * This method cannot be used for changing dimension order.</p>
+     * This method cannot be used for changing dimension order.
+     * The converse operation is the {@linkplain #GridGeometry(GridGeometry, 
GridGeometry) concatenation}.</p>
      *
      * @param  indices  the grid (not CRS) dimensions to select, in strictly 
increasing order.
      * @return the sub-grid geometry, or {@code this} if the given array 
contains all dimensions of this grid geometry.
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
index b9f3646dae..c9afd55f9b 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -37,7 +37,8 @@ import org.apache.sis.util.internal.Numerics;
 
 // Test dependencies
 import org.junit.Test;
-import static org.junit.Assert.*;
+import org.junit.jupiter.api.DisplayName;
+import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import static org.apache.sis.test.Assertions.assertMapEquals;
@@ -77,10 +78,10 @@ public final class GridExtentTest extends TestCase {
      * Verifies the low and high values in the specified dimension of the 
given extent
      */
     static void assertExtentEquals(final GridExtent extent, final int 
dimension, final int low, final int high) {
-        assertEquals("low",    low,  extent.getLow (dimension));
-        assertEquals("high",   high, extent.getHigh(dimension));
-        assertEquals("size",   high - low + 1, extent.getSize(dimension));
-        assertEquals("median", Numerics.ceilDiv(high + low, 2), 
extent.getMedian(dimension));
+        assertEquals(low,  extent.getLow (dimension), "low");
+        assertEquals(high, extent.getHigh(dimension), "high");
+        assertEquals(high - low + 1, extent.getSize(dimension), "size");
+        assertEquals(Numerics.ceilDiv(high + low, 2), 
extent.getMedian(dimension), "median");
     }
 
     /**
@@ -185,7 +186,7 @@ public final class GridExtentTest extends TestCase {
         GridExtent extent = new GridExtent(new DimensionNameType[] 
{DimensionNameType.COLUMN, DimensionNameType.ROW},
                                            new long[] {100, 200}, new long[] 
{500, 800}, true);
         extent = extent.insertDimension(offset, DimensionNameType.TIME, 40, 
50, false);
-        assertEquals("dimension", 3, extent.getDimension());
+        assertEquals(3, extent.getDimension(), "dimension");
         assertExtentEquals(extent, 0,        100, 500);
         assertExtentEquals(extent, rowIndex, 200, 800);
         assertExtentEquals(extent, offset,    40,  49);
@@ -201,14 +202,14 @@ public final class GridExtentTest extends TestCase {
     public void testSelectDimensions() {
         final GridExtent extent = create3D();
         GridExtent reduced = extent.selectDimensions(0, 1);
-        assertEquals("dimension", 2, reduced.getDimension());
+        assertEquals(2, reduced.getDimension(), "dimension");
         assertExtentEquals(reduced, 0, 100, 499);
         assertExtentEquals(reduced, 1, 200, 799);
         assertEquals(DimensionNameType.COLUMN, reduced.getAxisType(0).get());
         assertEquals(DimensionNameType.ROW,    reduced.getAxisType(1).get());
 
         reduced = extent.selectDimensions(2);
-        assertEquals("dimension", 1, reduced.getDimension());
+        assertEquals(1, reduced.getDimension(), "dimension");
         assertExtentEquals(reduced, 0, 40, 49);
         assertEquals(DimensionNameType.TIME, reduced.getAxisType(0).get());
     }
@@ -294,12 +295,8 @@ public final class GridExtentTest extends TestCase {
         assertExtentEquals(extent, 2, 40,  46);
         assertSame(extent.intersect(domain), extent);
         final GridExtent disjoint = domain.translate(0, 1000);
-        try {
-            extent.intersect(disjoint);
-            fail("Expected DisjointExtentException.");
-        } catch (DisjointExtentException e) {
-            assertNotNull(e.getMessage());
-        }
+        String message = assertThrows(DisjointExtentException.class, () -> 
extent.intersect(disjoint)).getMessage();
+        assertNotNull(message);
     }
 
     /**
@@ -325,12 +322,30 @@ public final class GridExtentTest extends TestCase {
         final GridExtent other = new GridExtent(
                 new DimensionNameType[] {DimensionNameType.COLUMN, 
DimensionNameType.TRACK, DimensionNameType.TIME},
                 new long[] {100, 200, 40}, new long[] {500, 800, 50}, false);
-        try {
-            domain.intersect(other);
-            fail("Should not be allowed");
-        } catch (IllegalArgumentException e) {
-            assertNotNull(e.getMessage());
-        }
+
+        String message = assertThrows(IllegalArgumentException.class, () -> 
domain.intersect(other)).getMessage();
+        assertNotNull(message);
+    }
+
+    /**
+     * Tests {@link GridExtent#GridExtent(GridExtent, GridExtent)}.
+     */
+    @Test
+    public void testConcatenate() {
+        final GridExtent domain = create3D();
+        final GridExtent other = new GridExtent(
+                new DimensionNameType[] {DimensionNameType.VERTICAL},
+                new long[] {-4}, new long[] {17}, true);
+        final GridExtent extent = new GridExtent(domain, other);
+
+        assertArrayEquals(new DimensionNameType[] {
+            DimensionNameType.COLUMN, DimensionNameType.ROW, 
DimensionNameType.TIME, DimensionNameType.VERTICAL
+        }, extent.getAxisTypes());
+
+        assertArrayEquals(new long[] {
+            100, 200, 40, -4,
+            499, 799, 49, 17
+        }, extent.getCoordinates());
     }
 
     /**
@@ -341,7 +356,7 @@ public final class GridExtentTest extends TestCase {
         final GeneralDirectPosition slicePoint = new 
GeneralDirectPosition(226.7, 47.2);
         final GridExtent extent = create3D();
         final GridExtent slice  = extent.slice(slicePoint, new int[] {1, 2});
-        assertEquals("dimension", 3, slice.getDimension());
+        assertEquals(3, slice.getDimension(), "dimension");
         assertExtentEquals(slice, 0, 100, 499);
         assertExtentEquals(slice, 1, 227, 227);
         assertExtentEquals(slice, 2,  47,  47);
@@ -352,13 +367,8 @@ public final class GridExtentTest extends TestCase {
          * change in future SIS version).
          */
         slicePoint.setOrdinate(0, 900);
-        try {
-            extent.slice(slicePoint, new int[] {1, 2});
-            fail("Expected PointOutsideCoverageException");
-        } catch (PointOutsideCoverageException e) {
-            final String message = e.getLocalizedMessage();
-            assertTrue(message, message.contains("(900, 47)"));     // See 
above comment.
-        }
+        String message = assertThrows(PointOutsideCoverageException.class, () 
-> extent.slice(slicePoint, new int[] {1, 2})).getLocalizedMessage();
+        assertTrue(message.contains("(900, 47)"), message);         // See 
above comment.
     }
 
     /**
@@ -372,12 +382,8 @@ public final class GridExtentTest extends TestCase {
         assertSubspaceEquals(extent, 0,  2  );
         assertSubspaceEquals(extent, 0,1,2  );
         assertSubspaceEquals(extent, 0,1,2,3);
-        try {
-            extent.getSubspaceDimensions(1);
-            fail("Should not reduce to 1 dimension.");
-        } catch (SubspaceNotSpecifiedException e) {
-            assertNotNull(e.getMessage());
-        }
+        String message = assertThrows(SubspaceNotSpecifiedException.class, () 
-> extent.getSubspaceDimensions(1)).getMessage();
+        assertNotNull(message);
     }
 
     /**
@@ -508,11 +514,12 @@ public final class GridExtentTest extends TestCase {
      * Verifies that a translation of zero cell results in the same {@link 
GridExtent} instance.
      */
     @Test
-    public void empty_translation_returns_same_extent_instance() {
+    @DisplayName("Empty translation returns same extent instance")
+    public void testZeroTranslation() {
         final GridExtent extent = new GridExtent(10, 10);
-        assertSame("Same instance returned in case of no-op", extent, 
extent.translate());
-        assertSame("Same instance returned in case of no-op", extent, 
extent.translate(0));
-        assertSame("Same instance returned in case of no-op", extent, 
extent.translate(0, 0));
+        assertSame(extent, extent.translate());
+        assertSame(extent, extent.translate(0));
+        assertSame(extent, extent.translate(0, 0));
     }
 
     /**
@@ -520,26 +527,27 @@ public final class GridExtentTest extends TestCase {
      * than the extent number of dimensions. No translation shall be applied 
in missing dimensions.
      */
     @Test
-    public void translating_only_first_dimensions_leave_others_untouched() {
+    @DisplayName("Translating only first dimensions leave others untouched")
+    public void testTranslateOneDimension() {
         final GridExtent base = new GridExtent(null, new long[] {
             0, 0, 0,
             2, 2, 2
         });
         final GridExtent translatedByX = base.translate(1);
-        assertArrayEquals("Lower corner", new long[] {1, 0, 0}, 
translatedByX.getLow() .getCoordinateValues());
-        assertArrayEquals("Upper corner", new long[] {3, 2, 2}, 
translatedByX.getHigh().getCoordinateValues());
+        assertArrayEquals(new long[] {1, 0, 0}, translatedByX.getLow() 
.getCoordinateValues(), "Lower corner");
+        assertArrayEquals(new long[] {3, 2, 2}, 
translatedByX.getHigh().getCoordinateValues(), "Upper corner");
 
         final GridExtent translatedByY = base.translate(0, -1);
-        assertArrayEquals("Lower corner", new long[] {0, -1, 0}, 
translatedByY.getLow() .getCoordinateValues());
-        assertArrayEquals("Upper corner", new long[] {2,  1, 2}, 
translatedByY.getHigh().getCoordinateValues());
+        assertArrayEquals(new long[] {0, -1, 0}, translatedByY.getLow() 
.getCoordinateValues(), "Lower corner");
+        assertArrayEquals(new long[] {2,  1, 2}, 
translatedByY.getHigh().getCoordinateValues(), "Upper corner");
 
         final GridExtent translatedByXAndY = base.translate(-1, 4);
-        assertArrayEquals("Lower corner", new long[] {-1, 4, 0}, 
translatedByXAndY.getLow() .getCoordinateValues());
-        assertArrayEquals("Upper corner", new long[] { 1, 6, 2}, 
translatedByXAndY.getHigh().getCoordinateValues());
+        assertArrayEquals(new long[] {-1, 4, 0}, translatedByXAndY.getLow() 
.getCoordinateValues(), "Lower corner");
+        assertArrayEquals(new long[] { 1, 6, 2}, 
translatedByXAndY.getHigh().getCoordinateValues(), "Upper corner");
 
         // Paranoiac check: ensure that base extent has been left untouched.
-        assertArrayEquals("Base lower corner", new long[] {0, 0, 0}, 
base.getLow() .getCoordinateValues());
-        assertArrayEquals("Base lower corner", new long[] {2, 2, 2}, 
base.getHigh().getCoordinateValues());
+        assertArrayEquals(new long[] {0, 0, 0}, base.getLow() 
.getCoordinateValues(), "Base lower corner");
+        assertArrayEquals(new long[] {2, 2, 2}, 
base.getHigh().getCoordinateValues(), "Base lower corner");
     }
 
     /**
@@ -547,17 +555,18 @@ public final class GridExtentTest extends TestCase {
      * when the given vector is long enough.
      */
     @Test
-    public void translating_all_dimensions() {
+    @DisplayName("Translating all dimensions")
+    public void testTranslateAllDimensions() {
         final GridExtent base = new GridExtent(null, new long[] {
             -1, -1, -2, 10,
              2,  2,  2, 20
         });
         final GridExtent translated = base.translate(-2, 1, 1, 100);
-        assertArrayEquals("Lower corner", new long[] {-3, 0, -1, 110}, 
translated.getLow() .getCoordinateValues());
-        assertArrayEquals("Upper corner", new long[] { 0, 3,  3, 120}, 
translated.getHigh().getCoordinateValues());
+        assertArrayEquals(new long[] {-3, 0, -1, 110}, translated.getLow() 
.getCoordinateValues(), "Lower corner");
+        assertArrayEquals(new long[] { 0, 3,  3, 120}, 
translated.getHigh().getCoordinateValues(), "Upper corner");
 
         // Paranoiac check: ensure that base extent has been left untouched.
-        assertArrayEquals("Base lower corner", new long[] {-1, -1, -2, 10}, 
base.getLow() .getCoordinateValues());
-        assertArrayEquals("Base lower corner", new long[] { 2,  2,  2, 20}, 
base.getHigh().getCoordinateValues());
+        assertArrayEquals(new long[] {-1, -1, -2, 10}, base.getLow() 
.getCoordinateValues(), "Base lower corner");
+        assertArrayEquals(new long[] { 2,  2,  2, 20}, 
base.getHigh().getCoordinateValues(), "Base lower corner");
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
index e0823cdc82..5c2d342226 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.coverage.grid;
 
+import org.opengis.util.FactoryException;
 import org.opengis.geometry.Envelope;
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.referencing.crs.DerivedCRS;
@@ -32,10 +33,11 @@ import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.util.ExtendedPrecisionMatrix;
+import org.apache.sis.util.ComparisonMode;
 
 // Test dependencies
 import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.referencing.crs.HardCodedCRS;
@@ -64,8 +66,8 @@ public final class GridGeometryTest extends TestCase {
      * Verifies grid extent coordinates.
      */
     static void assertExtentEquals(final long[] low, final long[] high, final 
GridExtent extent) {
-        assertArrayEquals("extent.low",  low,  extent.getLow() 
.getCoordinateValues());
-        assertArrayEquals("extent.high", high, 
extent.getHigh().getCoordinateValues());
+        assertArrayEquals(low,  extent.getLow() .getCoordinateValues(), 
"extent.low");
+        assertArrayEquals(high, extent.getHigh().getCoordinateValues(), 
"extent.high");
     }
 
     /**
@@ -123,16 +125,16 @@ public final class GridGeometryTest extends TestCase {
          * Verify properties that should be stored "as-is".
          */
         final MathTransform trCorner = 
grid.getGridToCRS(PixelInCell.CELL_CORNER);
-        assertSame("gridToCRS", identity, trCorner);
+        assertSame(identity, trCorner, "gridToCRS");
         assertExtentEquals(low, high, grid.getExtent());
         /*
          * Verify computed math transform.
          */
         final MathTransform trCenter = 
grid.getGridToCRS(PixelInCell.CELL_CENTER);
         assertNotSame(trCenter, trCorner);
-        assertFalse ("gridToCRS.isIdentity",          trCenter.isIdentity());
-        assertEquals("gridToCRS.sourceDimensions", 4, 
trCenter.getSourceDimensions());
-        assertEquals("gridToCRS.targetDimensions", 4, 
trCenter.getTargetDimensions());
+        assertFalse (trCenter.isIdentity(), "gridToCRS.isIdentity");
+        assertEquals(4, trCenter.getSourceDimensions(), 
"gridToCRS.sourceDimensions");
+        assertEquals(4, trCenter.getTargetDimensions(), 
"gridToCRS.targetDimensions");
         assertMatrixEquals("gridToCRS", Matrices.create(5, 5, new double[] {
                 1, 0, 0, 0, 0.5,
                 0, 1, 0, 0, 0.5,
@@ -148,8 +150,8 @@ public final class GridGeometryTest extends TestCase {
         /*
          * Verify other computed properties.
          */
-        assertArrayEquals("resolution", new double[] {1, 1, 1, 1}, 
grid.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", grid.isConversionLinear(0, 1, 2, 3));
+        assertArrayEquals(new double[] {1, 1, 1, 1}, 
grid.getResolution(false), "resolution");
+        assertTrue(grid.isConversionLinear(0, 1, 2, 3), "isConversionLinear");
         verifyGridToCRS(grid);
     }
 
@@ -168,16 +170,16 @@ public final class GridGeometryTest extends TestCase {
          * Verify properties that should be stored "as-is".
          */
         final MathTransform trCenter = 
grid.getGridToCRS(PixelInCell.CELL_CENTER);
-        assertSame("gridToCRS", identity, trCenter);
+        assertSame(identity, trCenter, "gridToCRS");
         assertExtentEquals(low, high, grid.getExtent());
         /*
          * Verify computed math transform.
          */
         final MathTransform trCorner = 
grid.getGridToCRS(PixelInCell.CELL_CORNER);
         assertNotSame(trCenter, trCorner);
-        assertFalse ("gridToCRS.isIdentity",          trCorner.isIdentity());
-        assertEquals("gridToCRS.sourceDimensions", 3, 
trCorner.getSourceDimensions());
-        assertEquals("gridToCRS.targetDimensions", 3, 
trCorner.getTargetDimensions());
+        assertFalse (trCorner.isIdentity(), "gridToCRS.isIdentity");
+        assertEquals(3, trCorner.getSourceDimensions(), 
"gridToCRS.sourceDimensions");
+        assertEquals(3, trCorner.getTargetDimensions(), 
"gridToCRS.targetDimensions");
         assertMatrixEquals("gridToCRS", new Matrix4(
                 1, 0, 0, -0.5,
                 0, 1, 0, -0.5,
@@ -192,8 +194,8 @@ public final class GridGeometryTest extends TestCase {
         /*
          * Verify other computed properties.
          */
-        assertArrayEquals("resolution", new double[] {1, 1, 1}, 
grid.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", grid.isConversionLinear(0, 1, 2));
+        assertArrayEquals(new double[] {1, 1, 1}, grid.getResolution(false), 
"resolution");
+        assertTrue(grid.isConversionLinear(0, 1, 2), "isConversionLinear");
         verifyGridToCRS(grid);
     }
 
@@ -224,8 +226,8 @@ public final class GridGeometryTest extends TestCase {
         /*
          * Verify other computed properties.
          */
-        assertArrayEquals("resolution", new double[] {2, 1, 3}, 
grid.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", grid.isConversionLinear(0, 1, 2));
+        assertArrayEquals(new double[] {2, 1, 3}, grid.getResolution(false), 
"resolution");
+        assertTrue(grid.isConversionLinear(0, 1, 2), "isConversionLinear");
         verifyGridToCRS(grid);
     }
 
@@ -275,7 +277,7 @@ public final class GridGeometryTest extends TestCase {
                 0, 1, 0.5,
                 0, 0, 1));
         final GridGeometry grid = new GridGeometry(extent, 
PixelInCell.CELL_CENTER, gridToCRS, null);
-        assertTrue("gridToCRS.isIdentity", 
grid.getGridToCRS(PixelInCell.CELL_CORNER).isIdentity());
+        assertTrue(grid.getGridToCRS(PixelInCell.CELL_CORNER).isIdentity(), 
"gridToCRS.isIdentity");
         verifyGridToCRS(grid);
     }
 
@@ -301,10 +303,10 @@ public final class GridGeometryTest extends TestCase {
         final MathTransform temporal  = MathTransforms.linear(3600, 60);
         final MathTransform gridToCRS = MathTransforms.compound(horizontal, 
vertical, temporal);
         final GridGeometry  grid      = new GridGeometry(extent, 
PixelInCell.CELL_CENTER, gridToCRS, null);
-        assertArrayEquals("resolution", new double[] {0.5, 0.25,        6.0, 
3600}, grid.getResolution(true),  STRICT);
-        assertArrayEquals("resolution", new double[] {0.5, 0.25, Double.NaN, 
3600}, grid.getResolution(false), STRICT);
-        assertFalse("isConversionLinear", grid.isConversionLinear(0, 1, 2, 3));
-        assertTrue ("isConversionLinear", grid.isConversionLinear(0, 1,    3));
+        assertArrayEquals(new double[] {0.5, 0.25,        6.0, 3600}, 
grid.getResolution(true),  "resolution");
+        assertArrayEquals(new double[] {0.5, 0.25, Double.NaN, 3600}, 
grid.getResolution(false), "resolution");
+        assertFalse(grid.isConversionLinear(0, 1, 2, 3), "isConversionLinear");
+        assertTrue (grid.isConversionLinear(0, 1,    3), "isConversionLinear");
     }
 
     /**
@@ -327,7 +329,7 @@ public final class GridGeometryTest extends TestCase {
         assertEnvelopeEquals(new GeneralEnvelope(
                 new double[] {-70,  5},
                 new double[] {+80, 15}), grid.getEnvelope(), STRICT);
-        assertArrayEquals("resolution", new double[] {0.5, 0.5}, 
grid.getResolution(false), STRICT);
+        assertArrayEquals(new double[] {0.5, 0.5}, grid.getResolution(false), 
"resolution");
         assertMatrixEquals("gridToCRS", new Matrix3(
                 0,   0.5, -89.75,
                 0.5, 0,  -179.75,
@@ -335,8 +337,8 @@ public final class GridGeometryTest extends TestCase {
         /*
          * Verify other computed properties.
          */
-        assertArrayEquals("resolution", new double[] {0.5, 0.5}, 
grid.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", grid.isConversionLinear(0, 1));
+        assertArrayEquals(new double[] {0.5, 0.5}, grid.getResolution(false), 
"resolution");
+        assertTrue(grid.isConversionLinear(0, 1), "isConversionLinear");
         verifyGridToCRS(grid);
     }
 
@@ -366,9 +368,9 @@ public final class GridGeometryTest extends TestCase {
                 0,    0,    1), matrix, STRICT);
 
         // Verify other computed properties.
-        assertArrayEquals("resolution", new double[] {0.5, 2}, 
grid.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", grid.isConversionLinear(0, 1));
-        assertSame("extent", extent, grid.getExtent());
+        assertArrayEquals(new double[] {0.5, 2}, grid.getResolution(false), 
"resolution");
+        assertTrue(grid.isConversionLinear(0, 1), "isConversionLinear");
+        assertSame(extent, grid.getExtent(), "extent");
         verifyGridToCRS(grid);
         /*
          * Same envelope and extent, but flip Y axis.
@@ -382,9 +384,9 @@ public final class GridGeometryTest extends TestCase {
                 0,    0,    1), matrix, STRICT);
 
         // Verify other computed properties.
-        assertArrayEquals("resolution", new double[] {0.5, 2}, 
grid.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", grid.isConversionLinear(0, 1));
-        assertSame("extent", extent, grid.getExtent());
+        assertArrayEquals(new double[] {0.5, 2}, grid.getResolution(false), 
"resolution");
+        assertTrue(grid.isConversionLinear(0, 1), "isConversionLinear");
+        assertSame(extent, grid.getExtent(), "extent");
         verifyGridToCRS(grid);
         /*
          * The use of `DISPLAY` mode in this particular case should be 
equivalent ro `REFLECTION_Y`.
@@ -423,9 +425,9 @@ public final class GridGeometryTest extends TestCase {
                 0,    0,    1), matrix, STRICT);
 
         // Verify other computed properties.
-        assertArrayEquals("resolution", new double[] {0.5, 2}, 
grid.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", grid.isConversionLinear(0, 1));
-        assertSame("extent", extent, grid.getExtent());
+        assertArrayEquals(new double[] {0.5, 2}, grid.getResolution(false), 
"resolution");
+        assertTrue(grid.isConversionLinear(0, 1), "isConversionLinear");
+        assertSame(extent, grid.getExtent(), "extent");
         verifyGridToCRS(grid);
         /*
          * Same extent and envelope, but reordering extend dimensions
@@ -443,9 +445,9 @@ public final class GridGeometryTest extends TestCase {
                 new long[] {  9,  14}, grid.getExtent());
 
         // Verify other computed properties.
-        assertArrayEquals("resolution", new double[] {0.5, 2}, 
grid.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", grid.isConversionLinear(0, 1));
-        assertNotSame("extent", extent, grid.getExtent());
+        assertArrayEquals(new double[] {0.5, 2}, grid.getResolution(false), 
"resolution");
+        assertTrue(grid.isConversionLinear(0, 1), "isConversionLinear");
+        assertNotSame(extent, grid.getExtent(), "extent");
         verifyGridToCRS(grid);
     }
 
@@ -467,7 +469,7 @@ public final class GridGeometryTest extends TestCase {
         final Matrix   gridToCRS = 
MathTransforms.getMatrix(grid.getGridToCRS(PixelInCell.CELL_CORNER));
         final Number[] numbers   = ((ExtendedPrecisionMatrix) 
gridToCRS).getElementAsNumbers(false);
         final double[] elements  = 
MatrixSIS.castOrCopy(gridToCRS).getElements();
-        assertArrayEquals(new double[] {360, 0, 0, -180, 0, 180, 0, -90, 0, 0, 
3000, -1000, 0, 0, 0, 1}, elements, STRICT);
+        assertArrayEquals(new double[] {360, 0, 0, -180, 0, 180, 0, -90, 0, 0, 
3000, -1000, 0, 0, 0, 1}, elements);
         assertEquals(elements.length, numbers.length);
         for (int i=0; i<elements.length; i++) {
             final double expected = elements[i];
@@ -475,7 +477,7 @@ public final class GridGeometryTest extends TestCase {
             if (expected == 0) {
                 assertNull(actual);
             } else {
-                assertEquals(expected, actual.doubleValue(), STRICT);
+                assertEquals(expected, actual.doubleValue());
             }
         }
     }
@@ -539,9 +541,9 @@ public final class GridGeometryTest extends TestCase {
             final MathTransform gridToCRS = MathTransforms.linear(mat);
             expected = new GridGeometry(extent, PixelInCell.CELL_CENTER, 
gridToCRS, HardCodedCRS.CARTESIAN_2D);
         }
-        assertSame("envelope", grid.getEnvelope(), upsampled.getEnvelope());
-        assertEquals("GridGeometry", expected, upsampled);
-        assertArrayEquals("resolution", new double[] {0.25, 0.5}, 
expected.getResolution(false), STRICT);
+        assertSame(grid.getEnvelope(), upsampled.getEnvelope(), "envelope");
+        assertEquals(expected, upsampled, "GridGeometry");
+        assertArrayEquals(new double[] {0.25, 0.5}, 
expected.getResolution(false), "resolution");
     }
 
     /**
@@ -613,8 +615,8 @@ public final class GridGeometryTest extends TestCase {
         GridGeometry reduced = grid.selectDimensions(0, 1);
         assertNotSame(grid, reduced);
         assertExtentEquals(new long[] {336, 20}, new long[] {401, 419}, 
reduced.getExtent());
-        assertSame("CRS", HardCodedCRS.WGS84, 
reduced.getCoordinateReferenceSystem());
-        assertArrayEquals("resolution", new double[] {0.5, 0.5}, 
reduced.getResolution(false), STRICT);
+        assertSame(HardCodedCRS.WGS84, reduced.getCoordinateReferenceSystem(), 
"CRS");
+        assertArrayEquals(new double[] {0.5, 0.5}, 
reduced.getResolution(false), "resolution");
         assertMatrixEquals("gridToCRS", new Matrix3(
                   0, 0.5,  -90,
                   0.5, 0, -180,
@@ -625,16 +627,16 @@ public final class GridGeometryTest extends TestCase {
         reduced = grid.selectDimensions(2);
         assertNotSame(grid, reduced);
         assertExtentEquals(new long[] {4}, new long[] {10}, 
reduced.getExtent());
-        assertSame("CRS", HardCodedCRS.GRAVITY_RELATED_HEIGHT, 
reduced.getCoordinateReferenceSystem());
-        assertArrayEquals("resolution", new double[] {2}, 
reduced.getResolution(false), STRICT);
+        assertSame(HardCodedCRS.GRAVITY_RELATED_HEIGHT, 
reduced.getCoordinateReferenceSystem(), "CRS");
+        assertArrayEquals(new double[] {2}, reduced.getResolution(false), 
"resolution");
         assertMatrixEquals("gridToCRS", new Matrix2(
                   2, 3,
                   0, 1), 
MathTransforms.getMatrix(reduced.getGridToCRS(PixelInCell.CELL_CORNER)), 
STRICT);
         /*
          * Verify other computed properties.
          */
-        assertArrayEquals("resolution", new double[] {0.5, 0.5, 2}, 
grid.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", grid.isConversionLinear(0, 1, 2));
+        assertArrayEquals(new double[] {0.5, 0.5, 2}, 
grid.getResolution(false), "resolution");
+        assertTrue(grid.isConversionLinear(0, 1, 2), "isConversionLinear");
         verifyGridToCRS(grid);
     }
 
@@ -668,8 +670,8 @@ public final class GridGeometryTest extends TestCase {
         /*
          * Verify other computed properties.
          */
-        assertArrayEquals("resolution", new double[] {0.5, 0.5}, 
reduced.getResolution(false), STRICT);
-        assertTrue("isConversionLinear", reduced.isConversionLinear(0, 1));
+        assertArrayEquals(new double[] {0.5, 0.5}, 
reduced.getResolution(false), "resolution");
+        assertTrue(reduced.isConversionLinear(0, 1), "isConversionLinear");
         verifyGridToCRS(reduced);
         /*
          * Test again by keeping the dimension without scale instead of 
discarding it.
@@ -680,6 +682,44 @@ public final class GridGeometryTest extends TestCase {
         assertMatrixEquals("gridToCRS", new Matrix2(0, 3, 0, 1), 
MathTransforms.getMatrix(tr), STRICT);
     }
 
+    /**
+     * Tests {@link GridGeometry#GridGeometry(GridGeometry, GridGeometry)}.
+     *
+     * @throws FactoryException if the constructor could not combine the CRS.
+     */
+    @Test
+    public void testConcatenate() throws FactoryException {
+        final GridGeometry lower = new GridGeometry(
+                new GridExtent(null, null, new long[] {17, 10}, true),
+                PixelInCell.CELL_CENTER,
+                MathTransforms.linear(new Matrix3(
+                    1,   0,  -7,
+                    0,  -1,  50,
+                    0,   0,   1)),
+                HardCodedCRS.WGS84);
+
+        final GridGeometry upper = new GridGeometry(
+                new GridExtent(null, null, new long[] {4}, true),
+                PixelInCell.CELL_CENTER,
+                MathTransforms.linear(new Matrix2(
+                    8, 20,
+                    0,  1)),
+                HardCodedCRS.TIME);
+
+        final GridGeometry expected = new GridGeometry(
+                new GridExtent(null, null, new long[] {17, 10, 4}, true),
+                PixelInCell.CELL_CENTER,
+                MathTransforms.linear(new Matrix4(
+                    1,   0,  0, -7,
+                    0,  -1,  0, 50,
+                    0,   0,  8, 20,
+                    0,   0,  0,  1)),
+                HardCodedCRS.WGS84_WITH_TIME);
+
+        final GridGeometry actual = new GridGeometry(lower, upper);
+        assertTrue(actual.equals(expected, ComparisonMode.DEBUG));
+    }
+
     /**
      * Tests {@link GridGeometry#createImageCRS(String, PixelInCell)}.
      */
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/ArraysExt.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/ArraysExt.java
index e6baf24db0..87ccac229d 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/ArraysExt.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/ArraysExt.java
@@ -67,7 +67,7 @@ import java.lang.reflect.Array;
  * objects.
  *
  * @author Martin Desruisseaux (IRD, Geomatys)
- * @version 1.4
+ * @version 1.5
  *
  * @see Arrays
  *
@@ -1219,6 +1219,24 @@ public final class ArraysExt extends Static {
         return copy;
     }
 
+    /**
+     * Returns the concatenation of the given arrays.
+     * If any of the supplied arrays is null or empty, then the other array is 
returned directly (not copied).
+     *
+     * @param  a1  the first array to concatenate, or {@code null}.
+     * @param  a2  the second array to concatenate, or {@code null}.
+     * @return the concatenation of given arrays. May be one of the given 
arrays returned without copying.
+     *
+     * @since 1.5
+     */
+    public static double[] concatenate(final double[] a1, final double[] a2) {
+        if (a1 == null || a1.length == 0) return a2;
+        if (a2 == null || a2.length == 0) return a1;
+        final double[] copy = Arrays.copyOf(a1, a1.length + a2.length);
+        System.arraycopy(a2, 0, copy, a1.length, a2.length);
+        return copy;
+    }
+
     /**
      * Removes the duplicated elements in the given array. This method should 
be invoked only for small arrays,
      * typically less than 10 distinct elements. For larger arrays, use {@link 
java.util.LinkedHashSet} instead.


Reply via email to