sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Fix formatting of units based on kg with a scale factor: https://issues.apache.org/jira/browse/SIS-382
Date Thu, 19 Jul 2018 22:42:34 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 7730aca801c62a93957f1357bc3bcdd60927ae14
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Jul 20 00:41:48 2018 +0200

    Fix formatting of units based on kg with a scale factor: https://issues.apache.org/jira/browse/SIS-382
---
 .../java/org/apache/sis/measure/UnitFormat.java    | 69 +++++++++++++++++-----
 .../org/apache/sis/measure/UnitFormatTest.java     | 35 +++++++++--
 2 files changed, 85 insertions(+), 19 deletions(-)

diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java b/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
index 5b640e4..033566c 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
@@ -40,6 +40,7 @@ import org.apache.sis.internal.util.FinalFieldSetter;
 import org.apache.sis.internal.util.XPaths;
 import org.apache.sis.math.Fraction;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.math.MathFunctions;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Characters;
 import org.apache.sis.util.Localized;
@@ -647,12 +648,27 @@ public class UnitFormat extends Format implements javax.measure.format.UnitForma
                 throw new IllegalArgumentException(Errors.format(Errors.Keys.NonRatioUnit_1,
                         "?⋅" + Style.OPEN + unit.getSystemUnit() + Style.CLOSE));
             }
