sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1785863 [3/4] - in /sis/branches/JDK7: ./ application/sis-console/src/main/java/org/apache/sis/console/ core/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ core/sis...
Date Tue, 07 Mar 2017 17:45:00 GMT
Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -41,12 +41,14 @@ import org.opengis.util.NoSuchIdentifier
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.metadata.AxisNames;
 import org.apache.sis.internal.referencing.provider.TransverseMercator;
+import org.apache.sis.internal.referencing.provider.PolarStereographicA;
 import org.apache.sis.metadata.iso.extent.Extents;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.referencing.datum.DefaultEllipsoid;
 import org.apache.sis.referencing.datum.DefaultPrimeMeridian;
 import org.apache.sis.referencing.datum.DefaultGeodeticDatum;
 import org.apache.sis.referencing.datum.DefaultVerticalDatum;
+import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.cs.DefaultVerticalCS;
 import org.apache.sis.referencing.cs.DefaultCartesianCS;
 import org.apache.sis.referencing.cs.DefaultSphericalCS;
@@ -72,6 +74,7 @@ import static org.apache.sis.internal.me
 /**
  * Definitions of referencing objects identified by the {@link CommonCRS} enumeration values.
  * This class is used only as a fallback if the objects can not be fetched from the EPSG database.
+ * This class should not be loaded when a connection to an EPSG geodetic dataset is available.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
@@ -107,7 +110,7 @@ final class StandardDefinitions {
             map.put(IDENTIFIERS_KEY, new NamedIdentifier(Citations.EPSG, String.valueOf(code)));
         }
         map.put(NAME_KEY, new NamedIdentifier(Citations.EPSG, name));
-        map.put(ALIAS_KEY, alias); // May be null, which is okay.
+        map.put(ALIAS_KEY, alias);                                      // May be null, which is okay.
         if (world) {
             map.put(DOMAIN_OF_VALIDITY_KEY, Extents.WORLD);
         }
@@ -126,29 +129,31 @@ final class StandardDefinitions {
     }
 
     /**
-     * Returns the operation method for Transverse Mercator projection using the SIS factory implementation.
-     * This method restricts the factory to SIS implementation instead than arbitrary factory in order to meet
-     * the contract saying that {@link CommonCRS} methods should never fail.
+     * Creates a Universal Transverse Mercator (UTM) or a Universal Polar Stereographic (UPS) projected CRS
+     * using the Apache SIS factory implementation. This method restricts the factory to SIS implementation
+     * instead than arbitrary factory in order to meet the contract saying that {@link CommonCRS} methods
+     * should never fail.
      *
      * @param code       the EPSG code, or 0 if none.
      * @param baseCRS    the geographic CRS on which the projected CRS is based.
-     * @param latitude   a latitude in the zone of the desired projection, to be snapped to 0°.
+     * @param isUTM      {@code true} for UTM or {@code false} for UPS. Note: redundant with the given latitude.
+     * @param latitude   a latitude in the zone of the desired projection, to be snapped to 0°, 90°S or 90°N.
      * @param longitude  a longitude in the zone of the desired projection, to be snapped to UTM central meridian.
      * @param derivedCS  the projected coordinate system.
      */
