sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 01/03: Adjustment in handling of offset in units.
Date Sat, 17 Nov 2018 17:22:16 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 e2519af18a0298be7139b7a069cac3e66262bc3e
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Nov 17 13:50:11 2018 +0100

    Adjustment in handling of offset in units.
---
 .../java/org/apache/sis/measure/AbstractUnit.java  | 32 ++++++++++++--
 .../main/java/org/apache/sis/measure/Units.java    |  7 ++++
 .../apache/sis/measure/ConventionalUnitTest.java   | 49 +++++++++++++++-------
 .../sis/internal/netcdf/ucar/VariableWrapper.java  | 10 ++---
 4 files changed, 74 insertions(+), 24 deletions(-)

diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java b/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
index 6e4783d..7f25a48 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
@@ -240,9 +240,20 @@ abstract class AbstractUnit<Q extends Quantity<Q>> implements
Unit<Q>, LenientCo
      * @return this unit offset by the specified value, or {@code this} if the given offset
is zero.
      */
     @Override
-    public final Unit<Q> shift(final double offset) {
+    public final Unit<Q> shift(double offset) {
         if (offset == 0) return this;
-        return transform(LinearConverter.offset(offset, 1));
+        double divisor = 1;
+        double m = 1;
+        do {
+            final double c = offset * m;
+            final double r = Math.rint(c);
+            if (Math.abs(c - r) <= Math.ulp(c)) {
+                offset  = r;
+                divisor = m;
+                break;
+            }
+        } while ((m *= 10) <= 1E6);
+        return transform(LinearConverter.offset(offset, divisor));
     }
 
     /**
@@ -255,8 +266,21 @@ abstract class AbstractUnit<Q extends Quantity<Q>> implements
Unit<Q>, LenientCo
     @Override
     public final Unit<Q> multiply(double multiplier) {
         if (multiplier == 1) return this;
-        final double divisor = inverse(multiplier);
-        if (divisor != 1) multiplier = 1;
+        double divisor = inverse(multiplier);
+        if (divisor != 1) {
+            multiplier = 1;
+        } else {
+            double m = 1;
+            do {
+                final double c = multiplier * m;
+                final double r = Math.rint(c);
+                if (Math.abs(c - r) <= Math.ulp(c)) {
+                    multiplier = r;
+                    divisor = m;
+                    break;
+                }
+            } while ((m *= 10) <= 1E6);
+        }
         return transform(LinearConverter.scale(multiplier, divisor));
     }
 
diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java b/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
index 81f353e..fa76b6d 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
@@ -1263,6 +1263,13 @@ public final class Units extends Static {
     /**
      * Invoked by {@code Units} static class initializer for registering SI conventional
units.
      * This method shall be invoked in a single thread by the {@code Units} class initializer
only.
+     *
+     * <p>If the {@code target} unit holds a list of {@linkplain SystemUnit#related()
related units}
+     * (i.e. conventional units that can not be computed easily by appending a SI prefix),
then the new
+     * conventional unit is added to that list of related units. For example "foot" is related
to "metre"
+     * and "degree Celsius" is related to "Kelvin", but "kilometre" is not recorded as related
to "metre"
+     * because this relationship can be inferred automatically without the need of a {@code
related} table.
+     * The unrecorded units are all SI units related to {@code target} by a scale factor
without offset.</p>
      */
     private static <Q extends Quantity<Q>> ConventionalUnit<Q> add(SystemUnit<Q>
target, UnitConverter toTarget, String symbol, byte scope, short epsg) {
         final ConventionalUnit<Q> unit = UnitRegistry.init(new ConventionalUnit<>(target,
toTarget, symbol, scope, epsg));
diff --git a/core/sis-utility/src/test/java/org/apache/sis/measure/ConventionalUnitTest.java
b/core/sis-utility/src/test/java/org/apache/sis/measure/ConventionalUnitTest.java
index c96fa42..d83f821 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/measure/ConventionalUnitTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/measure/ConventionalUnitTest.java
@@ -96,12 +96,12 @@ public final strictfp class ConventionalUnitTest extends TestCase {
     }
 
     /**
-     * Tests {@link SystemUnit#multiply(double)} and {@link SystemUnit#divide(double)}.
-     * Both are implemented by calls to {@link SystemUnit#transform(UnitConverter)}.
+     * Tests {@link SystemUnit#multiply(double)} and {@link SystemUnit#divide(double)} on
fundamental units.
+     * All those methods are implemented by calls to {@link SystemUnit#transform(UnitConverter)}.
      */
     @Test
-    @DependsOnMethod("testPower")
-    public void testTransformSystemUnit() {
+    @DependsOnMethod("verifyConstants")
+    public void testTransformFundamentalUnit() {
         assertSame(Units.METRE,      Units.METRE.multiply(   1));
         assertSame(Units.KILOMETRE,  Units.METRE.multiply(1000));
         assertSame(Units.MILLIMETRE, Units.METRE.divide  (1000));
@@ -111,6 +111,22 @@ public final strictfp class ConventionalUnitTest extends TestCase {
         verify    (Units.METRE,      Units.METRE.multiply( 100), "hm", 100);
         verify    (Units.METRE,      Units.METRE.multiply(  10), "dam", 10);
 
+        assertSame(Units.HOUR,        Units.SECOND.multiply(3600));
+        assertSame(Units.DEGREE,      Units.RADIAN.multiply(StrictMath.PI/180));
+        assertSame(Units.GRAD,        Units.RADIAN.multiply(StrictMath.PI/200));
+        assertSame(Units.ARC_SECOND,  Units.RADIAN.multiply(StrictMath.PI / (180*60*60)));
+        assertSame(Units.MICRORADIAN, Units.RADIAN.divide(1E6));
+    }
+
+    /**
+     * Tests the same methods than {@link #testTransformFundamentalUnit()}, but applied on
other system units than the
+     * fundamental ones. All tested methods are implemented by calls to {@link SystemUnit#transform(UnitConverter)}.
+     *
+     * @see <a href="https://en.wikipedia.org/wiki/SI_derived_unit">Derived units on
Wikipedia</a>
+     */
+    @Test
+    @DependsOnMethod({"testTransformFundamentalUnit", "testPower"})
+    public void testTransformDerivedUnit() {
         assertSame(Units.METRES_PER_SECOND, Units.METRES_PER_SECOND.multiply(   1));
         verify    (Units.METRES_PER_SECOND, Units.METRES_PER_SECOND.multiply(1000), "km∕s",
1E+3);
         verify    (Units.METRES_PER_SECOND, Units.METRES_PER_SECOND.divide  (1000), "mm∕s",
1E-3);
@@ -120,15 +136,6 @@ public final strictfp class ConventionalUnitTest extends TestCase {
         assertSame(Units.CUBIC_METRE,  Units.CUBIC_METRE .multiply(   1));
         verify    (Units.CUBIC_METRE,  Units.CUBIC_METRE .multiply(1E+9), "km³", 1E+9);
         verify    (Units.CUBIC_METRE,  Units.CUBIC_METRE .divide  (1E+9), "mm³", 1E-9);
-
-        assertSame(Units.HOUR,        Units.SECOND.multiply(3600));
-        assertSame(Units.DEGREE,      Units.RADIAN.multiply(StrictMath.PI/180));
-        assertSame(Units.GRAD,        Units.RADIAN.multiply(StrictMath.PI/200));
-        assertSame(Units.ARC_SECOND,  Units.RADIAN.multiply(StrictMath.PI / (180*60*60)));
-        assertSame(Units.MICRORADIAN, Units.RADIAN.divide(1E6));
-
-        assertSame(Units.GRAM, Units.KILOGRAM.divide(1E+3));
-        verify(Units.KILOGRAM, Units.KILOGRAM.divide(1E+6), "mg", 1E-6);
     }
 
     /**
@@ -136,7 +143,7 @@ public final strictfp class ConventionalUnitTest extends TestCase {
      * Both are implemented by calls to {@link ConventionalUnit#transform(UnitConverter)}.
      */
     @Test
-    @DependsOnMethod("testTransformSystemUnit")
+    @DependsOnMethod("testTransformDerivedUnit")
     public void testTransformConventionalUnit() {
         assertSame(Units.MILLIMETRE, Units.MILLIMETRE.multiply(   1));
         assertSame(Units.CENTIMETRE, Units.MILLIMETRE.multiply(  10));
@@ -150,7 +157,7 @@ public final strictfp class ConventionalUnitTest extends TestCase {
     }
 
     /**
-     * Tests operation on kilogram.
+     * Tests operations on kilogram.
      * The management of SI prefixes need to make a special case for kilogram.
      *
      * @see <a href="https://issues.apache.org/jira/browse/SIS-382">SIS-382</a>
@@ -158,6 +165,9 @@ public final strictfp class ConventionalUnitTest extends TestCase {
      */
     @Test
     public void testKilogram() {
+        assertSame(Units.GRAM, Units.KILOGRAM.divide(1E+3));
+        verify(Units.KILOGRAM, Units.KILOGRAM.divide(1E+6), "mg", 1E-6);
+
         Unit<?> unit = Units.KILOGRAM.divide(1E+9);
         assertEquals("µg", unit.getSymbol());
         unit = unit.divide(Units.METRE);
@@ -167,6 +177,15 @@ public final strictfp class ConventionalUnitTest extends TestCase {
     }
 
     /**
+     * Tests operations on Celsius units.
+     */
+    @Test
+    public void testCelsius() {
+        assertSame(Units.CELSIUS, Units.KELVIN.shift(+273.15));
+        assertSame(Units.KELVIN, Units.CELSIUS.shift(-273.15));
+    }
+
+    /**
      * Tests {@link ConventionalUnit#isCompatible(Unit)}.
      */
     @Test
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
index f2a8f5a..dac6ddb 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
@@ -127,20 +127,20 @@ final class VariableWrapper extends Variable {
     protected Unit<?> parseUnit(final String symbols) throws Exception {
         if (TIME_PATTERN.matcher(symbols).matches()) {
             /*
-             * UCAR library has to methods for getting epoch: getDate() and getDateOrigin().
-             * The former takes in account numbers that may appear before the unit, for example
+             * UCAR library has two methods for getting epoch: getDate() and getDateOrigin().
+             * The former adds to the origin the number that may appear before the unit,
for example
              * "2 hours since 1970-01-01 00:00:00". If there is no such number, then the
two methods
-             * are equivalent.
+             * are equivalent. It is not clear that adding such number is the right thing
to do.
              */
             final DateUnit temporal = new DateUnit(symbols);
-            epoch = temporal.getDate().toInstant();
+            epoch = temporal.getDateOrigin().toInstant();
             return Units.SECOND.multiply(temporal.getTimeUnit().getValueInSeconds());
         } else {
             /*
              * For all other units, we get the base unit (meter, radian, Kelvin, etc.) and
multiply by the scale factor.
              * We also need to take the offset in account for constructing the °C unit as
a unit shifted from its Kelvin
              * base. The UCAR library does not provide method giving directly this information,
so we infer it indirectly
-             * by converting value zero.
+             * by converting the 0 value.
              */
             final SimpleUnit ucar = SimpleUnit.factoryWithExceptions(symbols);
             if (ucar.isUnknownUnit()) {


Mime
View raw message