sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1785661 - in /sis/branches/JDK8/core: sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/ sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/ sis-referencing/src/main/java/org/a...
Date Mon, 06 Mar 2017 15:17:20 GMT
Author: desruisseaux
Date: Mon Mar  6 15:17:20 2017
New Revision: 1785661

URL: http://svn.apache.org/viewvc?rev=1785661&view=rev
Log:
Complete (for now) LocationFormat.

Modified:
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java [UTF-8] Mon Mar  6 15:17:20 2017
@@ -20,8 +20,11 @@ import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
 import java.util.Collection;
+import java.util.Arrays;
 import java.io.IOException;
+import java.math.RoundingMode;
 import java.text.Format;
+import java.text.NumberFormat;
 import java.text.ParseException;
 import java.text.ParsePosition;
 import javax.measure.Unit;
@@ -41,8 +44,10 @@ import org.opengis.geometry.DirectPositi
 import org.opengis.geometry.coordinate.Position;
 import org.apache.sis.io.CompoundFormat;
 import org.apache.sis.io.TableAppender;
+import org.apache.sis.measure.UnitFormat;
 import org.apache.sis.measure.Range;
 import org.apache.sis.measure.Angle;
+import org.apache.sis.measure.AngleFormat;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.geometry.Envelopes;
@@ -54,22 +59,45 @@ import org.apache.sis.referencing.Identi
 import org.apache.sis.referencing.crs.AbstractCRS;
 import org.apache.sis.referencing.cs.AxesConvention;
 import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
 
 
 /**
- * Formats {@link Location} instances.
+ * Formats {@link Location} instances in a tabular format.
+ * This format assumes a monospaced font and an encoding supporting drawing box characters (e.g. UTF-8).
  *
- * <p>This class is not thread-safe.</p>
+ * <div class="note"><b>Example:</b>
+ * the location identified by "32TNL83" in the {@linkplain MilitaryGridReferenceSystem military grid reference system}
+ * can be represented by the following string formatted using {@link Locale#ENGLISH}:
+ *
+ * {@preformat text
+ *   ┌─────────────────────────────────────────────────────────────┐
+ *   │ Location type:               Grid zone designator           │
+ *   │ Geographic identifier:       32TNL83                        │
+ *   │ East bound:                    580,000 m    —     9°57′00″E │
+ *   │ West bound:                    590,000 m    —    10°04′13″E │
+ *   │ South bound:                 4,530,000 m    —    40°54′58″N │
+ *   │ North bound:                 4,540,000 m    —    41°00′27″N │
+ *   │ Representative position:       585,000 m    —    10°00′36″E │
+ *   │                              4,535,000 m    —    40°57′42″N │
+ *   │ Coordinate reference system: WGS 84 / UTM zone 32N          │
+ *   └─────────────────────────────────────────────────────────────┘
+ * }
+ * </div>
+ *
+ * <div class="warning"><b>Limitation:</b>
+ * Current implementation supports only formatting, not parsing.
+ * This class is not thread-safe.
+ * </div>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.8
  * @version 0.8
  * @module
  */