-            final String text = Double.toString(scale);
-            int length = text.length();
-            if (text.endsWith(".0")) {
-                length -= 2;
+            boolean asFloatingPoint = (style == Style.UCUM);
+            if (!asFloatingPoint) {
+                double power = Math.log10(scale);
+                if (2*Math.ulp(power) >= Math.abs(power - (power = Math.round(power))))
{
+                    asFloatingPoint = false;
+                    toAppendTo.append("10");
+                    final String text = Integer.toString((int) power);
+                    for (int i=0; i<text.length(); i++) {
+                        toAppendTo.append(Characters.toSuperScript(text.charAt(i)));
+                    }
+                }
+            }
+            if (asFloatingPoint) {
+                final String text = Double.toString(scale);
+                int length = text.length();
+                if (text.endsWith(".0")) {
+                    length -= 2;
+                }
+                toAppendTo.append(text, 0, length);
             }
-            toAppendTo.append(text, 0, length).append(style.multiply);
+            toAppendTo.append(style.multiply);
         }
         Map<? extends Unit<?>, ? extends Number> components;
         if (unit instanceof AbstractUnit<?>) {
@@ -755,6 +771,7 @@ public class UnitFormat extends Format implements javax.measure.format.UnitForma
 
     /**
      * Appends the symbol for the given base unit of base dimension, or "?" if no symbol
was found.
+     * If the given object is a unit, then it should be an instance of {@link SystemUnit}.
      *
      * @param  base        the base unit or base dimension to format.
      * @param  style       whether to allow Unicode characters.
@@ -1194,11 +1211,13 @@ search:     while ((i = CharSequences.skipTrailingWhitespaces(symbols,
start, i)
                     /*
                      * If the first character is a digit, presume that the term is a multiplication
factor.
                      * The "*" character is used for raising the number on the left to the
power on the right.
-                     * Example: "10*6" is equal to one million.
+                     * Example: "10*6" is equal to one million. SIS also handles the "^"
character as "*".
                      *
                      * In principle, spaces are not allowed in unit symbols (in particular,
UCUM specifies that
                      * spaces should not be interpreted as multication operators).  However
in practice we have
                      * sometime units written in a form like "100 feet".
+                     *
+                     * If the last character is a super-script, then we assume a notation
like "10⁻⁴".
                      */
                     final char c = uom.charAt(0);
                     if (isDigit(c) || isSign(c)) {
@@ -1212,14 +1231,7 @@ search:     while ((i = CharSequences.skipTrailingWhitespaces(symbols,
start, i)
                                     return parseTerm(uom, s, length).multiply(multiplier);
                                 }
                             }
-                            s = uom.lastIndexOf(Style.EXPONENT_OR_MULTIPLY);      // Check
standard UCUM symbol first.
-                            if (s >= 0 || (s = uom.lastIndexOf(Style.EXPONENT)) >=
0) {
-                                final int base = Integer.parseInt(uom.substring(0, s));
-                                final int exp  = Integer.parseInt(uom.substring(s+1));
-                                multiplier = Math.pow(base, exp);
-                            } else {
-                                multiplier = Double.parseDouble(uom);
-                            }
+                            multiplier = parseMultiplicationFactor(uom);
                         } catch (NumberFormatException e) {
                             throw (ParserException) new ParserException(Errors.format(
                                     Errors.Keys.UnknownUnit_1, uom), symbols, lower).initCause(e);
@@ -1301,6 +1313,35 @@ search:     while ((i = CharSequences.skipTrailingWhitespaces(symbols,
start, i)
     }
 
     /**
+     * Parses a multiplication factor, which may be a single number or a base raised to an
exponent.
+     * For example all the following strings are equivalent: "1000", "1000.0", "1E3", "10*3",
"10^3", "10³".
+     */
+    private static double parseMultiplicationFactor(final String term) throws NumberFormatException
{
+        final String exponent;
+        int s = term.lastIndexOf(Style.EXPONENT_OR_MULTIPLY);        // Check standard UCUM
symbol first.
+        if (s >= 0 || (s = term.lastIndexOf(Style.EXPONENT)) >= 0) {
+            exponent = term.substring(s + 1);
+        } else {
+            s = term.length();
+            int c = term.codePointBefore(s);
+            if (!Characters.isSuperScript(c)) {
+                return Double.parseDouble(term);                     // No exponent symbol
and no superscript found.
+            }
+            // Example: "10⁻⁴". Split in base and exponent.
+            final StringBuilder buffer = new StringBuilder(s);
+            do {
+                buffer.append(Characters.toNormalScript((char) c));  // This API does not
support code points yet.
+                if ((s -= Character.charCount(c)) <= 0) break;
+                c = term.codePointBefore(s);
+            } while (Characters.isSuperScript(c));
+            exponent = buffer.reverse().toString();
+        }
+        final int base = Integer.parseInt(term.substring(0, s));
+        final int exp  = Integer.parseInt(exponent);
+        return (base == 10) ? MathFunctions.pow10(exp) : Math.pow(base, exp);
+    }
+
+    /**
      * Returns the unit for the given symbol, taking the SI prefix in account.
      * This method does not perform any arithmetic operation on {@code Unit}.
      * Returns {@code null} if no unit is found.
diff --git a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
index 2646c4d..0eded4b 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
@@ -36,7 +36,7 @@ import static org.junit.Assert.*;
  * Tests the {@link UnitFormat} class.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.8
  * @module
  */
@@ -261,6 +261,25 @@ public final strictfp class UnitFormatTest extends TestCase {
     }
 
     /**
+     * Tests the formatting of units that are derived from existing units by a multiplication
factor.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/SIS-382">SIS-382</a>
+     */
+    @Test
+    public void testFormatScaled() {
+        final UnitFormat f = new UnitFormat(Locale.UK);
+        f.setStyle(UnitFormat.Style.SYMBOL);
+        assertEquals("Mm",      f.format(Units.KILOMETRE .multiply(1000)));
+        assertEquals("10⁵⋅m",   f.format(Units.KILOMETRE .multiply( 100)));
+        assertEquals("10⁻⁴⋅m",  f.format(Units.MILLIMETRE.divide  (  10)));
+        assertEquals("mg",      f.format(Units.KILOGRAM  .divide  (1E+6)));
+        assertEquals("mg",      f.format(Units.GRAM      .divide  (1E+3)));
+        assertEquals("µg",      f.format(Units.KILOGRAM  .multiply(1E-9)));
+        assertEquals("cg",      f.format(Units.GRAM      .divide  ( 100)));
+        assertEquals("10⁻⁷⋅kg", f.format(Units.GRAM      .divide  (1E+4)));
+    }
+
+    /**
      * Tests parsing of names.
      */
     @Test
@@ -406,6 +425,8 @@ public final strictfp class UnitFormatTest extends TestCase {
 
     /**
      * Tests parsing of symbols composed of terms combined by arithmetic operations (e.g.
"m/s").
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/SIS-382">SIS-382</a>
      */
     @Test
     @DependsOnMethod("testParseTerms")
@@ -414,10 +435,14 @@ public final strictfp class UnitFormatTest extends TestCase {
         assertSame(Units.MILLIMETRE, f.parse("m/1000"));
         assertSame(Units.KILOMETRE,  f.parse( "1000*m"));
         assertSame(Units.KILOMETRE,  f.parse( "1000.0*m"));
-        ConventionalUnitTest.verify(Units.METRE, f.parse("10*-6⋅m"),   "µm", 1E-6);
-        ConventionalUnitTest.verify(Units.METRE, f.parse("10*-6.m"),   "µm", 1E-6);
-        ConventionalUnitTest.verify(Units.METRE, f.parse("10^-3.m"),   "mm", 1E-3);
-        ConventionalUnitTest.verify(Units.METRE, f.parse( "100 feet"), null, 30.48);
+        ConventionalUnitTest.verify(Units.METRE,    f.parse("10*-6⋅m"),   "µm", 1E-6);
+        ConventionalUnitTest.verify(Units.METRE,    f.parse("10*-6.m"),   "µm", 1E-6);
+        ConventionalUnitTest.verify(Units.METRE,    f.parse("10^-3.m"),   "mm", 1E-3);
+        ConventionalUnitTest.verify(Units.METRE,    f.parse("10⁻⁴.m"),    null, 1E-4);
+        ConventionalUnitTest.verify(Units.METRE,    f.parse( "100 feet"), null, 30.48);
+        ConventionalUnitTest.verify(Units.KILOGRAM, f.parse("10*3.kg"),   "Mg", 1E+3);
+        ConventionalUnitTest.verify(Units.KILOGRAM, f.parse("10⋅mg"),     "cg", 1E-5);
+        ConventionalUnitTest.verify(Units.KILOGRAM, f.parse("10^-6.kg"),  "mg", 1E-6);
     }
 
     /**


Mime
View raw message