sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1724531 [2/13] - in /sis/trunk: ./ application/sis-console/src/main/artifact/bin/ application/sis-console/src/main/artifact/log/ application/sis-console/src/main/java/org/apache/sis/console/ core/sis-build-helper/src/main/java/org/apache/s...
Date Wed, 13 Jan 2016 23:59:41 GMT
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -16,24 +16,26 @@
  */
 package org.apache.sis.internal.referencing.provider;
 
-import javax.xml.bind.annotation.XmlTransient;
+import javax.measure.unit.SI;
 import javax.measure.unit.NonSI;
+import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.ParameterNotFoundException;
-import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.operation.Transformation;
-import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.matrix.Matrix4;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
 
 
 /**
- * The provider for <cite>"Geographic2D offsets"</cite> (EPSG:9619).
- * The default implementation handles the 2D case.
- * The {@link GeographicOffsets3D} subclass adds a third dimension.
+ * Base class for all transformations that perform a translation in the geographic domain.
+ * This base class defines a provider for <cite>"Geographic3D offsets"</cite> (EPSG:9660),
+ * but subclasses will provide different operations.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.7
@@ -58,28 +60,34 @@ public class GeographicOffsets extends A
     static final ParameterDescriptor<Double> TY;
 
     /**
+     * The operation parameter descriptor for the <cite>"Vertical Offset"</cite> parameter value.
+     */
+    static final ParameterDescriptor<Double> TZ;
+
+    /**
      * The group of all parameters expected by this coordinate operation.
      */
     private static final ParameterDescriptorGroup PARAMETERS;
     static {
         final ParameterBuilder builder = builder();
-        TY = builder.addIdentifier("8601").addName("Latitude offset").create(0, NonSI.DEGREE_ANGLE);
+        TY = builder.addIdentifier("8601").addName("Latitude offset") .create(0, NonSI.DEGREE_ANGLE);
         TX = builder.addIdentifier("8602").addName("Longitude offset").create(0, NonSI.DEGREE_ANGLE);
-        PARAMETERS = builder.addIdentifier("9619").addName("Geographic2D offsets").createGroup(TY, TX);
+        TZ = builder.addIdentifier("8603").addName("Vertical Offset") .create(0, SI.METRE);
+        PARAMETERS = builder.addIdentifier("9660").addName("Geographic3D offsets").createGroup(TY, TX, TZ);
     }
 
     /**
      * Constructs a provider with default parameters.
      */
     public GeographicOffsets() {
-        super(2, 2, PARAMETERS);
+        super(3, 3, PARAMETERS);
     }
 
     /**
-     * For subclass constructor only.
+     * For subclasses constructor only.
      */
-    GeographicOffsets(ParameterDescriptorGroup parameters) {
-        super(3, 3, parameters);
+    GeographicOffsets(int dim, ParameterDescriptorGroup parameters) {
+        super(dim, dim, parameters);
     }
 
     /**
@@ -94,7 +102,7 @@ public class GeographicOffsets extends A
 
     /**
      * Creates a transform from the specified group of parameter values.
-     * The parameter values are unconditionally converted to degrees.
+     * The parameter values are unconditionally converted to degrees and metres.
      *
      * @param  factory Ignored (can be null).
      * @param  values The group of parameter values.
@@ -106,6 +114,10 @@ public class GeographicOffsets extends A
             throws ParameterNotFoundException
     {
         final Parameters pv = Parameters.castOrWrap(values);
-        return new AffineTransform2D(1, 0, 0, 1, pv.doubleValue(TX), pv.doubleValue(TY));
+        final Matrix4 t = new Matrix4();
+        t.m03 = pv.doubleValue(TX);
+        t.m13 = pv.doubleValue(TY);
+        t.m23 = pv.doubleValue(TZ);
+        return MathTransforms.linear(t);
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -84,6 +84,17 @@ public final class GeographicToGeocentri
     }
 
     /**
+     * Notifies {@code DefaultMathTransformFactory} that Geographic/geocentric conversions
+     * require values for the {@code "semi_major"} and {@code "semi_minor"} parameters.
+     *
+     * @return 1, meaning that the operation requires a source ellipsoid.
+     */
+    @Override
+    public int getEllipsoidsMask() {
+        return 1;
+    }
+
+    /**
      * Creates a transform from the specified group of parameter values.
      *
      * @param  factory The factory to use for creating the transform.

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LongitudeRotation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LongitudeRotation.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LongitudeRotation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LongitudeRotation.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -20,7 +20,6 @@ import javax.xml.bind.annotation.XmlTran
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.ParameterNotFoundException;
-import org.opengis.referencing.operation.Transformation;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
@@ -45,11 +44,11 @@ import org.apache.sis.parameter.Paramete
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  */
 @XmlTransient
-public final class LongitudeRotation extends AbstractProvider {
+public final class LongitudeRotation extends GeographicOffsets {
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -60,24 +59,14 @@ public final class LongitudeRotation ext
      */
     private static final ParameterDescriptorGroup PARAMETERS;
     static {
-        PARAMETERS = builder().addIdentifier("9601").addName("Longitude rotation").createGroup(GeographicOffsets.TX);
+        PARAMETERS = builder().addIdentifier("9601").addName("Longitude rotation").createGroup(TX);
     }
 
     /**
      * Constructs a provider with default parameters.
      */
     public LongitudeRotation() {
-        super(2, 2, PARAMETERS);
-    }
-
-    /**
-     * Returns the operation type.
-     *
-     * @return Interface implemented by all coordinate operations that use this method.
-     */
-    @Override
-    public Class<Transformation> getOperationType() {
-        return Transformation.class;
+        super(2, PARAMETERS);
     }
 
     /**
@@ -100,7 +89,7 @@ public final class LongitudeRotation ext
     public MathTransform createMathTransform(final MathTransformFactory factory, final ParameterValueGroup values)
             throws ParameterNotFoundException
     {
-        final double offset = Parameters.castOrWrap(values).doubleValue(GeographicOffsets.TX);
-        return new AffineTransform2D(1, 0, 0, 1, offset, 0);
+        final Parameters pv = Parameters.castOrWrap(values);
+        return new AffineTransform2D(1, 0, 0, 1, pv.doubleValue(TX), 0);
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -204,6 +204,17 @@ public abstract class MapProjection exte
      */
     protected abstract NormalizedProjection createProjection(final Parameters parameters) throws ParameterNotFoundException;
 
+    /**
+     * Notifies {@code DefaultMathTransformFactory} that map projections require
+     * values for the {@code "semi_major"} and {@code "semi_minor"} parameters.
+     *
+     * @return 1, meaning that the operation requires a source ellipsoid.
+     */
+    @Override
+    public final int getEllipsoidsMask() {
+        return 1;
+    }
+
 
 
 

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -96,27 +96,24 @@ public final class Molodensky extends Ge
         final ParameterBuilder builder = builder();
         AXIS_LENGTH_DIFFERENCE = builder.addName("Semi-major axis length difference").create(Double.NaN, SI.METRE);
         FLATTENING_DIFFERENCE  = builder.addName("Flattening difference").createBounded(-1, +1, Double.NaN, Unit.ONE);
-        builder.setRequired(false);
-        ParameterDescriptor<Double> a = builder.addName(TGT_SEMI_MAJOR.getName()).createStrictlyPositive(Double.NaN, SI.METRE);
-        ParameterDescriptor<Double> b = builder.addName(TGT_SEMI_MINOR.getName()).createStrictlyPositive(Double.NaN, SI.METRE);
         PARAMETERS = builder.setRequired(true)
                 .addIdentifier("9604")
                 .addName("Molodensky")
                 .addName(Citations.OGC, "Molodenski")
                 .createGroup(DIMENSION,                         // OGC only
+                             SRC_SEMI_MAJOR, SRC_SEMI_MINOR,    // OGC only
+                             TGT_SEMI_MAJOR, TGT_SEMI_MINOR,    // OGC only - redundant with differences
                              TX,                                // OGC and EPSG
                              TY,                                // OGC and EPSG
                              TZ,                                // OGC and EPSG
                              AXIS_LENGTH_DIFFERENCE,            // EPSG only
-                             FLATTENING_DIFFERENCE,             // EPSG only
-                             SRC_SEMI_MAJOR, SRC_SEMI_MINOR,    // OGC only
-                             a, b);                             // OGC only
+                             FLATTENING_DIFFERENCE);            // EPSG only
     }
 
     /**
      * Creates a descriptor for the internal parameters of {@link MolodenskyTransform}.
-     * This is identical to the standard parameters except that the last 3 OGC parameters
-     * are replaced by the eccentricity.
+     * This is similar to the standard parameters except that the redundant target axes
+     * lengths are omitted.
      *
      * @return Internal parameter descriptor.
      */
@@ -126,19 +123,19 @@ public final class Molodensky extends Ge
         ParameterDescriptor<Boolean> abridged = builder.addName("abridged").create(Boolean.class, null);
         return builder.addName("Molodensky")
                 .createGroup(DIMENSION,
+                             SRC_SEMI_MAJOR,
+                             SRC_SEMI_MINOR,
+                             AXIS_LENGTH_DIFFERENCE,
+                             FLATTENING_DIFFERENCE,
                              TX,
                              TY,
                              TZ,
-                             AXIS_LENGTH_DIFFERENCE,
-                             FLATTENING_DIFFERENCE,
-                             SRC_SEMI_MAJOR,
-                             MapProjection.ECCENTRICITY,
                              abridged);
     }
 
     /**
      * The providers for all combinations between 2D and 3D cases.
-     * Array length is 4. Index is build with following rule:
+     * Array length is 4. Index is built with following rule:
      * <ul>
      *   <li>Bit 1: dimension of source coordinates (0 for 2D, 1 for 3D).</li>
      *   <li>Bit 0: dimension of target coordinates (0 for 2D, 1 for 3D).</li>

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -16,13 +16,20 @@
  */
 package org.apache.sis.internal.referencing.provider;
 
