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 38b9d8fc69bcede94dca2a7d19dc2b7621a79333
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Wed Oct 17 18:29:37 2018 +0200

    Fix rounding error when converting 46°57'8.66" to decimal degrees.
    The slight rounding error become a 4 cm error in some map projections.
---
 .../java/org/apache/sis/internal/util/Numerics.java  |  2 +-
 .../org/apache/sis/measure/SexagesimalConverter.java | 20 +++++++++++++++++++-
 .../apache/sis/measure/SexagesimalConverterTest.java | 13 ++++++++++++-
 3 files changed, 32 insertions(+), 3 deletions(-)

diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
index 5e50833..a231706 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
@@ -71,7 +71,7 @@ public final class Numerics extends Static {
      * {@link org.apache.sis.util.ComparisonMode#APPROXIMATIVE}.
      *
      * <p>Historically, this was the relative tolerance threshold for 
considering two
-     * matrixes as equal. This value has been determined empirically in order 
to allow
+     * matrices as equal. This value has been determined empirically in order 
to allow
      * {@code 
org.apache.sis.referencing.operation.transform.ConcatenatedTransform} to
      * detect the cases where two {@link 
org.apache.sis.referencing.operation.transform.LinearTransform}
      * are equal for practical purpose. This threshold can be used as 
below:</p>
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java
 
b/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java
index 494de7d..dc1dbaf 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java
@@ -41,7 +41,7 @@ import static org.apache.sis.math.MathFunctions.truncate;
  * This class and all inner classes are immutable, and thus inherently 
thread-safe.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */
@@ -272,6 +272,22 @@ class SexagesimalConverter extends AbstractConverter {
         }
 
         /**
+         * After calculation of the remaining seconds or minutes, trims the 
rounding errors presumably
+         * caused by rounding errors in floating point arithmetic. This is 
required for avoiding the
+         * following conversion issue:
+         *
+         * <ol>
+         *   <li>Sexagesimal value: 46.570866 (from 46°57'8.66"N in EPSG:2056 
projected CRS)</li>
+         *   <li>value * 10000 = 465708.66000000003</li>
+         *   <li>deg = 46, min = 57, deg = 8.660000000032596</li>
+         * </ol>
+         */
+        private static double fixRoundingError(final double remainder) {
+            final double c = Math.rint(remainder * 1E+6) / 1E+6;
+            return (Math.abs(remainder - c) < 1E-9) ? c : remainder;
+        }
+
+        /**
          * Performs a conversion from sexagesimal degrees to fractional 
degrees.
          *
          * @throws IllegalArgumentException If the given angle can not be 
converted.
@@ -283,11 +299,13 @@ class SexagesimalConverter extends AbstractConverter {
                 sec = angle * divider;
                 deg = truncate(sec/10000); sec -= 10000*deg;
                 min = truncate(sec/  100); sec -=   100*min;
+                sec = fixRoundingError(sec);
             } else {
                 sec = 0;
                 min = angle * divider;
                 deg = truncate(min / 100);
                 min -= deg * 100;
+                min = fixRoundingError(min);
             }
             if (min <= -60 || min >= 60) {                              // Do 
not enter for NaN
                 if (Math.abs(Math.abs(min) - 100) <= (EPS * 100)) {
diff --git 
a/core/sis-utility/src/test/java/org/apache/sis/measure/SexagesimalConverterTest.java
 
b/core/sis-utility/src/test/java/org/apache/sis/measure/SexagesimalConverterTest.java
index d7187e8..f6c385e 100644
--- 
a/core/sis-utility/src/test/java/org/apache/sis/measure/SexagesimalConverterTest.java
+++ 
b/core/sis-utility/src/test/java/org/apache/sis/measure/SexagesimalConverterTest.java
@@ -30,7 +30,7 @@ import static org.apache.sis.test.Assert.*;
  * Test the {@link SexagesimalConverter} class.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */
@@ -93,6 +93,17 @@ public final strictfp class SexagesimalConverterTest extends 
TestCase {
     }
 
     /**
+     * Tests the fix for rounding error in conversion of 46°57'8.66".
+     * This fix is necessary for avoiding a 4 cm error with Apache SIS
+     * construction of EPSG:2056 projected CRS.
+     */
+    @Test
+    public void testRoundingErrorFix() {
+        final UnitConverter c = DMS.getConverterTo(Units.DEGREE);
+        assertEquals(46.95240555555556, c.convert(46.570866), STRICT);
+    }
+
+    /**
      * Verifies the unit symbols.
      */
     @Test

Reply via email to