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/02/15 17:13:00 UTC
[sis] 02/05: Consolidation of multi-grids NTv2 support with the
addition of documentation and tests.
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 <ma...@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);
+ }
+