sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Test GeodeticCalculator against the data set provided by Charles Karney (2010): Test set for geodesics [Data set] - http://doi.org/10.5281/zenodo.32156 We have to use a tolerance threshold of 1% of expected distance for now, because we do not yet implement ellipsoidal formulas.
Date Sun, 19 May 2019 20:03:08 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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new af89c09  Test GeodeticCalculator against the data set provided by Charles Karney
(2010): Test set for geodesics [Data set] - http://doi.org/10.5281/zenodo.32156 We have to
use a tolerance threshold of 1% of expected distance for now, because we do not yet implement
ellipsoidal formulas.
af89c09 is described below

commit af89c097dffe9e9a782bc364ef5f02b456ce6897
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sun May 19 22:00:38 2019 +0200

    Test GeodeticCalculator against the data set provided by Charles Karney (2010): Test set
for geodesics [Data set] - http://doi.org/10.5281/zenodo.32156
    We have to use a tolerance threshold of 1% of expected distance for now, because we do
not yet implement ellipsoidal formulas.
---
 .../sis/referencing/GeodeticCalculatorTest.java    | 111 ++++++++++++++++++
 .../apache/sis/internal/system/DataDirectory.java  |  18 ++-
 .../java/org/apache/sis/test/OptionalTestData.java | 126 +++++++++++++++++++++
 3 files changed, 251 insertions(+), 4 deletions(-)

diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticCalculatorTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticCalculatorTest.java
index 80c5610..0b004fb 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticCalculatorTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticCalculatorTest.java
@@ -19,14 +19,19 @@ package org.apache.sis.referencing;
 import java.awt.Shape;
 import java.awt.geom.Point2D;
 import java.awt.geom.PathIterator;
+import java.util.Arrays;
 import java.util.Random;
+import java.io.IOException;
+import java.io.LineNumberReader;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.internal.referencing.j2d.ShapeUtilities;
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.util.CharSequences;
 import org.apache.sis.measure.Units;
+import org.apache.sis.test.OptionalTestData;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
@@ -307,4 +312,110 @@ public final strictfp class GeodeticCalculatorTest extends TestCase
{
         }
         return length;
     }
