sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/05: Consolidation of multi-grids NTv2 support with the addition of documentation and tests.
Date Sat, 15 Feb 2020 17:13:00 GMT
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 901921cb3c030c749573c156a7b573cc850f8d12
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Feb 15 14:12:50 2020 +0100

    Consolidation of multi-grids NTv2 support with the addition of documentation and tests.
---
 .../referencing/provider/DatumShiftGridFile.java   |   3 +-
 .../referencing/provider/DatumShiftGridGroup.java  |  82 ++++++++++++-----
 .../internal/referencing/provider/NADCONTest.java  |  12 +--
 .../internal/referencing/provider/NTv2Test.java    | 101 ++++++++++++++++++---
 4 files changed, 154 insertions(+), 44 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
index c9c3794..fb5df9d 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
@@ -178,6 +178,7 @@ abstract class DatumShiftGridFile<C extends Quantity<C>, T extends
Quantity<T>>
     /**
      * Creates a new datum shift grid with the same configuration than the given grid,
      * except the size and transform which are set to the given values.
+     * The {@link #accuracy} is initialized to zero and should be updated by the caller.
      *
      * @param other             the other datum shift grid from which to copy parameters.
      * @param coordinateToGrid  conversion from the "real world" coordinates to grid indices
including fractional parts.
@@ -192,7 +193,7 @@ abstract class DatumShiftGridFile<C extends Quantity<C>, T extends
Quantity<T>>
         descriptor = other.descriptor;
         files      = other.files;
         this.nx    = nx;
-        accuracy   = other.accuracy;
+        // Accuracy to be set by caller. Initial value needs to be zero.
     }
 
     /**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridGroup.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridGroup.java
index 883f45b..966bbbf 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridGroup.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridGroup.java
@@ -37,8 +37,22 @@ import org.apache.sis.internal.util.CollectionsExt;
 
 /**
  * A group of datum shift grids. This is used when a NTv2 file contains more than one grid
with no common parent.
- * This class creates a synthetic parent which always delegate its work to a child (as opposed
to more classical
- * trees where the parent can do some work if no child can).
+ * This class creates a synthetic parent which always delegates its work to a child (as opposed
to more classical
+ * transform trees where the parent can do some work if no child can). Coordinate transformations
will be applied
+ * as below:
+ *
+ * <ol>
+ *   <li>{@link org.apache.sis.referencing.operation.transform.SpecializableTransform}
will try to locate the
+ *       most appropriate grid for given coordinates. This is the class where to put our
optimization efforts,
+ *       for example by checking the last used grid before to check all other grids.</li>
+ *   <li>Only if {@code SpecializableTransform} did not found a better transform, it
will fallback on a transform
+ *       backed by this {@code DatumShiftGridGroup}. In such case, {@link InterpolatedTransform}
will perform its
+ *       calculation by invoking {@link #interpolateInCell(double, double, double[])}. That
method tries again to
+ *       locate the best grid, but performance is less important there since that method
is only a fallback.</li>
+ *   <li>The default {@link DatumShiftGridFile#interpolateInCell(double, double, double[])}
implementation invokes
+ *       {@link #getCellValue(int, int, int)}. We provide that method for consistency, but
it should not be invoked
+ *       since we overrode {@link #interpolateInCell(double, double, double[])}.</li>
+ * </ol>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
@@ -49,7 +63,7 @@ final class DatumShiftGridGroup<C extends Quantity<C>, T extends
Quantity<T>> ex
     /**
      * The bounds of a sub-grid, together with the subsampling level compared to the grid
having the finest resolution.
      * All values in this class are integers, but nevertheless stored as {@code double} for
avoiding to cast them every
-     * time {@link #interpolateInCell(double, double, double[])} is executed.
+     * time {@link DatumShiftGridGroup#interpolateInCell(double, double, double[])} is executed.
      */
     private static final class Region {
         /** Grid bounds in units of the grid having finest resolution. */
@@ -90,9 +104,9 @@ final class DatumShiftGridGroup<C extends Quantity<C>, T extends
Quantity<T>> ex
     }
 
     /**
-     * For each {@code subgrids[i]}, {@code regions[i]} is the range of indices valid of
that grid.
-     * This array will be used only as a fallback if the {@code MathTransform} has not been
able to
-     * find the sub-grid itself. Since it should be rarely used, we do not bother using a
R-Tree.
+     * For each {@code subgrids[i]}, {@code regions[i]} is the range of indices valid for
that grid.
+     * This array will be used only as a fallback if {@code SpecializableTransform} has not
been able
+     * to find the sub-grid itself. Since it should be rarely used, we do not bother using
a R-Tree.
      */
     private final Region[] regions;
 
