sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1402085 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/measure/ test/java/org/apache/sis/measure/ test/java/org/apache/sis/test/suite/
Date Thu, 25 Oct 2012 10:34:48 GMT
Author: desruisseaux
Date: Thu Oct 25 10:34:47 2012
New Revision: 1402085

URL: http://svn.apache.org/viewvc?rev=1402085&view=rev
Log:
Tuned Angle.toString() and added tests.

Added:
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java   (with
props)
Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Angle.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Angle.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Angle.java?rev=1402085&r1=1402084&r2=1402085&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Angle.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Angle.java Thu Oct
25 10:34:47 2012
@@ -21,8 +21,8 @@ import java.text.Format;
 import java.text.ParseException;
 import java.io.Serializable;
 import net.jcip.annotations.Immutable;
-
 import org.apache.sis.util.Utilities;
+import org.apache.sis.math.MathFunctions;
 
 
 /**
@@ -100,7 +100,8 @@ public class Angle implements Comparable
             e.initCause(exception);
             throw e;
         }
-        if (getClass().isAssignableFrom(angle.getClass())) {
+        final Class<?> type = angle.getClass();
+        if (type == Angle.class || getClass().isAssignableFrom(type)) {
             this.θ = ((Angle) angle).θ;
         } else {
             throw new NumberFormatException(string);
@@ -164,6 +165,22 @@ public class Angle implements Comparable
     }
 
     /**
+     * Upper threshold before to format an angle as an ordinary number.
+     * This is set to 90° in the case of latitude numbers.
+     */
+    double maximum() {
+        return 360;
+    }
+
+    /**
+     * Returns the hemisphere character for an angle of the given sign.
+     * This is used only by {@link #toString()}, not by {@link AngleFormat}.
+     */
+    char hemisphere(final boolean negative) {
+        return 0;
+    }
+
+    /**
      * Returns a string representation of this {@code Angle} object.
      * This is a convenience method mostly for debugging purpose, since it uses a fixed locale.
      * Developers should consider using {@link AngleFormat} for end-user applications
instead
@@ -174,8 +191,26 @@ public class Angle implements Comparable
     @Override
     public String toString() {
         StringBuffer buffer = new StringBuffer(16);
-        synchronized (Angle.class) {
-            buffer = getAngleFormat().format(this, buffer, null);
+        double m = Math.abs(θ);
+        final boolean isSmall = m <= (1 / 3600E+6); // 1 micro-second.
+        if (isSmall || m > maximum()) {
+            final char h = hemisphere(MathFunctions.isNegative(θ));
+            if (h == 0) {
+                m = θ;  // Restore the sign.
+            }
+            char symbol = '°';
+            if (isSmall) {
+                symbol = '″';
+                m *= 3600;
+            }
+            buffer.append(m).append(symbol);
+            if (h != 0) {
+                buffer.append(h);
+            }
+        } else {
+            synchronized (Angle.class) {
+                buffer = getAngleFormat().format(this, buffer, null);
+            }
         }
         return buffer.toString();
     }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java?rev=1402085&r1=1402084&r2=1402085&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java Thu
Oct 25 10:34:47 2012
@@ -40,6 +40,7 @@ import static java.lang.Double.isInfinit
 import static org.apache.sis.math.MathFunctions.pow10;
 import static org.apache.sis.math.MathFunctions.truncate;
 import static org.apache.sis.math.MathFunctions.isNegative;
+import static org.apache.sis.math.MathFunctions.fractionDigitsForDelta;
 
 // Related to JDK7
 import java.util.Objects;
@@ -146,9 +147,9 @@ public class AngleFormat extends Format 
     /**
      * Constant for the fractional part of the degrees, minutes or seconds field. When formatting
      * a string, this value may be specified to the {@link FieldPosition} constructor in
order to
-     * get the bounding index where seconds have been written.
+     * get the bounding index where fraction digits have been written.
      */
