sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1724476 [1/3] - in /sis/branches/JDK7: ./ core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/sql/ core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ core/sis-referencing/src/main/java/org/apache/sis/inter...
Date Wed, 13 Jan 2016 18:31:16 GMT
Author: desruisseaux
Date: Wed Jan 13 18:31:16 2016
New Revision: 1724476

URL: http://svn.apache.org/viewvc?rev=1724476&view=rev
Log:
Merge the completion of CommonAuthorityFactory from the JDK8 branch.

Added:
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/GeodeticObjectBuilder.java
      - copied unchanged from r1724474, sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/GeodeticObjectBuilder.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java
      - copied unchanged from r1724474, sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TransverseMercatorTest.java
      - copied unchanged from r1724474, sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TransverseMercatorTest.java
Removed:
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectBuilder.java
Modified:
    sis/branches/JDK7/   (props changed)
    sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/sql/SQLUtilitiesTest.java   (props changed)
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform2D.java   (props changed)
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/MapProjectionTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/StandardDefinitionsTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/AuthorityFactoryProxyTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/CommonAuthorityFactoryTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/MapProjectionTestCase.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/UnitsMap.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java
    sis/branches/JDK7/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/SQLInvalidRecordNumberForDirectAccessException.java   (contents, props changed)
    sis/branches/JDK7/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/SQLNoDirectAccessAvailableException.java   (contents, props changed)
    sis/branches/JDK7/storage/sis-shapefile/src/main/resources/org/apache/sis/internal/shapefile/ShapefileByteReader.properties   (contents, props changed)
    sis/branches/JDK7/storage/sis-shapefile/src/main/resources/org/apache/sis/internal/shapefile/ShapefileByteReader_fr.properties   (contents, props changed)
    sis/branches/JDK7/storage/sis-shapefile/src/main/resources/org/apache/sis/storage/shapefile/InputFeatureStream.properties   (contents, props changed)
    sis/branches/JDK7/storage/sis-shapefile/src/main/resources/org/apache/sis/storage/shapefile/InputFeatureStream_fr.properties   (contents, props changed)

Propchange: sis/branches/JDK7/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Jan 13 18:31:16 2016
@@ -1,4 +1,4 @@
 /sis/branches/Android:1430670-1480699
 /sis/branches/JDK6:1394913-1508480
-/sis/branches/JDK8:1584960-1723784
+/sis/branches/JDK8:1584960-1724474
 /sis/trunk:1394364-1508466,1519089-1519674

Propchange: sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/sql/SQLUtilitiesTest.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Wed Jan 13 18:31:16 2016
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java?rev=1724476&r1=1724475&r2=1724476&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java [UTF-8] Wed Jan 13 18:31:16 2016
@@ -18,6 +18,7 @@ package org.apache.sis.internal.referenc
 
 import org.apache.sis.util.Static;
 import org.apache.sis.measure.Latitude;
+import org.apache.sis.internal.util.Numerics;
 
 import static java.lang.Math.*;
 import static org.apache.sis.math.MathFunctions.atanh;
@@ -31,7 +32,7 @@ import static org.apache.sis.internal.me
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.7
  * @module
  */
 public final class Formulas extends Static {
@@ -58,6 +59,14 @@ public final class Formulas extends Stat
     public static final double ANGULAR_TOLERANCE = LINEAR_TOLERANCE / (NAUTICAL_MILE * 60);
 
     /**
+     * The maximal longitude value before normalization if a centimetric precision is desired.
+     * This is about 4×10⁸ degrees.
+     *
+     * @see org.apache.sis.measure.Longitude#normalize(double)
+     */
+    public static final double LONGITUDE_MAX = (1L << Numerics.SIGNIFICAND_SIZE) * ANGULAR_TOLERANCE;
+
+    /**
      * The length of a <cite>Julian year</cite> in milliseconds.
      * From Wikipedia, <cite>"In astronomy, a Julian year (symbol: <b>a</b>) is a unit of measurement of time
      * defined as exactly 365.25 days of 86,400 SI seconds each."</cite>.

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java?rev=1724476&r1=1724475&r2=1724476&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java [UTF-8] Wed Jan 13 18:31:16 2016
@@ -168,7 +168,7 @@ public final class Equirectangular exten
         LATITUDE_OF_ORIGIN = createZeroConstant(builder     // Was used by EPSG:9823 (also EPSG:9842).
                 .addIdentifier("8801")
                 .addName("Latitude of natural origin")
-                .addName(Citations.OGC,     "latitude_of_origin")
+                .addName(Citations.OGC,     Constants.LATITUDE_OF_ORIGIN)
                 .addName(Citations.ESRI,    "Latitude_Of_Origin")
                 .addName(Citations.NETCDF,  "latitude_of_projection_origin")
                 .addName(Citations.GEOTIFF, "ProjCenterLat")

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java?rev=1724476&r1=1724475&r2=1724476&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java [UTF-8] Wed Jan 13 18:31:16 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/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java?rev=1724476&r1=1724475&r2=1724476&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] Wed Jan 13 18:31:16 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,7 +126,7 @@ 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
@@ -136,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.
@@ -152,9 +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),
+    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.
@@ -171,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>
@@ -178,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.
@@ -192,9 +209,11 @@ 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.
@@ -210,6 +229,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>28 to 38 in the North hemisphere</td></tr>
      * </table></blockquote>
      *
      * <div class="note"><b>Note:</b>
@@ -217,7 +237,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>
      */
-    ETRS89((short) 4258, (short) 4937, (short) 4936, (short) 6258, (short) 7019),
+    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.
@@ -231,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
@@ -251,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.
@@ -288,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.
@@ -316,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.
@@ -324,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<>();
     }
 
     /**
@@ -351,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();
+        }
     }
 
     /**
@@ -667,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 {
@@ -707,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 {
@@ -767,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));
+    }
+
 
 
 
@@ -1402,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/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=1724476&r1=1724475&r2=1724476&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] Wed Jan 13 18:31:16 2016
@@ -32,9 +32,17 @@ import org.opengis.referencing.cs.AxisDi
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.cs.EllipsoidalCS;
+import org.opengis.referencing.cs.CartesianCS;
 import org.opengis.referencing.crs.GeographicCRS;
+import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.crs.VerticalCRS;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.util.NoSuchIdentifierException;
+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.metadata.iso.extent.Extents;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.referencing.datum.DefaultEllipsoid;
@@ -47,6 +55,9 @@ import org.apache.sis.referencing.cs.Def
 import org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis;
 import org.apache.sis.referencing.crs.DefaultGeographicCRS;
 import org.apache.sis.referencing.crs.DefaultVerticalCRS;
+import org.apache.sis.referencing.crs.DefaultProjectedCRS;
+import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.Latitude;
 
@@ -81,15 +92,17 @@ final class StandardDefinitions {
     /**
      * Returns a map of properties for the given EPSG code, name and alias.
      *
-     * @param  code  The EPSG code.
+     * @param  code  The EPSG code, or 0 if none.
      * @param  name  The object name.
      * @param  alias The alias, or {@code null} if none.
      * @param  world {@code true} if the properties shall have an entry for the domain of validity.
      * @return The map of properties to give to constructors or factory methods.
      */