+
+    /**
+     * Compares computations against values provided in <cite>Karney (2010) Test set
for geodesics</cite>.
+     * This is an optional test executed only if the {@code $SIS_DATA/Tests/GeodTest.dat}
file is found.
+     *
+     * @throws IOException if an error occurred while reading the test file.
+     * @throws TransformException if an error occurred while transforming coordinates.
+     */
+    @Test
+    public void compareAgainstDataset() throws IOException, TransformException {
+        try (LineNumberReader reader = OptionalTestData.GEODESIC.reader()) {
+            final GeodeticCalculator c = new GeodeticCalculator(CommonCRS.WGS84.geographic());
+            final Geodesic reference = new Geodesic(Formulas.getAuthalicRadius(c.ellipsoid),
0);
+            final Random random = TestUtilities.createRandomNumberGenerator();
+            final double[] data = new double[7];
+            try {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    Arrays.fill(data, Double.NaN);
+                    final CharSequence[] split = CharSequences.split(line, ' ');
+                    for (int i=min(split.length, data.length); --i >= 0;) {
+                        data[i] = Double.parseDouble(split[i].toString());
+                    }
+                    /*
+                     * We aim for an 1 cm accuracy. However when spherical formulas are used
instead
+                     * than ellipsoidal formulas, an error up to 1% is expected (Wikipedia).
+                     */
+                    final double tolerance = data[6] * 0.01;                // 1% of distance.
+                    final double cosφ = abs(cos(toRadians(data[3])));       // For adjusting
longitude tolerance.
+                    c.setStartPoint(data[0], data[1]);                      // (φ₁, λ₁)
+                    if (random.nextBoolean()) {
+                        /*
+                         * Computes the end point from a distance and azimuth. The angular
tolerance
+                         * is derived from the linear tolerance, except at pole where we
disable the
+                         * check of longitude and azimuth values.
+                         */
+                        c.setStartingAzimuth (data[2]);
+                        c.setGeodesicDistance(data[6]);
+                        final double latitudeTolerance = tolerance * (1d/6371007 * (180/PI));
+                        final double longitudeTolerance;
+                        if (data[3] > 89.5) {
+                            longitudeTolerance = 180;               // TODO: remove after
we use spherical formulas.
+                        } else {
+                            longitudeTolerance = latitudeTolerance / cosφ;
+                        }
+                        final double azimuthTolerance = 0.5 / cosφ;
+                        compareGeodeticData(data, c, latitudeTolerance, longitudeTolerance,
+                                            azimuthTolerance, Formulas.LINEAR_TOLERANCE);
+                        /*
+                         * Replace the distance and azimuth values by values computed using
spherical formulas,
+                         * then compare again with values computed by GeodeticCalculator
but with low tolerance.
+                         */
+                        final GeodesicData gd = reference.Direct(data[0], data[1], data[2],
data[6]);
+                        data[3] = gd.lat2;
+                        data[4] = gd.lon2;
+                        data[5] = gd.azi2;
+                    } else {
+                        /*
+                         * Compute the distance and azimuth values between two points. We
perform
+                         * this test or the above test randomly instead of always executing
both
+                         * of them for making sure that GeodeticCalculator never see the
expected
+                         * values.
+                         */
+                        c.setEndPoint(data[3], data[4]);            // (φ₂, λ₂)
+                        compareGeodeticData(data, c,
+                                Formulas.ANGULAR_TOLERANCE,         // Latitude tolerance
+                                Formulas.ANGULAR_TOLERANCE,         // Longitude tolerance
+                                100 / cosφ, tolerance);             // Azimuth is inaccurate
for reason not yet identified.
+                        /*
+                         * Replace the distance and azimuth values by values computed using
spherical formulas,
+                         * then compare again with values computed by GeodeticCalculator
but with low tolerance.
+                         */
+                        final GeodesicData gd = reference.Inverse(data[0], data[1], data[3],
data[4]);
+                        data[2] = gd.azi1;
+                        data[5] = gd.azi2;
+                        data[6] = gd.s12;
+                    }
+                    compareGeodeticData(data, c, Formulas.ANGULAR_TOLERANCE,
+                            Formulas.ANGULAR_TOLERANCE, 1E-4, Formulas.LINEAR_TOLERANCE);
+                }
+            } catch (AssertionError e) {
+                out.printf("Test failure at line %d%nGeodetic calculator is:%n", reader.getLineNumber());
+                out.println(c);
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * Verifies that geodetic calculator results are equal to the given values.
+     * Order in the {@code data} array is as documented in {@link OptionalTestData#GEODESIC}.
+     */
+    private static void compareGeodeticData(final double[] expected, final GeodeticCalculator
c,
+            final double latitudeTolerance, final double longitudeTolerance, final double
azimuthTolerance,
+            final double linearTolerance) throws TransformException
+    {
+        final DirectPosition start = c.getStartPoint();
+        final DirectPosition end   = c.getEndPoint();
+        assertEquals("φ₁",  expected[0], start.getOrdinate(0),    Formulas.ANGULAR_TOLERANCE);
+        assertEquals("λ₁",  expected[1], start.getOrdinate(1),    Formulas.ANGULAR_TOLERANCE);
+        assertEquals("α₁",  expected[2], c.getStartingAzimuth(),  azimuthTolerance);
+        assertEquals("φ₂",  expected[3], end.getOrdinate(0),      latitudeTolerance);
+        assertEquals("λ₂",  expected[4], end.getOrdinate(1),      longitudeTolerance);
+        assertEquals("α₂",  expected[5], c.getEndingAzimuth(),    azimuthTolerance);
+        assertEquals("s₁₂", expected[6], c.getGeodesicDistance(), linearTolerance);
+    }
 }
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java
index 40ba41b..b1af2c4 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java
@@ -33,7 +33,7 @@ import org.apache.sis.util.resources.Messages;
  * Sub-directories of {@code SIS_DATA} where SIS looks for EPSG database, datum shift grids
and other resources.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.7
  * @module
  */
@@ -54,7 +54,14 @@ public enum DataDirectory {
      * The {@code "DomainsOfValidity"} directory.
      * This directory is used for storing shapefiles for the CRS domains of validity.
      */
-    DOMAINS_OF_VALIDITY;
+    DOMAINS_OF_VALIDITY,
+
+    /**
+     * The {@code "Tests" directory}.
+     * This directory is used for optional test files that are too large for inclusion in
source code repository.
+     * This is used at build time of Apache SIS project, but not used during normal execution.
+     */
+    TESTS;
 
     /**
      * The name of the environment variable.
@@ -215,11 +222,14 @@ public enum DataDirectory {
     }
 
     /**
-     * If the given path is relative, returns the path as a child of the directory represented
this enum.
+     * If the given path is relative, returns the path as a child of the directory represented
by this enum.
      * If no valid directory is configured by the {@code SIS_DATA} environment variable,
then the relative
      * path is returned as-is.
      *
-     * @param  file The path to resolve, or {@code null}.
+     * <p>This method is invoked for files that may be user-specified, for example
datum shift file specified
+     * in {@link org.opengis.parameter.ParameterValue}.</p>
+     *
+     * @param  file  the path to resolve, or {@code null}.
      * @return the path to use, or {@code null} if the given path was null.
      */
     public Path resolve(Path file) {
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/OptionalTestData.java b/core/sis-utility/src/test/java/org/apache/sis/test/OptionalTestData.java
new file mode 100644
index 0000000..c322e0c
--- /dev/null
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/OptionalTestData.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.test;
+
+import java.io.LineNumberReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.apache.sis.internal.system.DataDirectory;
+
+import static org.junit.Assume.assumeNotNull;
+
+
+/**
+ * All optional test data used by Apache SIS. Those data are not present on the source code
repository.
+ * They must be downloaded and installed by the developer in the {@code $SIS_DATA/Tests}
directory in
+ * order to enable the tests requiring those data.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+public enum OptionalTestData {
+    /**
+     * Geodesic distances, rhumb line length and azimuths on WGS84 ellipsoid computed from
a set of points.
+     *
+     * <dl>
+     *   <dt>File:</dt>
+     *   <dd>{@code GeodTest.dat}</dd>
+     *   <dt>Size:</dt>
+     *   <dd>86558916 bytes (83 Mb)</dd>
+     *   <dt>MD5 sum:</dt>
+     *   <dd>{@code 3461c4dc2500a8bad9394cd530b13dbe}</dd>
+     *   <dt>Source:</dt>
+     *   <dd><a href="http://doi.org/10.5281/zenodo.32156">Karney, C. F. F. (2010).
Test set for geodesics [Data set]. Zenodo.</a></dd>
+     * </dl>
+     *
+     * Each line in the test file gives the following numbers (space delimited):
+     *
+     * <ol>
+     *   <li>φ₁ — latitude at point 1 (degrees)</li>
+     *   <li>λ₁ — longitude at point 1 (degrees)</li>
+     *   <li>α₁ — azimuth at point 1 (degrees, clockwise from north)</li>
+     *   <li>φ₂ — latitude at point 2 (degrees)</li>
+     *   <li>λ₂ — longitude at point 2 (degrees)</li>
+     *   <li>α₂ — azimuth at point 2 (degrees, clockwise from north)</li>
+     *   <li>s₁₂ — geodesic distance from point 1 to point 2 (metres)</li>
+     *   <li>σ₁₂ — arc distance on the auxiliary sphere (degrees)</li>
+     *   <li>m₁₂ — reduced length of the geodesic (meters)</li>
+     *   <li>S₁₂ — the area between the geodesic and the equator (m²)</li>
+     * </ol>
+     */
+    GEODESIC("GeodTest.dat");
+
+    /**
+     * The filename in {@code $SIS_DATA/Tests} directory.
+     */
+    private final String filename;
+
+    /**
+     * Creates a new enumeration for the given file.
+     */
+    private OptionalTestData(final String filename) {
+        this.filename = filename;
+    }
+
+    /**
+     * Returns the path to the test file if {@code $IS_DATA} is defined an the file exists,
or {@code null} otherwise.
+     *
+     * @return path to the test file, or {@code null} if none.
+     */
+    private Path path() {
+        Path path = DataDirectory.TESTS.getDirectory();
+        if (path != null) {
+            path = path.resolve(filename);
+            if (Files.isRegularFile(path)) {
+                return path;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * If the test file represented by this enumeration exists, opens it as an input stream.
+     * If the file does not exist, throws {@link org.junit.AssumptionViolatedException} as
+     * by {@link org.junit.Assume} methods.
+     *
+     * @return an input stream for the test file represented by this enumeration.
+     * @throws IOException if an error occurred while opening the test file.
+     */
+    public InputStream open() throws IOException {
+        final Path path = path();
+        assumeNotNull(name(), path);
+        return Files.newInputStream(path);
+    }
+
+    /**
+     * If the test file represented by this enumeration exists, opens it as a UTF-8 character
reader.
+     * If the file does not exist, throws {@link org.junit.AssumptionViolatedException} as
by
+     * {@link org.junit.Assume} methods.
+     *
+     * @return an UTF-8 character reader for the test file represented by this enumeration.
+     * @throws IOException if an error occurred while opening the test file.
+     */
+    public LineNumberReader reader() throws IOException {
+        final InputStream in = open();
+        return new LineNumberReader(new InputStreamReader(in, "UTF-8"));
+    }
+}


Mime
View raw message