-    public static final int FRACTION_FIELD = 3;
+    private static final int FRACTION_FIELD = 3; // Not yet implemented.
 
     /**
      * Constant for hemisphere field. When formatting a string, this value may be specified
to the
@@ -278,7 +279,7 @@ public class AngleFormat extends Format 
         degreesFieldWidth     = 1;
         minutesFieldWidth     = 2;
         secondsFieldWidth     = 2;
-        minimumFractionDigits = 6;
+        fractionFieldWidth    = 16;  // Number of digits for accurate representation of 1″
ULP.
         degreesSuffix         = "°";
         minutesSuffix         = "′";
         secondsSuffix         = "″";
@@ -316,7 +317,8 @@ public class AngleFormat extends Format 
      * @param  pattern Pattern to use for parsing and formatting angle.
      * @throws IllegalArgumentException If the specified pattern is not legal.
      *
-     * @see #setFallbackAllowed(boolean)
+     * @see #setMinimumFractionDigits(int)
+     * @see #setMaximumFractionDigits(int)
      */
     public void applyPattern(final String pattern) throws IllegalArgumentException {
         applyPattern(pattern, SYMBOLS, '.');
@@ -450,6 +452,9 @@ scan:   for (int i=0; i<length;) {
      * See class description for an explanation of how patterns work.
      *
      * @return The formatting pattern.
+     *
+     * @see #getMinimumFractionDigits()
+     * @see #getMaximumFractionDigits()
      */
     public String toPattern() {
         return toPattern(SYMBOLS, '.');
@@ -461,8 +466,6 @@ scan:   for (int i=0; i<length;) {
      * @param symbols An array of 3 characters containing the reserved symbols as upper-case
letters.
      *        This is always the {@link #SYMBOLS} array, unless we apply localized patterns.
      * @param decimalSeparator The code point which represent decimal separator in the pattern.
-     *
-     * @see #isFallbackAllowed()
      */
     private String toPattern(final char[] symbols, final int decimalSeparator) {
         char symbol = 0;
@@ -605,7 +608,7 @@ scan:   for (int i=0; i<length;) {
      *
      * @return The {@code toAppendTo} buffer, returned for method calls chaining.
      */
-    public StringBuffer format(final double angle, StringBuffer toAppendTo, final FieldPosition
pos) {
+    public StringBuffer format(double angle, StringBuffer toAppendTo, final FieldPosition
pos) {
         if (isNaN(angle) || isInfinite(angle)) {
             return numberFormat().format(angle, toAppendTo,
                     (pos != null) ? pos : new FieldPosition(DecimalFormat.INTEGER_FIELD));
@@ -638,8 +641,23 @@ scan:   for (int i=0; i<length;) {
             degrees += correction;
         }
         /*
-         * At this point, 'degrees', 'minutes' and 'seconds'
-         * contain the final values to format.
+         * At this point the 'degrees', 'minutes' and 'seconds' variables contain the final
values
+         * to format. But before to perform the numbers formating,  if the pattern uses a
variable
+         * number of fraction digits, then limit the maximal number to the amount of significant
+         * fraction digits for a 'double' value. The intend is to avoid non-significant garbage
+         * that are pure artifacts from the conversion from base 2 to base 10.
+         */
+        int maximumFractionDigits = fractionFieldWidth;
+        if (maximumFractionDigits != minimumFractionDigits) {
+            if      (secondsFieldWidth != 0) angle *= 3600;
+            else if (minutesFieldWidth != 0) angle *=   60;
+            final int n = fractionDigitsForDelta(Math.ulp(angle));
+            if (n < maximumFractionDigits) {
+                maximumFractionDigits = Math.max(minimumFractionDigits, n);
+            }
+        }
+        /*
+         * Formatting fields.
          */
         if (prefix != null) {
             toAppendTo.append(prefix);
@@ -653,14 +671,14 @@ scan:   for (int i=0; i<length;) {
             field = PREFIX_FIELD;
         }
         toAppendTo = formatField(degrees, toAppendTo, (field == DEGREES_FIELD) ? pos : null,
-                degreesFieldWidth, (minutesFieldWidth == 0), degreesSuffix);
+                degreesFieldWidth, (minutesFieldWidth == 0) ? maximumFractionDigits : -1,
degreesSuffix);
         if (!isNaN(minutes)) {
             toAppendTo = formatField(minutes, toAppendTo, (field == MINUTES_FIELD) ? pos
: null,
-                    minutesFieldWidth, (secondsFieldWidth == 0), minutesSuffix);
+                    minutesFieldWidth, (secondsFieldWidth == 0) ? maximumFractionDigits :
-1, minutesSuffix);
         }
         if (!isNaN(seconds)) {
             toAppendTo = formatField(seconds, toAppendTo, (field == SECONDS_FIELD) ? pos
: null,
-                    secondsFieldWidth, true, secondsSuffix);
+                    secondsFieldWidth, maximumFractionDigits, secondsSuffix);
         }
         return toAppendTo;
     }
@@ -672,22 +690,24 @@ scan:   for (int i=0; i<length;) {
      * @param toAppendTo The buffer where to append the formatted angle.
      * @param pos        An optional object where to store the position of a field in the
formatted text.
      * @param width      The field width.
-     * @param decimal    {@code true} for formatting the decimal digits (last field only).
      * @param suffix     Suffix to append, or {@code null} if none.
+     * @param maximumFractionDigits
+     *          Maximal number of digits for formatting the decimal digits (last field only),
+     *          or -1 for formatting an other field than the last one.
      */
     private StringBuffer formatField(double value, StringBuffer toAppendTo, final FieldPosition
pos,
-                                     final int width, final boolean decimal, final String
suffix)
+            final int width, final int maximumFractionDigits, final String suffix)
     {
         final NumberFormat numberFormat = numberFormat();
         final int startPosition = toAppendTo.length();
-        if (!decimal) {
+        if (maximumFractionDigits < 0) {
             numberFormat.setMinimumIntegerDigits(width);
             numberFormat.setMaximumFractionDigits(0);
             toAppendTo = numberFormat.format(value, toAppendTo, dummyFieldPosition());
         } else if (useDecimalSeparator) {
             numberFormat.setMinimumIntegerDigits(width);
             numberFormat.setMinimumFractionDigits(minimumFractionDigits);
-            numberFormat.setMaximumFractionDigits(fractionFieldWidth);
+            numberFormat.setMaximumFractionDigits(maximumFractionDigits);
             toAppendTo = numberFormat.format(value, toAppendTo, dummyFieldPosition());
         } else {
             value *= pow10(fractionFieldWidth);
@@ -886,6 +906,8 @@ scan:   for (int i=0; i<length;) {
      * @param  pos    On input, index of the first {@code source} character to read.
      *                On output, index after the last parsed character.
      * @return The parsed string as an {@link Angle}, {@link Latitude} or {@link Longitude}
object.
+     *
+     * @see #isFallbackAllowed()
      */
     public Angle parse(final String source, final ParsePosition pos) {
         return parse(source, pos, false);
@@ -1233,6 +1255,8 @@ BigBoss:    switch (skipSuffix(source, p
      * @param  source The string to parse.
      * @return The parsed string as an {@link Angle}, {@link Latitude} or {@link Longitude}
object.
      * @throws ParseException If the string can not be fully parsed.
+     *
+     * @see #isFallbackAllowed()
      */
     public Angle parse(final String source) throws ParseException {
         final ParsePosition pos = new ParsePosition(0);

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java?rev=1402085&r1=1402084&r2=1402085&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java Thu Oct
25 10:34:47 2012
@@ -80,4 +80,22 @@ public final class Latitude extends Angl
     public Latitude(final String string) throws NumberFormatException {
         super(string);
     }
+
+    /**
+     * Returns the hemisphere character for an angle of the given sign.
+     * This is used only by {@link #toString()}, not by {@link AngleFormat}.
+     */
+    @Override
+    final char hemisphere(final boolean negative) {
+        return negative ? 'S' : 'N';
+    }
+
+    /**
+     * Upper threshold before to format an angle as an ordinary number.
+     * This is used only by {@link #toString()}, not by {@link AngleFormat}.
+     */
+    @Override
+    final double maximum() {
+        return 90;
+    }
 }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java?rev=1402085&r1=1402084&r2=1402085&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java Thu
Oct 25 10:34:47 2012
@@ -80,4 +80,13 @@ public final class Longitude extends Ang
     public Longitude(final String string) throws NumberFormatException {
         super(string);
     }
+
+    /**
+     * Returns the hemisphere character for an angle of the given sign.
+     * This is used only by {@link #toString()}, not by {@link AngleFormat}.
+     */
+    @Override
+    final char hemisphere(final boolean negative) {
+        return negative ? 'W' : 'E';
+    }
 }

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java?rev=1402085&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java Thu
Oct 25 10:34:47 2012
@@ -0,0 +1,71 @@
+/*
+ * 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.measure;
+
+import org.apache.sis.test.TestCase;
+import org.apache.sis.test.DependsOn;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link Angle} class.
+ *
+ * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
+ * @since   0.3 (derived from geotk-2.0)
+ * @version 0.3
+ * @module
+ */
+@DependsOn(AngleFormatTest.class)
+public final strictfp class AngleTest extends TestCase {
+    /**
+     * Tests the {@link Angle#toString()} method.
+     */
+    @Test
+    public void testToString() {
+        assertEquals("45°30′00″",  new Angle    (45.5).toString());
+        assertEquals("45°30′00″N", new Latitude (45.5).toString());
+        assertEquals("45°30′00″E", new Longitude(45.5).toString());
+
+        // Angle out of expected range.
+        assertEquals( "720.0°E", new Longitude( 720).toString());
+        assertEquals(  "99.0°S", new Latitude ( -99).toString());
+        assertEquals("-361.0°",  new Angle    (-361).toString());
+
+        // Small angles; should switch to scientific notation.
+        assertEquals("3.6E-7″",  new Angle    ( 1E-10).toString());
+        assertEquals("3.6E-7″N", new Latitude ( 1E-10).toString());
+        assertEquals("3.6E-7″W", new Longitude(-1E-10).toString());
+    }
+
+    /**
+     * Tests the parsing performed by the constructor.
+     */
+    @Test
+    public void testParse() {
+        assertEquals(new Angle    (45.5), new Angle    ("45°30′00″"));
+        assertEquals(new Latitude (45.5), new Latitude ("45°30′00″N"));
+        assertEquals(new Longitude(45.5), new Longitude("45°30′00″E"));
+
+        // Test the capability to differentiate 'E' of 'East' from exponential notation.
+        assertEquals(new Longitude(45.5), new Longitude("45.5°E"));
+        assertEquals(new Longitude(45.5), new Longitude("45.5E"));
+        assertEquals(new Longitude(455.), new Longitude("45.5E1"));
+        assertEquals(new Longitude(4.55), new Longitude("45.5E-1"));
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1402085&r1=1402084&r2=1402085&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
(original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
Thu Oct 25 10:34:47 2012
@@ -57,6 +57,7 @@ import org.junit.runners.Suite;
 
   // Formatting
   org.apache.sis.measure.AngleFormatTest.class,
+  org.apache.sis.measure.AngleTest.class,
   org.apache.sis.io.X364Test.class,
   org.apache.sis.io.EndOfLineFormatterTest.class,
   org.apache.sis.io.IndentedLineFormatterTest.class,



Mime
View raw message