@@ -104,22 +118,28 @@ final class DatumShiftGridGroup<C extends Quantity<C>, T extends
Quantity<T>> ex
      * @param  tiles      the tiles computed by {@link TileOrganizer}.
      * @param  grids      sub-grids associated to tiles computed by {@link TileOrganizer}.
      * @param  gridToCRS  conversion from grid indices to "real world" coordinates.
-     * @param  nx         number of cells along the <var>x</var> axis in the
grid.
-     * @param  ny         number of cells along the <var>y</var> axis in the
grid.
+     * @param  gridSize   number of cells along the <var>x</var> and <var>y</var>
axes in the grid.
      * @throws IOException declared because {@link Tile#getRegion()} declares it, but should
not happen.
      */
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    private DatumShiftGridGroup(final Tile[] tiles, final Map<Tile,DatumShiftGridFile<C,T>>
grids,
-            final AffineTransform2D gridToCRS, final int nx, final int ny)
+    @SuppressWarnings({"rawtypes", "unchecked"})                        // For generic array
creation.
+    private DatumShiftGridGroup(final Tile[] tiles,
+                                final Map<Tile,DatumShiftGridFile<C,T>> grids,
+                                final AffineTransform2D gridToCRS,
+                                final Dimension gridSize)
             throws IOException, NoninvertibleTransformException
     {
-        super(grids.get(tiles[0]), gridToCRS.inverse(), nx, ny);
+        super(grids.get(tiles[0]), gridToCRS.inverse(), gridSize.width, gridSize.height);
         final int n = grids.size();
         regions  = new Region[n];
         subgrids = new DatumShiftGridFile[n];
         for (int i=0; i<n; i++) {
-            regions [i] = new Region(tiles[i]);
-            subgrids[i] = grids.get(tiles[i]);
+            final Tile tile = tiles[i];
+            final DatumShiftGridFile<C,T> grid = grids.get(tile);
+            regions [i] = new Region(tile);
+            subgrids[i] = grid;
+            if (grid.accuracy > accuracy) {
+                accuracy = grid.accuracy;           // Conservatively set accuracy to the
largest value.
+            }
         }
     }
 
@@ -127,7 +147,7 @@ final class DatumShiftGridGroup<C extends Quantity<C>, T extends
Quantity<T>> ex
      * Puts the given sub-grid in a group. This method infers itself what would be the size
      * of a grid containing all given sub-grids.
      *
-     * @param  file  filename to report in case of error.
+     * @param  file      filename to report in case of error.
      * @param  subgrids  the sub-grids to put under a common root.
      * @throws FactoryException if the sub-grid can not be combined in a single mosaic or
pyramid.
      * @throws IOException declared because {@link Tile#getRegion()} declares it, but should
not happen.
@@ -140,21 +160,29 @@ final class DatumShiftGridGroup<C extends Quantity<C>, T extends
Quantity<T>> ex
         final Map<Tile,DatumShiftGridFile<C,T>> grids = new LinkedHashMap<>();
         for (final DatumShiftGridFile<C,T> grid : subgrids) {
             final int[] size = grid.getGridSize();
-            final Tile tile = new Tile(new Rectangle(size[0], size[1]),
+            final Tile  tile = new Tile(new Rectangle(size[0], size[1]),
                     (AffineTransform) grid.getCoordinateToGrid().inverse());
-            if (mosaic.add(tile)) {                                     // Should never be
false, but check anyway.
-                if (grids.put(tile, grid) != null) {
-                    throw new AssertionError(tile);                     // Should never happen
(paranoiac check).
-                }
+            /*
+             * Assertions below would fail if the tile has already been processed by TileOrganizer,
+             * or if it duplicates another tile. Since we created that tile just above, a
failure
+             * would be a bug in Tile or TileOrganizer.
+             */
+            if (!mosaic.add(tile) || grids.put(tile, grid) != null) {
+                throw new AssertionError(tile);
             }
         }
+        /*
+         * After processing by TileOrganizer, we should have only one group of tiles. If
we have more groups,
+         * it would mean that the cell size of the grid having larger cells is not a multiple
of cell size of
+         * the grid having smallest cells, or that cell indices in some grids, when expressed
in units of the
+         * smallest cells, would be fractional numbers. It should not happen in a NTv2 compliant
file.
+         */
         final Map.Entry<Tile,Tile[]> result = CollectionsExt.singletonOrNull(mosaic.tiles().entrySet());
         if (result == null) {
             throw new FactoryException(Resources.format(Resources.Keys.MisalignedDatumShiftGrid_1,
file));
         }
         final Tile global = result.getKey();
-        final Rectangle r = global.getRegion();
-        return new DatumShiftGridGroup<>(result.getValue(), grids, global.getGridToCRS(),
r.width, r.height);
+        return new DatumShiftGridGroup<>(result.getValue(), grids, global.getGridToCRS(),
global.getSize());
     }
 
     /**
@@ -192,8 +220,8 @@ final class DatumShiftGridGroup<C extends Quantity<C>, T extends
Quantity<T>> ex
 
     /**
      * Returns the number of dimensions of the translation vectors interpolated by this datum
shift grid.
-     * This implementation takes the first sub-grid as a template. The selected grid should
not matter
-     * since they shall all have the same number of target dimensions.
+     * This implementation takes the first sub-grid as a template. The choice of the grid
does not matter
+     * since all grids have the same number of target dimensions.
      */
     @Override
     public int getTranslationDimensions() {
@@ -204,7 +232,7 @@ final class DatumShiftGridGroup<C extends Quantity<C>, T extends
Quantity<T>> ex
      * Returns the translation stored at the given two-dimensional grid indices for the given
dimension.
      * This method is defined for consistency with {@link #interpolateInCell(double, double,
double[])}
      * but should never be invoked. The {@link InterpolatedTransform} class will rather invoke
the
-     * {@code interpolateInCell} method for efficiency.
+     * {@code interpolateInCell(…)} method for efficiency.
      *
      * @param  dim    the dimension of the translation vector component to get.
      * @param  gridX  the grid index on the <var>x</var> axis, from 0 inclusive
to {@code gridSize[0]} exclusive.
@@ -257,6 +285,10 @@ final class DatumShiftGridGroup<C extends Quantity<C>, T extends
Quantity<T>> ex
                 return;
             }
         }
+        /*
+         * The following method call will (indirectly) invokes the above `getCellValue(…)`
method.
+         * It can be used as a way to test that method.
+         */
         super.interpolateInCell(gridX, gridY, vector);
     }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java