-@SuppressWarnings("CloneableClassWithoutClone")                 // Because no additional field defined in this class.
-final class LocationFormat extends CompoundFormat<Location> {
+public class LocationFormat extends CompoundFormat<Location> {
     /**
      * For cross-version compatibility.
      */
@@ -166,18 +194,24 @@ final class LocationFormat extends Compo
     }
 
     /**
-     * Formats the given location.
+     * Writes a textual representation of the given location in the given stream or buffer.
+     *
+     * @param  location    the location to format.
+     * @param  toAppendTo  where to format the location.
+     * @throws IOException if an error occurred while writing to the given appendable.
      */
     @Override
-    @SuppressWarnings("fallthrough")
+    @SuppressWarnings({"fallthrough", "null"})
     public void format(final Location location, final Appendable toAppendTo) throws IOException {
+        ArgumentChecks.ensureNonNull("location", location);
         final Locale locale = getLocale(Locale.Category.DISPLAY);
         final Vocabulary vocabulary = Vocabulary.getResources(locale);
-        final TableAppender table = new TableAppender(toAppendTo, " ");
+        final TableAppender table = new TableAppender(toAppendTo, "│ ", " ", " │");
         table.setMultiLinesCells(true);
         /*
          * Location type.
          */
+        table.appendHorizontalSeparator();
         final LocationType type = location.getLocationType();
         if (type != null) {
             append(table, vocabulary, Vocabulary.Keys.LocationType, toString(type.getName(), locale));
@@ -240,9 +274,11 @@ final class LocationFormat extends Compo
                         envelope = Envelopes.transform(envelope, normCRS);  // Should only change order and sign.
                     }
                 }
-                GeographicCRS geogCRS = ReferencingUtilities.toNormalizedGeographicCRS(posCRS);
-                if (geogCRS != null) {
-                    geopos = transform(position, posCRS, geogCRS);
+                if (bbox != null) {     // Compute geographic position only if there is a geographic bounding box.
+                    GeographicCRS geogCRS = ReferencingUtilities.toNormalizedGeographicCRS(posCRS);
+                    if (geogCRS != null) {
+                        geopos = transform(position, posCRS, geogCRS);
+                    }
                 }
                 position = transform(position, posCRS, normCRS);
             }
@@ -261,27 +297,31 @@ final class LocationFormat extends Compo
          */
         if (bbox != null || envelope != null) {
             final CoordinateSystem cs = (crs != null) ? crs.getCoordinateSystem() : null;
-            String[] geographic = null;
-            String[] projected  = null;
-            String[] unitSymbol = null;
-            Format   geogFormat = null;
-            Format   projFormat = null;
-            Format   unitFormat = null;
-            int      maxGeogLength = 0;
-            int      maxProjLength = 0;
-            int      maxUnitLength = 0;
-            boolean  showProj  = false;
-            if (bbox != null) {
+            String[]     geographic = null;
+            String[]     projected  = null;
+            String[]     unitSymbol = null;
+            AngleFormat  geogFormat = null;
+            NumberFormat projFormat = null;
+            UnitFormat   unitFormat = null;
+            int          maxGeogLength = 0;
+            int          maxProjLength = 0;
+            int          maxUnitLength = 0;
+            boolean      showProj  = false;
+            if (bbox != null || geopos != null) {
+                geogFormat = (AngleFormat) getFormat(Angle.class);
                 geographic = new String[BOUND_KEY.length];
-                geogFormat = getFormat(Angle.class);
+                Arrays.fill(geographic, "");
             }
-            if (envelope != null) {
+            if (envelope != null || position != null) {
+                projFormat = (NumberFormat) getFormat(Number.class);
+                unitFormat = (UnitFormat)   getFormat(Unit.class);
                 projected  = new String[BOUND_KEY.length];
                 unitSymbol = new String[BOUND_KEY.length];
-                projFormat = getFormat(Number.class);
-                unitFormat = getFormat(Unit.class);
+                Arrays.fill(projected,  "");
+                Arrays.fill(unitSymbol, "");
             }
             for (int i=0; i<BOUND_KEY.length; i++) {
+                RoundingMode rounding = RoundingMode.FLOOR;
                 double g = Double.NaN;
                 double p = Double.NaN;
                 int dimension = 0;
@@ -291,6 +331,7 @@ final class LocationFormat extends Compo
                             break;
                     case 1: if (bbox     != null) g = bbox.getEastBoundLongitude();
                             if (envelope != null) p = envelope.getMaximum(0);
+                            rounding = RoundingMode.CEILING;
                             break;
                     case 2: if (bbox     != null) g = bbox.getSouthBoundLatitude();
                             if (envelope != null) p = envelope.getMinimum(1);
@@ -298,11 +339,13 @@ final class LocationFormat extends Compo
                             break;
                     case 3: if (bbox     != null) g = bbox.getNorthBoundLatitude();
                             if (envelope != null) p = envelope.getMaximum(1);
+                            rounding = RoundingMode.CEILING;
                             dimension = 1;
                             break;
                     case 5: dimension = 1;                            // Fall through
                     case 4: if (geopos   != null) g = geopos  .getOrdinate(dimension);
                             if (position != null) p = position.getOrdinate(dimension);
+                            rounding = RoundingMode.HALF_EVEN;
                             break;
                 }
                 if (!Double.isNaN(p)) {
@@ -310,22 +353,25 @@ final class LocationFormat extends Compo
                     if (cs != null) {
                         final Unit<?> unit = cs.getAxis(dimension).getUnit();
                         if (unit != null) {
-                            @SuppressWarnings("null")
                             final int length = (unitSymbol[i] = unitFormat.format(unit)).length();
                             if (length > maxUnitLength) {
                                 maxUnitLength = length;
                             }
                         }
                     }
-                    @SuppressWarnings("null")
+                    try {
+                        projFormat.setRoundingMode(rounding);
+                    } catch (UnsupportedOperationException e) {
+                        // Ignore.
+                    }
                     final int length = (projected[i] = projFormat.format(p)).length();
                     if (length > maxProjLength) {
                         maxProjLength = length;
                     }
                 }
                 if (!Double.isNaN(g)) {
+                    geogFormat.setRoundingMode(rounding);
                     final Angle angle = (dimension == 0) ? new Longitude(g) : new Latitude(g);
-                    @SuppressWarnings("null")
                     final int length = (geographic[i] = geogFormat.format(angle)).length();
                     if (length > maxGeogLength) {
                         maxGeogLength = length;
@@ -335,30 +381,31 @@ final class LocationFormat extends Compo
             if (!showProj) {
                 projected  = null;          // All projected coordinates are identical to geographic ones.
                 unitSymbol = null;
+                maxProjLength = 0;
+                maxUnitLength = 0;
             } else if (maxProjLength != 0) {
+                if (maxUnitLength != 0) {
+                    maxUnitLength++;
+                }
                 maxGeogLength += 4;         // Arbitrary space between projected and geographic coordinates.
             }
             /*
              * At this point all coordinates have been formatted in advance.
              */
+            final String separator = (projected != null && geographic != null) ? "    —" : "";
             for (int i=0; i<BOUND_KEY.length; i++) {
-                final String p = (projected  != null) ? projected [i] : null;
-                final String g = (geographic != null) ? geographic[i] : null;
-                if (p != null || g != null) {
+                final String p = (projected  != null) ? projected [i] : "";
+                final String u = (unitSymbol != null) ? unitSymbol[i] : "";
+                final String g = (geographic != null) ? geographic[i] : "";
+                if (!p.isEmpty() || !g.isEmpty()) {
                     final short key = BOUND_KEY[i];
                     if (key != 0) {
                         vocabulary.appendLabel(key, table);
                     }
                     table.nextColumn();
-                    if (p != null) {
-                        table.append(CharSequences.spaces(maxProjLength - p.length())).append(p);
-                        String unit = unitSymbol[i];
-                        if (unit == null) unit = "";
-                        table.append(CharSequences.spaces(maxUnitLength - unit.length())).append(unit);
-                    }
-                    if (g != null) {
-                        table.append(CharSequences.spaces(maxGeogLength - g.length())).append(g);
-                    }
+                    table.append(CharSequences.spaces(maxProjLength - p.length())).append(p);
+                    table.append(CharSequences.spaces(maxUnitLength - u.length())).append(u).append(separator);
+                    table.append(CharSequences.spaces(maxGeogLength - g.length())).append(g);
                     table.nextLine();
                 }
             }
@@ -373,6 +420,7 @@ final class LocationFormat extends Compo
         if (administrator != null) {
             append(table, vocabulary, Vocabulary.Keys.Administrator, toString(administrator.getName(), locale));
         }
+        table.appendHorizontalSeparator();
         table.flush();
         if (warning != null) {
             vocabulary.appendLabel(Vocabulary.Keys.Warnings, toAppendTo);
@@ -381,6 +429,27 @@ final class LocationFormat extends Compo
     }
 
     /**
+     * Creates the format to use for formatting a latitude, longitude or projected coordinate.
+     * This method is invoked by {@link #format(Location, Appendable)} when first needed.
+     *
+     * @param  valueType  {@code Angle.class}. {@code Number.class} or {@code Unit.class}.
+     * @return a new {@link AngleFormat}, {@link NumberFormat} or {@link UnitFormat} instance
+     *         depending on the argument value.
+     */
+    @Override
+    protected Format createFormat(final Class<?> valueType) {
+        final Format f = super.createFormat(valueType);
+        if (f instanceof NumberFormat) {
+            final NumberFormat nf = (NumberFormat) f;
+            nf.setMinimumFractionDigits(0);
+            nf.setMaximumFractionDigits(0);                     // 1 metre accuracy, assuming lengths in metres.
+        } else if (f instanceof AngleFormat) {
+            ((AngleFormat) f).applyPattern("D°MM′SS″");         // 30 metres accuracy.
+        }
+        return f;
+    }
+
+    /**
      * Appends the given value in the given table if it is not null.
      *
      * @param table        the table where to append the value.
@@ -400,9 +469,24 @@ final class LocationFormat extends Compo
 
     /**
      * Unsupported operation.
+     *
+     * @param  text  the character sequence for the location to parse.
+     * @param  pos   the position where to start the parsing.
+     * @return the parsed location, or {@code null} if the text is not recognized.
+     * @throws ParseException if an error occurred while parsing the location.
      */
     @Override
     public Location parse(CharSequence text, ParsePosition pos) throws ParseException {
         throw new ParseException(Errors.format(Errors.Keys.UnsupportedOperation_1, "parse"), pos.getIndex());
     }
+
+    /**
+     * Returns a clone of this format.
+     *
+     * @return a clone of this format.
+     */
+    @Override
+    public LocationFormat clone() {
+        return (LocationFormat) super.clone();
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java [UTF-8] Mon Mar  6 15:17:20 2017
@@ -100,7 +100,7 @@ class SimpleLocation extends AbstractLoc
      * In this simple implementation, this instance is its own geographic extent.
      */
     @Override
-    public final GeographicExtent getGeographicExtent() {
+    public GeographicExtent getGeographicExtent() {
         return this;
     }
 

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java [UTF-8] Mon Mar  6 15:17:20 2017
@@ -16,6 +16,11 @@
  */
 package org.apache.sis.referencing.gazetteer;
 
+import java.util.Locale;
+import org.opengis.metadata.extent.GeographicExtent;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -37,19 +42,82 @@ public final strictfp class LocationForm
     @Test
     public void testGeographic() {
         final SimpleLocation loc = new SimpleLocation(null, "A location");
-        loc.minX =   8;
-        loc.maxX =  20;
-        loc.minY = -10;
-        loc.maxY =  30;
-        final LocationFormat format = new LocationFormat(null, null);
+        loc.minX =  8;
+        loc.maxX = 10;
+        loc.minY = -2;
+        loc.maxY = 30;
+        final LocationFormat format = new LocationFormat(Locale.US, null);
         assertMultilinesEquals(
-                "Geographic identifier:       A location\n" +
-                "East bound:                   8°E\n" +
-                "West bound:                  20°E\n" +
-                "South bound:                 10°S\n" +
-                "North bound:                 30°N\n" +
-                "Representative position:     14°E\n" +
-                "                             10°N\n" +
-                "Coordinate reference system: WGS 84\n", format.format(loc));
+                "┌─────────────────────────────────────────┐\n" +
+                "│ Geographic identifier:       A location │\n" +
+                "│ East bound:                   8°00′00″E │\n" +
+                "│ West bound:                  10°00′00″E │\n" +
+                "│ South bound:                  2°00′00″S │\n" +
+                "│ North bound:                 30°00′00″N │\n" +
+                "│ Representative position:      9°00′00″E │\n" +
+                "│                              14°00′00″N │\n" +
+                "│ Coordinate reference system: WGS 84     │\n" +
+                "└─────────────────────────────────────────┘\n", format.format(loc));
+    }
+
+    /**
+     * Tests formatting of an instance having only projected coordinates.
+     */
+    @Test
+    @DependsOnMethod("testGeographic")
+    public void testProjected() {
+        final CoordinateReferenceSystem crs = CommonCRS.WGS84.universal(14, 9);
+        final SimpleLocation loc = new SimpleLocation(null, "A location") {
+            @Override public CoordinateReferenceSystem getCoordinateReferenceSystem() {return crs;}
+            @Override public GeographicExtent          getGeographicExtent()          {return null;}
+        };
+        loc.minX =  388719.35;
+        loc.maxX =  611280.65;
+        loc.minY = -221094.87;
+        loc.maxY = 3319206.22;
+        final LocationFormat format = new LocationFormat(Locale.US, null);
+        assertMultilinesEquals(
+                "┌────────────────────────────────────────────────────┐\n" +
+                "│ Geographic identifier:       A location            │\n" +
+                "│ East bound:                    388,719 m           │\n" +
+                "│ West bound:                    611,281 m           │\n" +
+                "│ South bound:                  -221,095 m           │\n" +
+                "│ North bound:                 3,319,207 m           │\n" +
+                "│ Representative position:       500,000 m           │\n" +
+                "│                              1,549,056 m           │\n" +
+                "│ Coordinate reference system: WGS 84 / UTM zone 32N │\n" +
+                "└────────────────────────────────────────────────────┘\n", format.format(loc));
+    }
+
+    /**
+     * Tests formatting of an instance having geographic and projected coordinates.
+     */
+    @Test
+    @DependsOnMethod({"testGeographic", "testProjected"})
+    public void testGeographicAndProjected() {
+        final CoordinateReferenceSystem crs = CommonCRS.WGS84.universal(14, 9);
+        final SimpleLocation.Projected loc = new SimpleLocation.Projected(null, "A location") {
+            @Override public CoordinateReferenceSystem getCoordinateReferenceSystem() {return crs;}
+        };
+        loc.minX =  388719.35;
+        loc.maxX =  611280.65;
+        loc.minY = -221094.87;
+        loc.maxY = 3319206.22;
+        loc.westBoundLongitude =  8;
+        loc.eastBoundLongitude = 10;
+        loc.southBoundLatitude = -2;
+        loc.northBoundLatitude = 30;
+        final LocationFormat format = new LocationFormat(Locale.US, null);
+        assertMultilinesEquals(
+                "┌─────────────────────────────────────────────────────────────┐\n" +
+                "│ Geographic identifier:       A location                     │\n" +
+                "│ East bound:                    388,719 m    —     8°00′00″E │\n" +
+                "│ West bound:                    611,281 m    —    10°00′00″E │\n" +
+                "│ South bound:                  -221,095 m    —     2°00′00″S │\n" +
+                "│ North bound:                 3,319,207 m    —    30°00′00″N │\n" +
+                "│ Representative position:       500,000 m    —     9°00′00″E │\n" +
+                "│                              1,549,056 m    —    14°00′43″N │\n" +
+                "│ Coordinate reference system: WGS 84 / UTM zone 32N          │\n" +
+                "└─────────────────────────────────────────────────────────────┘\n", format.format(loc));
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java [UTF-8] Mon Mar  6 15:17:20 2017
@@ -501,6 +501,11 @@ public final strictfp class MilitaryGrid
         assertEquals("Easting",   584500.0, position.getOrdinate(0), STRICT);
         assertEquals("Northing", 4539500.0, position.getOrdinate(1), STRICT);
 
+        position = decode(coder, "32TNL83");
+        assertEquals("32TNL83",   position, decode(coder, "32/T/NL/8/3"));
+        assertEquals("Easting",   585000.0, position.getOrdinate(0), STRICT);
+        assertEquals("Northing", 4535000.0, position.getOrdinate(1), STRICT);
+
         position = decode(coder, "32TNL");
         assertEquals("32TNL", position, decode(coder, "32/T/NL"));
         assertEquals("Easting",   550000.0, position.getOrdinate(0), STRICT);

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java [UTF-8] Mon Mar  6 15:17:20 2017
@@ -99,6 +99,7 @@ import static org.apache.sis.util.collec
  *
  * <div class="warning"><b>Limitation:</b>
  * Current implementation supports only formatting, not parsing.
+ * This class is not thread-safe.
  * </div>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java [UTF-8] Mon Mar  6 15:17:20 2017
@@ -42,8 +42,7 @@ import static org.apache.sis.util.Charac
  * <p>For example, the following code:</p>
  *
  * {@preformat java
- *     StringBuilder buffer = new StringBuilder();
- *     TableAppender table  = new TableAppender(buffer);
+ *     TableAppender table = new TableAppender(System.out);
  *     table.nextLine('═');
  *     table.append("English\tFrench\tr.e.d.\n");
  *     table.nextLine('-');
@@ -70,7 +69,7 @@ import static org.apache.sis.util.Charac
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.8
  * @module
  *
  * @see org.apache.sis.util.collection.TreeTableFormat
@@ -264,6 +263,26 @@ public class TableAppender extends Appen
     }
 
     /**
+     * Creates a new table formatter writing in the given output with the specified column separator and border.
+     *
+     * @param out          the underlying stream or buffer to write to.
+     * @param leftBorder   string to write on the left side of the table.
+     * @param separator    string to write between columns.
+     * @param rightBorder  string to write on the right side of the table.
+     *
+     * @since 0.8
+     */
+    public TableAppender(final Appendable out, final String leftBorder, final String separator, final String rightBorder) {
+        super(out);
+        ArgumentChecks.ensureNonNull("leftBorder",  leftBorder);
+        ArgumentChecks.ensureNonNull("separator",   separator);
+        ArgumentChecks.ensureNonNull("rightBorder", rightBorder);
+        this.leftBorder      = leftBorder;
+        this.rightBorder     = rightBorder;
+        this.columnSeparator = separator;
+    }
+
+    /**
      * Writes a border or a corner to the underlying stream or buffer.
      *
      * @param  horizontalBorder -1 for left border, +1 for right border,  0 for center.
@@ -293,7 +312,7 @@ public class TableAppender extends Appen
         switch (horizontalBorder) {
             case -1: border = leftBorder;  break;
             case +1: border = rightBorder; break;
-            case  0: border = columnSeparator;   break;
+            case  0: border = columnSeparator; break;
             default: throw new AssertionError(horizontalBorder);
         }
         assert (verticalBorder >= -1) && (verticalBorder <= +1) : verticalBorder;
@@ -523,7 +542,9 @@ public class TableAppender extends Appen
     }
 
     /**
-     * Writes an horizontal separator.
+     * Writes an horizontal separator using the {@code '─'} character.
+     *
+     * @see #nextLine(char)
      */
     public void appendHorizontalSeparator() {
         if (currentColumn != 0 || buffer.length() != 0) {
@@ -595,6 +616,8 @@ public class TableAppender extends Appen
      *
      * @param  fill  character filling the rest of the line (default to whitespace).
      *              This character may be use as a row separator.
+     *
+     * @see #appendHorizontalSeparator()
      */
     public void nextLine(final char fill) {
         if (buffer.length() != 0) {

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java [UTF-8] Mon Mar  6 15:17:20 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.measure;
 
+import java.math.RoundingMode;
 import java.util.Objects;
 import java.util.Locale;
 import java.text.Format;
@@ -32,11 +33,8 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.LocalizedParseException;
 
-import static java.lang.Math.abs;
-import static java.lang.Math.rint;
 import static java.lang.Double.NaN;
 import static java.lang.Double.isNaN;
-import static java.lang.Double.isInfinite;
 import static org.apache.sis.math.MathFunctions.pow10;
 import static org.apache.sis.math.MathFunctions.truncate;
 import static org.apache.sis.math.MathFunctions.isNegative;
@@ -120,7 +118,7 @@ import static org.apache.sis.math.Decima
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3
- * @version 0.4
+ * @version 0.8
  * @module
  *
  * @see Angle
@@ -299,6 +297,13 @@ public class AngleFormat extends Format
                    secondsSuffix;
 
     /**
+     * The rounding mode, or {@code null} for the default mode (which is {@link RoundingMode#HALF_EVEN}).
+     *
+     * @see #RoundingMode()
+     */
+    private RoundingMode roundingMode;
+
+    /**
      * {@code true} if the {@link #parse(String, ParsePosition)} method is allowed to fallback
      * on the build-in default symbols if the string to parse doesn't match the pattern.
      *
@@ -722,6 +727,68 @@ public class AngleFormat extends Format
     }
 
     /**
+     * Returns the rounding mode. Default value is {@link RoundingMode#HALF_EVEN}.
+     *
+     * @return the rounding mode.
+     *
+     * @see NumberFormat#getRoundingMode()
+     *
+     * @since 0.8
+     */
+    public RoundingMode getRoundingMode() {
+        return (roundingMode != null) ? roundingMode : RoundingMode.HALF_EVEN;
+    }
+
+    /**
+     * Sets the rounding mode to the specified value. The given mode can be one of the following:
+     *
+     * <table class="sis">
+     *   <caption>Supported rounding modes</caption>
+     *   <tr><th>Rounding mode</th>                             <th>Result</th></tr>
+     *   <tr><td>{@link RoundingMode#UP        UP}</td>         <td>Round away from zero.</td></tr>
+     *   <tr><td>{@link RoundingMode#DOWN      DOWN}</td>       <td>Round towards zero.</td></tr>
+     *   <tr><td>{@link RoundingMode#CEILING   CEILING}</td>    <td>Round towards positive infinity.</td></tr>
+     *   <tr><td>{@link RoundingMode#FLOOR     FLOOR}</td>      <td>Round towards negative infinity.</td></tr>
+     *   <tr><td>{@link RoundingMode#HALF_EVEN HALF_EVEN}</td>  <td>Round towards nearest neighbor.</td></tr>
+     * </table>
+     *
+     * The {@link RoundingMode#HALF_UP} and {@link RoundingMode#HALF_DOWN HALF_DOWN} values are not supported
+     * by the current {@code AngleFormat} implementation.
+     *
+     * @param  mode  the new rounding mode.
+     *
+     * @see NumberFormat#setRoundingMode(RoundingMode)
+     *
+     * @since 0.8
+     */
+    public void setRoundingMode(final RoundingMode mode) {
+        ArgumentChecks.ensureNonNull("mode", mode);
+        if (mode == RoundingMode.HALF_UP || mode == RoundingMode.HALF_DOWN) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedArgumentValue_1, mode));
+        }
+        roundingMode = mode;
+    }
+
+    /**
+     * Rounds the given value according current {@link #roundingMode}.
+     *
+     * @param  sign   the sign of the value to round.
+     * @param  value  the positive value to round.
+     * @return the rounded positive value.
+     */
+    private double round(final double sign, final double value) {
+        if (roundingMode != null) {
+            switch (roundingMode) {
+                case UP:      return Math.ceil (value);
+                case DOWN:    return Math.floor(value);
+                case CEILING: return Math.abs(Math.ceil (Math.copySign(value, sign)));
+                case FLOOR:   return Math.abs(Math.floor(Math.copySign(value, sign)));
+            }
+        }
+        return Math.rint(value);
+    }
+
+    /**
      * Returns the minimum number of digits allowed in the fraction portion of the last field.
      * This value can be set by the repetition of {@code 'd'}, {@code 'm'} or {@code 's'} symbol
      * in the pattern.
@@ -900,7 +967,7 @@ public class AngleFormat extends Format
     public StringBuffer format(final double angle, StringBuffer toAppendTo, final FieldPosition pos) {
         final int offset = toAppendTo.length();
         final int fieldPos = getField(pos);
-        if (isNaN(angle) || isInfinite(angle)) {
+        if (!Double.isFinite(angle)) {
             toAppendTo = numberFormat().format(angle, toAppendTo, dummyFieldPosition());
             if (fieldPos >= DEGREES_FIELD && fieldPos <= SECONDS_FIELD) {
                 pos.setBeginIndex(offset);
@@ -916,8 +983,11 @@ public class AngleFormat extends Format
         double minutes = NaN;
         double seconds = NaN;
         int maximumFractionDigits = fractionFieldWidth;
-        if (minutesFieldWidth != 0 && !isNaN(angle)) {
-            minutes = abs(degrees - (degrees = truncate(degrees))) * 60;
+        if (minutesFieldWidth == 0) {
+            final double p = pow10(maximumFractionDigits);
+            degrees = round(angle, degrees * p) / p;
+        } else {
+            minutes = Math.abs(degrees - (degrees = truncate(degrees))) * 60;
             /*
              * Limit the maximal number of fraction digits to the amount of significant digits for a 'double' value.
              * The intend is to avoid non-significant garbage that are pure artifacts from the conversion from base
@@ -929,13 +999,13 @@ public class AngleFormat extends Format
             final double p = pow10(maximumFractionDigits);
             if (secondsFieldWidth != 0) {
                 seconds = (minutes - (minutes = truncate(minutes))) * 60;
-                seconds = rint(seconds * p) / p;                            // Correction for rounding errors.
+                seconds = round(angle, seconds * p) / p;                     // Correction for rounding errors.
                 if (seconds >= 60) {                    // We do not expect > 60 (only == 60), but let be safe.
                     seconds = 0;
                     minutes++;
                 }
             } else {
-                minutes = rint(minutes * p) / p;        // Correction for rounding errors.
+                minutes = round(angle, minutes * p) / p;                     // Correction for rounding errors.
             }
             if (minutes >= 60) {                        // We do not expect > 60 (only == 60), but let be safe.
                 minutes = 0;
@@ -1116,7 +1186,7 @@ public class AngleFormat extends Format
     {
         try {
             showLeadingFields = true;
-            toAppendTo = format(abs(angle), toAppendTo, pos);
+            toAppendTo = format(Math.abs(angle), toAppendTo, pos);
         } finally {
             showLeadingFields = false;
         }
@@ -1759,8 +1829,8 @@ BigBoss:    switch (skipSuffix(source, p
     @Override
     public int hashCode() {
         return Objects.hash(degreesFieldWidth, minutesFieldWidth, secondsFieldWidth, fractionFieldWidth,
-                minimumFractionDigits, useDecimalSeparator, isFallbackAllowed, optionalFields, locale,
-                prefix, degreesSuffix, minutesSuffix, secondsSuffix) ^ (int) serialVersionUID;
+                minimumFractionDigits, useDecimalSeparator, isFallbackAllowed, optionalFields, roundingMode,
+                locale, prefix, degreesSuffix, minutesSuffix, secondsSuffix) ^ (int) serialVersionUID;
     }
 
     /**
@@ -1783,6 +1853,7 @@ BigBoss:    switch (skipSuffix(source, p
                    useDecimalSeparator   == cast.useDecimalSeparator   &&
                    isFallbackAllowed     == cast.isFallbackAllowed     &&
                    optionalFields        == cast.optionalFields        &&
+                   roundingMode          == cast.roundingMode          &&
                    Objects.equals(locale,        cast.locale)          &&
                    Objects.equals(prefix,        cast.prefix)          &&
                    Objects.equals(degreesSuffix, cast.degreesSuffix)   &&

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Mon Mar  6 15:17:20 2017
@@ -870,6 +870,11 @@ public final class Errors extends Indexe
         public static final short UnspecifiedFormatForClass_1 = 158;
 
         /**
+         * The “{0}” argument value is unsupported.
+         */
+        public static final short UnsupportedArgumentValue_1 = 170;
+
+        /**
          * The “{0}” datum is not supported by this operation.
          */
         public static final short UnsupportedDatum_1 = 168;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Mon Mar  6 15:17:20 2017
@@ -189,6 +189,7 @@ UnsupportedFormatVersion_2        = Vers
 UnsupportedImplementation_1       = Can not handle this instance of \u2018{0}\u2019 because arbitrary implementations are not yet supported.
 UnsupportedInterpolation_1        = The \u201c{0}\u201d interpolation is unsupported.
 UnsupportedOperation_1            = The \u2018{0}\u2019 operation is unsupported.
+UnsupportedArgumentValue_1        = The \u201c{0}\u201d argument value is unsupported.
 UnsupportedDatum_1                = The \u201c{0}\u201d datum is not supported by this operation.
 UnsupportedType_1                 = The \u2018{0}\u2019 type is not supported in this context.
 ValueAlreadyDefined_1             = A value is already defined for \u201c{0}\u201d.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Mon Mar  6 15:17:20 2017
@@ -185,6 +185,7 @@ UnsupportedFormatVersion_2        = La v
 UnsupportedImplementation_1       = Cette instance de \u2018{0}\u2019 ne peut pas \u00eatre g\u00e9r\u00e9e parce que les impl\u00e9mentations arbitraires ne sont pas encore support\u00e9es.
 UnsupportedInterpolation_1        = L\u2019interpolation \u201c{0}\u201d n\u2019est pas support\u00e9e.
 UnsupportedOperation_1            = L\u2019op\u00e9ration \u2018{0}\u2019 n\u2019est pas support\u00e9e.
+UnsupportedArgumentValue_1        = La valeur d\u2019argument \u00ab\u202f{0}\u202f\u00bb n\u2019est pas support\u00e9e.
 UnsupportedDatum_1                = Le r\u00e9f\u00e9rentiel \u00ab\u202f{0}\u202f\u00bb n\u2019est pas support\u00e9 par cette op\u00e9ration.
 UnsupportedType_1                 = Le type \u2018{0}\u2019 n\u2019est pas support\u00e9 dans ce contexte.
 ValueAlreadyDefined_1             = Une valeur est d\u00e9j\u00e0 d\u00e9finie pour \u00ab\u202f{0}\u202f\u00bb.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] Mon Mar  6 15:17:20 2017
@@ -65,7 +65,7 @@ Directory               = R\u00e9pertoir
 DittoMark               = \u2033
 Domain                  = Domaine
 DublinJulian            = Julien Dublin
-EastBound               = Bord est
+EastBound               = Limite est
 Ellipsoid               = Ellipso\u00efde
 EllipsoidChange         = Changement d\u2019ellipso\u00efde
 EllipsoidalHeight       = Hauteur ellipso\u00efdale
@@ -107,7 +107,7 @@ ModifiedJulian          = Julien modifi\
 Name                    = Nom
 None                    = Aucun
 Note                    = Note
-NorthBound              = Bord nord
+NorthBound              = Limite nord
 NumberOfValues          = Nombre de valeurs
 NumberOfNaN             = Nombre de \u2018NaN\u2019
 Obligation              = Obligation
@@ -132,7 +132,7 @@ RootMeanSquare          = Moyenne quadra
 Scale                   = \u00c9chelle
 SlashSeparatedList_2    = {0}/{1}
 Source                  = Source
-SouthBound              = Bord sud
+SouthBound              = Limite sud
 StandardDeviation       = \u00c9cart type
 StartDate               = Date de d\u00e9part
 SubsetOf_1              = Sous-ensemble de {0}
@@ -159,6 +159,6 @@ Version_2               = {0} version {1
 Versions                = Versions
 Vertical                = Vertical
 Warnings                = Avertissements
-WestBound               = Bord ouest
+WestBound               = Limite ouest
 World                   = Monde
 Write                   = \u00c9criture

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java?rev=1785661&r1=1785660&r2=1785661&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java [UTF-8] Mon Mar  6 15:17:20 2017
@@ -17,6 +17,7 @@
 package org.apache.sis.measure;
 
 import java.util.Locale;
+import java.math.RoundingMode;
 import java.text.FieldPosition;
 import java.text.AttributedCharacterIterator;
 import java.text.ParseException;
@@ -34,7 +35,7 @@ import static org.apache.sis.test.TestUt
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3
- * @version 0.4
+ * @version 0.8
  * @module
  */
 @DependsOn({
@@ -223,6 +224,30 @@ public final strictfp class AngleFormatT
     }
 
     /**
+     * Tests formatting the same value with different rounding modes.
+     *
+     * @since 0.8
+     */
+    @Test
+    @DependsOnMethod("testDegreeMinutesSeconds")
+    public void testRoundingMode() {
+        final AngleFormat f = new AngleFormat("DD°MM′SS″", Locale.CANADA);
+        Angle angle = new Angle(12.515625);
+        f.setRoundingMode(RoundingMode.DOWN);      assertEquals("12°30′56″", f.format(angle));
+        f.setRoundingMode(RoundingMode.UP);        assertEquals("12°30′57″", f.format(angle));
+        f.setRoundingMode(RoundingMode.FLOOR);     assertEquals("12°30′56″", f.format(angle));
+        f.setRoundingMode(RoundingMode.CEILING);   assertEquals("12°30′57″", f.format(angle));
+        f.setRoundingMode(RoundingMode.HALF_EVEN); assertEquals("12°30′56″", f.format(angle));
+
+        angle = new Angle(-12.515625);
+        f.setRoundingMode(RoundingMode.DOWN);      assertEquals("-12°30′56″", f.format(angle));
+        f.setRoundingMode(RoundingMode.UP);        assertEquals("-12°30′57″", f.format(angle));
+        f.setRoundingMode(RoundingMode.FLOOR);     assertEquals("-12°30′57″", f.format(angle));
+        f.setRoundingMode(RoundingMode.CEILING);   assertEquals("-12°30′56″", f.format(angle));
+        f.setRoundingMode(RoundingMode.HALF_EVEN); assertEquals("-12°30′56″", f.format(angle));
+    }
+
+    /**
      * Tests with optional digits.
      */
     @Test



Mime
View raw message