-    static ProjectedCRS createUTM(final int code, final GeographicCRS baseCRS,
+    static ProjectedCRS createUniversal(final int code, final GeographicCRS baseCRS, final boolean isUTM,
             final double latitude, final double longitude, final CartesianCS derivedCS)
     {
         final OperationMethod method;
         try {
-            method = DefaultFactories.forBuildin(MathTransformFactory.class,
-                                          DefaultMathTransformFactory.class)
-                    .getOperationMethod(TransverseMercator.NAME);
+            method = DefaultFactories.forBuildin(MathTransformFactory.class, DefaultMathTransformFactory.class)
+                            .getOperationMethod(isUTM ? TransverseMercator.NAME : PolarStereographicA.NAME);
         } catch (NoSuchIdentifierException e) {
-            throw new IllegalStateException(e);     // Should not happen with SIS implementation.
+            throw new IllegalStateException(e);                     // Should not happen with SIS implementation.
         }
         final ParameterValueGroup parameters = method.getParameters().createValue();
-        String name = TransverseMercator.Zoner.UTM.setParameters(parameters, true, latitude, longitude);
+        String name = isUTM ? TransverseMercator.Zoner.UTM.setParameters(parameters, latitude, longitude)
+                            : PolarStereographicA.setParameters(parameters, latitude >= 0);
         final DefaultConversion conversion = new DefaultConversion(properties(0, name, null, false), method, null, parameters);
 
         name = baseCRS.getName().getCode() + " / " + name;
@@ -165,17 +170,17 @@ final class StandardDefinitions {
      */
     static GeographicCRS createGeographicCRS(final short code, final GeodeticDatum datum, final EllipsoidalCS cs) {
         final String name;
-        String alias = null;
-        String scope = null;
+        String  alias = null;
+        String  scope = null;
         boolean world = false;
         switch (code) {
-            case 4326: name = "WGS 84"; world = true; scope = "Horizontal component of 3D system."; break;
-            case 4322: name = "WGS 72"; world = true; break;
-            case 4258: name = "ETRS89"; alias = "ETRS89-GRS80"; break;
-            case 4269: name = "NAD83";  break;
-            case 4267: name = "NAD27";  break;
-            case 4230: name = "ED50";   break;
-            case 4047: name = "Unspecified datum based upon the GRS 1980 Authalic Sphere"; world = true; break;
+            case 4326: name = "WGS 84"; world = true;           scope = "Horizontal component of 3D system."; break;
+            case 4322: name = "WGS 72"; world = true;           scope = "Horizontal component of 3D system."; break;
+            case 4258: name = "ETRS89"; alias = "ETRS89-GRS80"; scope = "Horizontal component of 3D system."; break;
+            case 4269: name = "NAD83";                          scope = "Geodetic survey.";                   break;
+            case 4267: name = "NAD27";                          scope = "Geodetic survey.";                   break;
+            case 4230: name = "ED50";                           scope = "Geodetic survey.";                   break;
+            case 4047: name = "Unspecified datum based upon the GRS 1980 Authalic Sphere"; world = true;      break;
             default:   throw new AssertionError(code);
         }
         final Map<String, Object> properties = properties(code, name, alias, world);
@@ -313,16 +318,17 @@ final class StandardDefinitions {
     @SuppressWarnings("fallthrough")
     static CoordinateSystem createCoordinateSystem(final short code) {
         final String name;
-        final int dim;                  // Number of dimension.
+        int type = 0;                   // 0= Cartesian (default), 1= Spherical, 2= Ellipsoidal
+        int dim = 2;                    // Number of dimension, default to 2.
         short axisCode;                 // Code of first axis + dim (or code after the last axis).
-        boolean isCartesian = false;
-        boolean isSpherical = false;
         switch (code) {
-            case 6422: name = "Ellipsoidal 2D"; dim = 2; axisCode = 108; break;
-            case 6423: name = "Ellipsoidal 3D"; dim = 3; axisCode = 111; break;
-            case 6404: name = "Spherical";      dim = 3; axisCode =  63; isSpherical = true; break;
-            case 6500: name = "Earth centred";  dim = 3; axisCode = 118; isCartesian = true; break;
-            case 4400: name = "Cartesian 2D";   dim = 2; axisCode =   3; isCartesian = true; break;
+            case 6422: name = "Ellipsoidal 2D"; type = 2;          axisCode =  108; break;
+            case 6423: name = "Ellipsoidal 3D"; type = 2; dim = 3; axisCode =  111; break;
+            case 6404: name = "Spherical";      type = 1; dim = 3; axisCode =   63; break;
+            case 6500: name = "Earth centred";            dim = 3; axisCode =  118; break;
+            case 4400: name = "Cartesian 2D";                      axisCode =    3; break;
+            case 1026: name = "Cartesian 2D for UPS north";        axisCode = 1067; break;
+            case 1027: name = "Cartesian 2D for UPS south";        axisCode = 1059; break;
             default:   throw new AssertionError(code);
         }
         final Map<String,?> properties = properties(code, name, null, false);
@@ -334,20 +340,13 @@ final class StandardDefinitions {
             case 1:  xAxis = createAxis(--axisCode);
             case 0:  break;
         }
-        if (isCartesian) {
-            if (zAxis != null) {
-                return new DefaultCartesianCS(properties, xAxis, yAxis, zAxis);
-            } else {
-                return new DefaultCartesianCS(properties, xAxis, yAxis);
-            }
-        } else if (isSpherical) {
-            return new DefaultSphericalCS(properties, xAxis, yAxis, zAxis);
-        } else {
-            if (zAxis != null) {
-                return new DefaultEllipsoidalCS(properties, xAxis, yAxis, zAxis);
-            } else {
-                return new DefaultEllipsoidalCS(properties, xAxis, yAxis);
-            }
+        switch (type) {
+            default: throw new AssertionError(type);
+            case 0:  return (zAxis != null) ? new DefaultCartesianCS  (properties, xAxis, yAxis, zAxis)
+                                            : new DefaultCartesianCS  (properties, xAxis, yAxis);
+            case 1:  return                   new DefaultSphericalCS  (properties, xAxis, yAxis, zAxis);
+            case 2:  return (zAxis != null) ? new DefaultEllipsoidalCS(properties, xAxis, yAxis, zAxis)
+                                            : new DefaultEllipsoidalCS(properties, xAxis, yAxis);
         }
     }
 
@@ -367,12 +366,10 @@ final class StandardDefinitions {
         switch (code) {
             case 1:    name = "Easting";
                        abrv = "E";
-                       unit = Units.METRE;
                        dir  = AxisDirection.EAST;
                        break;
             case 2:    name = "Northing";
                        abrv = "N";
-                       unit = Units.METRE;
                        dir  = AxisDirection.NORTH;
                        break;
             case 60:   name = "Spherical latitude";
@@ -393,7 +390,6 @@ final class StandardDefinitions {
                        break;
             case 62:   name = "Geocentric radius";
                        abrv = "R";                          // See HardCodedAxes.GEOCENTRIC_RADIUS in tests.
-                       unit = Units.METRE;
                        dir  = AxisDirection.UP;
                        rm   = RangeMeaning.EXACT;
                        min  = 0;
@@ -440,6 +436,23 @@ final class StandardDefinitions {
                        abrv = "Z";
                        dir  = AxisDirection.GEOCENTRIC_Z;
                        break;
+            case 1057: // Actually no axis allocated by EPSG here, but createCoordinateSystem(1027) needs this number.
+            case 1056: name = "Easting";
+                       abrv = "E";
+                       dir  = CoordinateSystems.directionAlongMeridian(AxisDirection.NORTH, 90);
+                       break;
+            case 1058: name = "Northing";
+                       abrv = "N";
+                       dir  = CoordinateSystems.directionAlongMeridian(AxisDirection.NORTH, 0);
+                       break;
+            case 1065: name = "Easting";
+                       abrv = "E";
+                       dir  = CoordinateSystems.directionAlongMeridian(AxisDirection.SOUTH, 90);
+                       break;
+            case 1066: name = "Northing";
+                       abrv = "N";
+                       dir  = CoordinateSystems.directionAlongMeridian(AxisDirection.SOUTH, 180);
+                       break;
             default:   throw new AssertionError(code);
         }
         final Map<String,Object> properties = properties(code, name, null, false);

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractCRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractCRS.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractCRS.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractCRS.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -169,7 +169,7 @@ public class AbstractCRS extends Abstrac
      *   </tr>
      * </table>
      *
-     * @param properties  The properties to be given to the coordinate reference system.
+     * @param properties  the properties to be given to the coordinate reference system.
      * @param cs          the coordinate system.
      */
     public AbstractCRS(final Map<String,?> properties, final CoordinateSystem cs) {

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.cs;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Locale;
+import java.util.Objects;
 import javax.measure.Unit;
 import javax.measure.quantity.Angle;
 import javax.measure.UnitConverter;
@@ -45,6 +46,7 @@ import org.apache.sis.referencing.Identi
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Units;
+import org.apache.sis.util.Utilities;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.jaxb.Context;
@@ -69,9 +71,6 @@ import static org.apache.sis.util.collec
  */
 import static org.apache.sis.internal.referencing.NilReferencingObject.UNNAMED;
 
-// Branch-dependent imports
-import java.util.Objects;
-
 
 /**
  * Coordinate system axis name, direction, unit and range of values.
@@ -567,12 +566,13 @@ public class DefaultCoordinateSystemAxis
      * i.e. it is caller responsibility to determine if range shall be considered as metadata.
      *
      * @param  that  the axis to compare with this axis.
+     * @param  mode  whether the unit comparison is approximative or exact.
      * @param  cr    {@code true} for comparing also the range minimum and maximum values.
      * @return {@code true} if unit, direction and optionally range extremum are equal.
      */
-    private boolean equalsIgnoreMetadata(final CoordinateSystemAxis that, final boolean cr) {
-        return Objects.equals(getUnit(),      that.getUnit()) &&
-               Objects.equals(getDirection(), that.getDirection()) &&
+    private boolean equalsIgnoreMetadata(final CoordinateSystemAxis that, final ComparisonMode mode, final boolean cr) {
+        return Objects.equals(getDirection(), that.getDirection()) &&
+               Utilities.deepEquals(getUnit(), that.getUnit(), mode) &&
                (!cr || (doubleToLongBits(getMinimumValue()) == doubleToLongBits(that.getMinimumValue()) &&
                         doubleToLongBits(getMaximumValue()) == doubleToLongBits(that.getMaximumValue())));
     }
@@ -618,7 +618,7 @@ public class DefaultCoordinateSystemAxis
             }
             case BY_CONTRACT: {
                 final CoordinateSystemAxis that = (CoordinateSystemAxis) object;
-                return equalsIgnoreMetadata(that, true) &&
+                return equalsIgnoreMetadata(that, mode, true) &&
                        Objects.equals(getAbbreviation(), that.getAbbreviation()) &&
                        Objects.equals(getRangeMeaning(), that.getRangeMeaning());
             }
@@ -629,8 +629,8 @@ public class DefaultCoordinateSystemAxis
          * coordinate operation may shift some ordinate values (typically ±360° on longitudes).
          */
         final CoordinateSystemAxis that = (CoordinateSystemAxis) object;
-        if (!equalsIgnoreMetadata(that, RangeMeaning.WRAPAROUND.equals(this.getRangeMeaning()) &&
-                                        RangeMeaning.WRAPAROUND.equals(that.getRangeMeaning())))
+        if (!equalsIgnoreMetadata(that, mode, RangeMeaning.WRAPAROUND.equals(this.getRangeMeaning()) &&
+                                              RangeMeaning.WRAPAROUND.equals(that.getRangeMeaning())))
         {
             return false;
         }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -17,6 +17,7 @@
 package org.apache.sis.referencing.datum;
 
 import java.util.Map;
+import java.util.Objects;
 import javax.measure.Unit;
 import javax.measure.quantity.Length;
 import javax.measure.UnitConverter;
@@ -42,6 +43,7 @@ import org.apache.sis.referencing.Abstra
 import org.apache.sis.io.wkt.Formatter;
 import org.apache.sis.io.wkt.Convention;
 import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.Utilities;
 import org.apache.sis.measure.Units;
 
 import static java.lang.Math.*;
@@ -49,9 +51,6 @@ import static java.lang.Double.*;
 import static org.apache.sis.util.ArgumentChecks.ensureStrictlyPositive;
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
 
-// Branch-dependent imports
-import java.util.Objects;
-
 
 /**
  * Geometric figure that can be used to describe the approximate shape of the earth.
@@ -717,7 +716,7 @@ public class DefaultEllipsoid extends Ab
                  */
                 final Ellipsoid that = (Ellipsoid) object;
                 final Unit<Length> unit = getAxisUnit();  // In case the user override this method.
-                if (!Objects.equals(unit, that.getAxisUnit())) {
+                if (!Utilities.deepEquals(unit, that.getAxisUnit(), mode)) {
                     return false;
                 }
                 final UnitConverter c = mode.isApproximative() ? unit.getConverterTo(Units.METRE) : null;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -43,6 +43,7 @@ import org.opengis.referencing.cs.Cartes
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.datum.DatumFactory;
 import org.opengis.referencing.datum.EngineeringDatum;
+import org.apache.sis.internal.referencing.provider.TransverseMercator.Zoner;
 import org.apache.sis.internal.referencing.GeodeticObjectBuilder;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.metadata.iso.citation.Citations;
@@ -60,8 +61,6 @@ import org.apache.sis.util.resources.Err
 import org.apache.sis.util.iso.DefaultNameSpace;
 import org.apache.sis.util.iso.SimpleInternationalString;
 
-import static org.apache.sis.internal.referencing.provider.TransverseMercator.Zoner.UTM;
-
 
 /**
  * Creates coordinate reference systems in the "{@code OGC}", "{@code CRS}" or {@code "AUTO(2)"} namespaces.
@@ -177,14 +176,18 @@ import static org.apache.sis.internal.re
  *
  * <div class="note"><b>Examples:</b>
  * {@code "AUTO2:42001,1,-100,45"} identifies a Universal Transverse Mercator (UTM) projection
- * for a zone containing the point at (45°N, 100°W).</div>
+ * for a zone containing the point at (45°N, 100°W) with axes in metres.</div>
  *
  * Codes in the {@code "AUTO"} namespace are the same than codes in the {@code "AUTO2"} namespace, except that
  * the {@linkplain org.apache.sis.measure.Units#valueOfEPSG(int) EPSG code} of the desired unit of measurement
  * was used instead than the unit factor.
  * The {@code "AUTO"} namespace was defined in the <cite>Web Map Service</cite> (WMS) 1.1.1 specification
  * while the {@code "AUTO2"} namespace is defined in WMS 1.3.0.
- * In Apache SIS implementation, the unit parameter (either as factor or as EPSG code) is optional and default to metres.
+ * In Apache SIS implementation, the unit parameter (either as factor or as EPSG code) is optional and defaults to metres.
+ *
+ * <p>In the {@code AUTO(2):42001} case, the UTM zone is calculated as specified in WMS 1.3 specification,
+ * i.e. <strong>without</strong> taking in account the Norway and Svalbard special cases and without
+ * switching to polar stereographic projections for high latitudes.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.7
@@ -587,7 +590,7 @@ public class CommonAuthorityFactory exte
              * 42005: WGS 84 / Auto Mollweide         —   defined by "Central_Meridian" only.
              */
             case 42001: isUTM  = true; break;
-            case 42002: isUTM  = (latitude == 0) && (UTM.centralMeridian(UTM.zone(longitude)) == longitude); break;
+            case 42002: isUTM  = (latitude == 0) && (Zoner.UTM.centralMeridian(Zoner.UTM.zone(0, longitude)) == longitude); break;
             case 42003: method = "Orthographic";       param = Constants.LATITUDE_OF_ORIGIN;  break;
             case 42004: method = "Equirectangular";    param = Constants.STANDARD_PARALLEL_1; break;
             case 42005: method = "Mollweide";                                                 break;
@@ -596,14 +599,17 @@ public class CommonAuthorityFactory exte
         /*
          * For the (Universal) Transverse Mercator case (AUTO:42001 and 42002), we delegate to the CommonCRS
          * enumeration if possible because CommonCRS will itself delegate to the EPSG factory if possible.
+         * The Math.signum(latitude) instruction is for preventing "AUTO:42001" to handle the UTM special cases
+         * (Norway and Svalbard) or to switch on the Universal Polar Stereographic projection for high latitudes,
+         * because the WMS specification does not said that we should.
          */
         final CommonCRS datum = CommonCRS.WGS84;
-        final GeographicCRS baseCRS;
-        final ProjectedCRS crs;
-        CartesianCS cs;
+        final GeographicCRS baseCRS;                // To be set, directly or indirectly, to WGS84.geographic().
+        final ProjectedCRS crs;                     // Temporary UTM projection, for extracting other properties.
+        CartesianCS cs;                             // Coordinate system with (E,N) axes in metres.
         try {
             if (isUTM != null && isUTM) {
-                crs = datum.UTM(latitude, longitude);
+                crs = datum.universal(Math.signum(latitude), longitude);
                 if (factor == (isLegacy ? Constants.EPSG_METRE : 1)) {
                     return crs;
                 }
@@ -612,7 +618,7 @@ public class CommonAuthorityFactory exte
             } else {
                 cs = projectedCS;
                 if (cs == null) {
-                    crs = datum.UTM(latitude, longitude);
+                    crs = datum.universal(Math.signum(latitude), longitude);
                     projectedCS = cs = crs.getCoordinateSystem();
                     baseCRS = crs.getBaseCRS();
                 } else {
@@ -640,10 +646,10 @@ public class CommonAuthorityFactory exte
              */
             final GeodeticObjectBuilder builder = new GeodeticObjectBuilder();
             if (isUTM != null) {
-                if (isUTM) {
+                if (isUTM && crs != null) {
                     builder.addName(crs.getName());
                 } // else default to the conversion name, which is "Transverse Mercator".
-                builder.setTransverseMercator(isUTM, latitude, longitude);
+                builder.setTransverseMercator(isUTM ? Zoner.UTM : Zoner.ANY, latitude, longitude);
             } else {
                 builder.setConversionMethod(method)
                        .addName(PROJECTION_NAMES[projection - FIRST_PROJECTION_CODE])

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -23,7 +23,13 @@
  * <p>The most commonly used kinds of Reference Systems in Apache SIS are the <cite>Coordinate Reference Systems</cite>
  * (CRS), which handle coordinates of arbitrary dimensions. The SIS implementations can handle 2D and 3D coordinates,
  * as well as 4D, 5D, <i>etc</i>. An other less-frequently used kind of Reference System uses labels instead, as in
- * postal address. This package is the root for both kinds, with an emphasis on the one for coordinates.</p>
+ * postal address. This package is the root for both kinds, with an emphasis on the one for coordinates.
+ * The two kinds of referencing system are implemented in the following packages:</p>
+ * <ul>
+ *   <li>{@link org.apache.sis.referencing.crs} for <cite>referencing by coordinates</cite> (ISO 19111)</li>
+ *   <li>{@link org.apache.sis.referencing.gazetteer} for <cite>referencing by geographic identifiers</cite>
+ *       (ISO 19112), together with the linking from geographic identifiers to coordinates.</li>
+ * </ul>
  *
  * <div class="section">Fetching geodetic object instances</div>
  * Geodetic objects can be instantiated either

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -90,7 +90,7 @@ public abstract strictfp class Transform
      */
     @Test
     public final void testTransform() throws FactoryException, TransformException {
-        final ProjectedCRS    targetCRS  = CommonCRS.WGS84.UTM(10, -123.5);
+        final ProjectedCRS    targetCRS  = CommonCRS.WGS84.universal(10, -123.5);
         final GeographicCRS   sourceCRS  = targetCRS.getBaseCRS();
         final Conversion      conversion = targetCRS.getConversionFromBase();
         final MathTransform2D transform  = (MathTransform2D) conversion.getMathTransform();
@@ -199,7 +199,7 @@ public abstract strictfp class Transform
     @Test
     @DependsOnMethod("testTransform")
     public final void testTransformNotOverPole() throws FactoryException, TransformException {
-        final ProjectedCRS  sourceCRS  = CommonCRS.WGS84.UTM(10, -3.5);
+        final ProjectedCRS  sourceCRS  = CommonCRS.WGS84.universal(10, -3.5);
         final GeographicCRS targetCRS  = sourceCRS.getBaseCRS();
         final Conversion    conversion = inverse(sourceCRS.getConversionFromBase());
         final G rectangle = createFromExtremums(sourceCRS, 199980, 4490220, 309780, 4600020);

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ServicesForMetadataTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ServicesForMetadataTest.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ServicesForMetadataTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ServicesForMetadataTest.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -50,7 +50,7 @@ import static org.apache.sis.test.TestUt
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.7
+ * @version 0.8
  * @module
  */
 @DependsOn({
@@ -213,4 +213,14 @@ public final strictfp class ServicesForM
                 ((CoordinateReferenceSystem) components[1]).getCoordinateSystem(),
                 AxisDirection.UP, AxisDirection.NORTH, AxisDirection.EAST);
     }
+
+    /**
+     * Tests {@link org.apache.sis.metadata.iso.extent.Extents#centroid(GeographicBoundingBox)}.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testGeographicBoundingBoxCentroid() {
+        org.apache.sis.metadata.iso.extent.ExtentsTest.testCentroid();
+    }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TransverseMercatorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TransverseMercatorTest.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TransverseMercatorTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TransverseMercatorTest.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -37,13 +37,18 @@ import static org.junit.Assert.*;
  */
 public final strictfp class TransverseMercatorTest extends TestCase {
     /**
-     * Tests {@link TransverseMercator.Zoner#zone(double)}.
+     * Tests {@link TransverseMercator.Zoner#zone(double)},
+     * including the special cases for Norway and Svalbard.
      */
     @Test
     public void testZone() {
-        assertEquals(1,  TransverseMercator.Zoner.UTM.zone(-180));
-        assertEquals(10, TransverseMercator.Zoner.UTM.zone(-123));
-        assertEquals(60, TransverseMercator.Zoner.UTM.zone(179.9));
+        assertEquals("180°E",        1, TransverseMercator.Zoner.UTM.zone( 0, -180  ));
+        assertEquals("123°E",       10, TransverseMercator.Zoner.UTM.zone( 0, -123  ));
+        assertEquals("179.9°W",     60, TransverseMercator.Zoner.UTM.zone( 0,  179.9));
+        assertEquals( "4°E band T", 31, TransverseMercator.Zoner.UTM.zone(45,    4  ));
+        assertEquals( "4°E band V", 32, TransverseMercator.Zoner.UTM.zone(56,    4  ));
+        assertEquals("20°E band W", 34, TransverseMercator.Zoner.UTM.zone(71,   20  ));
+        assertEquals("20°E band X", 33, TransverseMercator.Zoner.UTM.zone(72,   20  ));
     }
 
     /**
@@ -57,7 +62,7 @@ public final strictfp class TransverseMe
     }
 
     /**
-     * Tests {@link TransverseMercator.Zoner#setParameters(ParameterValueGroup, boolean, double, double)}
+     * Tests {@link TransverseMercator.Zoner#setParameters(ParameterValueGroup, double, double)}
      * followed by {@link TransverseMercator.Zoner#zone(ParameterValueGroup)}.
      */
     @Test
@@ -67,17 +72,17 @@ public final strictfp class TransverseMe
     })
     public void testSetParameters() {
         final ParameterValueGroup p = TransverseMercator.PARAMETERS.createValue();
-        assertEquals("UTM zone 10N", TransverseMercator.Zoner.UTM.setParameters(p, true, 0, -122));
+        assertEquals("UTM zone 10N", TransverseMercator.Zoner.UTM.setParameters(p, 0, -122));
         assertEquals(Constants.CENTRAL_MERIDIAN, -123, p.parameter(Constants.CENTRAL_MERIDIAN).doubleValue(), STRICT);
         assertEquals(Constants.FALSE_NORTHING, 0, p.parameter(Constants.FALSE_NORTHING).doubleValue(), STRICT);
         assertEquals("UTM.zone(parameters)", 10, TransverseMercator.Zoner.UTM.zone(p));
 
-        assertEquals("Transverse Mercator", TransverseMercator.Zoner.UTM.setParameters(p, false, 0, -122));
+        assertEquals("Transverse Mercator", TransverseMercator.Zoner.ANY.setParameters(p, 0, -122));
         assertEquals(Constants.CENTRAL_MERIDIAN, -122, p.parameter(Constants.CENTRAL_MERIDIAN).doubleValue(), STRICT);
         assertEquals(Constants.FALSE_NORTHING, 0, p.parameter(Constants.FALSE_NORTHING).doubleValue(), STRICT);
         assertEquals("UTM.zone(parameters)", 0, TransverseMercator.Zoner.UTM.zone(p));
 
-        assertEquals("UTM zone 10S", TransverseMercator.Zoner.UTM.setParameters(p, false, -0.0, -123));
+        assertEquals("UTM zone 10S", TransverseMercator.Zoner.ANY.setParameters(p, -0.0, -123));
         assertEquals(Constants.CENTRAL_MERIDIAN, -123, p.parameter(Constants.CENTRAL_MERIDIAN).doubleValue(), STRICT);
         assertEquals(Constants.FALSE_NORTHING, 10000000, p.parameter(Constants.FALSE_NORTHING).doubleValue(), STRICT);
         assertEquals("UTM.zone(parameters)", -10, TransverseMercator.Zoner.UTM.zone(p));

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -75,28 +75,30 @@ public final strictfp class CRSTest exte
      *
      * @throws FactoryException if a CRS can not be constructed.
      *
-     * @see CommonCRSTest#testForCode()
+     * @see EPSGFactoryFallbackTest#testCreateCRS()
      */
     @Test
     public void testForEpsgCode() throws FactoryException {
-        verifyForCode(CommonCRS.WGS84 .geographic(),   "EPSG:4326");
-        verifyForCode(CommonCRS.WGS84 .geographic(),   "urn:ogc:def:crs:EPSG::4326");
-        verifyForCode(CommonCRS.WGS84 .geographic(),   "urn:x-ogc:def:crs:EPSG::4326");
-        verifyForCode(CommonCRS.WGS84 .geographic(),   "http://www.opengis.net/gml/srs/epsg.xml#4326");
-        verifyForCode(CommonCRS.WGS72 .geographic(),   "EPSG:4322");
-        verifyForCode(CommonCRS.SPHERE.geographic(),   "EPSG:4047");
-        verifyForCode(CommonCRS.NAD83 .geographic(),   "EPSG:4269");
-        verifyForCode(CommonCRS.NAD27 .geographic(),   "EPSG:4267");
-        verifyForCode(CommonCRS.ETRS89.geographic(),   "EPSG:4258");
-        verifyForCode(CommonCRS.ED50  .geographic(),   "EPSG:4230");
-        verifyForCode(CommonCRS.WGS84 .geocentric(),   "EPSG:4978");
-        verifyForCode(CommonCRS.WGS72 .geocentric(),   "EPSG:4984");
-        verifyForCode(CommonCRS.ETRS89.geocentric(),   "EPSG:4936");
-        verifyForCode(CommonCRS.WGS84 .geographic3D(), "EPSG:4979");
-        verifyForCode(CommonCRS.WGS72 .geographic3D(), "EPSG:4985");
-        verifyForCode(CommonCRS.ETRS89.geographic3D(), "EPSG:4937");
+        verifyForCode(CommonCRS.WGS84 .geographic(),           "EPSG:4326");
+        verifyForCode(CommonCRS.WGS84 .geographic(),           "urn:ogc:def:crs:EPSG::4326");
+        verifyForCode(CommonCRS.WGS84 .geographic(),           "urn:x-ogc:def:crs:EPSG::4326");
+        verifyForCode(CommonCRS.WGS84 .geographic(),           "http://www.opengis.net/gml/srs/epsg.xml#4326");
+        verifyForCode(CommonCRS.WGS72 .geographic(),           "EPSG:4322");
+        verifyForCode(CommonCRS.SPHERE.geographic(),           "EPSG:4047");
+        verifyForCode(CommonCRS.NAD83 .geographic(),           "EPSG:4269");
+        verifyForCode(CommonCRS.NAD27 .geographic(),           "EPSG:4267");
+        verifyForCode(CommonCRS.ETRS89.geographic(),           "EPSG:4258");
+        verifyForCode(CommonCRS.ED50  .geographic(),           "EPSG:4230");
+        verifyForCode(CommonCRS.WGS84 .geocentric(),           "EPSG:4978");
+        verifyForCode(CommonCRS.WGS72 .geocentric(),           "EPSG:4984");
+        verifyForCode(CommonCRS.ETRS89.geocentric(),           "EPSG:4936");
+        verifyForCode(CommonCRS.WGS84 .geographic3D(),         "EPSG:4979");
+        verifyForCode(CommonCRS.WGS72 .geographic3D(),         "EPSG:4985");
+        verifyForCode(CommonCRS.ETRS89.geographic3D(),         "EPSG:4937");
+        verifyForCode(CommonCRS.WGS84 .universal(88, 120),     "EPSG:5041");
+        verifyForCode(CommonCRS.WGS84 .universal(-40, 2),      "EPSG:32731");
         verifyForCode(CommonCRS.Vertical.MEAN_SEA_LEVEL.crs(), "EPSG:5714");
-        verifyForCode(CommonCRS.Vertical.DEPTH.crs(), "EPSG:5715");
+        verifyForCode(CommonCRS.Vertical.DEPTH.crs(),          "EPSG:5715");
     }
 
     /**
@@ -104,7 +106,7 @@ public final strictfp class CRSTest exte
      *
      * @throws FactoryException if a CRS can not be constructed.
      *
-     * @see CommonCRSTest#testForCode()
+     * @see EPSGFactoryFallbackTest#testCreateCRS()
      */
     @Test
     @DependsOnMethod("testForEpsgCode")

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -79,6 +79,8 @@ public final strictfp class CommonCRSTes
             assertNoCodeCollision(codes, crs, crs.geographic);
             assertNoCodeCollision(codes, crs, crs.geocentric);
             assertNoCodeCollision(codes, crs, crs.geo3D);
+            assertNoCodeCollision(codes, crs, crs.northUPS);
+            assertNoCodeCollision(codes, crs, crs.southUPS);
             for (int zone = crs.firstZone; zone <= crs.lastZone; zone++) {
                 if (crs.northUTM != 0) assertNoCodeCollision(codes, crs, crs.northUTM + zone);
                 if (crs.southUTM != 0) assertNoCodeCollision(codes, crs, crs.southUTM + zone);
@@ -276,19 +278,54 @@ public final strictfp class CommonCRSTes
     }
 
     /**
-     * Tests {@link CommonCRS#UTM(double, double)}.
+     * Tests {@link CommonCRS#universal(double, double)} with Universal Transverse Mercator (UTM) projections.
      *
      * @since 0.7
      */
     @Test
     @DependsOnMethod("testGeographic")
     public void testUTM() {
-        final ProjectedCRS crs = CommonCRS.WGS72.UTM(-45, -122);
+        final ProjectedCRS crs = CommonCRS.WGS72.universal(-45, -122);
         assertEquals("name", "WGS 72 / UTM zone 10S", crs.getName().getCode());
         final ParameterValueGroup pg = crs.getConversionFromBase().getParameterValues();
-        assertEquals(Constants.LATITUDE_OF_ORIGIN, -123, pg.parameter(Constants.CENTRAL_MERIDIAN).doubleValue(), STRICT);
-        assertEquals(Constants.FALSE_NORTHING, 10000000, pg.parameter(Constants.FALSE_NORTHING).doubleValue(),   STRICT);
-        assertSame("Expected a cached instance.", crs, CommonCRS.WGS72.UTM(-45, -122));
-        assertNotSame("Expected a new instance.", crs, CommonCRS.WGS72.UTM(+45, -122));
+        assertEquals(Constants.LATITUDE_OF_ORIGIN,    0, pg.parameter(Constants.LATITUDE_OF_ORIGIN).doubleValue(), STRICT);
+        assertEquals(Constants.CENTRAL_MERIDIAN,   -123, pg.parameter(Constants.CENTRAL_MERIDIAN)  .doubleValue(), STRICT);
+        assertEquals(Constants.SCALE_FACTOR,     0.9996, pg.parameter(Constants.SCALE_FACTOR)      .doubleValue(), STRICT);
+        assertEquals(Constants.FALSE_EASTING,    500000, pg.parameter(Constants.FALSE_EASTING)     .doubleValue(), STRICT);
+        assertEquals(Constants.FALSE_NORTHING, 10000000, pg.parameter(Constants.FALSE_NORTHING)    .doubleValue(), STRICT);
+        assertSame("Expected a cached instance.", crs, CommonCRS.WGS72.universal(-45, -122));
+        assertNotSame("Expected a new instance.", crs, CommonCRS.WGS72.universal(+45, -122));
+    }
+
+    /**
+     * Tests {@link CommonCRS#universal(double, double)} with Universal Polar Stereographic (UPS) projections.
+     *
+     * @since 0.8
+     */
+    @Test
+    @DependsOnMethod("testGeographic")
+    public void testUPS() {
+        final ProjectedCRS crs = CommonCRS.WGS72.universal(-85, -122);
+        assertEquals("name", "WGS 72 / Universal Polar Stereographic South", crs.getName().getCode());
+        final ParameterValueGroup pg = crs.getConversionFromBase().getParameterValues();
+        assertEquals(Constants.LATITUDE_OF_ORIGIN, -90, pg.parameter(Constants.LATITUDE_OF_ORIGIN).doubleValue(), STRICT);
+        assertEquals(Constants.CENTRAL_MERIDIAN,     0, pg.parameter(Constants.CENTRAL_MERIDIAN)  .doubleValue(), STRICT);
+        assertEquals(Constants.SCALE_FACTOR,     0.994, pg.parameter(Constants.SCALE_FACTOR)      .doubleValue(), STRICT);
+        assertEquals(Constants.FALSE_EASTING,  2000000, pg.parameter(Constants.FALSE_EASTING)     .doubleValue(), STRICT);
+        assertEquals(Constants.FALSE_NORTHING, 2000000, pg.parameter(Constants.FALSE_NORTHING)    .doubleValue(), STRICT);
+        assertSame("Expected a cached instance.", crs, CommonCRS.WGS72.universal(-85, -122));
+        assertNotSame("Expected a new instance.", crs, CommonCRS.WGS72.universal(+85, -122));
+    }
+
+    /**
+     * Tests {@link CommonCRS#forDatum(CoordinateReferenceSystem)}.
+     *
+     * @sinc 0.8
+     */
+    @Test
+    @DependsOnMethod("testGeographic")
+    public void testForDatum() {
+        assertSame("WGS84", CommonCRS.WGS84, CommonCRS.forDatum(CommonCRS.WGS84.geographic()));
+        assertSame("WGS72", CommonCRS.WGS72, CommonCRS.forDatum(CommonCRS.WGS72.geographic()));
     }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/EPSGFactoryFallbackTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/EPSGFactoryFallbackTest.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/EPSGFactoryFallbackTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/EPSGFactoryFallbackTest.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -19,11 +19,13 @@ package org.apache.sis.referencing;
 import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Set;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.crs.VerticalCRS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.GeocentricCRS;
+import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.datum.Datum;
 import org.opengis.referencing.datum.Ellipsoid;
@@ -74,6 +76,12 @@ public final strictfp class EPSGFactoryF
                 EPSGFactoryFallback.INSTANCE.getAuthorityCodes(GeographicCRS.class));
         assertSetEquals(Arrays.asList("5714", "5715", "5703"),
                 EPSGFactoryFallback.INSTANCE.getAuthorityCodes(VerticalCRS.class));
+        /*
+         * There is two many ProjectedCRS codes for enumerating all of them, so test only a sampling.
+         */
+        final Set<String> codes = EPSGFactoryFallback.INSTANCE.getAuthorityCodes(ProjectedCRS.class);
+        assertTrue(codes.containsAll(Arrays.asList("5041", "5042", "32601", "32660", "32701", "32760")));
+        assertTrue(Collections.disjoint(codes, Arrays.asList("7030", "6326", "4326", "4978", "32600", "32700", "5714")));
     }
 
     /**
@@ -141,6 +149,8 @@ public final strictfp class EPSGFactoryF
         verifyCreateCRS(CommonCRS.WGS84 .geographic3D(),          "4979");
         verifyCreateCRS(CommonCRS.WGS72 .geographic3D(),          "4985");
         verifyCreateCRS(CommonCRS.ETRS89.geographic3D(),          "4937");
+        verifyCreateCRS(CommonCRS.WGS84 .universal(-88, 120),     "5042");
+        verifyCreateCRS(CommonCRS.WGS84 .universal( 40, 14),     "32633");
         verifyCreateCRS(CommonCRS.Vertical.MEAN_SEA_LEVEL.crs(),  "5714");
         verifyCreateCRS(CommonCRS.Vertical.DEPTH.crs(),           "5715");
     }

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/StandardDefinitionsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/StandardDefinitionsTest.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/StandardDefinitionsTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/StandardDefinitionsTest.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -71,18 +71,42 @@ public final strictfp class StandardDefi
     }
 
     /**
-     * Tests {@link StandardDefinitions#createUTM(int, GeographicCRS, double, boolean, CartesianCS)}.
+     * Tests {@link StandardDefinitions#createUniversal(int, GeographicCRS, boolean, double, double, CartesianCS)}
+     * for a Universal Transverse Mercator (UTM) projection.
      *
      * @since 0.7
      */
     @Test
     @DependsOnMethod("testCreateGeographicCRS")
     public void testCreateUTM() {
-        final ProjectedCRS crs = StandardDefinitions.createUTM(32610, HardCodedCRS.WGS84, 15, -122, HardCodedCS.PROJECTED);
+        final ProjectedCRS crs = StandardDefinitions.createUniversal(32610, HardCodedCRS.WGS84, true, 15, -122, HardCodedCS.PROJECTED);
         assertEquals("name", "WGS 84 / UTM zone 10N", crs.getName().getCode());
         final ParameterValueGroup pg = crs.getConversionFromBase().getParameterValues();
-        assertEquals(Constants.LATITUDE_OF_ORIGIN, -123, pg.parameter(Constants.CENTRAL_MERIDIAN).doubleValue(), STRICT);
-        assertEquals(Constants.FALSE_NORTHING,        0, pg.parameter(Constants.FALSE_NORTHING).doubleValue(),   STRICT);
+        assertEquals(Constants.LATITUDE_OF_ORIGIN,    0, pg.parameter(Constants.LATITUDE_OF_ORIGIN).doubleValue(), STRICT);
+        assertEquals(Constants.CENTRAL_MERIDIAN,   -123, pg.parameter(Constants.CENTRAL_MERIDIAN)  .doubleValue(), STRICT);
+        assertEquals(Constants.SCALE_FACTOR,     0.9996, pg.parameter(Constants.SCALE_FACTOR)      .doubleValue(), STRICT);
+        assertEquals(Constants.FALSE_EASTING,    500000, pg.parameter(Constants.FALSE_EASTING)     .doubleValue(), STRICT);
+        assertEquals(Constants.FALSE_NORTHING,        0, pg.parameter(Constants.FALSE_NORTHING)    .doubleValue(), STRICT);
+    }
+
+    /**
+     * Tests {@link StandardDefinitions#createUniversal(int, GeographicCRS, boolean, double, double, CartesianCS)}
+     * for a Universal Polar Stereographic (UPS) projection. This test cheats a little bit on the coordinate system
+     * by laziness; we are more interested in the projection parameters.
+     *
+     * @since 0.8
+     */
+    @Test
+    @DependsOnMethod("testCreateGeographicCRS")
+    public void testCreateUPS() {
+        final ProjectedCRS crs = StandardDefinitions.createUniversal(5041, HardCodedCRS.WGS84, false, 90, -122, HardCodedCS.PROJECTED);
+        assertEquals("name", "WGS 84 / Universal Polar Stereographic North", crs.getName().getCode());
+        final ParameterValueGroup pg = crs.getConversionFromBase().getParameterValues();
+        assertEquals(Constants.LATITUDE_OF_ORIGIN,  90, pg.parameter(Constants.LATITUDE_OF_ORIGIN).doubleValue(), STRICT);
+        assertEquals(Constants.CENTRAL_MERIDIAN,     0, pg.parameter(Constants.CENTRAL_MERIDIAN)  .doubleValue(), STRICT);
+        assertEquals(Constants.SCALE_FACTOR,     0.994, pg.parameter(Constants.SCALE_FACTOR)      .doubleValue(), STRICT);
+        assertEquals(Constants.FALSE_EASTING,  2000000, pg.parameter(Constants.FALSE_EASTING)     .doubleValue(), STRICT);
+        assertEquals(Constants.FALSE_NORTHING, 2000000, pg.parameter(Constants.FALSE_NORTHING)    .doubleValue(), STRICT);
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -209,7 +209,7 @@ public final strictfp class EPSGInstalle
              * This implies the creation of a coordinate operation.
              */
             final ProjectedCRS p = factory.createProjectedCRS("EPSG:32215");
-            assertTrue(Utilities.deepEquals(CommonCRS.WGS72.UTM(1, -93), p, ComparisonMode.DEBUG));
+            assertTrue(Utilities.deepEquals(CommonCRS.WGS72.universal(1, -93), p, ComparisonMode.DEBUG));
             /*
              * Get the authority codes. We choose a type that implies an SQL statement
              * with both "DEPRECATED" and "SHOW_CRS" conditions in their "WHERE" clause.

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -175,7 +175,7 @@ public final strictfp class CoordinateOp
         testIdentityTransform(CommonCRS.WGS84.geographic3D());
         testIdentityTransform(CommonCRS.WGS84.geocentric());
         testIdentityTransform(CommonCRS.WGS84.spherical());
-        testIdentityTransform(CommonCRS.WGS84.UTM(0, 0));
+        testIdentityTransform(CommonCRS.WGS84.universal(0, 0));
         testIdentityTransform(CommonCRS.Vertical.DEPTH.crs());
         testIdentityTransform(CommonCRS.Temporal.JULIAN.crs());
     }

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateDomain.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateDomain.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateDomain.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateDomain.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -37,6 +37,12 @@ import org.apache.sis.referencing.datum.
  * @module
  */
 public strictfp class CoordinateDomain {
+    /*
+     * Note: this class is not declared as an enum yet because moving ARTICLE_CIRCLE and HEIGHT constants
+     *       after the enum declarations causes an "illegal forward reference" compiler error with JDK 8.
+     */
+
+
     /**
      * Latitude of the Article circle, which is 66°33′45.7″ as of March 30, 2015
      * Note that this value fluctuate by 2° over 40,000-year periods.

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -82,6 +82,17 @@ public final class JDK8 {
         return buffer.toString();
     }
 
+    /**
+     * Returns {@code true} if the given value is neither NaN or infinite.
+     *
+     * @param  value  the value to test.
+     * @return whether the given value is finite.
+     *
+     * @since 0.8
+     */
+    public static boolean isFinite(final double value) {
+        return !Double.isNaN(value) && !Double.isInfinite(value);
+    }
 
     /**
      * Compares two numbers as unsigned long.

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -55,6 +55,11 @@ public final class Modules {
     /**
      * The {@value} module name.
      */
+    public static final String REFERENCING_BY_IDENTIFIERS = "org.apache.sis.referencing.gazetteer";
+
+    /**
+     * The {@value} module name.
+     */
     public static final String STORAGE = "org.apache.sis.storage";
 
     /**

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/OptionalDependency.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/OptionalDependency.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/OptionalDependency.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/OptionalDependency.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -46,6 +46,7 @@ public abstract class OptionalDependency
      * @param dependency  the Maven artifact name (<strong>not</strong> a name from the {@link Modules} class)
      *        of the optional module on which the {@code module} depend.
      */
+    @SuppressWarnings("ThisEscapedInObjectConstruction")
     protected OptionalDependency(final String module, final String dependency) {
         super(module);
         this.dependency = dependency;

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -91,7 +91,7 @@ public class Cloner {
         try {
             if (valueType != type) {
                 method = valueType.getMethod("clone", (Class<?>[]) null);
-                type = valueType; // Set only if the above line succeed.
+                type = valueType;                                           // Set only if the above line succeed.
                 /*
                  * If the class implementing the 'clone()' method is not public, we may not be able to access that
                  * method even if it is public. Try to make the method accessible. If we fail for security reason,

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -16,7 +16,9 @@
  */
 package org.apache.sis.internal.util;
 
+import java.text.Format;
 import java.util.Locale;
+import java.util.TimeZone;
 import org.opengis.metadata.citation.Citation;
 import org.apache.sis.internal.simple.CitationConstant;
 import org.apache.sis.internal.system.Modules;
@@ -30,7 +32,7 @@ import org.apache.sis.internal.system.Op
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.6
- * @version 0.7
+ * @version 0.8
  * @module
  */
 public class MetadataServices extends OptionalDependency {
@@ -132,4 +134,17 @@ public class MetadataServices extends Op
     public String getInformation(String key, Locale locale) {
         return null;
     }
+
+    /**
+     * Creates a format for {@link org.opengis.geometry.DirectPosition} instances.
+     *
+     * @param  locale    the locale for the new {@code Format}, or {@code null} for {@code Locale.ROOT}.
+     * @param  timezone  the timezone, or {@code null} for UTC.
+     * @return a {@link org.apache.sis.geometry.CoordinateFormat}.
+     *
+     * @since 0.8
+     */
+    public Format createCoordinateFormat(final Locale locale, final TimeZone timezone) {
+        throw moduleNotFound();
+    }
 }

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -32,6 +32,7 @@ import java.text.SimpleDateFormat;
 import javax.measure.Unit;
 
 import org.opengis.referencing.IdentifiedObject;
+import org.opengis.geometry.DirectPosition;
 import org.apache.sis.measure.Angle;
 import org.apache.sis.measure.AngleFormat;
 import org.apache.sis.measure.Range;
@@ -40,6 +41,7 @@ import org.apache.sis.measure.UnitFormat
 import org.apache.sis.util.Localized;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.internal.util.MetadataServices;
 import org.apache.sis.internal.util.LocalizedParseException;
 
 import static org.apache.sis.internal.util.StandardDateFormat.UTC;
@@ -54,22 +56,38 @@ import org.apache.sis.internal.jdk8.Unch
  * large blocks of data, for example a metadata tree or a <cite>Well Known Text</cite> (WKT).
  * Those blocks of data usually contain smaller elements like numbers and dates, whose parsing
  * and formatting can be delegated to {@link NumberFormat} and {@link DateFormat} respectively.
+ * Subclasses can obtain instances of those formats by call to {@link #getFormat(Class)} where
+ * the argument is the type of the value to parse or format.
+ * {@code CompoundFormat} supports at least the following value types, but subclasses may add more types:
  *
- * <p>Since {@code CompoundFormat} may work on larger texts than the usual {@code Format} classes,
+ * <table class="sis">
+ *   <caption>Supported value types</caption>
+ *   <tr><th>Value type</th>              <th>Format type</th>                                      <th>Remarks</th></tr>
+ *   <tr><td>{@link DirectPosition}</td>  <td>{@link org.apache.sis.geometry.CoordinateFormat}</td> <td>Requires {@code sis-referencing} module.</td></tr>
+ *   <tr><td>{@link Angle}</td>           <td>{@link AngleFormat}</td>                              <td></td></tr>
+ *   <tr><td>{@link Date}</td>            <td>{@link DateFormat}</td>                               <td>Timezone specified by {@link #getTimeZone()}.</td></tr>
+ *   <tr><td>{@link Number}</td>          <td>{@link NumberFormat}</td>                             <td></td></tr>
+ *   <tr><td>{@link Unit}</td>            <td>{@link UnitFormat}</td>                               <td></td></tr>
+ *   <tr><td>{@link Range}</td>           <td>{@link RangeFormat}</td>                              <td></td></tr>
+ *   <tr><td>{@link Class}</td>           <td>(internal)</td>                                       <td></td></tr>
+ * </table>
+ *
+ * <div class="section">Sources and destinations</div>
+ * Since {@code CompoundFormat} may work on larger texts than the usual {@code Format} classes,
  * it defines {@code parse} and {@code format} methods working with arbitrary {@link CharSequence}
  * and {@link Appendable} instances. The standard {@code Format} methods redirect to the above-cited
- * methods.</p>
- *
- * <p>The abstract methods to be defined by subclasses are:</p>
+ * methods.
  *
+ * <div class="section">Sub-classing</div>
+ * The abstract methods to be defined by subclasses are:
  * <ul>
- *   <li>{@link #getValueType()} returns the {@code <T>} class or a subclass.</li>
- *   <li>{@link #parse(CharSequence, ParsePosition)} may throws {@code ParseException}.</li>
- *   <li>{@link #format(Object, Appendable)} may throws {@code IOException}.</li>
+ *   <li>{@link #getValueType()}</li>
+ *   <li>{@link #format(Object, Appendable)}</li>
+ *   <li>{@link #parse(CharSequence, ParsePosition)}</li>
  * </ul>
  *
  * <div class="note"><b>API note:</b>
- * In the standard {@link Format} class, the {@code parse} methods either accept a {@link ParsePosition} argument
+ * in the standard {@link Format} class, the {@code parse} methods either accept a {@link ParsePosition} argument
  * and returns {@code null} on error, or does not take position argument and throws a {@link ParseException} on error.
  * In this {@code CompoundFormat} class, the {@code parse} method both takes a {@code ParsePosition} argument and
  * throws a {@code ParseException} on error. This allows both substring parsing and more accurate exception message
@@ -213,13 +231,14 @@ public abstract class CompoundFormat<T>
      *     <ul>
      *       <li>this method returns {@code null}, or</li>
      *       <li>a {@code ParseException} is thrown with an {@linkplain ParseException#getErrorOffset() error offset}
-     *           set to the index of the first unparsable character. This is usually the same information than the
-     *           above-cited {@code pos} error index, but implementations are free to adopt a slightly different policy.</li>
+     *           set to the index of the first unparsable character.</li>
      *     </ul>
      *   </li>
      * </ul>
      *
-     * <div class="note"><b>Example:</b>
+     * <div class="note"><b>Note:</b>
+     * if a {@code ParseException} is thrown, its error offset is usually the same than the {@code ParsePosition}
+     * error index, but implementations are free to adopt a slightly different policy. For example
      * if parsing of the {@code "30.0 40,0"} coordinate fails on the coma in the last number, then the {@code pos}
      * {@linkplain ParsePosition#getErrorIndex() error index} may be set to 5 (the beginning of the {@code "40.0"}
      * character sequence) or to 7 (the coma position), depending on the implementation.</div>
@@ -230,6 +249,7 @@ public abstract class CompoundFormat<T>
      *
      * @param  text  the character sequence for the object to parse.
      * @param  pos   the position where to start the parsing.
+     *               On return, the position where the parsing stopped or where an error occurred.
      * @return the parsed object, or {@code null} if the text is not recognized.
      * @throws ParseException if an error occurred while parsing the object.
      */
@@ -399,18 +419,18 @@ public abstract class CompoundFormat<T>
      * Creates a new format to use for parsing and formatting values of the given type.
      * This method is invoked by {@link #getFormat(Class)} the first time that a format
      * is needed for the given type.
-     *
-     * <p>The default implementation creates the following formats:</p>
+     * The class given in argument can be any of the classes listed in the "Value type" column below:
      *
      * <table class="sis">
-     *   <caption>Supported formats by type</caption>
-     *   <tr><th>Value type</th>     <th>Format</th></tr>
-     *   <tr><td>{@link Angle}</td>  <td>{@link AngleFormat}</td></tr>
-     *   <tr><td>{@link Date}</td>   <td>{@link DateFormat}</td></tr>
-     *   <tr><td>{@link Number}</td> <td>{@link NumberFormat}</td></tr>
-     *   <tr><td>{@link Unit}</td>   <td>{@link UnitFormat}</td></tr>
-     *   <tr><td>{@link Range}</td>  <td>{@link RangeFormat}</td></tr>
-     *   <tr><td>{@link Class}</td>  <td>(internal)</td></tr>
+     *   <caption>Supported value types</caption>
+     *   <tr><th>Value type</th>              <th>Format type</th></tr>
+     *   <tr><td>{@link DirectPosition}</td>  <td>{@link org.apache.sis.geometry.CoordinateFormat}</td></tr>
+     *   <tr><td>{@link Angle}</td>           <td>{@link AngleFormat}</td></tr>
+     *   <tr><td>{@link Date}</td>            <td>{@link DateFormat}</td></tr>
+     *   <tr><td>{@link Number}</td>          <td>{@link NumberFormat}</td></tr>
+     *   <tr><td>{@link Unit}</td>            <td>{@link UnitFormat}</td></tr>
+     *   <tr><td>{@link Range}</td>           <td>{@link RangeFormat}</td></tr>
+     *   <tr><td>{@link Class}</td>           <td>(internal)</td></tr>
      * </table>
      *
      * Subclasses can override this method for adding more types, or for configuring the
@@ -453,6 +473,8 @@ public abstract class CompoundFormat<T>
             return new UnitFormat(locale);
         } else if (valueType == Range.class) {
             return new RangeFormat(locale);
+        } else if (valueType == DirectPosition.class) {
+            return MetadataServices.getInstance().createCoordinateFormat(locale, getTimeZone());
         } else if (valueType == Class.class) {
             return ClassFormat.INSTANCE;
         } else {

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java [UTF-8] Tue Mar  7 17:44:59 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/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -41,16 +41,32 @@ import static java.lang.Math.*;
 /**
  * Formats a {@link Statistics} object.
  * By default, newly created {@code StatisticsFormat} instances will format statistical values
- * in a tabular format using spaces as the column separator. This default configuration matches
- * the {@link Statistics#toString()} format.
+ * in a tabular format using spaces as the column separator.
  *
- * <div class="section">Limitations</div>
- * The current implementation can only format statistics - parsing is not yet implemented.
+ * <div class="note"><b>Example:</b>
+ * {@preformat text
+ *     Number of values:     8726
+ *     Minimum value:       6.853
+ *     Maximum value:       8.259
+ *     Mean value:          7.421
+ *     Root Mean Square:    7.846
+ *     Standard deviation:  6.489
+ * }
+ * </div>
+ *
+ * Like most {@link java.text.Format} implementations, this class is not thread-safe.
+ * Each thread should use its own {@code StatisticsFormat} instance or apply synchronization.
+ *
+ * <div class="warning"><b>Limitation:</b>
+ * the current implementation can only format statistics — parsing is not yet implemented.
+ * </div>
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3
  * @version 0.8
  * @module
+ *
+ * @see Statistics#toString()
  */
 public class StatisticsFormat extends TabularFormat<Statistics> {
     /**

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -27,6 +27,8 @@ import javax.measure.Quantity;
 import org.apache.sis.math.Fraction;
 import org.apache.sis.util.Characters;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.LenientComparable;
 import org.apache.sis.util.resources.Errors;
 
 
@@ -57,7 +59,7 @@ import org.apache.sis.util.resources.Err
  * @version 0.8
  * @module
  */
-abstract class AbstractUnit<Q extends Quantity<Q>> implements Unit<Q>, Serializable {
+abstract class AbstractUnit<Q extends Quantity<Q>> implements Unit<Q>, LenientComparable, Serializable {
     /**
      * For cross-version compatibility.
      */
@@ -270,16 +272,28 @@ abstract class AbstractUnit<Q extends Qu
     /**
      * Compares this unit with the given object for equality.
      *
-     * @param  other  the other object to compares with this unit, or {@code null}.
-     * @return {@code true} if the given object is equals to this unit.
+     * @param  other  the other object to compare with this unit, or {@code null}.
+     * @return {@code true} if the given object is equal to this unit.
      */
     @Override
-    public boolean equals(final Object other) {
-        if (other != null && other.getClass() == getClass()) {
-            final AbstractUnit<?> that = (AbstractUnit<?>) other;
-            return epsg == that.epsg && scope == that.scope && Objects.equals(symbol, that.symbol);
+    public final boolean equals(final Object other) {
+        return equals(other, ComparisonMode.STRICT);
+    }
+
+    /**
+     * Compares this unit with the given object for equality,
+     * optionally ignoring metadata and rounding errors.
+     */
+    @Override
+    public boolean equals(final Object other, final ComparisonMode mode) {
+        if (other == null || other.getClass() != getClass()) {
+            return false;
+        }
+        if (mode.isIgnoringMetadata()) {
+            return true;
         }
-        return false;
+        final AbstractUnit<?> that = (AbstractUnit<?>) other;
+        return epsg == that.epsg && scope == that.scope && Objects.equals(symbol, that.symbol);
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java [UTF-8] Tue Mar  7 17:44:59 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,18 +33,17 @@ 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;
 import static org.apache.sis.math.DecimalFunctions.fractionDigitsForDelta;
 
+// Branch-dependent imports
 import org.apache.sis.internal.jdk8.JDK8;
 
+
 /**
  * Parses and formats angles according a specified pattern. The pattern is a string
  * containing any characters, with a special meaning for the following characters:
@@ -121,7 +121,7 @@ import org.apache.sis.internal.jdk8.JDK8
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3
- * @version 0.4
+ * @version 0.8
  * @module
  *
  * @see Angle
@@ -300,6 +300,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.
      *
@@ -723,6 +730,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.
@@ -901,7 +970,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 (!JDK8.isFinite(angle)) {
             toAppendTo = numberFormat().format(angle, toAppendTo, dummyFieldPosition());
             if (fieldPos >= DEGREES_FIELD && fieldPos <= SECONDS_FIELD) {
                 pos.setBeginIndex(offset);
@@ -917,8 +986,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
@@ -930,13 +1002,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;
@@ -1117,7 +1189,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;
         }
@@ -1760,8 +1832,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;
     }
 
     /**
@@ -1784,6 +1856,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/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/ConcatenatedConverter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/ConcatenatedConverter.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/ConcatenatedConverter.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/ConcatenatedConverter.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -20,6 +20,9 @@ import java.util.List;
 import java.util.ArrayList;
 import javax.measure.UnitConverter;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.LenientComparable;
+import org.apache.sis.util.Utilities;
 
 
 /**
@@ -30,7 +33,7 @@ import org.apache.sis.util.ArgumentCheck
  * @version 0.8
  * @module
  */
-final class ConcatenatedConverter extends AbstractConverter {
+final class ConcatenatedConverter extends AbstractConverter implements LenientComparable {
     /**
      * For cross-version compatibility.
      */
@@ -161,4 +164,17 @@ final class ConcatenatedConverter extend
         }
         return false;
     }
+
+    /**
+     * Compares this converter with the given object for equality, optionally ignoring rounding errors.
+     */
+    @Override
+    public boolean equals(final Object other, final ComparisonMode mode) {
+        if (other instanceof ConcatenatedConverter) {
+            final ConcatenatedConverter o = (ConcatenatedConverter) other;
+            return Utilities.deepEquals(c1, o.c1, mode) &&
+                   Utilities.deepEquals(c2, o.c2, mode);
+        }
+        return false;
+    }
 }



Mime
View raw message