You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2020/03/23 00:01:13 UTC

[sis] branch geoapi-4.0 updated (0045413 -> a01eaf3)

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

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


    from 0045413  Implement derivate for "Azimuthal Equidistant (Spherical)" projection.
     new 7336b1c  Tune the checks for error conditions in GridCoverage and BufferedGridCoverage construction.
     new a01eaf3  Reduce the amount of copy-and-paste in the test and add some assertions. This review does not yet include the change of exception type.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/sis/coverage/grid/GridCoverage.java |   7 +-
 .../sis/coverage/grid/GridCoverageBuilder.java     |   1 +
 .../apache/sis/coverage/grid/ImageRenderer.java    |   8 +-
 .../coverage/j2d/BufferedGridCoverage.java         |  55 +++-
 .../org/apache/sis/internal/feature/Resources.java |   6 +
 .../sis/internal/feature/Resources.properties      |   1 +
 .../sis/internal/feature/Resources_fr.properties   |   1 +
 .../sis/coverage/grid/GridCoverageBuilderTest.java | 305 +++++++++------------
 .../org/apache/sis/internal/map/CanvasContext.java |   2 +-
 .../gazetteer/MilitaryGridReferenceSystem.java     |   3 +-
 .../java/org/apache/sis/internal/jdk9/JDK9.java    |  11 +
 .../main/java/org/apache/sis/math/Fraction.java    |  14 +-
 12 files changed, 213 insertions(+), 201 deletions(-)


[sis] 02/02: Reduce the amount of copy-and-paste in the test and add some assertions. This review does not yet include the change of exception type.

Posted by de...@apache.org.
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 a01eaf33ff6ef9a70acd7ba8b7d78346d49822ef
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Mar 22 17:17:48 2020 +0100

    Reduce the amount of copy-and-paste in the test and add some assertions.
    This review does not yet include the change of exception type.
---
 .../sis/coverage/grid/GridCoverageBuilder.java     |   1 +
 .../sis/coverage/grid/GridCoverageBuilderTest.java | 301 +++++++++------------
 2 files changed, 134 insertions(+), 168 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
index 1fe7fcd..182a71c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
@@ -44,6 +44,7 @@ import org.opengis.referencing.datum.PixelInCell;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 
+
 /**
  * Helper class for the creation of {@link GridCoverage} instances. This builder can creates the
  * parameters to be given to {@linkplain GridCoverage2D} and {@linkplain BufferedGridCoverage}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
index 3bed297..c2fcfb3 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
@@ -18,151 +18,138 @@ package org.apache.sis.coverage.grid;
 
 import java.awt.image.BufferedImage;
 import java.awt.image.DataBuffer;
-import java.awt.image.DataBufferInt;
+import java.awt.image.DataBufferByte;
 import java.awt.image.RenderedImage;
 import java.awt.image.WritableRaster;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.Matrix;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
-import org.apache.sis.referencing.CommonCRS;
-import org.apache.sis.test.TestCase;
+import org.apache.sis.referencing.crs.HardCodedCRS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.util.NullArgumentException;
-import org.junit.Assert;
+import org.apache.sis.test.TestCase;
 import org.junit.Test;
-import org.opengis.referencing.datum.PixelInCell;
-import org.opengis.referencing.operation.MathTransform;
+
+import static org.junit.Assert.*;
+
 
 /**
- *
  * Tests the {@link GridCoverageBuilder} helper class.
  *
  * @author  Johann Sorel (Geomatys)
  * @version 1.1
  * @since   1.1
  */