+import javax.measure.unit.Unit;
+import javax.measure.unit.SI;
+import javax.measure.unit.NonSI;
 import javax.xml.bind.annotation.XmlTransient;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterValueGroup;
+import org.apache.sis.measure.Longitude;
+import org.apache.sis.parameter.Parameters;
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.metadata.iso.citation.Citations;
-import org.apache.sis.parameter.Parameters;
 import org.apache.sis.referencing.operation.projection.NormalizedProjection;
+import org.apache.sis.internal.util.Constants;
+import org.apache.sis.math.MathFunctions;
 
 
 /**
@@ -31,7 +38,7 @@ import org.apache.sis.referencing.operat
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @author  Rueben Schulz (UBC)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see <a href="http://www.remotesensing.org/geotiff/proj_list/transverse_mercator.html">Transverse Mercator on RemoteSensing.org</a>
@@ -44,6 +51,19 @@ public final class TransverseMercator ex
     private static final long serialVersionUID = -3386587506686432398L;
 
     /**
+     * Width of a Universal Transverse Mercator (UTM) zone, in degrees.
+     *
+     * @see #zone(double)
+     * @see #centralMeridian(int)
+     */
+    private static final double ZONE_WIDTH = 6;
+
+    /**
+     * The {@value} string, which is also the EPSG name for this projection.
+     */
+    public static final String NAME = "Transverse Mercator";
+
+    /**
      * The operation parameter descriptor for the <cite>Latitude of natural origin</cite> (φ₀) parameter value.
      * Valid values range is [-90 … 90]° and default value is 0°.
      */
@@ -64,7 +84,7 @@ public final class TransverseMercator ex
     /**
      * The group of all parameters expected by this coordinate operation.
      */