-    private static Map<String,Object> properties(final short code, final String name, final String alias, final boolean world) {
+    private static Map<String,Object> properties(final int code, final String name, final String alias, final boolean world) {
         final Map<String,Object> map = new HashMap<>(8);
-        map.put(IDENTIFIERS_KEY, new NamedIdentifier(Citations.EPSG, String.valueOf(code)));
+        if (code != 0) {
+            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.
         if (world) {
@@ -110,6 +123,36 @@ 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.
+     *
+     * @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 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,
+            final double latitude, final double longitude, final CartesianCS derivedCS)
+    {
+        final OperationMethod method;
+        try {
+            method = DefaultFactories.forBuildin(MathTransformFactory.class,
+                                          DefaultMathTransformFactory.class)
+                    .getOperationMethod(TransverseMercator.NAME);
+        } catch (NoSuchIdentifierException e) {
+            throw new IllegalStateException(e);     // Should not happen with SIS implementation.
+        }
+        final ParameterValueGroup parameters = method.getParameters().createValue();
+        String name = TransverseMercator.setParameters(parameters, true, latitude, longitude);
+        final DefaultConversion conversion = new DefaultConversion(properties(0, name, null, false), method, null, parameters);
+
+        name = baseCRS.getName().getCode() + " / " + name;
+        return new DefaultProjectedCRS(properties(code, name, null, false), baseCRS, conversion, derivedCS);
+    }
+
+    /**
      * Creates a geodetic CRS from hard-coded values for the given code.
      *
      * @param  code  The EPSG code.
@@ -271,6 +314,7 @@ final class StandardDefinitions {
             case 6422: name = "Ellipsoidal 2D"; dim = 2; axisCode = 108; break;
             case 6423: name = "Ellipsoidal 3D"; dim = 3; axisCode = 111; 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;
             default:   throw new AssertionError(code);
         }
         final Map<String,?> properties = properties(code, name, null, false);
@@ -283,12 +327,17 @@ final class StandardDefinitions {
             case 0:  break;
         }
         if (isCartesian) {
-            return new DefaultCartesianCS(properties, xAxis, yAxis, zAxis);
-        }
-        if (zAxis != null) {
-            return new DefaultEllipsoidalCS(properties, xAxis, yAxis, zAxis);
+            if (zAxis != null) {
+                return new DefaultCartesianCS(properties, xAxis, yAxis, zAxis);
+            } else {
+                return new DefaultCartesianCS(properties, xAxis, yAxis);
+            }
         } else {
-            return new DefaultEllipsoidalCS(properties, xAxis, yAxis);
+            if (zAxis != null) {
+                return new DefaultEllipsoidalCS(properties, xAxis, yAxis, zAxis);
+            } else {
+                return new DefaultEllipsoidalCS(properties, xAxis, yAxis);
+            }
         }
     }
 
@@ -306,6 +355,16 @@ final class StandardDefinitions {
         RangeMeaning rm = null;
         final AxisDirection dir;
         switch (code) {
+            case 1:    name = "Easting";
+                       abrv = "E";
+                       unit = SI.METRE;
+                       dir  = AxisDirection.EAST;
+                       break;
+            case 2:    name = "Northing";
+                       abrv = "N";
+                       unit = SI.METRE;
+                       dir  = AxisDirection.NORTH;
+                       break;
             case 108:  // Used in Ellipsoidal 3D.
             case 106:  name = AxisNames.GEODETIC_LATITUDE;
                        abrv = "φ";

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=1724476&r1=1724475&r2=1724476&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] Wed Jan 13 18:31:16 2016
@@ -21,11 +21,11 @@ import java.util.Set;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Collections;
-import java.util.Arrays;
-import java.util.Locale;
+import javax.measure.unit.SI;
 import javax.measure.unit.NonSI;
-import org.opengis.util.NameFactory;
+import javax.measure.unit.Unit;
 import org.opengis.util.FactoryException;
+import org.opengis.util.InternationalString;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
@@ -34,31 +34,43 @@ import org.opengis.referencing.crs.CRSFa
 import org.opengis.referencing.crs.CRSAuthorityFactory;
 import org.opengis.referencing.crs.EngineeringCRS;
 import org.opengis.referencing.crs.GeographicCRS;
+import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.crs.VerticalCRS;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.cs.CSFactory;
 import org.opengis.referencing.cs.CartesianCS;
 import org.opengis.referencing.cs.AxisDirection;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.datum.DatumFactory;
 import org.opengis.referencing.datum.EngineeringDatum;
+import org.apache.sis.internal.referencing.GeodeticObjectBuilder;
 import org.apache.sis.metadata.iso.citation.Citations;
-import org.apache.sis.metadata.iso.citation.DefaultCitation;
-import org.apache.sis.internal.simple.SimpleIdentifier;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.Constants;
+import org.apache.sis.measure.Units;
 import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.referencing.cs.AxisFilter;
+import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.iso.DefaultNameSpace;
+import org.apache.sis.util.iso.SimpleInternationalString;
+
+import static org.apache.sis.internal.referencing.provider.TransverseMercator.zone;
+import static org.apache.sis.internal.referencing.provider.TransverseMercator.centralMeridian;
 
 
 /**
- * Creates coordinate reference systems in the "{@code OGC}" or "{@code CRS}" namespace.
+ * Creates coordinate reference systems in the "{@code OGC}", "{@code CRS}" or {@code "AUTO(2)"} namespaces.
  * All namespaces recognized by this factory are defined by the Open Geospatial Consortium (OGC).
  * Most codes map to one of the constants in the {@link CommonCRS} enumeration.
  *
  * <table class="sis">
  *   <caption>Recognized Coordinate Reference System codes</caption>
  *   <tr>
+ *     <th>Namespace</th>
  *     <th>Code</th>
  *     <th>Name</th>
  *     <th>Datum type</th>
@@ -66,6 +78,7 @@ import org.apache.sis.util.CharSequences
  *     <th>Axis direction</th>
  *     <th>Units</th>
  *   </tr><tr>
+ *     <td>CRS</td>
  *     <td>1</td>
  *     <td>Computer display</td>
  *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultEngineeringCRS Engineering}</td>
@@ -73,6 +86,7 @@ import org.apache.sis.util.CharSequences
  *     <td>(east, south)</td>
  *     <td>pixels</td>
  *   </tr><tr>
+ *     <td>CRS</td>
  *     <td>27</td>
  *     <td>NAD27</td>
  *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS Geographic}</td>
@@ -80,6 +94,7 @@ import org.apache.sis.util.CharSequences
  *     <td>(east, north)</td>
  *     <td>degrees</td>
  *   </tr><tr>
+ *     <td>CRS</td>
  *     <td>83</td>
  *     <td>NAD83</td>
  *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS Geographic}</td>
@@ -87,6 +102,7 @@ import org.apache.sis.util.CharSequences
  *     <td>(east, north)</td>
  *     <td>degrees</td>
  *   </tr><tr>
+ *     <td>CRS</td>
  *     <td>84</td>
  *     <td>WGS84</td>
  *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS Geographic}</td>
@@ -94,42 +110,130 @@ import org.apache.sis.util.CharSequences
  *     <td>(east, north)</td>
  *     <td>degrees</td>
  *   </tr><tr>
+ *     <td>CRS</td>
  *     <td>88</td>
  *     <td>NAVD88</td>
  *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultVerticalCRS Vertical}</td>
  *     <td>{@linkplain org.apache.sis.referencing.cs.DefaultVerticalCS Vertical}</td>
  *     <td>up</td>
  *     <td>metres</td>
+ *   </tr><tr>
+ *     <td>AUTO2</td>
+ *     <td>42001</td>
+ *     <td>WGS 84 / Auto UTM</td>
+ *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS Projected}</td>
+ *     <td>{@linkplain org.apache.sis.referencing.cs.DefaultCartesianCS Cartesian}</td>
+ *     <td>(east, north)</td>
+ *     <td>user-specified</td>
+ *   </tr><tr>
+ *     <td>AUTO2</td>
+ *     <td>42002</td>
+ *     <td>WGS 84 / Auto Tr Mercator</td>
+ *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS Projected}</td>
+ *     <td>{@linkplain org.apache.sis.referencing.cs.DefaultCartesianCS Cartesian}</td>
+ *     <td>(east, north)</td>
+ *     <td>user-specified</td>
+ *   </tr><tr>
+ *     <td>AUTO2</td>
+ *     <td>42003</td>
+ *     <td>WGS 84 / Auto Orthographic</td>
+ *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS Projected}</td>
+ *     <td>{@linkplain org.apache.sis.referencing.cs.DefaultCartesianCS Cartesian}</td>
+ *     <td>(east, north)</td>
+ *     <td>user-specified</td>
+ *   </tr><tr>
+ *     <td>AUTO2</td>
+ *     <td>42004</td>
+ *     <td>WGS 84 / Auto Equirectangular</td>
+ *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS Projected}</td>
+ *     <td>{@linkplain org.apache.sis.referencing.cs.DefaultCartesianCS Cartesian}</td>
+ *     <td>(east, north)</td>
+ *     <td>user-specified</td>
+ *   </tr><tr>
+ *     <td>AUTO2</td>
+ *     <td>42005</td>
+ *     <td>WGS 84 / Auto Mollweide</td>
+ *     <td>{@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS Projected}</td>
+ *     <td>{@linkplain org.apache.sis.referencing.cs.DefaultCartesianCS Cartesian}</td>
+ *     <td>(east, north)</td>
+ *     <td>user-specified</td>
  *   </tr>
  * </table>
  *
  * <div class="section">Note on codes in CRS namespace</div>
- * The format is usually <code>"CRS:</code><var>n</var><code>"</code> where <var>n</var> is a number like 27, 83 or 84.
+ * The format is usually "{@code CRS:}<var>n</var>" where <var>n</var> is a number like 27, 83 or 84.
  * However this factory is lenient and allows the {@code CRS} part to be repeated as in {@code "CRS:CRS84"}.
  * It also accepts {@code "OGC"} as a synonymous of the {@code "CRS"} namespace.
  *
  * <div class="note"><b>Examples:</b>
  * {@code "CRS:27"}, {@code "CRS:83"}, {@code "CRS:84"}, {@code "CRS:CRS84"}, {@code "OGC:CRS84"}.</div>
  *
+ * <div class="section">Note on codes in AUTO(2) namespace</div>
+ * The format is usually "{@code AUTO2:}<var>n</var>,<var>factor</var>,<var>λ₀</var>,<var>φ₀</var>"
+ * where <var>n</var> is a number between 42001 and 42005 inclusive, <var>factor</var> is a conversion
+ * factor from the CRS units to metres (e.g. 0.3048 for a CRS with axes in feet) and (<var>λ₀</var>,<var>φ₀</var>)
+ * is the longitude and latitude of a point in the projection centre.
+ *
+ * <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>
+ *
+ * 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.
+ *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.7
  * @version 0.7
  * @module
  *
  * @see CommonCRS
- * @see Citations#OGC
  */
 public class CommonAuthorityFactory extends GeodeticAuthorityFactory implements CRSAuthorityFactory {
     /**
-     * The codes known to this factory, associated with their CRS type. This is set to an empty map
-     * at {@code CommonAuthorityFactory} construction time, but filled only when first needed.
+     * The {@value} prefix for a code identified by parameters.
+     * This is defined in annexes B.7 to B.11 of WMS 1.3 specification.
+     * The {@code "AUTO(2)"} namespaces are not considered by SIS as real authorities.
      */
-    private final Map<String,Class<?>> codes;
+    private static final String AUTO2 = "AUTO2";
+
+    /**
+     * The bit for saying that a namespace is the legacy {@code "AUTO"} namespace.
+     */
+    private static final int LEGACY_MASK = 0x80000000;
+
+    /**
+     * First code in the AUTO(2) namespace.
+     */
+    private static final int FIRST_PROJECTION_CODE = 42001;
 
     /**
-     * The authority for this factory.
+     * Names of objects in the AUTO(2) namespace for codes from 42001 to 42005 inclusive.
+     * Those names are defined in annexes B.7 to B.11 of WMS 1.3 specification.
+     *
+     * @see #getDescriptionText(String)
+     */
+    private static final String[] PROJECTION_NAMES = {
+        "WGS 84 / Auto UTM",
+        "WGS 84 / Auto Tr. Mercator",
+        "WGS 84 / Auto Orthographic",
+        "WGS 84 / Auto Equirectangular",
+        "WGS 84 / Auto Mollweide"
+    };
+
+    /**
+     * The parameter separator for codes in the {@code "AUTO(2)"} namespace.
+     */
+    private static final char SEPARATOR = ',';
+
+    /**
+     * The codes known to this factory, associated with their CRS type. This is set to an empty map
+     * at {@code CommonAuthorityFactory} construction time and filled only when first needed.
      */
-    private final Citation authority;
+    private final Map<String,Class<?>> codes;
 
     /**
      * The "Computer display" reference system (CRS:1). Created when first needed.
@@ -139,19 +243,14 @@ public class CommonAuthorityFactory exte
     private CoordinateReferenceSystem displayCRS;
 
     /**
+     * The coordinate system for map projection in metres, created when first needed.
+     */
+    private volatile CartesianCS projectedCS;
+
+    /**
      * Constructs a default factory for the {@code CRS} authority.
-     *
-     * @param nameFactory The factory to use for parsing authority code as {@link org.opengis.util.GenericName} instances.
      */
-    public CommonAuthorityFactory(final NameFactory nameFactory) {
-        super(nameFactory);
-        final DefaultCitation c = new DefaultCitation(Citations.OGC);
-        c.setIdentifiers(Arrays.asList(
-                new SimpleIdentifier(null, Constants.OGC, false),
-                new SimpleIdentifier(null, Constants.CRS, false)
-        ));
-        c.freeze();
-        authority = c;
+    public CommonAuthorityFactory() {
         codes = new LinkedHashMap<>();
     }
 
@@ -160,16 +259,82 @@ public class CommonAuthorityFactory exte
      * The authority for this factory is the <cite>Open Geospatial Consortium</cite>.
      *
      * @return The OGC authority.
+     *
+     * @see Citations#OGC
      */
     @Override
     public Citation getAuthority() {
-        return authority;
+        return Citations.OGC;
     }
 
     /**
-     * Provides a complete set of the known codes provided by this authority.
-     * The returned set contains only numeric identifiers like {@code "84"}, {@code "27"}, <i>etc</i>.
-     * The authority name ({@code "CRS"}) is not included in the character strings.
+     * Returns {@code true} if the given portion of the code is equal, ignoring case, to the given namespace.
+     */
+    private static boolean regionMatches(final String namespace, final String code, final int start, final int end) {
+        return (namespace.length() == end - start) && code.regionMatches(true, start, namespace, 0, namespace.length());
+    }
+
+    /**
+     * Returns the index where the code begins, ignoring spaces and the {@code "OGC"}, {@code "CRS"}, {@code "AUTO"},
+     * {@code "AUTO1"} or {@code "AUTO2"} namespaces if present. If a namespace is found and is a legacy one, then
+     * this {@link #LEGACY_MASK} bit will be set.
+     *
+     * @return Index where the code begin, possibly with the {@link #LEGACY_MASK} bit set.
+     * @throws NoSuchAuthorityCodeException if an authority is present but is not one of the recognized authorities.
+     */
+    private static int skipNamespace(final String code) throws NoSuchAuthorityCodeException {
+        int isLegacy = 0;
+        int s = code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR);
+        if (s >= 0) {
+            final int end   = CharSequences.skipTrailingWhitespaces(code, 0, s);
+            final int start = CharSequences.skipLeadingWhitespaces (code, 0, end);
+            if (!regionMatches(Constants.CRS, code, start, end) &&
+                !regionMatches(Constants.OGC, code, start, end))
+            {
+                boolean isRecognized = false;
+                final int length = AUTO2.length() - 1;
+                if (code.regionMatches(true, start, AUTO2, 0, length)) {
+                    switch (end - start - length) {         // Number of extra characters after "AUTO".
+                        case 0: {                           // Namespace is exactly "AUTO" (ignoring case).
+                            isRecognized = true;
+                            isLegacy = LEGACY_MASK;
+                            break;
+                        }
+                        case 1: {                           // Namespace has one more character than "AUTO".
+                            final char c = code.charAt(end - 1);
+                            isRecognized = (c >= '1' && c <= '2');
+                            if (c == '1') {
+                                isLegacy = LEGACY_MASK;
+                            }
+                        }
+                    }
+                }
+                if (!isRecognized) {
+                    throw new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.UnknownAuthority_1,
+                            CharSequences.trimWhitespaces(code.substring(0, s))), Constants.OGC, code);
+                }
+            }
+        }
+        s = CharSequences.skipLeadingWhitespaces(code, s+1, code.length());
+        /*
+         * Above code removed the "CRS" part when it is used as a namespace, as in "CRS:84".
+         * The code below removes the "CRS" prefix when it is concatenated within the code,
+         * as in "CRS84". Together, those two checks handle redundant codes like "CRS:CRS84"
+         * (malformed code, but seen in practice).
+         */
+        if (code.regionMatches(true, s, Constants.CRS, 0, Constants.CRS.length())) {
+            s = CharSequences.skipLeadingWhitespaces(code, s + Constants.CRS.length(), code.length());
+        }
+        if (s >= code.length()) {
+            throw new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.EmptyArgument_1, "code"), Constants.OGC, code);
+        }
+        return s | isLegacy;
+    }
+
+    /**
+     * Provides a complete set of the known codes provided by this factory.
+     * The returned set contains a namespace followed by numeric identifiers
+     * like {@code "CRS:84"}, {@code "CRS:27"}, {@code "AUTO2:42001"}, <i>etc</i>.
      *
      * @param  type The spatial reference objects type.
      * @return The set of authority codes for spatial reference objects of the given type.
@@ -184,11 +349,14 @@ public class CommonAuthorityFactory exte
         }
         synchronized (codes) {
             if (codes.isEmpty()) {
-                add(Constants.CRS84, GeographicCRS.class);   // Put first the codes that are most likely to be requested.
-                add(Constants.CRS83, GeographicCRS.class);
+                add(Constants.CRS1,  EngineeringCRS.class);
                 add(Constants.CRS27, GeographicCRS.class);
+                add(Constants.CRS83, GeographicCRS.class);
+                add(Constants.CRS84, GeographicCRS.class);
                 add(Constants.CRS88, VerticalCRS.class);
-                add(Constants.CRS1,  EngineeringCRS.class);
+                for (int code = FIRST_PROJECTION_CODE; code < FIRST_PROJECTION_CODE + PROJECTION_NAMES.length; code++) {
+                    add(code, ProjectedCRS.class);
+                }
             }
         }
         return all ? Collections.unmodifiableSet(codes.keySet()) : new FilteredCodes(codes, type).keySet();
@@ -197,13 +365,59 @@ public class CommonAuthorityFactory exte
     /**
      * Adds an element in the {@link #codes} map, witch check against duplicated values.
      */