-public class GridCoverageBuilderTest extends TestCase {
-
+public final strictfp class GridCoverageBuilderTest extends TestCase {
     /**
      * Tests {@link GridCoverageBuilder#setValues(RenderedImage)}.
      */
     @Test
-    public void createFromImageTest() {
-
-        final RenderedImage image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
-
+    public void testBuildFromImage() {
+        final RenderedImage image = new BufferedImage(5, 8, BufferedImage.TYPE_INT_ARGB);
         final GridCoverageBuilder builder = new GridCoverageBuilder();
-        builder.setValues(image);
-
-        { //sample dimensions and an undefined grid geometry should be created
-            GridCoverage coverage = builder.build();
-            Assert.assertEquals(4, coverage.getSampleDimensions().size());
-            Assert.assertFalse(coverage.getGridGeometry().isDefined(GridGeometry.CRS));
-            Assert.assertFalse(coverage.getGridGeometry().isDefined(GridGeometry.ENVELOPE));
-            Assert.assertFalse(coverage.getGridGeometry().isDefined(GridGeometry.GRID_TO_CRS));
-        }
-
-        { //should cause an exceptin number of sample dimensions do not match
-            builder.setRanges(new SampleDimension.Builder().setName(0).build());
-            try {
-                GridCoverage coverage = builder.build();
-                Assert.fail("Wrong number of sample dimensions, build should fail");
-            } catch (IllegalArgumentException ex) {
-                //ok
-            }
-        }
-
-        { //number of sample matches, should build correctly
-            final SampleDimension r0 = new SampleDimension.Builder().setName(0).build();
-            final SampleDimension r1 = new SampleDimension.Builder().setName(1).build();
-            final SampleDimension r2 = new SampleDimension.Builder().setName(2).build();
-            final SampleDimension r3 = new SampleDimension.Builder().setName(3).build();
-            builder.setRanges(r0, r1, r2, r3);
-            GridCoverage coverage = builder.build();
-        }
-
-        { //should cause an exceptin extent size do not match
-            final GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
-            env.setRange(0, 0, 10);
-            env.setRange(1, 0, 10);
-            final GridGeometry grid = new GridGeometry(new GridExtent(8, 6), env);
-            builder.setDomain(grid);
-            try {
-                GridCoverage coverage = builder.build();
-                Assert.fail("Wrong extent size, build should fail");
-            } catch (IllegalArgumentException ex) {
-                //ok
-            }
-        }
-
-        { //extent size matches, should build correctly
-            final GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
-            env.setRange(0, 0, 10);
-            env.setRange(1, 0, 10);
-            final GridGeometry grid = new GridGeometry(new GridExtent(10, 10), env);
-            builder.setDomain(grid);
-            GridCoverage coverage = builder.build();
-        }
+        assertSame(builder, builder.setValues(image));
+        final GridCoverage coverage = testBuilder(builder, 4);
+        assertSame(image, coverage.render(null));
     }
 
     /**
      * Tests {@link GridCoverageBuilder#setValues(WritableRaster)}.
      */
     @Test
-    public void createFromRasterTest() {
-
-        final WritableRaster raster = new BufferedImage(10, 10, BufferedImage.TYPE_3BYTE_BGR).getRaster();
-
+    public void testBuildFromRaster() {
+        final WritableRaster raster = new BufferedImage(5, 8, BufferedImage.TYPE_3BYTE_BGR).getRaster();
         final GridCoverageBuilder builder = new GridCoverageBuilder();
-        builder.setValues(raster);
+        assertSame(builder, builder.setValues(raster));
+        final GridCoverage coverage = testBuilder(builder, 3);
+        assertSame(raster, coverage.render(null).getTile(0,0));
+    }
 
-        { //sample dimensions and an undefined grid geometry should be created
-            GridCoverage coverage = builder.build();
-            Assert.assertEquals(3, coverage.getSampleDimensions().size());
-            Assert.assertFalse(coverage.getGridGeometry().isDefined(GridGeometry.CRS));
-            Assert.assertFalse(coverage.getGridGeometry().isDefined(GridGeometry.ENVELOPE));
-            Assert.assertFalse(coverage.getGridGeometry().isDefined(GridGeometry.GRID_TO_CRS));
+    /**
+     * Tests {@link GridCoverageBuilder#build()} with various properties defined.
+     * Before to invoke this method, caller must invoke a {@code GridCoverageBuilder.setValues(…)} method
+     * with an image or raster of size 5×8 pixels. This method starts by an attempt to build the coverage
+     * with no other property set, then add properties like sample dimensions and grid extent one by one.
+     *
+     * @param  builder   the builder to test. Values must be already defined.
+     * @param  numBands  the expected number of sample dimensions in the coverage.
+     * @return the grid coverage created by the given builder.
+     */
+    private static GridCoverage testBuilder(final GridCoverageBuilder builder, final int numBands) {
+        /*
+         * Test creation with no properties other than data explicity set.
+         * A default list of sample dimensions should be created.
+         * Grid geometry should be undefined except for grid extent.
+         */
+        {
+            final GridCoverage coverage = builder.build();
+            assertEquals("numBands", numBands, coverage.getSampleDimensions().size());
+            final GridGeometry gg = coverage.getGridGeometry();
+            assertFalse("isDefined(CRS)",         gg.isDefined(GridGeometry.CRS));
+            assertFalse("isDefined(ENVELOPE)",    gg.isDefined(GridGeometry.ENVELOPE));
+            assertFalse("isDefined(GRID_TO_CRS)", gg.isDefined(GridGeometry.GRID_TO_CRS));
+            assertTrue ("isDefined(EXTENT)",      gg.isDefined(GridGeometry.EXTENT));
         }
-
-        { //should cause an exceptin number of sample dimensions do not match
-            builder.setRanges(new SampleDimension.Builder().setName(0).build());
+        /*
+         * Test creation with the sample dimensions specified. First, we try with a wrong
+         * number of sample dimensions; that construction shall fail. Then try again with
+         * the right number of sample dimensions. That time, construction shall succeed
+         * and the coverage shall contain the sample dimensions that we specified.
+         */
+        {
+            assertSame(builder, builder.setRanges(new SampleDimension.Builder().setName(0).build()));
             try {
-                GridCoverage coverage = builder.build();
-                Assert.fail("Wrong number of sample dimensions, build should fail");
+                builder.build();
+                fail("Wrong number of sample dimensions, build() should fail.");
             } catch (IllegalArgumentException ex) {
-                //ok
+                assertNotNull(ex.getMessage());
             }
-        }
-
-        { //number of sample matches, should build correctly
-            final SampleDimension r0 = new SampleDimension.Builder().setName(0).build();
-            final SampleDimension r1 = new SampleDimension.Builder().setName(1).build();
-            final SampleDimension r2 = new SampleDimension.Builder().setName(2).build();
-            builder.setRanges(r0, r1, r2);
-            GridCoverage coverage = builder.build();
-        }
-
-        { //should cause an exceptin extent size do not match
-            final GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
-            env.setRange(0, 0, 10);
-            env.setRange(1, 0, 10);
-            final GridGeometry grid = new GridGeometry(new GridExtent(8, 6), env);
-            builder.setDomain(grid);
-            try {
-                GridCoverage coverage = builder.build();
-                Assert.fail("Wrong extent size, build should fail");
-            } catch (IllegalArgumentException ex) {
-                //ok
+            final SampleDimension[] ranges = new SampleDimension[numBands];
+            for (int i=0; i<numBands; i++) {
+                ranges[i] = new SampleDimension.Builder().setName(i).build();
             }
+            assertSame(builder, builder.setRanges(ranges));
+            final GridCoverage coverage = builder.build();
+            assertArrayEquals("sampleDimensions", ranges, coverage.getSampleDimensions().toArray());
         }
+        /*
+         * Test creation with grid extent and envelope specified. First, we try with a
+         * wrong grid extent; that construction shall fail. Then try again with correct
+         * grid extent.
+         */
+        final GridCoverage coverage = testSetDomain(builder, 5, 8);
+        final Matrix gridToCRS = MathTransforms.getMatrix(coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER));
+        assertEquals(2.0, gridToCRS.getElement(0, 0), STRICT);
+        assertEquals(0.5, gridToCRS.getElement(1, 1), STRICT);
+        return coverage;
+    }
 
-        { //extent size matches, should build correctly
-            final GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
-            env.setRange(0, 0, 10);
-            env.setRange(1, 0, 10);
-            final GridGeometry grid = new GridGeometry(new GridExtent(10, 10), env);
-            builder.setDomain(grid);
-            GridCoverage coverage = builder.build();
+    /**
+     * Tests {@link GridCoverageBuilder#setDomain(GridGeometry)}.
+     *
+     * @param  builder  the builder to test. Values must be already defined.
+     * @param  width    expected width in pixels.
+     * @param  height   expected height in pixels.
+     * @return the grid coverage created by the given builder.
+     */
+    private static GridCoverage testSetDomain(final GridCoverageBuilder builder, final int width, final int height) {
+        final GeneralEnvelope env = new GeneralEnvelope(HardCodedCRS.WGS84);
+        env.setRange(0, 0, 10);     // Scale factor of 2 for grid size of 10.
+        env.setRange(1, 0,  4);     // Scale factor of ½ for grid size of 8.
+        GridGeometry grid = new GridGeometry(new GridExtent(8, 5), env);
+        assertSame(builder, builder.setDomain(grid));
+        try {
+            builder.build();
+            fail("Wrong extent size, build() should fail.");
+        } catch (IllegalArgumentException ex) {
+            assertNotNull(ex.getMessage());
         }
+        grid = new GridGeometry(new GridExtent(width, height), env);
+        assertSame(builder, builder.setDomain(grid));
+        return builder.build();
     }
 
     /**
@@ -170,46 +157,18 @@ public class GridCoverageBuilderTest extends TestCase {
      */
     @Test
     public void createFromBufferTest() {
-
-        final DataBuffer buffer = new DataBufferInt(new int[]{1,2,3,4,5,6},6);
-
+        final DataBuffer buffer = new DataBufferByte(new byte[] {1,2,3,4,5,6}, 6);
         final GridCoverageBuilder builder = new GridCoverageBuilder();
-        builder.setValues(buffer);
-        final SampleDimension r0 = new SampleDimension.Builder().setName(0).build();
-        builder.setRanges(r0);
-
-        { // size is undefined, build should fail
-            try {
-                GridCoverage coverage = builder.build();
-                Assert.fail("Extent is undefined, build should fail");
-            } catch (NullArgumentException ex) {
-                //ok
-            }
-        }
-
-        { //should cause an exceptin extent size do not match
-            final GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
-            env.setRange(0, 0, 10);
-            env.setRange(1, 0, 10);
-            final GridGeometry grid = new GridGeometry(new GridExtent(8, 6), env);
-            builder.setDomain(grid);
-            try {
-                GridCoverage coverage = builder.build();
-                Assert.fail("Wrong extent size, build should fail");
-            } catch (IllegalGridGeometryException ex) {
-                //ok
-            }
+        assertSame(builder, builder.setValues(buffer));
+        assertSame(builder, builder.setRanges(new SampleDimension.Builder().setName(0).build()));
+        try {
+            builder.build();
+            fail("Extent is undefined, build() should fail.");
+        } catch (NullArgumentException ex) {
+            assertNotNull(ex.getMessage());
         }
-
-        { //extent size matches, should build correctly
-            final GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
-            env.setRange(0, 0, 10);
-            env.setRange(1, 0, 10);
-            final GridGeometry grid = new GridGeometry(new GridExtent(3, 2), env);
-            builder.setDomain(grid);
-            GridCoverage coverage = builder.build();
-        }
-
+        final GridCoverage coverage = testSetDomain(builder, 3, 2);
+        assertSame(buffer, coverage.render(null).getTile(0,0).getDataBuffer());
     }
 
     /**
@@ -217,31 +176,37 @@ public class GridCoverageBuilderTest extends TestCase {
      */
     @Test
     public void flippedAxisTest() {
-
-        final RenderedImage image = new BufferedImage(360, 180, BufferedImage.TYPE_INT_ARGB);
-
-        final GeneralEnvelope domain = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
+        final RenderedImage image = new BufferedImage(36, 18, BufferedImage.TYPE_INT_ARGB);
+        final GeneralEnvelope domain = new GeneralEnvelope(HardCodedCRS.WGS84);
         domain.setRange(0, -180, +180);
-        domain.setRange(1, -90, +90);
+        domain.setRange(1,  -90,  +90);
 
         final GridCoverageBuilder builder = new GridCoverageBuilder();
-        builder.setValues(image);
-        builder.setDomain(domain);
-
-        { //check positive increasing grid to crs
-            GridCoverage coverage = builder.build();
-            Assert.assertTrue(domain.equals(coverage.getGridGeometry().getEnvelope(), 0.0, false));
-            MathTransform gridToCRS = coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER);
-            Assert.assertEquals(new AffineTransform2D(1, 0, 0, 1, -179.5, -89.5), gridToCRS);
+        assertSame(builder, builder.setValues(image));
+        assertSame(builder, builder.setDomain(domain));
+        /*
+         * Test creation with the default axis direction:
+         * latitude values are increasing with row indices.
+         */
+        {
+            final GridCoverage coverage = builder.build();
+            final GridGeometry gg = coverage.getGridGeometry();
+            assertTrue(domain.equals(gg.getEnvelope(), STRICT, false));
+            MathTransform gridToCRS = gg.getGridToCRS(PixelInCell.CELL_CENTER);
+            assertEquals(new AffineTransform2D(10, 0, 0, 10, -175, -85), gridToCRS);
         }
-
-        { //check negative increasing grid to crs
-            builder.flipAxis(1);
-            GridCoverage coverage = builder.build();
-            Assert.assertTrue(domain.equals(coverage.getGridGeometry().getEnvelope(), 0.0, false));
-            MathTransform gridToCRS = coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER);
-            Assert.assertEquals(new AffineTransform2D(1, 0, 0, -1, -179.5, 89.5), gridToCRS);
+        /*
+         * Test creation with the reverse Y axis direction:
+         * latitude values are decreasing with row indices.
+         * This is the common orientation for images.
+         */
+        {
+            assertSame(builder, builder.flipAxis(1));
+            final GridCoverage coverage = builder.build();
+            final GridGeometry gg = coverage.getGridGeometry();
+            assertTrue(domain.equals(gg.getEnvelope(), STRICT, false));
+            MathTransform gridToCRS = gg.getGridToCRS(PixelInCell.CELL_CENTER);
+            assertEquals(new AffineTransform2D(10, 0, 0, -10, -175, 85), gridToCRS);
         }
-
     }
 }


[sis] 01/02: Tune the checks for error conditions in GridCoverage and BufferedGridCoverage construction.

Posted by de...@apache.org.
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 7336b1cedac7632512316f0b18d16975e5a7a39d
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Mar 22 15:56:40 2020 +0100

    Tune the checks for error conditions in GridCoverage and BufferedGridCoverage construction.
---
 .../org/apache/sis/coverage/grid/GridCoverage.java |  7 ++-
 .../apache/sis/coverage/grid/ImageRenderer.java    |  8 +---
 .../coverage/j2d/BufferedGridCoverage.java         | 55 ++++++++++++++++------
 .../org/apache/sis/internal/feature/Resources.java |  6 +++
 .../sis/internal/feature/Resources.properties      |  1 +
 .../sis/internal/feature/Resources_fr.properties   |  1 +
 .../sis/coverage/grid/GridCoverageBuilderTest.java |  4 +-
 .../org/apache/sis/internal/map/CanvasContext.java |  2 +-
 .../gazetteer/MilitaryGridReferenceSystem.java     |  3 +-
 .../java/org/apache/sis/internal/jdk9/JDK9.java    | 11 +++++
 .../main/java/org/apache/sis/math/Fraction.java    | 14 +++---
 11 files changed, 79 insertions(+), 33 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
index d8b55d9..4c84fa1 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
@@ -98,12 +98,15 @@ public abstract class GridCoverage {
      *
      * @param  domain  the grid extent, CRS and conversion from cell indices to CRS.
      * @param  range   sample dimensions for each image band.
+     * @throws NullPointerException if an argument is {@code null} or if the list contains a null element.
+     * @throws IllegalArgumentException if the {@code range} list is empty.
      */
     protected GridCoverage(final GridGeometry domain, final Collection<? extends SampleDimension> range) {
-        ArgumentChecks.ensureNonNull("domain", domain);
-        ArgumentChecks.ensureNonNull("range",  range);
+        ArgumentChecks.ensureNonNull ("domain", domain);
+        ArgumentChecks.ensureNonEmpty("range", range);
         gridGeometry = domain;
         sampleDimensions = range.toArray(new SampleDimension[range.size()]);
+        ArgumentChecks.ensureNonEmpty("range", sampleDimensions);
         for (int i=0; i<sampleDimensions.length; i++) {
             ArgumentChecks.ensureNonNullElement("range", i, sampleDimensions[i]);
         }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
index df4095d..52baa47 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
@@ -444,13 +444,9 @@ public class ImageRenderer {
          * Add the offset specified by the user (if any), or the default offset. The default is 0, 1, 2…
          * for interleaved sample model (all bands in one bank) and 0, 0, 0… for banded sample model.
          */
-        if (bandOffsets != null) {
+        if (bandOffsets != null || isInterleaved) {
             for (int i=0; i<offsets.length; i++) {
-                offsets[i] = Math.addExact(offsets[i], bandOffsets[i]);
-            }
-        } else if (isInterleaved) {
-            for (int i=1; i<offsets.length; i++) {
-                offsets[i] = Math.addExact(offsets[i], i);
+                offsets[i] = Math.addExact(offsets[i], (bandOffsets != null) ? bandOffsets[i] : i);
             }
         }
         final Point location = new Point(imageX, imageY);
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverage.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverage.java
index 0f1a68a..a6e643c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverage.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.coverage.j2d;
 
+import java.util.Collection;
 import java.awt.image.DataBuffer;
 import java.awt.image.DataBufferByte;
 import java.awt.image.DataBufferDouble;
@@ -25,15 +26,18 @@ import java.awt.image.DataBufferShort;
 import java.awt.image.DataBufferUShort;
 import java.awt.image.RasterFormatException;
 import java.awt.image.RenderedImage;
-import java.util.Collection;
 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.coverage.grid.IllegalGridGeometryException;
 import org.apache.sis.coverage.grid.ImageRenderer;
+import org.apache.sis.internal.feature.Resources;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+
+// Branch-specific imports
+import org.apache.sis.internal.jdk9.JDK9;
 import org.opengis.coverage.CannotEvaluateException;
 
 
@@ -58,24 +62,45 @@ public class BufferedGridCoverage extends GridCoverage {
      * Constructs a grid coverage using the specified grid geometry, sample dimensions and data buffer.
      * This method stores the given buffer by reference (no copy).
      *
-     * @param grid   the grid extent, CRS and conversion from cell indices to CRS.
-     * @param bands  sample dimensions for each image band.
-     * @param data   the sample values, potentially multi-banded.
+     * @param  domain  the grid extent, CRS and conversion from cell indices to CRS.
+     * @param  range   sample dimensions for each image band.
+     * @param  data    the sample values, potentially multi-banded.
+     * @throws NullPointerException if an argument is {@code null}.
+     * @throws IllegalArgumentException if the data buffer has an incompatible number of banks.
+     * @throws IllegalGridGeometryException if the grid extent is larger than the data buffer capacity.
+     * @throws ArithmeticException if the grid extent is larger than 64 bits integer capacity.
      */
-    public BufferedGridCoverage(final GridGeometry grid, final Collection<? extends SampleDimension> bands, final DataBuffer data) {
-        super(grid, bands);
+    public BufferedGridCoverage(final GridGeometry domain, final Collection<? extends SampleDimension> range, final DataBuffer data) {
+        super(domain, range);
         this.data = data;
         ArgumentChecks.ensureNonNull("data", data);
-
-        //verify buffer size
-        GridExtent extent = grid.getExtent();
-        long expectedSize = extent.getSize(0) * bands.size();
-        for (int i = 1; i <extent.getDimension(); i++) {
-            expectedSize *= extent.getSize(i);
+        /*
+         * The buffer shall either contain all values in a single bank (pixel interleaved sample model)
+         * or contain as many banks as bands (banded sample model).
+         */
+        final int numBands = range.size();
+        final int numBanks = data.getNumBanks();
+        if (numBanks != 1 && numBands != numBands) {
+            throw new IllegalArgumentException(Resources.format(Resources.Keys.MismatchedBandCount_2, numBands, numBands));
+        }
+        /*
+         * Verify that the buffer has enough elements for all cells in grid extent.
+         * Note that the buffer may have all elements in a single bank.
+         */
+        long expectedSize = numBands;
+        final GridExtent extent = domain.getExtent();
+        for (int i = extent.getDimension(); --i >= 0;) {
+            expectedSize = Math.multiplyExact(expectedSize, extent.getSize(i));
         }
-        long buffersize = Math.multiplyExact(data.getSize(), data.getNumBanks());
-        if (buffersize < expectedSize) {
-            throw new IllegalGridGeometryException("Expecting a buffer size of at least " + expectedSize + " to contain all samples from given grid geometry, but buffer is only " + buffersize);
+        final long bufferSize = JDK9.multiplyFull(data.getSize(), numBanks);
+        if (bufferSize < expectedSize) {
+            final StringBuilder b = new StringBuilder();
+            for (int i=0; i < extent.getDimension(); i++) {
+                if (i != 0) b.append(" × ");
+                b.append(extent.getSize(i));
+            }
+            throw new IllegalGridGeometryException(Resources.format(
+                    Resources.Keys.InsufficientBufferCapacity_3, b, numBands, expectedSize - bufferSize));
         }
     }
 
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
index a83ddec..315979f 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
@@ -222,6 +222,12 @@ public final class Resources extends IndexedResourceBundle {
         public static final short IncompatibleTile_2 = 35;
 
         /**
+         * Data buffer capacity is insufficient for a grid of {0} cells × {1} bands. Missing {2}
+         * elements.
+         */
+        public static final short InsufficientBufferCapacity_3 = 71;
+
+        /**
          * Invalid or unsupported “{1}” expression at index {0}.
          */
         public static final short InvalidExpression_2 = 56;
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
index 4a69e74..ef7e9b3 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
@@ -51,6 +51,7 @@ ImageAllowsTransparency           = Image allows transparency.
 ImageHasAlphaChannel              = Image has alpha channel.
 ImageIsOpaque                     = Image is opaque.
 IncompatibleTile_2                = The ({0}, {1}) tile has an unexpected size, number of bands or sample layout.
+InsufficientBufferCapacity_3      = Data buffer capacity is insufficient for a grid of {0} cells \u00d7 {1} bands. Missing {2} elements.
 InvalidExpression_2               = Invalid or unsupported \u201c{1}\u201d expression at index {0}.
 IterationIsFinished               = Iteration is finished.
 IterationNotStarted               = Iteration did not started.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
index 2dfc07f..7eb8dc9 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
@@ -56,6 +56,7 @@ ImageAllowsTransparency           = L\u2019image permet la transparence.
 ImageHasAlphaChannel              = L\u2019image a un canal alpha.
 ImageIsOpaque                     = L\u2019image est opaque.
 IncompatibleTile_2                = La tuile ({0}, {1}) a une taille, un nombre de bandes ou une disposition des valeurs inattendu.
+InsufficientBufferCapacity_3      = La capacit\u00e9 du buffer est insuffisante pour une grille de {0} cellules \u00d7 {1} bandes. Il lui manque {2} \u00e9l\u00e9ments.
 InvalidExpression_2               = Expression \u00ab\u202f{1}\u202f\u00bb invalide ou non-support\u00e9e \u00e0 l\u2019index {0}.
 IterationIsFinished               = L\u2019it\u00e9ration est termin\u00e9e.
 IterationNotStarted               = L\u2019it\u00e9ration n\u2019a pas commenc\u00e9e.
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
index dae3155..3bed297 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
@@ -43,7 +43,7 @@ import org.opengis.referencing.operation.MathTransform;
 public class GridCoverageBuilderTest extends TestCase {
 
     /**
-     * Tests {@link GridCoverageBuilder#setValues(Image)}.
+     * Tests {@link GridCoverageBuilder#setValues(RenderedImage)}.
      */
     @Test
     public void createFromImageTest() {
@@ -105,7 +105,7 @@ public class GridCoverageBuilderTest extends TestCase {
     }
 
     /**
-     * Tests {@link GridCoverageBuilder#setValues(Raster)}.
+     * Tests {@link GridCoverageBuilder#setValues(WritableRaster)}.
      */
     @Test
     public void createFromRasterTest() {
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasContext.java b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasContext.java
index 59ae628..6f76f0b 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasContext.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasContext.java
@@ -176,7 +176,7 @@ final class CanvasContext extends CoordinateOperationContext {
         /*
          * Estimate spatial resolution at the point of interest. The calculation is done in
          * (longitude, latitude, height) space where the height is optional. The angles are
-         * converted to meters using the authalic radius.
+         * converted to meters using the radius of conformal sphere.
          */
         if (!(resolution > 0)) {
             final double[] poi = canvas.getObjectivePOI();
diff --git a/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java b/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
index b8cde89..1f9acd3 100644
--- a/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
+++ b/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
@@ -72,6 +72,7 @@ import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.Latitude;
 
 // Branch-dependent imports
+import org.apache.sis.internal.jdk9.JDK9;
 import org.opengis.metadata.citation.Party;
 import org.opengis.referencing.gazetteer.Location;
 import org.opengis.referencing.gazetteer.LocationType;
@@ -1150,7 +1151,7 @@ public class MilitaryGridReferenceSystem extends ReferencingByIdentifiers {
          */
         @Override
         public long estimateSize() {
-            return (xEnd - (long) gridX) * Math.abs(yEnd - (long) yStart) / (step * (long) step);
+            return (xEnd - (long) gridX) * Math.abs(yEnd - (long) yStart) / JDK9.multiplyFull(step, step);
         }
 
         /**
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
index f886b3f..1e3c7bb 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
@@ -127,4 +127,15 @@ public final class JDK9 {
         name = (separator >= 1) ? name.substring(0, separator) : "";
         return name;
     }
+
+    /**
+     * Place holder for {@code Math.multiplyFull​(int, int)}.
+     *
+     * @param  x  the first value.
+     * @param  y  the second value.
+     * @return Product of the two values.
+     */
+    public static long multiplyFull​(int x, int y) {
+        return ((long) x) * ((long) y);
+    }
 }
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java b/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java
index e15e2e2..99778b9 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java
@@ -21,6 +21,7 @@ import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.collection.WeakHashSet;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.internal.jdk9.JDK9;
 
 
 /**
@@ -216,7 +217,7 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri
      * However it should never happen. Even in the worst scenario:</p>
      *
      * {@prefomat java
-     *     long n = Integer.MIN_VALUE * (long) Integer.MAX_VALUE;
+     *     long n = Math.multiplyFull(Integer.MIN_VALUE, Integer.MAX_VALUE);
      *     n += n;
      * }
      *
@@ -319,8 +320,8 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri
      * @throws ArithmeticException if the result overflows.
      */
     public Fraction multiply(final Fraction other) {
-        return simplify(this, numerator   * (long) other.numerator,
-                              denominator * (long) other.denominator);
+        return simplify(this, JDK9.multiplyFull(numerator,   other.numerator),
+                              JDK9.multiplyFull(denominator, other.denominator));
     }
 
     /**
@@ -331,8 +332,8 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri
      * @throws ArithmeticException if the result overflows.
      */
     public Fraction divide(final Fraction other) {
-        return simplify(this, numerator   * (long) other.denominator,
-                              denominator * (long) other.numerator);
+        return simplify(this, JDK9.multiplyFull(numerator,   other.denominator),
+                              JDK9.multiplyFull(denominator, other.numerator));
     }
 
     /**
@@ -506,7 +507,8 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri
      */
     @Override
     public int compareTo(final Fraction other) {
-        return Long.signum(numerator * (long) other.denominator - other.numerator * (long) denominator);
+        return Long.signum(JDK9.multiplyFull(numerator, other.denominator)
+                         - JDK9.multiplyFull(other.numerator, denominator));
     }
 
     /**