-    private static final ParameterDescriptorGroup PARAMETERS;
+    static final ParameterDescriptorGroup PARAMETERS;
     static {
         final ParameterBuilder builder = builder();
         LATITUDE_OF_ORIGIN = createLatitude(builder
@@ -80,7 +100,7 @@ public final class TransverseMercator ex
 
         PARAMETERS = builder
                 .addIdentifier(              "9807")
-                .addName(                    "Transverse Mercator")
+                .addName(                    NAME)
                 .addName(                    "Gauss-Kruger")
                 .addName(                    "Gauss-Boaga")
                 .addName(                    "TM")
@@ -119,4 +139,81 @@ public final class TransverseMercator ex
     protected NormalizedProjection createProjection(final Parameters parameters) {
         return new org.apache.sis.referencing.operation.projection.TransverseMercator(this, parameters);
     }
+
+    /**
+     * Sets the parameter values for a Transverse Mercator projection and returns a suggested conversion name.
+     *
+     * <blockquote><table class="sis">
+     *   <caption>Transverse Mercator parameters</caption>
+     *   <tr><th>Parameter name</th>                 <th>Value</th></tr>
+     *   <tr><td>Latitude of natural origin</td>     <td>Given latitude, or 0° if UTM projection</td></tr>
+     *   <tr><td>Longitude of natural origin</td>    <td>Given longitude, optionally snapped to a UTM central meridian</td></tr>
+     *   <tr><td>Scale factor at natural origin</td> <td>0.9996</td></tr>
+     *   <tr><td>False easting</td>                  <td>500000 metres</td></tr>
+     *   <tr><td>False northing</td>                 <td>0 (North hemisphere) or 10000000 (South hemisphere) metres</td></tr>
+     * </table></blockquote>
+     *
+     * @param  group      The parameters for which to set the values.
+     * @param  isUTM      {@code true} for Universal Transverse Mercator (UTM) projection.
+     * @param  latitude   The latitude in the center of the desired projection.
+     * @param  longitude  The longitude in the center of the desired projection.
+     * @return A name like <cite>"Transverse Mercator"</cite> or <cite>"UTM zone 10N"</cite>,
+     *         depending on the arguments given to this method.
+     *
+     * @since 0.7
+     */
+    public static String setParameters(final ParameterValueGroup group,
+            final boolean isUTM, double latitude, double longitude)
+    {
+        final boolean isSouth = MathFunctions.isNegative(latitude);
+        int zone = zone(longitude);
+        if (isUTM) {
+            latitude = 0;
+            longitude = centralMeridian(zone);
+        } else if (longitude != centralMeridian(zone)) {
+            zone = 0;
+        }
+        String name = NAME;
+        if (zone != 0) {
+            name = "UTM zone " + zone + (isSouth ? 'S' : 'N');
+        }
+        group.parameter(Constants.LATITUDE_OF_ORIGIN).setValue(latitude,  NonSI.DEGREE_ANGLE);
+        group.parameter(Constants.CENTRAL_MERIDIAN)  .setValue(longitude, NonSI.DEGREE_ANGLE);
+        group.parameter(Constants.SCALE_FACTOR)      .setValue(0.9996, Unit.ONE);
+        group.parameter(Constants.FALSE_EASTING)     .setValue(500000, SI.METRE);
+        group.parameter(Constants.FALSE_NORTHING)    .setValue(isSouth ? 10000000 : 0, SI.METRE);
+        return name;
+    }
+
+    /**
+     * Computes the UTM zone from a meridian in the zone.
+     *
+     * @param  longitude A meridian inside the desired zone, in degrees relative to Greenwich.
+     *         Positive longitudes are toward east, and negative longitudes toward west.
+     * @return The UTM zone number numbered from 1 to 60 inclusive, or 0 if the given central meridian was NaN.
+     *
+     * @since 0.7
+     */
+    public static int zone(double longitude) {
+        /*
+         * Casts to int are equivalent to Math.floor(double) for positive values, which is guaranteed
+         * to be the case here since we normalize the central meridian to the [MIN_VALUE … MAX_VALUE] range.
+         */
+        double z = (longitude - Longitude.MIN_VALUE) / ZONE_WIDTH;                          // Zone number with fractional part.
+        z -= Math.floor(z / ((Longitude.MAX_VALUE - Longitude.MIN_VALUE) / ZONE_WIDTH))     // Roll in the [0 … 60) range.
+                          * ((Longitude.MAX_VALUE - Longitude.MIN_VALUE) / ZONE_WIDTH);
+        return (int) (z + 1);   // Cast only after addition in order to handle NaN as documented.
+    }
+
+    /**
+     * Computes the central meridian of a given UTM zone.
+     *
+     * @param zone The UTM zone as a number in the [1 … 60] range.
+     * @return The central meridian of the given UTM zone.
+     *
+     * @since 0.7
+     */
+    public static double centralMeridian(final int zone) {
+        return (zone - 0.5) * ZONE_WIDTH + Longitude.MIN_VALUE;
+    }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -44,11 +44,14 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.util.PatchedUnitFormat;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.LenientComparable;
 import org.apache.sis.util.ObjectConverters;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.UnconvertibleObjectException;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
 import static org.apache.sis.util.Utilities.deepEquals;
@@ -504,6 +507,19 @@ public class DefaultParameterValue<T> ex
     }
 
     /**
+     * Same as {@link #isFile(Object)}, but accepts also a {@link String} if the type specified
+     * in the parameter descriptor is one of the types documented in {@link #valueFile()}.
+     */
+    private boolean isOrNeedFile(final Object value) {
+        if (value instanceof String) {
+            final Class<?> type = descriptor.getValueClass();
+            return (type == URI.class) || (type == URL.class)
+                   || File.class.isAssignableFrom(type);
+        }
+        return isFile(value);
+    }
+
+    /**
      * Returns the exception to throw when an incompatible method is invoked for the value type.
      */
     private IllegalStateException missingOrIncompatibleValue(final Object value) {
@@ -539,9 +555,25 @@ public class DefaultParameterValue<T> ex
      * @see #getValue()
      */
     @Override
-    public void setValue(final Object value) throws InvalidParameterValueException {
-        // Use 'unit' instead than 'getUnit()' despite class Javadoc claims because units are not expected
-        // to be involved in this method. We just want the current unit setting to be unchanged.
+    public void setValue(Object value) throws InvalidParameterValueException {
+        /*
+         * Try to convert the value only for a limited amount of types. In particular we want to allow conversions
+         * between java.io.File and java.nio.file.Path for easier transition between JDK6 and JDK7. We do not want
+         * to allow too many conversions for reducing the risk of unexpected behavior.  If we fail to convert, try
+         * to set the value anyway since the user may have redefined the setValue(Object, Unit) method.
+         */
+        if (isOrNeedFile(value)) try {
+            value = ObjectConverters.convert(value, descriptor.getValueClass());
+        } catch (UnconvertibleObjectException e) {
+            // Level.FINE (not WARNING) because this log duplicates the exception
+            // that 'setValue(Object, Unit)' may throw (with a better message).
+            Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION),
+                    DefaultParameterValue.class, "setValue", e);
+        }
+        /*
+         * Use 'unit' instead than 'getUnit()' despite class Javadoc claims because units are not expected
+         * to be involved in this method. We just want the current unit setting to be unchanged.
+         */
         setValue(value, unit);
     }
 
@@ -829,6 +861,8 @@ public class DefaultParameterValue<T> ex
      * @return An unmodifiable implementation of the given parameter, or {@code null} if the given parameter was null.
      *
      * @since 0.6
+     *
+     * @see DefaultParameterValueGroup#unmodifiable(ParameterValueGroup)
      */
     public static <T> DefaultParameterValue<T> unmodifiable(final ParameterValue<T> parameter) {
         return UnmodifiableParameterValue.create(parameter);
@@ -895,9 +929,9 @@ public class DefaultParameterValue<T> ex
         WKTUtilities.appendName(descriptor, formatter, ElementKind.PARAMETER);
         final Convention convention = formatter.getConvention();
         final boolean isWKT1 = convention.majorVersion() == 1;
-        Unit<?> unit = getUnit();   // Gives to users a chance to override this property.
+        Unit<?> unit = getUnit();                                   // Gives to users a chance to override this property.
         if (unit == null) {
-            final T value = getValue();   // Gives to users a chance to override this property.
+            final T value = getValue();                             // Gives to users a chance to override this property.
             if (!isWKT1 && isFile(value)) {
                 formatter.append(value.toString(), null);
                 return WKTKeywords.ParameterFile;

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -18,8 +18,10 @@ package org.apache.sis.parameter;
 
 import java.util.List;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.Map;
 import java.util.IdentityHashMap;
+import java.util.Iterator;
 import java.io.Serializable;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlElement;
@@ -38,9 +40,7 @@ import org.apache.sis.util.LenientCompar
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
-
-import static org.apache.sis.util.Utilities.deepEquals;
-import static org.apache.sis.referencing.IdentifiedObjects.isHeuristicMatchForName;
+import org.apache.sis.util.Utilities;
 
 // Branch-dependent imports
 import org.apache.sis.internal.jdk7.Objects;
@@ -103,7 +103,7 @@ import org.apache.sis.internal.jdk7.Obje
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see DefaultParameterDescriptorGroup
@@ -206,7 +206,7 @@ public class DefaultParameterValueGroup
     @Override
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     public List<GeneralParameterValue> values() {
-        return values;  // Intentionally modifiable.
+        return values;                                          // Intentionally modifiable.
     }
 
     /**
@@ -305,7 +305,7 @@ public class DefaultParameterValueGroup
         for (int i=0; i<size; i++) {
             final GeneralParameterDescriptor descriptor = values.descriptor(i);
             if (descriptor instanceof ParameterDescriptor<?>) {
-                if (isHeuristicMatchForName(descriptor, name)) {
+                if (IdentifiedObjects.isHeuristicMatchForName(descriptor, name)) {
                     if (fallback < 0) {
                         fallback = i;
                     } else {
@@ -334,7 +334,7 @@ public class DefaultParameterValueGroup
      *
      * @param  name The name of the parameter to search for.
      * @return The set of all parameter group for the given name.
-     * @throws ParameterNotFoundException If no descriptor was found for the given name.
+     * @throws ParameterNotFoundException if no descriptor was found for the given name.
      */
     @Override
     public List<ParameterValueGroup> groups(final String name) throws ParameterNotFoundException {
@@ -345,7 +345,7 @@ public class DefaultParameterValueGroup
         for (int i=0; i<size; i++) {
             final GeneralParameterDescriptor descriptor = values.descriptor(i);
             if (descriptor instanceof ParameterDescriptorGroup) {
-                if (isHeuristicMatchForName(descriptor, name)) {
+                if (IdentifiedObjects.isHeuristicMatchForName(descriptor, name)) {
                     groups.add((ParameterValueGroup) values.get(i));
                 }
             }
@@ -358,8 +358,8 @@ public class DefaultParameterValueGroup
         if (groups.isEmpty()) {
             final ParameterDescriptorGroup descriptor = values.descriptor;
             if (!(descriptor.descriptor(name) instanceof ParameterDescriptorGroup)) {
-                throw new ParameterNotFoundException(Errors.format(
-                        Errors.Keys.ParameterNotFound_2, descriptor.getName(), name), name);
+                throw new ParameterNotFoundException(Errors.format(Errors.Keys.ParameterNotFound_2,
+                        Verifier.getDisplayName(descriptor), name), name);
             }
         }
         return groups;
@@ -377,8 +377,8 @@ public class DefaultParameterValueGroup
      *
      * @param  name The name of the parameter group to create.
      * @return A newly created parameter group for the given name.
-     * @throws ParameterNotFoundException If no descriptor was found for the given name.
-     * @throws InvalidParameterCardinalityException If this parameter group already contains the
+     * @throws ParameterNotFoundException if no descriptor was found for the given name.
+     * @throws InvalidParameterCardinalityException if this parameter group already contains the
      *         {@linkplain ParameterDescriptorGroup#getMaximumOccurs() maximum number of occurrences}
      *         of subgroups of the given name.
      */
@@ -400,7 +400,14 @@ public class DefaultParameterValueGroup
 
     /**
      * Compares the specified object with this parameter for equality.
-     * The strictness level is controlled by the second argument.
+     * The strictness level is controlled by the second argument:
+     *
+     * <ul>
+     *   <li>{@link ComparisonMode#STRICT} and {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}
+     *       take in account the parameter order.</li>
+     *   <li>{@link ComparisonMode#IGNORE_METADATA} and {@link ComparisonMode#APPROXIMATIVE APPROXIMATIVE}
+     *       ignore the order of parameter values (but not necessarily the order of parameter descriptors).</li>
+     * </ul>
      *
      * @param  object The object to compare to {@code this}.
      * @param  mode The strictness level of the comparison.
@@ -420,15 +427,37 @@ public class DefaultParameterValueGroup
                            Objects.equals(values, that.values);
                 }
             } else if (object instanceof ParameterValueGroup) {
-                final ParameterValueGroup that = (ParameterValueGroup) object;
-                return deepEquals(getDescriptor(), that.getDescriptor(), mode) &&
-                       deepEquals(values(), that.values(), mode);
+                return equals(this, (ParameterValueGroup) object, mode);
             }
         }
         return false;
     }
 
     /**
+     * Compares the given objects for equality, ignoring parameter order in "ignore metadata" mode.
+     */
+    static boolean equals(final Parameters expected, final ParameterValueGroup actual, final ComparisonMode mode) {
+        if (!Utilities.deepEquals(expected.getDescriptor(), actual.getDescriptor(), mode)) {
+            return false;
+        }
+        if (!mode.isIgnoringMetadata()) {
+            return Utilities.deepEquals(expected.values(), actual.values(), mode);
+        }
+        final List<GeneralParameterValue> values = new LinkedList<GeneralParameterValue>(expected.values());
+scan:   for (final GeneralParameterValue param : actual.values()) {
+            final Iterator<GeneralParameterValue> it = values.iterator();
+            while (it.hasNext()) {
+                if (Utilities.deepEquals(it.next(), param, mode)) {
+                    it.remove();
+                    continue scan;
+                }
+            }
+            return false;   // A parameter from 'actual' has not been found in 'expected'.
+        }
+        return values.isEmpty();
+    }
+
+    /**
      * Compares the specified object with this parameter for equality.
      * This method is implemented as below:
      *
@@ -449,7 +478,7 @@ public class DefaultParameterValueGroup
     /**
      * Returns a hash value for this parameter.
      *
-     * @return The hash code value. This value doesn't need to be the same
+     * @return The hash code value. This value does not need to be the same
      *         in past or future versions of this class.
      */
     @Override

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -20,9 +20,6 @@ import javax.measure.unit.Unit;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.GeneralParameterDescriptor;
-import org.opengis.parameter.ParameterValueGroup;
-import org.opengis.referencing.cs.CoordinateSystem;             // For javadoc
-import org.opengis.referencing.crs.CoordinateReferenceSystem;   // For javadoc
 import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.measure.Range;
@@ -428,8 +425,6 @@ public class ParameterBuilder extends Bu
      *         for the group to create.
      * @return The parameter descriptor group for a map projection.
      *
-     * @see org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory#createBaseToDerived(CoordinateReferenceSystem, ParameterValueGroup, CoordinateSystem)
-     *
      * @since 0.6
      */
     public ParameterDescriptorGroup createGroupForMapProjection(final ParameterDescriptor<?>... parameters) {

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -124,6 +124,24 @@ public abstract class Parameters impleme
     }
 
     /**
+     * Returns the given parameter value group as an unmodifiable {@code Parameters} instance.
+     * If the given parameters is already an unmodifiable instance of {@code Parameters},
+     * then it is returned as-is. Otherwise this method copies all parameter values in a new,
+     * unmodifiable, parameter group instance.
+     *
+     * @param  parameters The parameters to make unmodifiable, or {@code null}.
+     * @return An unmodifiable group with the same parameters than the given group,
+     *         or {@code null} if the given argument was null.
+     *
+     * @since 0.7
+     *
+     * @see DefaultParameterValue#unmodifiable(ParameterValue)
+     */
+    public static Parameters unmodifiable(final ParameterValueGroup parameters) {
+        return UnmodifiableParameterValueGroup.create(parameters);
+    }
+
+    /**
      * Returns the given parameter value group as a {@code Parameters} instance.
      * If the given parameters is already an instance of {@code Parameters}, then it is returned as-is.
      * Otherwise this method returns a wrapper which delegate all method invocations to the given instance.
@@ -352,15 +370,32 @@ public abstract class Parameters impleme
      * The {@link DefaultParameterValueGroup} subclass will override this method with a more efficient
      * implementation which avoid creating some deferred parameters.
      */
+    @SuppressWarnings("null")
     ParameterValue<?> parameterIfExist(final String name) throws ParameterNotFoundException {
+        ParameterValue<?> fallback  = null;
+        ParameterValue<?> ambiguity = null;
         for (final GeneralParameterValue value : values()) {
             if (value instanceof ParameterValue<?>) {
-                if (isHeuristicMatchForName(value.getDescriptor(), name)) {
-                    return (ParameterValue<?>) value;
+                final ParameterValue<?> param = (ParameterValue<?>) value;
+                final ParameterDescriptor<?> descriptor = param.getDescriptor();
+                if (name.equals(descriptor.getName().toString())) {
+                    return param;
+                }
+                if (isHeuristicMatchForName(descriptor, name)) {
+                    if (fallback == null) {
+                        fallback = param;
+                    } else {
+                        ambiguity = param;
+                    }
                 }
             }
         }
-        return null;
+        if (ambiguity != null) {
+            throw new ParameterNotFoundException(Errors.format(Errors.Keys.AmbiguousName_3,
+                    IdentifiedObjects.toString(fallback .getDescriptor().getName()),
+                    IdentifiedObjects.toString(ambiguity.getDescriptor().getName()), name), name);
+        }
+        return fallback;
     }
 
     /**
@@ -408,7 +443,7 @@ public abstract class Parameters impleme
     }
 
     /**
-     * Returns the value of the parameter identified by the given descriptor.
+     * Returns the value of the parameter identified by the given descriptor, or {@code null} if none.
      * This method uses the following information from the given {@code parameter} descriptor:
      *
      * <ul>
@@ -465,6 +500,30 @@ public abstract class Parameters impleme
     }
 
     /**
+     * Returns the value of the parameter identified by the given descriptor, or throws an exception if none.
+     * The default implementation invokes {@link #getValue(ParameterDescriptor)} and verifies that the returned
+     * value is non-null.
+     *
+     * @param  <T> The type of the parameter value.
+     * @param  parameter The name or alias of the parameter to look for, together with the desired type and unit of value.
+     * @return The requested parameter value if it exists, or the {@linkplain DefaultParameterDescriptor#getDefaultValue()
+     *         default value} otherwise provided that it is not {@code null}.
+     * @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
+     * @throws IllegalStateException if the value is not defined and there is no default value.
+     *
+     * @since 0.7
+     */
+    public <T> T getMandatoryValue(final ParameterDescriptor<T> parameter) throws ParameterNotFoundException {
+        final T value = getValue(parameter);
+        if (value != null) {
+            return value;
+        } else {
+            throw new IllegalStateException(Errors.format(Errors.Keys.MissingValueForParameter_1,
+                    Verifier.getDisplayName(parameter)));
+        }
+    }
+
+    /**
      * Returns the default value of the given descriptor, or throws an exception if the
      * descriptor does not define a default value. This check should be kept consistent
      * with the {@link DefaultParameterValue#incompatibleValue(Object)} check.
@@ -721,7 +780,7 @@ public abstract class Parameters impleme
                     target.setValue(((Number) v).doubleValue(), unit);
                 } else if (v instanceof double[]) {
                     target.setValue((double[]) v, unit);
-                } else {
+                } else if (v != target.getValue()) {    // Accept null value if the target value is already null.
                     throw new InvalidParameterValueException(Errors.format(
                             Errors.Keys.IllegalArgumentValue_2, name, v), name, v);
                 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -17,6 +17,7 @@
 package org.apache.sis.parameter;
 
 import javax.measure.unit.Unit;
+import javax.xml.bind.annotation.XmlTransient;
 import org.opengis.parameter.ParameterValue;
 import org.apache.sis.internal.util.Cloner;
 import org.apache.sis.util.collection.WeakHashSet;
@@ -54,6 +55,7 @@ import org.apache.sis.util.resources.Err
  * @version 0.6
  * @module
  */
+@XmlTransient
 final class UnmodifiableParameterValue<T> extends DefaultParameterValue<T> {
     /**
      * For cross-version compatibility.
@@ -117,6 +119,7 @@ final class UnmodifiableParameterValue<T
      * Returns a modifiable copy of this parameter.
      */
     @Override
+    @SuppressWarnings("CloneDoesntCallSuperClone")
     public DefaultParameterValue<T> clone() {
         return new DefaultParameterValue<T>(this);
     }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -174,7 +174,7 @@ public class AbstractIdentifiedObject ex
      * The name for this object or code. Shall never be {@code null}.
      *
      * <p><b>Consider this field as final!</b>
-     * This field is modified only at unmarshalling time by {@link #addName(ReferenceIdentifier)}.</p>
+     * This field is modified only at unmarshalling time by {@link Names#add(Identifier)}.</p>
      *
      * @see #getName()
      * @see #getNames()
@@ -187,7 +187,7 @@ public class AbstractIdentifiedObject ex
      * we may get both on unmarshalling.
      *
      * <p><b>Consider this field as final!</b>
-     * This field is modified only at unmarshalling time by {@link #addName(ReferenceIdentifier)}.</p>
+     * This field is modified only at unmarshalling time by {@link Names#add(Identifier)}.</p>
      */
     private Collection<GenericName> alias;
 
@@ -654,6 +654,8 @@ public class AbstractIdentifiedObject ex
      *           <cite>When object name matter</cite> below.</td></tr>
      *   <tr><td>{@link ComparisonMode#APPROXIMATIVE APPROXIMATIVE}:</td>
      *       <td>Same as {@code IGNORE_METADATA}, with some tolerance threshold on numerical values.</td></tr>
+     *   <tr><td>{@link ComparisonMode#ALLOW_VARIANT ALLOW_VARIANT}:</td>
+     *       <td>Same as {@code APPROXIMATIVE}, but ignores coordinate system axes.</td></tr>
      *   <tr><td>{@link ComparisonMode#DEBUG DEBUG}:</td>
      *        <td>Special mode for figuring out why two objects expected to be equal are not.</td></tr>
      * </table>
@@ -728,6 +730,7 @@ public class AbstractIdentifiedObject ex
             }
             case IGNORE_METADATA:
             case APPROXIMATIVE:
+            case ALLOW_VARIANT:
             case DEBUG: {
                 return implementsSameInterface(object);
             }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -33,6 +33,8 @@ import org.opengis.referencing.Identifie
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.Citations;
+import org.apache.sis.internal.referencing.DeprecatedCode;
+import org.apache.sis.internal.referencing.DeprecatedName;
 import org.apache.sis.util.iso.Types;
 import org.apache.sis.util.Deprecable;
 import org.apache.sis.util.resources.Errors;

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -122,7 +122,7 @@ public final class CRS extends Static {
      *   <tr><td>EPSG:4979</td> <td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Geographic 3D</td> <td>World Geodetic System 1984</td></tr>
      *   <tr><td>EPSG:4984</td> <td>{@link CommonCRS#WGS72  WGS72}</td>  <td>Geocentric</td>    <td>World Geodetic System 1972</td></tr>
      *   <tr><td>EPSG:4985</td> <td>{@link CommonCRS#WGS72  WGS72}</td>  <td>Geographic 3D</td> <td>World Geodetic System 1972</td></tr>
-     *   <tr><td>EPSG:5715</td> <td>{@link CommonCRS.Vertical#DEPTH  DEPTH}</td> <td>Vertical</td> <td>Mean Sea Level depth</td></tr>
+     *   <tr><td>EPSG:5715</td> <td>{@link CommonCRS.Vertical#DEPTH DEPTH}</td> <td>Vertical</td> <td>Mean Sea Level depth</td></tr>
      *   <tr><td>EPSG:5714</td> <td>{@link CommonCRS.Vertical#MEAN_SEA_LEVEL MEAN_SEA_LEVEL}</td> <td>Vertical</td> <td>Mean Sea Level height</td></tr>
      * </table></blockquote>
      *
@@ -138,6 +138,8 @@ public final class CRS extends Static {
      * @throws NoSuchAuthorityCodeException If there is no known CRS associated to the given code.
      * @throws FactoryException if the CRS creation failed for an other reason.
      *
+     * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory
+     *
      * @category factory
      */
     public static CoordinateReferenceSystem forCode(final String code)
@@ -260,7 +262,7 @@ public final class CRS extends Static {
      * CRS as horizontal if it is two-dimensional and comply with one of the following conditions:
      *
      * <ul>
-     *   <li>is an instance of {@link GeographicCRS}, or</li>
+     *   <li>is an instance of {@link GeographicCRS} (or an equivalent {@link GeodeticCRS}), or</li>
      *   <li>is an instance of {@link ProjectedCRS}, or</li>
      *   <li>is an instance of {@link EngineeringCRS} (following
      *     <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#111">ISO 19162 §16.1</a>
@@ -282,8 +284,17 @@ public final class CRS extends Static {
      * @category information
      */
     public static boolean isHorizontalCRS(final CoordinateReferenceSystem crs) {
-        if (crs instanceof GeographicCRS || crs instanceof ProjectedCRS || crs instanceof EngineeringCRS) {
-            return crs.getCoordinateSystem().getDimension() == 2;
+        /*
+         * In order to determine if the CRS is geographic, checking the CoordinateSystem type is more reliable
+         * then checking if the CRS implements the GeographicCRS interface.  This is because the GeographicCRS
+         * interface is GeoAPI-specific, so a CRS may be OGC-compliant without implementing that interface.
+         */
+        final boolean isGeodetic = (crs instanceof GeodeticCRS);
+        if (isGeodetic || crs instanceof ProjectedCRS || crs instanceof EngineeringCRS) {
+            final CoordinateSystem cs = crs.getCoordinateSystem();
+            if (cs.getDimension() == 2) {
+                return !isGeodetic || (cs instanceof EllipsoidalCS);
+            }
         }
         return false;
     }
@@ -307,21 +318,24 @@ public final class CRS extends Static {
         if (isHorizontalCRS(crs)) {
             return (SingleCRS) crs;
         }
-        if (crs instanceof GeographicCRS) {
-            EllipsoidalCS cs = ((GeographicCRS) crs).getCoordinateSystem();
-            final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
-            if (i >= 0) {
-                final CoordinateSystemAxis xAxis = cs.getAxis(i > 0 ? 0 : 1);
-                final CoordinateSystemAxis yAxis = cs.getAxis(i > 1 ? 1 : 2);
-                cs = CommonCRS.DEFAULT.geographic().getCoordinateSystem();
-                if (!Utilities.equalsIgnoreMetadata(cs.getAxis(0), xAxis) ||
-                    !Utilities.equalsIgnoreMetadata(cs.getAxis(1), yAxis))
-                {
-                    // We can not reuse the name of the existing CS, because it typically
-                    // contains text about axes including the axis that we just dropped.
-                    cs = new DefaultEllipsoidalCS(singletonMap(EllipsoidalCS.NAME_KEY, "Ellipsoidal 2D"), xAxis, yAxis);
+        if (crs instanceof GeodeticCRS) {
+            CoordinateSystem cs = crs.getCoordinateSystem();
+            if (cs instanceof EllipsoidalCS) {  // See comment in isHorizontalCRS(…) method.
+                final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
+                if (i >= 0) {
+                    final CoordinateSystemAxis xAxis = cs.getAxis(i > 0 ? 0 : 1);
+                    final CoordinateSystemAxis yAxis = cs.getAxis(i > 1 ? 1 : 2);
+                    cs = CommonCRS.DEFAULT.geographic().getCoordinateSystem();
+                    if (!Utilities.equalsIgnoreMetadata(cs.getAxis(0), xAxis) ||
+                        !Utilities.equalsIgnoreMetadata(cs.getAxis(1), yAxis))
+                    {
+                        // We can not reuse the name of the existing CS, because it typically
+                        // contains text about axes including the axis that we just dropped.
+                        cs = new DefaultEllipsoidalCS(singletonMap(EllipsoidalCS.NAME_KEY, "Ellipsoidal 2D"), xAxis, yAxis);
+                    }
+                    return new DefaultGeographicCRS(IdentifiedObjects.getProperties(crs),
+                            ((GeodeticCRS) crs).getDatum(), (EllipsoidalCS) cs);
                 }
-                return new DefaultGeographicCRS(IdentifiedObjects.getProperties(crs), ((GeographicCRS) crs).getDatum(), cs);
             }
         }
         if (crs instanceof CompoundCRS) {
@@ -384,17 +398,19 @@ public final class CRS extends Static {
                 }
             } while ((a = !a) == allowCreateEllipsoidal);
         }
-        if (allowCreateEllipsoidal && crs instanceof GeographicCRS) {
+        if (allowCreateEllipsoidal && crs instanceof GeodeticCRS) {
             final CoordinateSystem cs = crs.getCoordinateSystem();
-            final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
-            if (i >= 0) {
-                final CoordinateSystemAxis axis = cs.getAxis(i);
-                VerticalCRS c = CommonCRS.Vertical.ELLIPSOIDAL.crs();
-                if (!c.getCoordinateSystem().getAxis(0).equals(axis)) {
-                    final Map<String,?> properties = IdentifiedObjects.getProperties(c);
-                    c = new DefaultVerticalCRS(properties, c.getDatum(), new DefaultVerticalCS(properties, axis));
+            if (cs instanceof EllipsoidalCS) {  // See comment in isHorizontalCRS(…) method.
+                final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
+                if (i >= 0) {
+                    final CoordinateSystemAxis axis = cs.getAxis(i);
+                    VerticalCRS c = CommonCRS.Vertical.ELLIPSOIDAL.crs();
+                    if (!c.getCoordinateSystem().getAxis(0).equals(axis)) {
+                        final Map<String,?> properties = IdentifiedObjects.getProperties(c);
+                        c = new DefaultVerticalCRS(properties, c.getDatum(), new DefaultVerticalCS(properties, axis));
+                    }
+                    return c;
                 }
-                return c;
             }
         }
         return null;

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -18,6 +18,8 @@ package org.apache.sis.referencing;
 
 import java.util.Map;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
 import javax.measure.unit.SI;
 import javax.measure.unit.NonSI;
 import javax.measure.unit.Unit;
@@ -32,7 +34,9 @@ import org.opengis.referencing.crs.Verti
 import org.opengis.referencing.crs.TemporalCRS;
 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.crs.CRSAuthorityFactory;
 import org.opengis.referencing.cs.TimeCS;
 import org.opengis.referencing.cs.VerticalCS;
 import org.opengis.referencing.cs.CartesianCS;
@@ -45,7 +49,6 @@ import org.opengis.referencing.datum.Ver
 import org.opengis.referencing.datum.VerticalDatumType;
 import org.opengis.referencing.datum.TemporalDatum;
 import org.opengis.referencing.datum.DatumAuthorityFactory;
-import org.opengis.referencing.crs.CRSAuthorityFactory;
 import org.apache.sis.referencing.datum.DefaultVerticalDatum;
 import org.apache.sis.referencing.datum.DefaultTemporalDatum;
 import org.apache.sis.referencing.cs.AxesConvention;
@@ -56,12 +59,17 @@ import org.apache.sis.referencing.crs.De
 import org.apache.sis.referencing.crs.DefaultVerticalCRS;
 import org.apache.sis.referencing.crs.DefaultGeographicCRS;
 import org.apache.sis.referencing.crs.DefaultGeocentricCRS;
+import org.apache.sis.internal.referencing.provider.TransverseMercator;
+import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.system.SystemListener;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.math.MathFunctions;
+import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Units;
 
 import static java.util.Collections.singletonMap;
@@ -72,6 +80,9 @@ import static org.apache.sis.internal.ut
 import static org.apache.sis.internal.util.Constants.CRS83;
 import static org.apache.sis.internal.util.Constants.CRS84;
 
+// Branch-dependent imports
+import org.apache.sis.internal.jdk8.JDK8;
+
 
 /**
  * Frequently-used geodetic CRS and datum that are guaranteed to be available in SIS.
@@ -115,9 +126,12 @@ import static org.apache.sis.internal.ut
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.5
+ * @version 0.7
  * @module
+ *
+ * @see org.apache.sis.referencing.factory.CommonAuthorityFactory
  */
+@SuppressWarnings("DoubleCheckedLocking")
 public enum CommonCRS {
     /**
      * World Geodetic System 1984.
@@ -133,9 +147,11 @@ public enum CommonCRS {
      *   <tr><th>Semi-minor axis length:</th>  <td>6356752 <i>(approximative)</i></td></tr>
      *   <tr><th>Inverse flattening:</th>      <td>298.257223563 <i>(definitive)</i></td></tr>
      *   <tr><th>Ellipsoid axes unit:</th>     <td>{@link SI#METRE}</td></tr>
+     *   <tr><th>UTM zones:</th>               <td>1 to 60 in North and South hemispheres</td></tr>
      * </table></blockquote>
      */
-    WGS84((short) 4326, (short) 4979, (short) 4978, (short) 6326, (short) 7030),
+    WGS84((short) 4326, (short) 4979, (short) 4978, (short) 6326, (short) 7030,     // Geodetic info
+          (short) 32600, (short) 32700, (byte) 1, (byte) 60),                       // UTM info
 
     /**
      * World Geodetic System 1972.
@@ -149,32 +165,11 @@ public enum CommonCRS {
      *   <tr><th>Semi-minor axis length:</th>  <td>6356751 <i>(approximative)</i></td></tr>
      *   <tr><th>Inverse flattening:</th>      <td>298.26 <i>(definitive)</i></td></tr>
      *   <tr><th>Ellipsoid axes unit:</th>     <td>{@link SI#METRE}</td></tr>
+     *   <tr><th>UTM zones:</th>               <td>1 to 60 in North and South hemispheres</td></tr>
      * </table></blockquote>
      */
-    WGS72((short) 4322, (short) 4985, (short) 4984, (short) 6322, (short) 7043),
-
-    /**
-     * European Terrestrial Reference System 1989.
-     * The ellipsoid is <cite>"GRS 1980"</cite>, also known as <cite>"International 1979"</cite>.
-     * This ellipsoid is very close, but not identical, to the {@linkplain #WGS84} one.
-     *
-     * <blockquote><table class="compact" summary="ETRS89 properties.">
-     *   <tr><th>EPSG identifiers:</th>        <td>4258 &nbsp;(<i>datum:</i> 6258, &nbsp;<i>ellipsoid:</i> 7019)</td></tr>
-     *   <tr><th>Primary names:</th>           <td>"ETRS89" &nbsp;(<i>datum:</i> "European Terrestrial Reference System 1989", &nbsp;<i>ellipsoid:</i> "GRS 1980")</td></tr>
-     *   <tr><th>Abbreviations or aliases:</th><td>"ETRF89", "EUREF89", "ETRS89-GRS80" &nbsp;(<i>ellipsoid:</i> "International 1979")</td></tr>
-     *   <tr><th>Prime meridian:</th>          <td>Greenwich</td></tr>
-     *   <tr><th>Semi-major axis length:</th>  <td>6378137</td></tr>
-     *   <tr><th>Semi-minor axis length:</th>  <td>6356752 <i>(approximative)</i></td></tr>
-     *   <tr><th>Inverse flattening:</th>      <td>298.257222101 <i>(definitive)</i></td></tr>
-     *   <tr><th>Ellipsoid axes unit:</th>     <td>{@link SI#METRE}</td></tr>
-     * </table></blockquote>
-     *
-     * <div class="note"><b>Note:</b>
-     * {@link #NAD83} uses the same ellipsoid for a different datum.
-     * The <cite>Web Map Server</cite> {@code "CRS:83"} authority code uses the NAD83 datum,
-     * while the {@code "IGNF:MILLER"} authority code uses the GRS80 datum.</div>
-     */
-    ETRS89((short) 4258, (short) 4937, (short) 4936, (short) 6258, (short) 7019),
+    WGS72((short) 4322, (short) 4985, (short) 4984, (short) 6322, (short) 7043,     // Geodetic info
+          (short) 32200, (short) 32300, (byte) 1, (byte) 60),                       // UTM info
 
     /**
      * North American Datum 1983.
@@ -191,6 +186,7 @@ public enum CommonCRS {
      *   <tr><th>Semi-minor axis length:</th>  <td>6356752 <i>(approximative)</i></td></tr>
      *   <tr><th>Inverse flattening:</th>      <td>298.257222101 <i>(definitive)</i></td></tr>
      *   <tr><th>Ellipsoid axes unit:</th>     <td>{@link SI#METRE}</td></tr>
+     *   <tr><th>UTM zones:</th>               <td>1 to 23 in the North hemisphere</td></tr>
      * </table></blockquote>
      *
      * <div class="note"><b>Note:</b>
@@ -198,7 +194,8 @@ public enum CommonCRS {
      * The <cite>Web Map Server</cite> {@code "CRS:83"} authority code uses the NAD83 datum,
      * while the {@code "IGNF:MILLER"} authority code uses the GRS80 datum.</div>
      */
-    NAD83((short) 4269, (short) 0, (short) 0, (short) 6269, (short) 7019),
+    NAD83((short) 4269, (short) 0, (short) 0, (short) 6269, (short) 7019,           // Geodetic info
+          (short) 26900, (short) 0, (byte) 1, (byte) 23),                           // UTM info
 
     /**
      * North American Datum 1927.
@@ -212,9 +209,36 @@ public enum CommonCRS {
      *   <tr><th>Semi-major axis length:</th>  <td>6378206.4</td></tr>
      *   <tr><th>Semi-minor axis length:</th>  <td>6356583.8 <i>(definitive)</i></td></tr>
      *   <tr><th>Ellipsoid axes unit:</th>     <td>{@link SI#METRE}</td></tr>
+     *   <tr><th>UTM zones:</th>               <td>1 to 22 in the North hemisphere</td></tr>
      * </table></blockquote>
      */
-    NAD27((short) 4267, (short) 0, (short) 0, (short) 6267, (short) 7008),
+    NAD27((short) 4267, (short) 0, (short) 0, (short) 6267, (short) 7008,           // Geodetic info
+          (short) 26700, (short) 0, (byte) 1, (byte) 22),                           // UTM info
+
+    /**
+     * European Terrestrial Reference System 1989.
+     * The ellipsoid is <cite>"GRS 1980"</cite>, also known as <cite>"International 1979"</cite>.
+     * This ellipsoid is very close, but not identical, to the {@linkplain #WGS84} one.
+     *
+     * <blockquote><table class="compact" summary="ETRS89 properties.">
+     *   <tr><th>EPSG identifiers:</th>        <td>4258 &nbsp;(<i>datum:</i> 6258, &nbsp;<i>ellipsoid:</i> 7019)</td></tr>
+     *   <tr><th>Primary names:</th>           <td>"ETRS89" &nbsp;(<i>datum:</i> "European Terrestrial Reference System 1989", &nbsp;<i>ellipsoid:</i> "GRS 1980")</td></tr>
+     *   <tr><th>Abbreviations or aliases:</th><td>"ETRF89", "EUREF89", "ETRS89-GRS80" &nbsp;(<i>ellipsoid:</i> "International 1979")</td></tr>
+     *   <tr><th>Prime meridian:</th>          <td>Greenwich</td></tr>
+     *   <tr><th>Semi-major axis length:</th>  <td>6378137</td></tr>
+     *   <tr><th>Semi-minor axis length:</th>  <td>6356752 <i>(approximative)</i></td></tr>
+     *   <tr><th>Inverse flattening:</th>      <td>298.257222101 <i>(definitive)</i></td></tr>
+     *   <tr><th>Ellipsoid axes unit:</th>     <td>{@link SI#METRE}</td></tr>
+     *   <tr><th>UTM zones:</th>               <td>28 to 38 in the North hemisphere</td></tr>
+     * </table></blockquote>
+     *
+     * <div class="note"><b>Note:</b>
+     * {@link #NAD83} uses the same ellipsoid for a different datum.
+     * The <cite>Web Map Server</cite> {@code "CRS:83"} authority code uses the NAD83 datum,
+     * while the {@code "IGNF:MILLER"} authority code uses the GRS80 datum.</div>
+     */
+    ETRS89((short) 4258, (short) 4937, (short) 4936, (short) 6258, (short) 7019,    // Geodetic info
+           (short) 25800, (short) 0, (byte) 28, (byte) 38),                         // UTM info
 
     /**
      * European Datum 1950.
@@ -228,9 +252,11 @@ public enum CommonCRS {
      *   <tr><th>Semi-minor axis length:</th>  <td>6356912 <i>(approximative)</i></td></tr>
      *   <tr><th>Inverse flattening:</th>      <td>297 <i>(definitive)</i></td></tr>
      *   <tr><th>Ellipsoid axes unit:</th>     <td>{@link SI#METRE}</td></tr>
+     *   <tr><th>UTM zones:</th>               <td>28 to 38 in the North hemisphere</td></tr>
      * </table></blockquote>
      */
-    ED50((short) 4230, (short) 0, (short) 0, (short) 6230, (short) 7022),
+    ED50((short) 4230, (short) 0, (short) 0, (short) 6230, (short) 7022,            // Geodetic info
+           (short) 23000, (short) 0, (byte) 28, (byte) 38),                         // UTM info
 
     /**
      * Unspecified datum based upon the GRS 1980 Authalic Sphere. Spheres use a simpler algorithm for
@@ -248,7 +274,8 @@ public enum CommonCRS {
      *
      * @see org.apache.sis.referencing.datum.DefaultEllipsoid#getAuthalicRadius()
      */
-    SPHERE((short) 4047, (short) 0, (short) 0, (short) 6047, (short) 7048);
+    SPHERE((short) 4047, (short) 0, (short) 0, (short) 6047, (short) 7048,          // Geodetic info
+           (short) 0, (short) 0, (byte) 0, (byte) 0);                               // UTM info
 
     /**
      * The enum for the default CRS.
@@ -285,6 +312,16 @@ public enum CommonCRS {
     final short ellipsoid;
 
     /**
+     * EPSG codes of pseudo "UTM zone zero" (North case and South case), or 0 if none.
+     */
+    final short northUTM, southUTM;
+
+    /**
+     * Zone number of the first UTM and last UTM zone defined in the EPSG database, inclusive.
+     */
+    final byte firstZone, lastZone;
+
+    /**
      * The cached object. This is initially {@code null}, then set to various kind of objects depending
      * on which method has been invoked. The kind of object stored in this field may change during the
      * application execution.
@@ -313,6 +350,14 @@ public enum CommonCRS {
     private transient volatile GeocentricCRS cachedGeocentric;
 
     /**
+     * The Universal Transverse Mercator projections, created when first needed.
+     * All accesses to this map shall be synchronized on {@code cachedUTM}.
+     *
+     * @see #UTM(double, double)
+     */
+    private final Map<Integer,ProjectedCRS> cachedUTM;
+
+    /**
      * Creates a new constant for the given EPSG or SIS codes.
      *
      * @param geographic The EPSG code for the two-dimensional geographic CRS.
@@ -321,14 +366,19 @@ public enum CommonCRS {
      * @param datum      The EPSG code for the datum.
      * @param ellipsoid  The EPSG code for the ellipsoid.
      */
-    private CommonCRS(final short geographic, final short geo3D, final short geocentric,
-            final short datum, final short ellipsoid)
+    private CommonCRS(final short geographic, final short geo3D, final short geocentric, final short datum, final short ellipsoid,
+            final short northUTM, final short southUTM, final byte firstZone, final byte lastZone)
     {
         this.geographic = geographic;
         this.geocentric = geocentric;
         this.geo3D      = geo3D;
         this.datum      = datum;
         this.ellipsoid  = ellipsoid;
+        this.northUTM   = northUTM;
+        this.southUTM   = southUTM;
+        this.firstZone  = firstZone;
+        this.lastZone   = lastZone;
+        cachedUTM = new HashMap<Integer,ProjectedCRS>();
     }
 
     /**
@@ -348,11 +398,15 @@ public enum CommonCRS {
     /**
      * Invoked by when the cache needs to be cleared after a classpath change.
      */
+    @SuppressWarnings("NestedSynchronizedStatement")    // Safe because cachedUTM never call any method of 'this'.
     synchronized void clear() {
         cached           = null;
         cachedGeo3D      = null;
         cachedNormalized = null;
         cachedGeocentric = null;
+        synchronized (cachedUTM) {
+            cachedUTM.clear();
+        }
     }
 
     /**
@@ -460,7 +514,7 @@ public enum CommonCRS {
                         failure(this, "geographic", e);
                     }
                     /*
-                     * All constants defined in this enumeration use the same coordinate system, EPSG:6422.
+                     * All constants defined in this enumeration use the same coordinate system, EPSG::6422.
                      * We will arbitrarily create this CS only for the most frequently created CRS,
                      * and share that CS instance for all other constants.
                      */
@@ -517,7 +571,7 @@ public enum CommonCRS {
                         }
                     }
                     /*
-                     * All constants defined in this enumeration use the same coordinate system, EPSG:6423.
+                     * All constants defined in this enumeration use the same coordinate system, EPSG::6423.
                      * We will arbitrarily create this CS only for the most frequently created CRS,
                      * and share that CS instance for all other constants.
                      */
@@ -575,7 +629,7 @@ public enum CommonCRS {
                         }
                     }
                     /*
-                     * All constants defined in this enumeration use the same coordinate system, EPSG:6500.
+                     * All constants defined in this enumeration use the same coordinate system, EPSG::6500.
                      * We will arbitrarily create this CS only for the most frequently created CRS,
                      * and share that CS instance for all other constants.
                      */
@@ -664,7 +718,7 @@ public enum CommonCRS {
                 object = ellipsoid(cached);
                 if (object == null) {
                     if (this == NAD83) {
-                        object = ETRS89.ellipsoid(); // Share the same instance for NAD83 and ETRS89.
+                        object = ETRS89.ellipsoid();            // Share the same instance for NAD83 and ETRS89.
                     } else {
                         final DatumAuthorityFactory factory = datumFactory();
                         if (factory != null) try {
@@ -704,7 +758,7 @@ public enum CommonCRS {
                 object = primeMeridian(cached);
                 if (object == null) {
                     if (this != DEFAULT) {
-                        object = DEFAULT.primeMeridian(); // Share the same instance for all constants.
+                        object = DEFAULT.primeMeridian();           // Share the same instance for all constants.
                     } else {
                         final DatumAuthorityFactory factory = datumFactory();
                         if (factory != null) try {
@@ -764,6 +818,130 @@ public enum CommonCRS {
         return (datum != null) ? datum.getPrimeMeridian() : null;
     }
 
+    /*
+     * NOTE ABOUT MAP PROJECTION CONVENIENCE METHODS:
+     * There is no convenience method for projections other than UTM because this enumeration is not a
+     * factory for arbitrary CRS (the UTM projection has the advantage of being constrained to zones).
+     * World-wide projections like "WGS 84 / World Mercator" are not handled neither because they make
+     * sense only for some datum like WGS84 or WGS72. Application to more regional datum like NAD27 or
+     * ED50 would be more questionable.
+     */
+
+    /**
+     * Returns a Universal Transverse Mercator (UTM) projection for the zone containing the given point.
+     * There is a total of 120 UTM zones, with 60 zones in the North hemisphere and 60 zones in the South hemisphere.
+     * The projection zone is determined from the arguments as below:
+     *
+     * <ul>
+     *   <li>The sign of the <var>latitude</var> argument determines the hemisphere:
+     *       North for positive latitudes (including positive zero) or
+     *       South for negative latitudes (including negative zero).
+     *       The latitude magnitude is ignored, except for ensuring that the latitude is inside the [-90 … 90]° range.</li>
+     *   <li>The value of the <var>longitude</var> argument determines the 6°-width zone,
+     *       numbered from 1 for the zone starting at 180°W up to 60 for the zone finishing at 180°E.
+     *       Longitudes outside the [-180 … 180]° range will be rolled as needed before to compute the zone.</li>
+     * </ul>
+     *
+     * <div class="note"><b>Warning:</b>
+     * be aware of parameter order! For this method, latitude is first.
+     * This order is for consistency with the non-normalized {@linkplain #geographic() geographic} CRS
+     * of all items in this {@code CommonCRS} enumeration.</div>
+     *
+     * The map projection uses the following parameters:
+     *
+     * <blockquote><table class="sis">
+     *   <caption>Universal Transverse Mercator (UTM) parameters</caption>
+     *   <tr><th>Parameter name</th>                 <th>Value</th></tr>
+     *   <tr><td>Latitude of natural origin</td>     <td>0°</td></tr>
+     *   <tr><td>Longitude of natural origin</td>    <td>Central meridian of the UTM zone containing the given longitude</td></tr>
+     *   <tr><td>Scale factor at natural origin</td> <td>0.9996</td></tr>
+     *   <tr><td>False easting</td>                  <td>500000 metres</td></tr>
+     *   <tr><td>False northing</td>                 <td>0 (North hemisphere) or 10000000 (South hemisphere) metres</td></tr>
+     * </table></blockquote>
+     *
+     * The coordinate system axes are (Easting, Northing) in metres.
+     *
+     * @param  latitude  A latitude in the desired UTM projection zone.
+     * @param  longitude A longitude in the desired UTM projection zone.
+     * @return A Universal Transverse Mercator projection for the zone containing the given point.
+     *
+     * @since 0.7
+     */
+    public ProjectedCRS UTM(final double latitude, final double longitude) {
+        ArgumentChecks.ensureBetween("latitude",   Latitude.MIN_VALUE,     Latitude.MAX_VALUE,     latitude);
+        ArgumentChecks.ensureBetween("longitude", -Formulas.LONGITUDE_MAX, Formulas.LONGITUDE_MAX, longitude);
+        final boolean isSouth = MathFunctions.isNegative(latitude);
+        final int zone = TransverseMercator.zone(longitude);
+        final Integer key = isSouth ? -zone : zone;
+        ProjectedCRS crs;
+        synchronized (cachedUTM) {
+            crs = cachedUTM.get(key);
+        }
+        if (crs == null) {
+            int code = 0;
+            if (zone >= firstZone && zone <= lastZone) {
+                code = JDK8.toUnsignedInt(isSouth ? southUTM : northUTM);
+                if (code != 0) {
+                    code += zone;
+                    final CRSAuthorityFactory factory = crsFactory();
+                    if (factory != null) try {
+                        return factory.createProjectedCRS(String.valueOf(code));
+                    } catch (FactoryException e) {
+                        failure(this, "UTM", e);
+                    }
+                }
+            }
+            /*
+             * All constants defined in this enumeration use the same coordinate system, EPSG::4400.
+             * We will arbitrarily create this CS only for a frequently created CRS, and share that
+             * CS instance for all other constants.
+             */
+            CartesianCS cs = null;
+            synchronized (DEFAULT.cachedUTM) {
+                final Iterator<ProjectedCRS> it = DEFAULT.cachedUTM.values().iterator();
+                if (it.hasNext()) {
+                    cs = it.next().getCoordinateSystem();
+                }
+            }
+            if (cs == null) {
+                if (this != DEFAULT) {
+                    cs = DEFAULT.UTM(latitude, longitude).getCoordinateSystem();
+                } else {
+                    cs = (CartesianCS) StandardDefinitions.createCoordinateSystem((short) 4400);
+                }
+            }
+            crs = StandardDefinitions.createUTM(code, geographic(), latitude, longitude, cs);
+            final ProjectedCRS other;
+            synchronized (cachedUTM) {
+                other = JDK8.putIfAbsent(cachedUTM, key, crs);
+            }
+            if (other != null) {
+                return other;
+            }
+        }
+        return crs;
+    }
+
+    /**
+     * Returns the CRS for the given EPSG code, or {@code null} if none.
+     * This is a helper method for {@link #forCode(String, String, FactoryException)} only.
+     */
+    private CoordinateReferenceSystem forCode(final int code) {
+        if (code == geographic) return geographic();
+        if (code == geocentric) return geocentric();
+        if (code == geo3D)      return geographic3D();
+        final double latitude;
+        int zone;
+        if (northUTM != 0 && (zone = code - northUTM) >= firstZone && zone <= lastZone) {
+            latitude = +1;
+        } else if (southUTM != 0 && (zone = code - southUTM) >= firstZone && zone <= lastZone) {
+            latitude = -1;
+        } else {
+            return null;
+        }
+        return UTM(latitude, TransverseMercator.centralMeridian(zone));
+    }
+
 
 
 
@@ -788,13 +966,15 @@ public enum CommonCRS {
      *
      * <blockquote><table class="sis">
      *   <caption>Geodetic objects accessible by enumeration constants</caption>
-     *   <tr><th>Name or alias</th>             <th>Object type</th> <th>Enumeration value</th></tr>
-     *   <tr><td>Barometric altitude</td>       <td>CRS, Datum</td>  <td>{@link #BAROMETRIC}</td></tr>
-     *   <!-- <s>Ellipsoidal height</s> intentionally omitted        <td><s>{@link #ELLIPSOIDAL}</s></td> -->
-     *   <tr><td>Mean Sea Level</td>            <td>Datum</td>       <td>{@link #MEAN_SEA_LEVEL}</td></tr>
-     *   <tr><td>Mean Sea Level depth</td>      <td>CRS</td>         <td>{@link #DEPTH}</td></tr>
-     *   <tr><td>Mean Sea Level height</td>     <td>CRS</td>         <td>{@link #MEAN_SEA_LEVEL}</td></tr>
-     *   <tr><td>Other surface</td>             <td>CRS, Datum</td>  <td>{@link #OTHER_SURFACE}</td></tr>
+     *   <tr><th>Name or alias</th>                      <th>Object type</th> <th>Enumeration value</th></tr>
+     *   <tr><td>Barometric altitude</td>                <td>CRS, Datum</td>  <td>{@link #BAROMETRIC}</td></tr>
+     *   <!-- <s>Ellipsoidal height</s> intentionally omitted                 <td><s>{@link #ELLIPSOIDAL}</s></td> -->
+     *   <tr><td>Mean Sea Level</td>                     <td>Datum</td>       <td>{@link #MEAN_SEA_LEVEL}</td></tr>
+     *   <tr><td>Mean Sea Level depth</td>               <td>CRS</td>         <td>{@link #DEPTH}</td></tr>
+     *   <tr><td>Mean Sea Level height</td>              <td>CRS</td>         <td>{@link #MEAN_SEA_LEVEL}</td></tr>
+     *   <tr><td>NAVD88 height</td>                      <td>CRS</td>         <td>{@link #NAVD88}</td></tr>
+     *   <tr><td>North American Vertical Datum 1988</td> <td>Datum</td>       <td>{@link #NAVD88}</td></tr>
+     *   <tr><td>Other surface</td>                      <td>CRS, Datum</td>  <td>{@link #OTHER_SURFACE}</td></tr>
      * </table></blockquote>
      *
      * <div class="note"><b>Note:</b>
@@ -804,8 +984,10 @@ public enum CommonCRS {
      *
      * @author  Martin Desruisseaux (Geomatys)
      * @since   0.4
-     * @version 0.4
+     * @version 0.7
      * @module
+     *
+     * @see org.apache.sis.referencing.factory.CommonAuthorityFactory
      */
     public static enum Vertical {
         /**
@@ -849,6 +1031,24 @@ public enum CommonCRS {
         DEPTH(true, (short) 5715, (short) 5100),
 
         /**
+         * North American Vertical Datum 1988 height.
+         *
+         * <blockquote><table class="compact" summary="Mean Sea Level properties.">
+         *   <tr><th>WMS identifier:</th>           <td>CRS:88</td></tr>
+         *   <tr><th>EPSG identifiers:</th>         <td>5703 &nbsp;(<i>datum:</i> 5103)</td></tr>
+         *   <tr><th>Primary names:</th>            <td>"NAVD88 height" &nbsp;(<i>datum:</i> "North American Vertical Datum 1988")</td></tr>
+         *   <tr><th>Abbreviations or aliases:</th> <td>" North American Vertical Datum of 1988 height (m)" &nbsp;(<i>datum:</i> "NAVD88")</td></tr>
+         *   <tr><th>Direction:</th>                <td>{@link AxisDirection#UP}</td></tr>
+         *   <tr><th>Unit:</th>                     <td>{@link SI#METRE}</td></tr>
+         * </table></blockquote>
+         *
+         * @see CommonCRS#NAD83
+         *
+         * @since 0.7
+         */
+        NAVD88(true, (short) 5703, (short) 5103),
+
+        /**
          * Height measured along the normal to the ellipsoid used in the definition of horizontal datum.
          * The unit of measurement is metres.
          *
@@ -1377,9 +1577,10 @@ public enum CommonCRS {
                 final int n = Integer.parseInt(code);
                 if (n != 0) { // CommonCRS uses 0 as a sentinel value for "no EPSG code".
                     for (final CommonCRS candidate : CommonCRS.values()) {
-                        if (candidate.geographic == n) return candidate.geographic();
-                        if (candidate.geocentric == n) return candidate.geocentric();
-                        if (candidate.geo3D      == n) return candidate.geographic3D();
+                        final CoordinateReferenceSystem crs = candidate.forCode(n);
+                        if (crs != null) {
+                            return crs;
+                        }
                     }
                     for (final CommonCRS.Vertical candidate : CommonCRS.Vertical.values()) {
                         if (candidate.isEPSG && candidate.crs == n) {

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java?rev=1724531&r1=1724530&r2=1724531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java [UTF-8] Wed Jan 13 23:59:38 2016
@@ -89,7 +89,7 @@ import org.apache.sis.internal.jdk7.Obje
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.7
  * @module
  */
 public class NamedIdentifier extends ImmutableIdentifier implements GenericName {
@@ -143,7 +143,59 @@ public class NamedIdentifier extends Imm
 
     /**
      * Constructs an identifier from the given properties. The content of the properties map is used as
-     * described in the {@linkplain ImmutableIdentifier#ImmutableIdentifier(Map) super-class constructor}.
+     * described in the {@linkplain ImmutableIdentifier#ImmutableIdentifier(Map) super-class constructor},
+     * with the addition of an optional {@code "name"} property.
+     *
+     * <table class="sis">
+     *   <caption>Recognized properties</caption>
+     *   <tr>
+     *     <th>Property name</th>
+     *     <th>Value type</th>
+     *     <th>Returned by</th>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code "name"}</td>
+     *     <td>{@link GenericName}</td>
+     *     <td>(none)</td>
+     *   </tr>
+     *   <tr>
+     *     <th colspan="3" class="hsep">Defined in parent class (reminder)</th>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.metadata.Identifier#CODE_KEY}</td>
+     *     <td>{@link String}</td>
+     *     <td>{@link #getCode()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.metadata.Identifier#CODESPACE_KEY}</td>
+     *     <td>{@link String}</td>
+     *     <td>{@link #getCodeSpace()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.metadata.Identifier#AUTHORITY_KEY}</td>
+     *     <td>{@link String} or {@link Citation}</td>
+     *     <td>{@link #getAuthority()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.metadata.Identifier#VERSION_KEY}</td>
+     *     <td>{@link String}</td>
+     *     <td>{@link #getVersion()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.metadata.Identifier#DESCRIPTION_KEY}</td>
+     *     <td>{@link String} or {@link InternationalString}</td>
+     *     <td>{@link #getDescription()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.apache.sis.referencing.AbstractIdentifiedObject#LOCALE_KEY}</td>
+     *     <td>{@link Locale}</td>
+     *     <td>(none)</td>
+     *   </tr>
+     * </table>
+     *
+     * The {@value org.opengis.metadata.Identifier#CODE_KEY} property is mandatory and all other properties
+     * are optional. If a {@code "name"} property is provided, then calls to name-related methods like
+     * {@link #tip()}, {@link #head()} and {@link #scope()} will delegate to the given name.
      *
      * @param  properties The properties to be given to this identifier.
      * @throws InvalidParameterValueException if a property has an invalid value.
@@ -151,6 +203,8 @@ public class NamedIdentifier extends Imm
      */
     public NamedIdentifier(final Map<String,?> properties) throws IllegalArgumentException {
         super(properties);
+        name = (GenericName) properties.get("name");
+        isNameSupplied = (name != null);
     }
 
     /**
@@ -172,7 +226,7 @@ public class NamedIdentifier extends Imm
         super(authority, Citations.getCodeSpace(authority), toString(code));
         if (code instanceof InternationalString) {
             name = createName(authority, super.getCodeSpace(), code);
-            isNameSupplied = true; // Because 'code' is an international string.
+            isNameSupplied = true;      // Because 'code' is an international string.
         }
     }
 
@@ -205,7 +259,7 @@ public class NamedIdentifier extends Imm
         super(authority, codeSpace, toString(code), version, description);
         if (code instanceof InternationalString) {
             name = createName(authority, codeSpace, code);
-            isNameSupplied = true; // Because 'code' is an international string.
+            isNameSupplied = true;      // Because 'code' is an international string.
         }
     }
 
@@ -233,7 +287,7 @@ public class NamedIdentifier extends Imm
     /**
      * Returns the generic name of this identifier.
      * The name will be constructed automatically the first time it will be needed.
-     * The name's scope is inferred from the shortest alternative title (if any).
+     * The name's head is inferred from the shortest alternative title (if any).
      * This heuristic rule is compatible to the ISO 19115 remark saying that the
      * {@linkplain Citation#getAlternateTitles() alternate titles} often contains abbreviation
      * (for example "DCW" as an alternative title for "Digital Chart of the World").
@@ -261,7 +315,7 @@ public class NamedIdentifier extends Imm
      */
     private static GenericName createName(final Citation authority, String codeSpace, final CharSequence code) {
         if (codeSpace == null) {
-            codeSpace = Citations.getCodeSpace(authority);   // Whitespaces trimed by Citations.
+            codeSpace = Citations.getCodeSpace(authority);          // Whitespaces trimed by Citations.
         }
         final NameFactory factory = DefaultFactories.forBuildin(NameFactory.class);
         if (codeSpace != null) {
@@ -411,7 +465,7 @@ public class NamedIdentifier extends Imm
         }
         if (super.equals(object)) {
             if (!isNameSupplied) {
-                return true; // No need to compare names if they are computed from the same values.
+                return true;            // No need to compare names if they are computed from the same values.
             }
             final NamedIdentifier that = (NamedIdentifier) object;
             return Objects.equals(this.getName(), that.getName());




Mime
View raw message