index 523752f..11945ce 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java
@@ -93,12 +93,11 @@ public final strictfp class NADCONTest extends DatumShiftTestCase {
      * The point used for this test is given by {@link #samplePoint(int)}.
      *
      * @throws URISyntaxException if the URL to the test file can not be converted to a path.
-     * @throws IOException if an error occurred while loading the grid.
-     * @throws FactoryException if an error occurred while computing the grid.
+     * @throws FactoryException if an error occurred while loading or computing the grid.
      * @throws TransformException if an error occurred while computing the envelope or testing
the point.
      */
     @Test
-    public void testLoader() throws URISyntaxException, IOException, FactoryException, TransformException
{
+    public void testLoader() throws URISyntaxException, FactoryException, TransformException
{
         testNADCON(getResource(TEST_FILE + ".laa"),     // Latitude shifts
                    getResource(TEST_FILE + ".loa"),     // Longitude shifts
                    -99.75, -98.0, 37.5, 39.75);
@@ -112,12 +111,11 @@ public final strictfp class NADCONTest extends DatumShiftTestCase {
      *
      * @param  latitudeShifts   path to the official {@code "conus.las"} file.
      * @param  longitudeShifts  path to the official {@code "conus.los"} file.
-     * @throws IOException if an error occurred while loading the grid.
-     * @throws FactoryException if an error occurred while computing the grid.
+     * @throws FactoryException if an error occurred while loading or computing the grid.
      * @throws TransformException if an error occurred while computing the envelope or testing
the point.
      */
     public static void testNADCON(final Path latitudeShifts, final Path longitudeShifts)
-            throws IOException, FactoryException, TransformException
+            throws FactoryException, TransformException
     {
         testNADCON(latitudeShifts, longitudeShifts, -131, -63, 20, 50);
     }
@@ -132,7 +130,7 @@ public final strictfp class NADCONTest extends DatumShiftTestCase {
      */
     private static void testNADCON(final Path latitudeShifts, final Path longitudeShifts,
             final double xmin, final double xmax, final double ymin, final double ymax)
-            throws IOException, FactoryException, TransformException
+            throws FactoryException, TransformException
     {
         final DatumShiftGridFile<Angle,Angle> grid = NADCON.getOrLoad(latitudeShifts,
longitudeShifts);
         assertInstanceOf("Should not be compressed.", DatumShiftGridFile.Float.class, grid);
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NTv2Test.java
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NTv2Test.java
index 5d29b20..3efe47f 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NTv2Test.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NTv2Test.java
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.nio.file.Files;
 import java.nio.file.StandardOpenOption;
 import java.nio.channels.WritableByteChannel;
@@ -33,16 +34,22 @@ import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.measure.Units;
+import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.internal.system.DataDirectory;
+import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
+import static org.junit.Assume.assumeTrue;
 import static org.opengis.test.Assert.*;
+import static org.apache.sis.internal.referencing.provider.DatumShiftGridLoader.DEGREES_TO_SECONDS;
 
 
 /**
  * Tests the {@link NTv2} grid loader.
+ * It will also indirectly tests {@link DatumShiftGridGroup} class.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
  *
  * @see GeocentricTranslationTest#testFranceGeocentricInterpolationPoint()
  * @see org.apache.sis.referencing.operation.transform.MolodenskyTransformTest#testFranceGeocentricInterpolationPoint()
@@ -50,6 +57,7 @@ import static org.opengis.test.Assert.*;
  * @since 0.7
  * @module
  */
+@DependsOn(DatumShiftGridFileTest.class)
 public final strictfp class NTv2Test extends DatumShiftTestCase {
     /**
      * Name of the file containing a small extract of the "{@code NTF_R93.gsb}" file.
@@ -58,6 +66,13 @@ public final strictfp class NTv2Test extends DatumShiftTestCase {
     public static final String TEST_FILE = "NTF_R93-extract.gsb";
 
     /**
+     * Name of the file to load for testing the multi-grids support.
+     * This file should be present in the {@code $SIS_DATA/DatumChanges} directory.
+     * The test will be skipped if this file is absent.
+     */
+    private static final String MULTIGRID_TEST_FILE = "NTv2_0.gsb";
+
+    /**
      * Best accuracy found in the "{@code NTF_R93.gsb}" file.
      */
     private static final float ACCURACY = 0.001618f;
@@ -87,11 +102,10 @@ public final strictfp class NTv2Test extends DatumShiftTestCase {
      * explicitly if they can provide a path to the {@code "NTF_R93.gsb"} file.
      *
      * @param  file  path to the official {@code "NTF_R93.gsb"} file.
-     * @throws IOException if an error occurred while loading the grid.
-     * @throws FactoryException if an error occurred while computing the grid.
+     * @throws FactoryException if an error occurred while loading or computing the grid.
      * @throws TransformException if an error occurred while computing the envelope or testing
the point.
      */
-    public static void testRGF93(final Path file) throws IOException, FactoryException, TransformException
{
+    public static void testRGF93(final Path file) throws FactoryException, TransformException
{
         testRGF93(file, -19800, 36000, 147600, 187200);
     }
 
@@ -104,15 +118,15 @@ public final strictfp class NTv2Test extends DatumShiftTestCase {
      * @param  ymax  value of the {@code "N_LAT"} record.
      */
     private static void testRGF93(final Path file, final double xmin, final double xmax,
-            final double ymin, final double ymax) throws IOException, FactoryException, TransformException
+            final double ymin, final double ymax) throws FactoryException, TransformException
     {
         final double cellSize = 360;
         final DatumShiftGridFile<Angle,Angle> grid = NTv2.getOrLoad(file);
         assertInstanceOf("Should not be compressed.", DatumShiftGridFile.Float.class, grid);
         assertEquals("coordinateUnit",  Units.ARC_SECOND, grid.getCoordinateUnit());
         assertEquals("translationUnit", Units.ARC_SECOND, grid.getTranslationUnit());
-        assertEquals("translationDimensions", 2, grid.getTranslationDimensions());
-        assertTrue  ("isCellValueRatio", grid.isCellValueRatio());
+        assertEquals("translationDimensions", 2,          grid.getTranslationDimensions());
+        assertTrue  ("isCellValueRatio",                  grid.isCellValueRatio());
         assertEquals("cellPrecision", (ACCURACY / 10) / cellSize, grid.getCellPrecision(),
0.5E-6 / cellSize);
         /*
          * Verify the envelope and the conversion between geographic coordinates and grid
indices.
@@ -142,8 +156,8 @@ public final strictfp class NTv2Test extends DatumShiftTestCase {
         final double[] indices  = new double[position.length];
         final double[] vector   = new double[2];
         for (int i=0; i<expected.length; i++) {
-            position[i] *= DatumShiftGridLoader.DEGREES_TO_SECONDS;
-            expected[i] *= DatumShiftGridLoader.DEGREES_TO_SECONDS;
+            position[i] *= DEGREES_TO_SECONDS;
+            expected[i] *= DEGREES_TO_SECONDS;
             expected[i] -= position[i];  // We will test the interpolated shifts rather than
final coordinates.
         }
         grid.getCoordinateToGrid().transform(position, 0, indices, 0, 1);
@@ -151,14 +165,79 @@ public final strictfp class NTv2Test extends DatumShiftTestCase {
         vector[0] *= -cellSize;   // Was positive toward west.
         vector[1] *= +cellSize;
         assertArrayEquals("interpolateInCell", expected, vector,
-                FranceGeocentricInterpolationTest.ANGULAR_TOLERANCE * DatumShiftGridLoader.DEGREES_TO_SECONDS);
+                FranceGeocentricInterpolationTest.ANGULAR_TOLERANCE * DEGREES_TO_SECONDS);
 
         // Same test than above, but let DatumShiftGrid do the conversions for us.
         assertArrayEquals("interpolateAt", expected, grid.interpolateAt(position),
-                FranceGeocentricInterpolationTest.ANGULAR_TOLERANCE * DatumShiftGridLoader.DEGREES_TO_SECONDS);
+                FranceGeocentricInterpolationTest.ANGULAR_TOLERANCE * DEGREES_TO_SECONDS);
         assertSame("Grid should be cached.", grid, NTv2.getOrLoad(file));
     }
 
+    /**
+     * Tests using a file containing many grids. This tests depends on the {@value #MULTIGRID_TEST_FILE}
+     * to be present in the {@code $SIS_DATA/DatumChanges} directory. This test is executed
only if the
+     * {@link #RUN_EXTENSIVE_TESTS} flag is set.
+     *
+     * @throws FactoryException if an error occurred while loading or computing the grid.
+     * @throws TransformException if an error occurred while computing the envelope or testing
the point.
+     */
+    @Test
+    public void testMultiGrids() throws FactoryException, TransformException {
+        assumeTrue(RUN_EXTENSIVE_TESTS);
+        final Path file = DataDirectory.DATUM_CHANGES.resolve(Paths.get(MULTIGRID_TEST_FILE));
+        assumeTrue(Files.exists(file));
+        final DatumShiftGridFile<Angle,Angle> grid = NTv2.getOrLoad(file);
+        assertInstanceOf("Should contain many grids.", DatumShiftGridGroup.class, grid);
+        assertEquals("coordinateUnit",  Units.ARC_SECOND, grid.getCoordinateUnit());
+        assertEquals("translationUnit", Units.ARC_SECOND, grid.getTranslationUnit());
+        assertEquals("translationDimensions", 2,          grid.getTranslationDimensions());
+        assertTrue  ("isCellValueRatio",                  grid.isCellValueRatio());
+        /*
+         * Area of use declared in EPSG database for coordinate operation EPSG::1693:
+         *
+         *     40.04°N  to  83.17°N    and    141.01°W  to  47.74°W.
+         *
+         * In the assertions below, the `cellSize` value has been verified in the NTv2
+         * file header but the envelope bounds have been determined empirically.
+         */
+        final double cellSize = 300;                                    // Number of arc-seconds
in a cell.
+        final Envelope envelope = grid.getDomainOfValidity();
+        assertEquals("xmin", (-142.29 - 0.5/cellSize) * DEGREES_TO_SECONDS, envelope.getMinimum(0),
1E-10);
+        assertEquals("xmax", ( -43.96 + 0.5/cellSize) * DEGREES_TO_SECONDS, envelope.getMaximum(0),
1E-10);
+        assertEquals("ymin", (  39.96 - 0.5/cellSize) * DEGREES_TO_SECONDS, envelope.getMinimum(1),
1E-10);
+        assertEquals("ymax", (  84.04 + 0.5/cellSize) * DEGREES_TO_SECONDS, envelope.getMaximum(1),
1E-10);
+        /*
+         * Test a point. This point is located on the 3th grid in the NTv2 file.
+         * Consequently if the NTv2 implementation just pickups the first grid,
+         * then this test would fail with an error around 100 metres.
+         */
+        final double[] position = {-134.998106062 * DEGREES_TO_SECONDS, 61.000285047 * DEGREES_TO_SECONDS};
+        final double[] expected = {-135.0         * DEGREES_TO_SECONDS, 61.0         * DEGREES_TO_SECONDS};
+        final double[] indices  = new double[position.length];
+        grid.getCoordinateToGrid().transform(position, 0, indices, 0, 1);
+        final int gridX = Math.toIntExact(Math.round(indices[0]));
+        final int gridY = Math.toIntExact(Math.round(indices[1]));
+        assertEquals("gridX", 1092, gridX);                                 // Value determined
empirically.
+        assertEquals("gridY",  252, gridY);
+        /*
+         * First check the value computed by `getCellValue(…)`. This method is only a fallback
and
+         * should not be invoked in normal usage, so a direct invocation is the only way
to test it.
+         */
+        final double[] result = new double[] {
+            position[0] - grid.getCellValue(0, gridX, gridY) * cellSize,
+            position[1] + grid.getCellValue(1, gridX, gridY) * cellSize
+        };
+        assertArrayEquals("getCellValue", expected, result, 0.001);
+        /*
+         * Check `interpolateInCell(…)`, which is the method invoked by `InterpolatedTransform`
+         * when `SpecializableTransform` has not been able to find the most appropriate grid.
+         */
+        grid.interpolateInCell(indices[0], indices[1], result);
+        result[0] = position[0] - result[0] * cellSize;
+        result[1] = position[1] + result[1] * cellSize;
+        assertArrayEquals("interpolateInCell", expected, result, Formulas.ANGULAR_TOLERANCE
* DEGREES_TO_SECONDS);
+    }
+
 
 
 


Mime
View raw message