-    private void add(final byte code, final Class<? extends SingleCRS> type) throws FactoryException {
-        if (codes.put(String.valueOf(code).intern(), type) != null) {
+    private void add(final int code, final Class<? extends SingleCRS> type) throws FactoryException {
+        final String namespace = (code >= FIRST_PROJECTION_CODE) ? AUTO2 : Constants.CRS;
+        if (codes.put(namespace + DefaultNameSpace.DEFAULT_SEPARATOR + code, type) != null) {
             throw new FactoryException();    // Should never happen, but we are paranoiac.
         }
     }
 
     /**
+     * Returns a description of the object corresponding to a code.
+     * The description can be used for example in a combo box in a graphical user interface.
+     *
+     * <p>Codes in the {@code "AUTO(2)"} namespace do not need parameters for this method.
+     * But if parameters are nevertheless specified, then they will be taken in account.</p>
+     *
+     * <table class="sis">
+     *   <caption>Examples</caption>
+     *   <tr><th>Argument value</th>                <th>Return value</th></tr>
+     *   <tr><td>{@code CRS:84}</td>                <td>WGS 84</td></tr>
+     *   <tr><td>{@code AUTO2:42001}</td>           <td>WGS 84 / Auto UTM</td></tr>
+     *   <tr><td>{@code AUTO2:42001,1,-100,45}</td> <td>WGS 84 / UTM zone 47N</td></tr>
+     * </table>
+     *
+     * @param  code Value in the CRS or AUTO(2) code space.
+     * @return A description of the object.
+     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
+     * @throws FactoryException if an error occurred while fetching the description.
+     */
+    @Override
+    public InternationalString getDescriptionText(final String code) throws FactoryException {
+        final int s = skipNamespace(code) & ~LEGACY_MASK;
+        final String localCode = code.substring(s, CharSequences.skipTrailingWhitespaces(code, s, code.length()));
+        if (localCode.indexOf(SEPARATOR) < 0) {
+            /*
+             * For codes in the "AUTO(2)" namespace without parameters, we can not rely on the default implementation
+             * since it would fail to create the ProjectedCRS instance. Instead we return a generic description.
+             * Note that we do not execute this block if parametes were specified. If there is parameters,
+             * then we instead rely on the default implementation for a more accurate description text.
+             */
+            final int codeValue;
+            try {
+                codeValue = Integer.parseInt(localCode);
+            } catch (NumberFormatException exception) {
+                throw noSuchAuthorityCode(localCode, code, exception);
+            }
+            final int i = codeValue - FIRST_PROJECTION_CODE;
+            if (i >= 0 && i < PROJECTION_NAMES.length) {
+                return new SimpleInternationalString(PROJECTION_NAMES[i]);
+            }
+        }
+        return new SimpleInternationalString(createCoordinateReferenceSystem(localCode).getName().getCode());
+    }
+
+    /**
      * Creates an object from the specified code.
      * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)}.
      *
@@ -216,41 +430,223 @@ public class CommonAuthorityFactory exte
 
     /**
      * Creates a coordinate reference system from the specified code.
+     * This method performs the following steps:
      *
+     * <ol>
+     *   <li>Skip the {@code "OGC"}, {@code "CRS"}, {@code "AUTO"}, {@code "AUTO1"} or {@code "AUTO2"} namespace
+     *       if present (ignoring case). All other namespaces will cause an exception to be thrown.</li>
+     *   <li>Skip the {@code "CRS"} prefix if present. This additional check is for accepting codes like
+     *       {@code "OGC:CRS84"} (not a valid CRS code, but seen in practice).</li>
+     *   <li>In the remaining text, interpret the integer value as documented in this class javadoc.
+     *       Note that some codes require coma-separated parameters after the integer value.</li>
+     * </ol>
+     *
+     * @param  code Value allocated by OGC.
+     * @return The coordinate reference system for the given code.
      * @throws FactoryException if the object creation failed.
      */
     @Override
     public CoordinateReferenceSystem createCoordinateReferenceSystem(final String code) throws FactoryException {
-        String c = trimAuthority(code, authority).toUpperCase(Locale.US);
-        if (c.startsWith(Constants.CRS)) {
-            /*
-             * "trimAuthority" removed "CRS" when it was separated from the code, as in "CRS:84".
-             * This block removes "CRS" when it is concatenated with the code, as in "CRS84".
-             */
-            c = CharSequences.trimWhitespaces(c.substring(Constants.CRS.length()));
+        ArgumentChecks.ensureNonNull("code", code);
+        final String localCode;
+        final boolean isLegacy;
+        String complement = null;
+        { // Block for keeping 'start' and 'end' variables locale.
+            int start = skipNamespace(code);
+            isLegacy = (start & LEGACY_MASK) != 0;
+            start &= ~LEGACY_MASK;
+            final int startOfParameters = code.indexOf(SEPARATOR, start);
+            int end = CharSequences.skipTrailingWhitespaces(code, start, code.length());
+            if (startOfParameters >= 0) {
+                complement = code.substring(startOfParameters + 1);
+                end = CharSequences.skipTrailingWhitespaces(code, start, startOfParameters);
+            }
+            localCode = code.substring(start, end);
         }
-        final int n;
+        int codeValue = 0;
+        double[] parameters = ArraysExt.EMPTY_DOUBLE;
         try {
-            n = Integer.parseInt(c);
+            codeValue = Integer.parseInt(localCode);
+            if (complement != null) {
+                parameters = CharSequences.parseDoubles(complement, SEPARATOR);
+            }
         } catch (NumberFormatException exception) {
-            // If a number can not be parsed, then this is an invalid authority code.
-            NoSuchAuthorityCodeException e = noSuchAuthorityCode(CoordinateReferenceSystem.class, code);
-            e.initCause(exception);
-            throw e;
+            throw noSuchAuthorityCode(localCode, code, exception);
+        }
+        /*
+         * At this point we have isolated the code value from the parameters (if any). Verify the number of arguments.
+         * Then codes in the AUTO(2) namespace are delegated to a separated method while codes in the CRS namespaces
+         * are handled below.
+         */
+        final int count = parameters.length;
+        if (codeValue >= FIRST_PROJECTION_CODE) {
+            int expected;
+            short errorKey = 0;
+            if (count < (expected = 2)) {
+                errorKey = Errors.Keys.TooFewArguments_2;
+            } else if (count > (expected = 3)) {
+                errorKey = Errors.Keys.TooManyArguments_2;
+            }
+            if (errorKey == 0) {
+                return createAuto(code, codeValue, isLegacy,
+                        (count > 2) ? parameters[0] : isLegacy ? Constants.EPSG_METRE : 1,
+                                      parameters[count - 2],
+                                      parameters[count - 1]);
+            }
+            throw new NoSuchAuthorityCodeException(Errors.format(errorKey, expected, count), AUTO2, localCode, code);
+        }
+        if (count != 0) {
+            throw new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.UnexpectedCharactersAfter_2,
+                    localCode, complement), Constants.CRS, localCode, code);
         }
         final CommonCRS crs;
-        switch (n) {
+        switch (codeValue) {
             case Constants.CRS1:  return displayCRS();
             case Constants.CRS84: crs = CommonCRS.WGS84; break;
             case Constants.CRS83: crs = CommonCRS.NAD83; break;
             case Constants.CRS27: crs = CommonCRS.NAD27; break;
             case Constants.CRS88: return CommonCRS.Vertical.NAVD88.crs();
-            default: throw noSuchAuthorityCode(CoordinateReferenceSystem.class, code);
+            default: throw noSuchAuthorityCode(localCode, code, null);
         }
         return crs.normalizedGeographic();
     }
 
     /**
+     * Creates a projected CRS from parameters in the {@code AUTO(2)} namespace.
+     *
+     * @param  code        The user-specified code, used only for error reporting.
+     * @param  projection  The projection code (e.g. 42001).
+     * @param  isLegacy    {@code true} if the code was found in {@code "AUTO"} or {@code "AUTO1"} namespace.
+     * @param  factor      The multiplication factor for the unit of measurement.
+     * @param  longitude   A longitude in the desired projection zone.
+     * @param  latitude    A latitude in the desired projection zone.
+     * @return The projected CRS for the given projection and parameters.
+     */
+    @SuppressWarnings("null")
+    private ProjectedCRS createAuto(final String code, final int projection, final boolean isLegacy,
+            final double factor, final double longitude, final double latitude) throws FactoryException
+    {
+        Boolean isUTM = null;
+        String method = null;
+        String param  = null;
+        switch (projection) {
+            /*
+             * 42001: Universal Transverse Mercator   —   central meridian must be in the center of a UTM zone.
+             * 42002: Transverse Mercator             —   like 42001 except that central meridian can be anywhere.
+             * 42003: WGS 84 / Auto Orthographic      —   defined by "Central_Meridian" and "Latitude_of_Origin".
+             * 42004: WGS 84 / Auto Equirectangular   —   defined by "Central_Meridian" and "Standard_Parallel_1".
+             * 42005: WGS 84 / Auto Mollweide         —   defined by "Central_Meridian" only.
+             */
+            case 42001: isUTM  = true; break;
+            case 42002: isUTM  = (latitude == 0) && (centralMeridian(zone(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;
+            default: throw noSuchAuthorityCode(String.valueOf(projection), code, null);
+        }
+        /*
+         * 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.
+         */
+        final CommonCRS datum = CommonCRS.WGS84;
+        final GeographicCRS baseCRS;
+        final ProjectedCRS crs;
+        CartesianCS cs;
+        try {
+            if (isUTM != null && isUTM) {
+                crs = datum.UTM(latitude, longitude);
+                if (factor == (isLegacy ? Constants.EPSG_METRE : 1)) {
+                    return crs;
+                }
+                baseCRS = crs.getBaseCRS();
+                cs = crs.getCoordinateSystem();
+            } else {
+                cs = projectedCS;
+                if (cs == null) {
+                    crs = datum.UTM(latitude, longitude);
+                    projectedCS = cs = crs.getCoordinateSystem();
+                    baseCRS = crs.getBaseCRS();
+                } else {
+                    crs = null;
+                    baseCRS = datum.geographic();
+                }
+            }
+            /*
+             * At this point we got a coordinate system with axes in metres.
+             * If the user asked for another unit of measurement, change the axes now.
+             */
+            final Unit<?> unit;
+            if (isLegacy) {
+                unit = createUnitFromEPSG(factor);
+            } else {
+                unit = (factor != 1) ? Units.multiply(SI.METRE, factor) : SI.METRE;
+            }
+            if (!SI.METRE.equals(unit)) {
+                cs = (CartesianCS) CoordinateSystems.replaceAxes(cs, new AxisFilter() {
+                    @Override public Unit<?> getUnitReplacement(Unit<?> ignored) {
+                        assert SI.METRE.equals(ignored) : ignored;
+                        return unit;
+                    }
+
+                    @Override public boolean accept(CoordinateSystemAxis axis) {
+                        return true;
+                    }
+
+                    @Override public AxisDirection getDirectionReplacement(AxisDirection direction) {
+                        return direction;
+                    }
+                });
+            }
+            /*
+             * Set the projection name, operation method and parameters. The parameters for the Transverse Mercator
+             * projection are a little bit more tedious to set, so we use a convenience method for that.
+             */
+            final GeodeticObjectBuilder builder = new GeodeticObjectBuilder();
+            if (isUTM != null) {
+                if (isUTM) {
+                    builder.addName(crs.getName());
+                } // else default to the conversion name, which is "Transverse Mercator".
+                builder.setTransverseMercator(isUTM, latitude, longitude);
+            } else {
+                builder.setConversionMethod(method)
+                       .addName(PROJECTION_NAMES[projection - FIRST_PROJECTION_CODE])
+                       .setParameter(Constants.CENTRAL_MERIDIAN, longitude, NonSI.DEGREE_ANGLE);
+                if (param != null) {
+                    builder.setParameter(param, latitude, NonSI.DEGREE_ANGLE);
+                }
+            }
+            return builder.createProjectedCRS(baseCRS, cs);
+        } catch (IllegalArgumentException e) {
+            throw noSuchAuthorityCode(String.valueOf(projection), code, e);
+        }
+    }
+
+    /**
+     * Returns the unit of measurement for the given EPSG code.
+     * This is used only for codes in the legacy {@code "AUTO"} namespace.
+     */
+    private static Unit<?> createUnitFromEPSG(final double code) throws NoSuchAuthorityCodeException {
+        String message = null;      // Error message to be used only in case of failure.
+        final String s;             // The string representation of the code, to be used only in case of failure.
+        final int c = (int) code;
+        if (c == code) {
+            final Unit<?> unit = Units.valueOfEPSG(c);
+            if (Units.isLinear(unit)) {
+                return unit;
+            } else if (unit != null) {
+                message = Errors.format(Errors.Keys.NonLinearUnit_1, unit);
+            }
+            s = String.valueOf(c);
+        } else {
+            s = String.valueOf(code);
+        }
+        if (message == null) {
+            message = Errors.format(Errors.Keys.NoSuchAuthorityCode_3, Constants.EPSG, Unit.class, s);
+        }
+        throw new NoSuchAuthorityCodeException(message, Constants.EPSG, s);
+    }
+
+    /**
      * Returns the "Computer display" reference system (CRS:1). This is rarely used.
      */
     private synchronized CoordinateReferenceSystem displayCRS() throws FactoryException {
@@ -269,4 +665,18 @@ public class CommonAuthorityFactory exte
         }
         return displayCRS;
     }
+
+    /**
+     * Creates an exception for an unknown authority code.
+     *
+     * @param  localCode  The unknown authority code, without namespace.
+     * @param  code       The unknown authority code as specified by the user (may include namespace).
+     * @param  cause      The failure cause, or {@code null} if none.
+     * @return An exception initialized with an error message built from the specified informations.
+     */
+    private static NoSuchAuthorityCodeException noSuchAuthorityCode(String localCode, String code, Exception cause) {
+        return (NoSuchAuthorityCodeException) new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.NoSuchAuthorityCode_3,
+                Constants.OGC, CoordinateReferenceSystem.class, localCode),
+                Constants.OGC, localCode, code).initCause(cause);
+    }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java?rev=1724476&r1=1724475&r2=1724476&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java [UTF-8] Wed Jan 13 18:31:16 2016
@@ -39,7 +39,6 @@ import org.opengis.referencing.datum.*;
 import org.opengis.referencing.operation.*;
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
-import org.opengis.util.NameFactory;
 import org.opengis.util.FactoryException;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.extent.Extent;
@@ -249,10 +248,9 @@ public abstract class ConcurrentAuthorit
      * on experience gained.
      *
      * @param dataAccessClass The class of Data Access Object (DAO) created by {@link #newDataAccess()}.
-     * @param nameFactory The factory to use for parsing authority codes as {@link org.opengis.util.GenericName} instances.
      */
-    protected ConcurrentAuthorityFactory(Class<DAO> dataAccessClass, NameFactory nameFactory) {
-        this(dataAccessClass, nameFactory, 100, 8);
+    protected ConcurrentAuthorityFactory(Class<DAO> dataAccessClass) {
+        this(dataAccessClass, 100, 8);
         /*
          * NOTE: if the default maximum number of Data Access Objects (currently 8) is augmented,
          * make sure to augment the number of runner threads in the "StressTest" class to a greater amount.
@@ -262,19 +260,17 @@ public abstract class ConcurrentAuthorit
     /**
      * Constructs an instance with the specified number of entries to keep by strong references.
      * If a number of object greater than {@code maxStrongReferences} are created, then the strong references
-     * for the eldest ones will be replaced by weak references.
+     * for the eldest objects will be replaced by weak references.
      *
      * @param dataAccessClass The class of Data Access Object (DAO) created by {@link #newDataAccess()}.
-     * @param nameFactory The factory to use for parsing authority code as {@link org.opengis.util.GenericName} instances.
      * @param maxStrongReferences The maximum number of objects to keep by strong reference.
      * @param maxConcurrentQueries The maximal amount of Data Access Objects to use concurrently.
      *        If more than this amount of threads are querying this {@code ConcurrentAuthorityFactory} concurrently,
      *        additional threads will be blocked until a Data Access Object become available.
      */
     protected ConcurrentAuthorityFactory(final Class<DAO> dataAccessClass,
-            final NameFactory nameFactory, final int maxStrongReferences, final int maxConcurrentQueries)
+            final int maxStrongReferences, final int maxConcurrentQueries)
     {
-        super(nameFactory);
         ArgumentChecks.ensureNonNull("dataAccessClass", dataAccessClass);
         ArgumentChecks.ensurePositive("maxStrongReferences", maxStrongReferences);
         ArgumentChecks.ensureStrictlyPositive("maxConcurrentQueries", maxConcurrentQueries);

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java?rev=1724476&r1=1724475&r2=1724476&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java [UTF-8] Wed Jan 13 18:31:16 2016
@@ -28,18 +28,14 @@ import org.opengis.metadata.Identifier;
 import org.opengis.metadata.extent.Extent;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.parameter.ParameterDescriptor;
-import org.opengis.util.ScopedName;
-import org.opengis.util.GenericName;
-import org.opengis.util.NameFactory;
 import org.opengis.util.FactoryException;
 import org.opengis.util.InternationalString;
 import org.apache.sis.internal.util.Citations;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.util.iso.DefaultNameSpace;
 import org.apache.sis.util.iso.AbstractFactory;
-import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.resources.Errors;
-import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Debug;
@@ -69,28 +65,9 @@ import org.apache.sis.util.Debug;
  */
 public abstract class GeodeticAuthorityFactory extends AbstractFactory implements AuthorityFactory {
     /**
-     * The factory to use for parsing authority code as {@link GenericName} instances.
-     */
-    protected final NameFactory nameFactory;
-
-    /**
      * Creates a new authority factory for geodetic objects.
-     *
-     * @param nameFactory The factory to use for parsing authority codes as {@link GenericName} instances.
-     */
-    protected GeodeticAuthorityFactory(final NameFactory nameFactory) {
-        ArgumentChecks.ensureNonNull("nameFactory", nameFactory);
-        this.nameFactory = nameFactory;
-    }
-
-    /**
-     * Creates a new authority factory with the same configuration than the given factory.
-     *
-     * @param parent The factory from which to copy the configuration.
      */
-    protected GeodeticAuthorityFactory(final GeodeticAuthorityFactory parent) {
-        ArgumentChecks.ensureNonNull("parent", parent);
-        nameFactory = parent.nameFactory;
+    protected GeodeticAuthorityFactory() {
     }
 
     /**
@@ -134,6 +111,10 @@ public abstract class GeodeticAuthorityF
      * This may be costly since it involves a full object creation.
      * Subclasses are encouraged to provide a more efficient implementation if they can.
      *
+     * @param  code Value allocated by authority.
+     * @return A description of the object, or {@code null} if the object
+     *         corresponding to the specified {@code code} has no description.
+     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
      * @throws FactoryException if an error occurred while fetching the description.
      */
     @Override
@@ -1144,42 +1125,36 @@ public abstract class GeodeticAuthorityF
     }
 
     /**
-     * Trims the authority scope, if presents. For example if this factory is an EPSG authority factory
+     * Trims the authority scope, if present. For example if this factory is an EPSG authority factory
      * and the specified code start with the {@code "EPSG:"} prefix, then the prefix is removed.
      * Otherwise, the string is returned unchanged (except for leading and trailing spaces).
      *
      * @param  code The code to trim.
-     * @return The code without the authority scope.
+     * @param  authority The authority factory, or {@code null} for {@link #getAuthority()}.
+     * @return The code with the authority part removed if that part matched the expected authority.
      */
-    final String trimAuthority(String code, Citation authority) {
-        code = code.trim();
-        final GenericName name = nameFactory.parseGenericName(null, code);
-        if (name instanceof ScopedName) {
-            final GenericName scope = ((ScopedName) name).path();
+    final String trimAuthority(final String code, Citation authority) {
+        int s = code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR);
+        if (s >= 0) {
             if (authority == null) {
                 authority = getAuthority();     // Costly operation for EPSGDataAccess.
             }
-            if (Citations.identifierMatches(authority, null, scope.toString().trim())) {
-                return CharSequences.trimWhitespaces(name.tip().toString().trim());
+            if (Citations.identifierMatches(authority, null, code.substring(0, s))) {    // Comparison ignores spaces.
+                final int n = code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR, s + 1);
+                if (n >= 0) {
+                    /*
+                     * The separator sometime appears twice, as in "EPSG::4326" or "EPSG:8.8:4326".
+                     * The part between the two separators is the verion number, which we ignore in
+                     * this simple version.
+                     */
+                    s = n;
+                }
+                final int length = code.length();
+                s = CharSequences.skipLeadingWhitespaces(code, s+1, length);
+                return code.substring(s, CharSequences.skipTrailingWhitespaces(code, s, length));
             }
         }
-        return code;
-    }
-
-    /**
-     * Creates an exception for an unknown authority code.
-     * This convenience method is provided for implementation of {@code createFoo(String)} methods in subclasses.
-     *
-     * @param  type  The GeoAPI interface that was to be created (e.g. {@code CoordinateReferenceSystem.class}).
-     * @param  code  The unknown authority code.
-     * @return An exception initialized with an error message built from the specified informations.
-     */
-    final NoSuchAuthorityCodeException noSuchAuthorityCode(final Class<?> type, final String code) {
-        final Citation authority = getAuthority();
-        final String name = Citations.getIdentifier(authority, false);
-        return new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.NoSuchAuthorityCode_3,
-                   (name != null) ? name : Vocabulary.formatInternational(Vocabulary.Keys.Untitled),
-                   type, code), name, trimAuthority(code, authority), code);
+        return CharSequences.trimWhitespaces(code);
     }
 
     /**




Mime
View raw message