sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1785863 [2/4] - in /sis/branches/JDK7: ./ application/sis-console/src/main/java/org/apache/sis/console/ core/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ core/sis...
Date Tue, 07 Mar 2017 17:45:00 GMT
Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/GeodeticObjectBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/GeodeticObjectBuilder.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/GeodeticObjectBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/GeodeticObjectBuilder.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -45,6 +45,7 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.referencing.provider.TransverseMercator;
+import org.apache.sis.internal.referencing.provider.PolarStereographicA;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.referencing.Builder;
 import org.apache.sis.referencing.CommonCRS;
@@ -230,27 +231,68 @@ public class GeodeticObjectBuilder exten
      *
      * <blockquote><table class="sis">
      *   <caption>Transverse Mercator parameters</caption>
-     *   <tr><th>Parameter name</th>                 <th>Value</th></tr>
+     *   <tr><th>Parameter name</th>                 <th>Parameter value</th></tr>
      *   <tr><td>Latitude of natural origin</td>     <td>Given latitude, snapped to 0° in the UTM case</td></tr>
      *   <tr><td>Longitude of natural origin</td>    <td>Given longitude, optionally snapped to a UTM zone</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>Scale factor at natural origin</td> <td>0.9996 in UTM case</td></tr>
+     *   <tr><td>False easting</td>                  <td>500000 metres in UTM case</td></tr>
      *   <tr><td>False northing</td>                 <td>0 (North hemisphere) or 10000000 (South hemisphere) metres</td></tr>
      * </table></blockquote>
      *
-     * @param  isUTM      if {@code true}, the given central meridian will be snapped to the central meridian of a UTM zone.
+     * Note that calculation of UTM zone contains special cases for Norway and Svalbard.
+     * If not desired, those exceptions can be avoided by making sure that the given latitude is below 56°N.
+     *
+     * <p>If the given {@code zoner} is {@link TransverseMercator.Zoner#ANY ANY}, then this method will use the given
+     * latitude and longitude verbatim (without snapping them to a zone) but will still use the UTM scale factor,
+     * false easting and false northing.
+     *
+     * @param  zoner      whether to use UTM or MTM zones, or {@code ANY} for using arbitrary central meridian.
      * @param  latitude   the latitude in the center of the desired projection.
      * @param  longitude  the longitude in the center of the desired projection.
      * @return {@code this}, for method calls chaining.
      * @throws FactoryException if the operation method for the Transverse Mercator projection can not be obtained.
+     *
+     * @see CommonCRS#UTM(double, double)
      */
-    public GeodeticObjectBuilder setTransverseMercator(boolean isUTM, double latitude, double longitude)
+    public GeodeticObjectBuilder setTransverseMercator(TransverseMercator.Zoner zoner, double latitude, double longitude)
             throws FactoryException
     {
         ArgumentChecks.ensureBetween("latitude",   Latitude.MIN_VALUE,     Latitude.MAX_VALUE,     latitude);
         ArgumentChecks.ensureBetween("longitude", -Formulas.LONGITUDE_MAX, Formulas.LONGITUDE_MAX, longitude);
         setConversionMethod(TransverseMercator.NAME);
-        setConversionName(TransverseMercator.Zoner.UTM.setParameters(parameters, isUTM, latitude, longitude));
+        setConversionName(zoner.setParameters(parameters, latitude, longitude));
+        return this;
+    }
+
+    /**
+     * Sets the operation method, parameters and conversion name for a Polar Stereographic projection.
+     * This convenience method delegates to the following methods:
+     *
+     * <ul>
+     *   <li>{@link #setConversionName(String)} with a name like <cite>"Universal Polar Stereographic North"</cite>,
+     *       depending on the argument given to this method.</li>
+     *   <li>{@link #setConversionMethod(String)} with the name of the Polar Stereographic (variant A) projection method.</li>
+     *   <li>{@link #setParameter(String, double, Unit)} for each of the parameters enumerated below:</li>
+     * </ul>
+     *
+     * <blockquote><table class="sis">
+     *   <caption>Universal Polar Stereographic parameters</caption>
+     *   <tr><th>Parameter name</th>                 <th>Parameter value</th></tr>
+     *   <tr><td>Latitude of natural origin</td>     <td>90°N or 90°S</td></tr>
+     *   <tr><td>Longitude of natural origin</td>    <td>0°</td></tr>
+     *   <tr><td>Scale factor at natural origin</td> <td>0.994</td></tr>
+     *   <tr><td>False easting</td>                  <td>2000000 metres</td></tr>
+     *   <tr><td>False northing</td>                 <td>2000000 metres</td></tr>
+     * </table></blockquote>
+     *
+     * @param  north  {@code true} for North pole, or {@code false} for South pole.
+     * @return {@code this}, for method calls chaining.
+     * @throws FactoryException if the operation method for the Polar Stereographic (variant A)
+     *         projection can not be obtained.
+     */
+    public GeodeticObjectBuilder setPolarStereographic(final boolean north) throws FactoryException {
+        setConversionMethod(PolarStereographicA.NAME);
+        setConversionName(PolarStereographicA.setParameters(parameters, north));
         return this;
     }
 

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -321,6 +321,11 @@ public final class Resources extends Ind
         public static final short NoSuchOperationMethod_1 = 50;
 
         /**
+         * No horizontal component found in the “{0}” coordinate reference system.
+         */
+        public static final short NonHorizontalCRS_1 = 71;
+
+        /**
          * Non invertible {0}×{1} matrix.
          */
         public static final short NonInvertibleMatrix_2 = 51;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties [ISO-8859-1] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties [ISO-8859-1] Tue Mar  7 17:44:59 2017
@@ -73,6 +73,7 @@ MissingSpatioTemporalDimension_1  = No s
 MissingValueForParameter_1        = Missing value for \u201c{0}\u201d parameter.
 NoConvergence                     = No convergence.
 NoConvergenceForPoints_2          = No convergence for points {0} and {1}.
+NonHorizontalCRS_1                = No horizontal component found in the \u201c{0}\u201d coordinate reference system.
 NonInvertibleMatrix_2             = Non invertible {0}\u00d7{1} matrix.
 NonInvertibleOperation_1          = Can not invert the \u201c{0}\u201d operation.
 NonInvertibleTransform            = Transform is not invertible.

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties [ISO-8859-1] Tue Mar  7 17:44:59 2017
@@ -78,6 +78,7 @@ MissingSpatioTemporalDimension_1  = Aucu
 MissingValueForParameter_1        = Aucune valeur n\u2019a \u00e9t\u00e9 d\u00e9finie pour le param\u00e8tre \u00ab\u202f{0}\u202f\u00bb.
 NoConvergence                     = Le calcul ne converge pas.
 NoConvergenceForPoints_2          = Le calcul ne converge pas pour les points {0} et {1}.
+NonHorizontalCRS_1                = Aucune composante horizontale n\u2019a \u00e9t\u00e9 trouv\u00e9e dans le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es \u00ab\u202f{0}\u202f\u00bb.
 NonInvertibleMatrix_2             = Matrice {0}\u00d7{1} non inversible.
 NonInvertibleOperation_1          = Ne peut pas inverser l\u2019op\u00e9ration \u00ab\u202f{0}\u202f\u00bb.
 NonInvertibleTransform            = La transformation n\u2019est pas inversible.

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -22,6 +22,8 @@ import java.util.Iterator;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Locale;
+import java.util.TimeZone;
+import java.text.Format;
 import javax.measure.Unit;
 import javax.measure.quantity.Length;
 
@@ -57,9 +59,12 @@ import org.opengis.metadata.citation.Onl
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.opengis.metadata.extent.GeographicExtent;
 import org.opengis.metadata.extent.VerticalExtent;
+import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.Envelope;
 
 import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.geometry.CoordinateFormat;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.IdentifiedObjects;
@@ -101,7 +106,7 @@ import org.apache.sis.internal.jdk8.JDK8
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.7
+ * @version 0.8
  * @module
  */
 public final class ServicesForMetadata extends ReferencingServices {
@@ -393,6 +398,21 @@ public final class ServicesForMetadata e
         }
     }
 
+    /**
+     * Creates a two-dimensional geographic position associated to the default geographic CRS.
+     * Axis order is (longitude, latitude).
+     *
+     * @param  λ  the longitude value.
+     * @param  φ  the latitude value.
+     * @return the direct position for the given geographic coordinate.
+     *
+     * @since 0.8
+     */
+    @Override
+    public DirectPosition geographic(final double λ, final double φ) {
+        return new DirectPosition2D(CommonCRS.defaultGeographic(), λ, φ);
+    }
+
 
 
 
@@ -561,6 +581,20 @@ public final class ServicesForMetadata e
     }
 
     /**
+     * Creates a format for {@link DirectPosition} instances.
+     *
+     * @param  locale    the locale for the new {@code Format}, or {@code null} for {@code Locale.ROOT}.
+     * @param  timezone  the timezone, or {@code null} for UTC.
+     * @return a {@link org.apache.sis.geometry.CoordinateFormat}.
+     *
+     * @since 0.8
+     */
+    @Override
+    public Format createCoordinateFormat(final Locale locale, final TimeZone timezone) {
+        return new CoordinateFormat(locale, timezone);
+    }
+
+    /**
      * Returns an axis direction from a pole along a meridian.
      * The given meridian is usually, but not necessarily, relative to the Greenwich meridian.
      *

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -17,14 +17,21 @@
 package org.apache.sis.internal.referencing.provider;
 
 import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.internal.util.Constants;
+import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.measure.Latitude;
+import org.apache.sis.measure.Units;
 
 
 /**
  * The provider for <cite>"Polar Stereographic (Variant A)"</cite> projection (EPSG:9810).
+ * Also used for the definition of Universal Polar Stereographic (UPS) projection.
  *
  * @author  Rueben Schulz (UBC)
  * @author  Martin Desruisseaux (Geomatys)
@@ -101,4 +108,62 @@ public final class PolarStereographicA e
     public PolarStereographicA() {
         super(PARAMETERS);
     }
+
+    /**
+     * False Easting and false Northing value used in Universal Polar Stereographic (UPS) projections.
+     */
+    public static final double UPS_SHIFT = 2000000;
+
+    /**
+     * Sets the parameter values for a Universal Polar Stereographic projection
+     * and returns a suggested conversion name.
+     *
+     * <blockquote><table class="sis">
+     *   <caption>Universal Polar Stereographic parameters</caption>
+     *   <tr><th>Parameter name</th>                 <th>Value</th></tr>
+     *   <tr><td>Latitude of natural origin</td>     <td>90°N or 90°S</td></tr>
+     *   <tr><td>Longitude of natural origin</td>    <td>0°</td></tr>
+     *   <tr><td>Scale factor at natural origin</td> <td>0.994</td></tr>
+     *   <tr><td>False easting</td>                  <td>2000000 metres</td></tr>
+     *   <tr><td>False northing</td>                 <td>2000000 metres</td></tr>
+     * </table></blockquote>
+     *
+     * @param  group  the parameters for which to set the values.
+     * @param  north  {@code true} for North pole, or {@code false} for South pole.
+     * @return a name like <cite>"Universal Polar Stereographic North"</cite>,
+     *         depending on the arguments given to this method.
+     *
+     * @since 0.8
+     */
+    public static String setParameters(final ParameterValueGroup group, final boolean north) {
+        group.parameter(Constants.LATITUDE_OF_ORIGIN).setValue(north ? Latitude.MAX_VALUE : Latitude.MIN_VALUE, Units.DEGREE);
+        group.parameter(Constants.CENTRAL_MERIDIAN)  .setValue(0,         Units.DEGREE);
+        group.parameter(Constants.SCALE_FACTOR)      .setValue(0.994,     Units.UNITY);
+        group.parameter(Constants.FALSE_EASTING)     .setValue(UPS_SHIFT, Units.METRE);
+        group.parameter(Constants.FALSE_NORTHING)    .setValue(UPS_SHIFT, Units.METRE);
+        return "Universal Polar Stereographic " + (north ? "North" : "South");
+    }
+
+    /**
+     * If the given parameter values are those of a Universal Polar Stereographic projection,
+     * returns -1 for South pole or +1 for North pole. Otherwise returns 0. It is caller's
+     * responsibility to verify that the operation method is {@value #NAME}.
+     *
+     * @param  group  the Transverse Mercator projection parameters.
+     * @return +1 if UPS north, -1 if UPS south, or 0 if the given parameters are not for a UPS projection.
+     *
+     * @since 0.8
+     */
+    public static int isUPS(final ParameterValueGroup group) {
+        if (Numerics.epsilonEqual(group.parameter(Constants.SCALE_FACTOR)    .doubleValue(Units.UNITY),     0.994, Numerics.COMPARISON_THRESHOLD) &&
+            Numerics.epsilonEqual(group.parameter(Constants.FALSE_EASTING)   .doubleValue(Units.METRE), UPS_SHIFT, Formulas.LINEAR_TOLERANCE) &&
+            Numerics.epsilonEqual(group.parameter(Constants.FALSE_NORTHING)  .doubleValue(Units.METRE), UPS_SHIFT, Formulas.LINEAR_TOLERANCE) &&
+            Numerics.epsilonEqual(group.parameter(Constants.CENTRAL_MERIDIAN).doubleValue(Units.DEGREE),        0, Formulas.ANGULAR_TOLERANCE))
+        {
+            final double φ = group.parameter(Constants.LATITUDE_OF_ORIGIN).doubleValue(Units.DEGREE);
+            if (Numerics.epsilonEqual(φ, Latitude.MAX_VALUE, Formulas.ANGULAR_TOLERANCE)) return +1;
+            if (Numerics.epsilonEqual(φ, Latitude.MIN_VALUE, Formulas.ANGULAR_TOLERANCE)) return -1;
+        }
+        return 0;
+    }
 }

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=1785863&r1=1785862&r2=1785863&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] Tue Mar  7 17:44:59 2017
@@ -141,7 +141,8 @@ public final class TransverseMercator ex
      */
     public static enum Zoner {
         /**
-         * Computes zones for the Universal Transverse Mercator (UTM) projections.
+         * Universal Transverse Mercator (UTM) projection zones.
+         * The zone computation includes special cases for Norway and Svalbard.
          *
          * <blockquote><table class="sis">
          *   <caption>Universal Transverse Mercator parameters</caption>
@@ -153,11 +154,37 @@ public final class TransverseMercator ex
          *   <tr><td>False northing</td>                 <td>0 (North hemisphere) or 10000000 (South hemisphere) metres</td></tr>
          * </table></blockquote>
          */
-        UTM(Longitude.MIN_VALUE, 6, 0.9996, 500000, 10000000),
+        UTM(Longitude.MIN_VALUE, 6, 0.9996, 500000, 10000000) {
+            /** Computes the zone from a meridian in the zone. */
+            @Override public int zone(final double φ, final double λ) {
+                int zone = super.zone(φ, λ);
+                switch (zone) {
+                    /*
+                     * Between 56° and 64°, zone  32 is widened to 9° at the expense of zone 31 to accommodate Norway.
+                     * Between 72° and 84°, zones 33 and 35 are widened to 12° to accommodate Svalbard. To compensate,
+                     * zones 31 and 37 are widened to 9° and zones 32, 34, and 36 are eliminated.
+                     * In this switch statement, only the zones that are reduced or eliminated needs to appear.
+                     */
+                    case 31: if (isNorway  (φ)) {if (λ >=  3) zone++;             } break;   //  3° is zone 31 central meridian.
+                    case 32: if (isSvalbard(φ)) {if (λ >=  9) zone++; else zone--;} break;   //  9° is zone 32 central meridian.
+                    case 34: if (isSvalbard(φ)) {if (λ >= 21) zone++; else zone--;} break;   // 21° is zone 34 central meridian.
+                    case 36: if (isSvalbard(φ)) {if (λ >= 33) zone++; else zone--;} break;   // 33° is zone 36 central meridian.
+                }
+                return zone;
+            }
+
+            /** Indicates whether the given zone needs to be handled in a special way for the given latitude. */
+            @Override public boolean isSpecialCase(final int zone, final double φ) {
+                if (zone >= 31 && zone <= 37) {
+                    return isSvalbard(φ) || (zone <= 32 && isNorway(φ));
+                }
+                return false;
+            }
+        },
 
         /**
-         * Computes zones for the Modified Transverse Mercator (MTM) projections.
-         * This projection is in used in Canada only.
+         * Modified Transverse Mercator (MTM) projection zones.
+         * This projection is used in Canada only.
          *
          * <blockquote><table class="sis">
          *   <caption>Modified Transverse Mercator parameters</caption>
@@ -169,13 +196,23 @@ public final class TransverseMercator ex
          *   <tr><td>False northing</td>                 <td>0 metres</td></tr>
          * </table></blockquote>
          */
-        MTM(-51.5, -3, 0.9999, 304800, Double.NaN);
+        MTM(-51.5, -3, 0.9999, 304800, Double.NaN),
+
+        /**
+         * Like UTM, but allows <cite>latitude of origin</cite> and <cite>central meridian</cite> to be anywhere.
+         * The given central meridian is not snapped to the UTM zone center and no special case is applied for
+         * Norway or Svalbard.
+         *
+         * <p>This zoner matches the behavior of {@code AUTO(2):42002} authority code specified in the
+         * OGC <cite>Web Map Service</cite> (WMS) specification.</p>
+         */
+        ANY(Longitude.MIN_VALUE, 6, 0.9996, 500000, 10000000);
 
         /**
          * Longitude of the beginning of zone 1. This is the westmost longitude if {@link #width} is positive,
          * or the eastmost longitude if {@code width} is negative.
          */
-        private final double origin;
+        public final double origin;
 
         /**
          * Width of a zone, in degrees of longitude.
@@ -184,22 +221,22 @@ public final class TransverseMercator ex
          * @see #zone(double)
          * @see #centralMeridian(int)
          */
-        private final double width;
+        public final double width;
 
         /**
-         * The scale factor of UTM projections.
+         * The scale factor of zoned projections.
          */
-        private final double scale;
+        public final double scale;
 
         /**
-         * The false easting of UTM projections, in metres.
+         * The false easting of zoned projections, in metres.
          */
-        private final double easting;
+        public final double easting;
 
         /**
-         * The false northing in South hemisphere of UTM projection, in metres.
+         * The false northing in South hemisphere of zoned projection, in metres.
          */
-        private final double northing;
+        public final double northing;
 
         /**
          * Creates a new instance for computing zones using the given parameters.
@@ -220,32 +257,34 @@ public final class TransverseMercator ex
          *   <tr><th>Parameter name</th>                 <th>Value</th></tr>
          *   <tr><td>Latitude of natural origin</td>     <td>Given latitude, or 0° if zoned projection</td></tr>
          *   <tr><td>Longitude of natural origin</td>    <td>Given longitude, optionally snapped to a zone 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>Scale factor at natural origin</td> <td>0.9996 for UTM or 0.9999 for MTM</td></tr>
+         *   <tr><td>False easting</td>                  <td>500000 metres for UTM or 304800 metres for MTM</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  zoned      {@code true} for snapping the given latitude/longitude to a zone.
          * @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.
          */
-        public String setParameters(final ParameterValueGroup group,
-                final boolean zoned, double latitude, double longitude)
-        {
+        public final String setParameters(final ParameterValueGroup group, double latitude, double longitude) {
             final boolean isSouth = MathFunctions.isNegative(latitude);
-            int zone = zone(longitude);
-            if (zoned) {
-                latitude = 0;
+            int zone = zone(latitude, longitude);
+            String name;
+            if (this == ANY) {
+                name = "UTM";
+                if (latitude != 0 || longitude != centralMeridian(zone)) {
+                    name = NAME;
+                    zone = 0;
+                }
+            } else {
+                name      = name();
+                latitude  = 0;
                 longitude = centralMeridian(zone);
-            } else if (longitude != centralMeridian(zone)) {
-                zone = 0;
             }
-            String name = NAME;
             if (zone != 0) {
-                name = name() + " zone " + zone + (isSouth ? 'S' : 'N');
+                name = name + " zone " + zone + (isSouth ? 'S' : 'N');
             }
             group.parameter(Constants.LATITUDE_OF_ORIGIN).setValue(latitude,  Units.DEGREE);
             group.parameter(Constants.CENTRAL_MERIDIAN)  .setValue(longitude, Units.DEGREE);
@@ -256,14 +295,14 @@ public final class TransverseMercator ex
         }
 
         /**
-         * If the given parameter values are those of an UTM projection, returns the zone number (negative if South).
+         * If the given parameter values are those of a zoned projection, returns the zone number (negative if South).
          * Otherwise returns 0. It is caller's responsibility to verify that the operation method is {@value #NAME}.
          *
          * @param  group  the Transverse Mercator projection parameters.
-         * @return UTM zone number (positive if North, negative if South),
-         *         or 0 if the given parameters are not for a UTM projection.
+         * @return zone number (positive if North, negative if South),
+         *         or 0 if the given parameters are not for a zoned projection.
          */
-        public int zone(final ParameterValueGroup group) {
+        public final int zone(final ParameterValueGroup group) {
             if (Numerics.epsilonEqual(group.parameter(Constants.SCALE_FACTOR)      .doubleValue(Units.UNITY), scale,   Numerics.COMPARISON_THRESHOLD) &&
                 Numerics.epsilonEqual(group.parameter(Constants.FALSE_EASTING)     .doubleValue(Units.METRE), easting, Formulas.LINEAR_TOLERANCE) &&
                 Numerics.epsilonEqual(group.parameter(Constants.LATITUDE_OF_ORIGIN).doubleValue(Units.DEGREE),      0, Formulas.ANGULAR_TOLERANCE))
@@ -272,7 +311,7 @@ public final class TransverseMercator ex
                 final boolean isNorth = Numerics.epsilonEqual(v, 0, Formulas.LINEAR_TOLERANCE);
                 if (isNorth || Numerics.epsilonEqual(v, northing, Formulas.LINEAR_TOLERANCE)) {
                     v = group.parameter(Constants.CENTRAL_MERIDIAN).doubleValue(Units.DEGREE);
-                    int zone = zone(v);
+                    int zone = zone(0, v);
                     if (Numerics.epsilonEqual(centralMeridian(zone), v, Formulas.ANGULAR_TOLERANCE)) {
                         if (!isNorth) zone = -zone;
                         return zone;
@@ -283,31 +322,95 @@ public final class TransverseMercator ex
         }
 
         /**
-         * Computes the UTM zone from a meridian in the zone.
+         * Computes the 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.
-         */
-        public int zone(double longitude) {
+         * @param  φ  a latitude for which to get the zone. Used for taking in account the special cases.
+         * @param  λ  a meridian inside the desired zone, in degrees relative to Greenwich.
+         *            Positive longitudes are toward east, and negative longitudes toward west.
+         * @return the zone number numbered from 1 inclusive, or 0 if the given central meridian was NaN.
+         */
+        public int zone(final double φ, final double λ) {
+            double z = (λ - origin) / width;                                              // Zone number with fractional part.
+            final double count = (Longitude.MAX_VALUE - Longitude.MIN_VALUE) / width;
+            z -= Math.floor(z / count) * count;                                           // Roll in the [0 … 60) range.
             /*
              * 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.
+             * to be the case here since we normalize the central meridian to the [MIN_VALUE … MAX_VALUE]
+             * range. We cast only after addition in order to handle NaN as documented.
              */
-            double z = (longitude - origin) / width;                                      // Zone number with fractional part.
-            z -= Math.floor(z / ((Longitude.MAX_VALUE - Longitude.MIN_VALUE) / width))    // Roll in the [0 … 60) range.
-                              * ((Longitude.MAX_VALUE - Longitude.MIN_VALUE) / width);
-            return (int) (z + 1);   // Cast only after addition in order to handle NaN as documented.
+            return (int) (z + 1);
         }
 
         /**
-         * Computes the central meridian of a given UTM zone.
+         * Returns the number of zones.
          *
-         * @param  zone  the UTM zone as a number in the [1 … 60] range.
-         * @return the central meridian of the given UTM zone.
+         * @return number of zones.
          */
-        public double centralMeridian(final int zone) {
+        public final int zoneCount() {
+            return (int) ((Longitude.MAX_VALUE - Longitude.MIN_VALUE) / width);
+        }
+
+        /**
+         * Computes the central meridian of a given zone.
+         *
+         * @param  zone  the zone as a number starting with 1.
+         * @return the central meridian of the given zone.
+         */
+        public final double centralMeridian(final int zone) {
             return (zone - 0.5) * width + origin;
         }
+
+        /**
+         * Indicates whether the given zone needs to be handled in a special way for the given latitude.
+         *
+         * @param  zone  the zone to test if it is a special case.
+         * @param  φ     the latitude for which to test if there is a special case.
+         * @return whether the given zone at the given latitude is a special case.
+         */
+        public boolean isSpecialCase(final int zone, final double φ) {
+            return false;
+        }
+
+        /**
+         * First exception in UTM projection, corresponding to latitude band V.
+         * This method is public for {@code MilitaryGridReferenceSystemTest.verifyZonerConsistency()} purpose only.
+         *
+         * @param  φ  the latitude in degrees to test.
+         * @return whether the given latitude is in the Norway latitude band.
+         */
+        public static boolean isNorway(final double φ) {
+            return (φ >= 56) && (φ < 64);
+        }
+
+        /**
+         * Second exception in UTM projection, corresponding to latitude band X.
+         * This method is public for {@code MilitaryGridReferenceSystemTest.verifyZonerConsistency()} purpose only.
+         *
+         * @param  φ  the latitude in degrees to test.
+         * @return whether the given latitude is in the Svalbard latitude band.
+         */
+        public static boolean isSvalbard(final double φ) {
+            return (φ >= SVALBARD_BOUNDS) && (φ < NORTH_BOUNDS);
+        }
+
+        /**
+         * Southernmost bound of the first latitude band ({@code 'C'}), inclusive.
+         *
+         * @see #NORTH_BOUNDS
+         */
+        public static final double SOUTH_BOUNDS = -80;
+
+        /**
+         * Southernmost bounds (inclusive) of the last latitude band, which contains Svalbard.
+         * This latitude band is 12° height instead of 8°.
+         */
+        public static final double SVALBARD_BOUNDS = 72;
+
+        /**
+         * Northernmost bound of the last latitude band ({@code 'X'}), exclusive.
+         *
+         * @see #SOUTH_BOUNDS
+         */
+        public static final double NORTH_BOUNDS = 84;
     }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -28,6 +28,7 @@ import org.opengis.parameter.ParameterVa
 import org.opengis.parameter.ParameterDescriptor;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Numbers;
+import org.apache.sis.util.Utilities;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.measure.Range;
@@ -67,7 +68,7 @@ import static org.apache.sis.util.Argume
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Johann Sorel (Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.8
  * @module
  *
  * @see DefaultParameterValue
@@ -468,7 +469,7 @@ public class DefaultParameterDescriptor<
                     final ParameterDescriptor<?> that = (ParameterDescriptor<?>) object;
                     return getValueClass() == that.getValueClass() &&
                            Objects.deepEquals(getDefaultValue(), that.getDefaultValue()) &&
-                           Objects.equals(getUnit(), that.getUnit()) &&
+                           Utilities.deepEquals(getUnit(), that.getUnit(), mode) &&
                            (isHeuristicMatchForName(that.getName().getCode()) ||
                             IdentifiedObjects.isHeuristicMatchForName(that, getName().getCode()));
                 }
@@ -481,7 +482,7 @@ public class DefaultParameterDescriptor<
                            Objects.    equals(getMinimumValue(), that.getMinimumValue()) &&
                            Objects.    equals(getMaximumValue(), that.getMaximumValue()) &&
                            Objects.deepEquals(getDefaultValue(), that.getDefaultValue()) &&
-                           Objects.    equals(getUnit(),         that.getUnit());
+                           Utilities.deepEquals(getUnit(),       that.getUnit(), mode);
                 }
                 case STRICT: {
                     final DefaultParameterDescriptor<?> that = (DefaultParameterDescriptor<?>) object;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -118,7 +118,7 @@ import static org.apache.sis.util.Utilit
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4
- * @version 0.7
+ * @version 0.8
  * @module
  *
  * @see DefaultParameterDescriptor
@@ -787,8 +787,8 @@ public class DefaultParameterValue<T> ex
             } else if (object instanceof ParameterValue<?>) {
                 final ParameterValue<?> that = (ParameterValue<?>) object;
                 return deepEquals(getDescriptor(), that.getDescriptor(), mode) &&
-                       deepEquals(getValue(),      that.getValue(), mode) &&
-                       Objects.equals(getUnit(),   that.getUnit());
+                       deepEquals(getValue(),      that.getValue(),      mode) &&
+                       deepEquals(getUnit(),       that.getUnit(),       mode);
             }
         }
         return false;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -97,8 +97,11 @@ import static org.apache.sis.util.collec
  *   <tr><td><code>{@linkplain IdentifiedObject}[]</code></td><td>Accepted only for {@link ContentLevel#NAME_SUMMARY}.</td></tr>
  * </table>
  *
+ * Like most {@link java.text.Format} implementations, this class is not thread-safe.
+ * Each thread should use its own {@code ParameterFormat} instance or apply synchronization.
+ *
  * <div class="warning"><b>Limitation:</b>
- * Current implementation supports only formatting, not parsing.
+ * the current implementation can only format parameters — parsing is not yet implemented.
  * </div>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -145,23 +145,29 @@ public final class CRS extends Static {
      *
      * <blockquote><table class="sis">
      *   <caption>Minimal set of supported authority codes</caption>
-     *   <tr><th>Code</th>      <th>Enum</th>                            <th>CRS Type</th>      <th>Description</th></tr>
-     *   <tr><td>CRS:27</td>    <td>{@link CommonCRS#NAD27  NAD27}</td>  <td>Geographic</td>    <td>Like EPSG:4267 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
-     *   <tr><td>CRS:83</td>    <td>{@link CommonCRS#NAD83  NAD83}</td>  <td>Geographic</td>    <td>Like EPSG:4269 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
-     *   <tr><td>CRS:84</td>    <td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Geographic</td>    <td>Like EPSG:4326 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
-     *   <tr><td>EPSG:4047</td> <td>{@link CommonCRS#SPHERE SPHERE}</td> <td>Geographic</td>    <td>GRS 1980 Authalic Sphere</td></tr>
-     *   <tr><td>EPSG:4230</td> <td>{@link CommonCRS#ED50   ED50}</td>   <td>Geographic</td>    <td>European Datum 1950</td></tr>
-     *   <tr><td>EPSG:4258</td> <td>{@link CommonCRS#ETRS89 ETRS89}</td> <td>Geographic</td>    <td>European Terrestrial Reference Frame 1989</td></tr>
-     *   <tr><td>EPSG:4267</td> <td>{@link CommonCRS#NAD27  NAD27}</td>  <td>Geographic</td>    <td>North American Datum 1927</td></tr>
-     *   <tr><td>EPSG:4269</td> <td>{@link CommonCRS#NAD83  NAD83}</td>  <td>Geographic</td>    <td>North American Datum 1983</td></tr>
-     *   <tr><td>EPSG:4322</td> <td>{@link CommonCRS#WGS72  WGS72}</td>  <td>Geographic</td>    <td>World Geodetic System 1972</td></tr>
-     *   <tr><td>EPSG:4326</td> <td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Geographic</td>    <td>World Geodetic System 1984</td></tr>
-     *   <tr><td>EPSG:4936</td> <td>{@link CommonCRS#ETRS89 ETRS89}</td> <td>Geocentric</td>    <td>European Terrestrial Reference Frame 1989</td></tr>
-     *   <tr><td>EPSG:4937</td> <td>{@link CommonCRS#ETRS89 ETRS89}</td> <td>Geographic 3D</td> <td>European Terrestrial Reference Frame 1989</td></tr>
-     *   <tr><td>EPSG:4978</td> <td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Geocentric</td>    <td>World Geodetic System 1984</td></tr>
-     *   <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><th>Code</th>      <th>Enum</th>                            <th>CRS Type</th>        <th>Description</th></tr>
+     *   <tr><td>CRS:27</td>    <td>{@link CommonCRS#NAD27  NAD27}</td>  <td>Geographic</td>      <td>Like EPSG:4267 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
+     *   <tr><td>CRS:83</td>    <td>{@link CommonCRS#NAD83  NAD83}</td>  <td>Geographic</td>      <td>Like EPSG:4269 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
+     *   <tr><td>CRS:84</td>    <td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Geographic</td>      <td>Like EPSG:4326 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
+     *   <tr><td>EPSG:4047</td> <td>{@link CommonCRS#SPHERE SPHERE}</td> <td>Geographic</td>      <td>GRS 1980 Authalic Sphere</td></tr>
+     *   <tr><td>EPSG:4230</td> <td>{@link CommonCRS#ED50   ED50}</td>   <td>Geographic</td>      <td>European Datum 1950</td></tr>
+     *   <tr><td>EPSG:4258</td> <td>{@link CommonCRS#ETRS89 ETRS89}</td> <td>Geographic</td>      <td>European Terrestrial Reference Frame 1989</td></tr>
+     *   <tr><td>EPSG:4267</td> <td>{@link CommonCRS#NAD27  NAD27}</td>  <td>Geographic</td>      <td>North American Datum 1927</td></tr>
+     *   <tr><td>EPSG:4269</td> <td>{@link CommonCRS#NAD83  NAD83}</td>  <td>Geographic</td>      <td>North American Datum 1983</td></tr>
+     *   <tr><td>EPSG:4322</td> <td>{@link CommonCRS#WGS72  WGS72}</td>  <td>Geographic</td>      <td>World Geodetic System 1972</td></tr>
+     *   <tr><td>EPSG:4326</td> <td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Geographic</td>      <td>World Geodetic System 1984</td></tr>
+     *   <tr><td>EPSG:4936</td> <td>{@link CommonCRS#ETRS89 ETRS89}</td> <td>Geocentric</td>      <td>European Terrestrial Reference Frame 1989</td></tr>
+     *   <tr><td>EPSG:4937</td> <td>{@link CommonCRS#ETRS89 ETRS89}</td> <td>Geographic 3D</td>   <td>European Terrestrial Reference Frame 1989</td></tr>
+     *   <tr><td>EPSG:4978</td> <td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Geocentric</td>      <td>World Geodetic System 1984</td></tr>
+     *   <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:5041</td> <td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Projected</td>       <td>WGS 84 / UPS North (E,N)</td></tr>
+     *   <tr><td>EPSG:5042</td> <td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Projected</td>       <td>WGS 84 / UPS South (E,N)</td></tr>
+     *   <tr><td>EPSG:322##</td><td>{@link CommonCRS#WGS72  WGS72}</td>  <td>Projected</td>       <td>WGS 72 / UTM zone ##N</td></tr>
+     *   <tr><td>EPSG:323##</td><td>{@link CommonCRS#WGS72  WGS72}</td>  <td>Projected</td>       <td>WGS 72 / UTM zone ##S</td></tr>
+     *   <tr><td>EPSG:326##</td><td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Projected</td>       <td>WGS 84 / UTM zone ##N</td></tr>
+     *   <tr><td>EPSG:327##</td><td>{@link CommonCRS#WGS84  WGS84}</td>  <td>Projected</td>       <td>WGS 84 / UTM zone ##S</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>

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=1785863&r1=1785862&r2=1785863&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] Tue Mar  7 17:44:59 2017
@@ -19,26 +19,29 @@ package org.apache.sis.referencing;
 import java.util.Map;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import javax.measure.Unit;
 import javax.measure.quantity.Time;
+import org.opengis.metadata.Identifier;
 import org.opengis.util.FactoryException;
 import org.opengis.util.InternationalString;
 import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.crs.GeodeticCRS;
 import org.opengis.referencing.crs.VerticalCRS;
 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.SingleCRS;
 import org.opengis.referencing.cs.TimeCS;
 import org.opengis.referencing.cs.VerticalCS;
 import org.opengis.referencing.cs.CartesianCS;
 import org.opengis.referencing.cs.SphericalCS;
 import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.cs.AxisDirection;
+import org.opengis.referencing.datum.Datum;
 import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.datum.GeodeticDatum;
 import org.opengis.referencing.datum.PrimeMeridian;
@@ -66,9 +69,11 @@ import org.apache.sis.internal.system.Mo
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.util.Constants;
 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.util.Exceptions;
+import org.apache.sis.util.Utilities;
 import org.apache.sis.math.MathFunctions;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Units;
@@ -147,8 +152,8 @@ public enum CommonCRS {
      *   <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,     // Geodetic info
-          (short) 32600, (short) 32700, (byte) 1, (byte) 60),                       // UTM info
+    WGS84((short) 4326, (short) 4979, (short) 4978, (short) 6326, (short) 7030,             // Geodetic info
+          (short) 5041, (short) 5042, (short) 32600, (short) 32700, (byte) 1, (byte) 60),   // UPS and UTM info
 
     /**
      * World Geodetic System 1972.
@@ -165,8 +170,8 @@ public enum CommonCRS {
      *   <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,     // Geodetic info
-          (short) 32200, (short) 32300, (byte) 1, (byte) 60),                       // UTM info
+    WGS72((short) 4322, (short) 4985, (short) 4984, (short) 6322, (short) 7043,             // Geodetic info
+          (short) 0, (short) 0, (short) 32200, (short) 32300, (byte) 1, (byte) 60),         // UPS and UTM info
 
     /**
      * North American Datum 1983.
@@ -191,8 +196,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,           // Geodetic info
-          (short) 26900, (short) 0, (byte) 1, (byte) 23),                           // UTM info
+    NAD83((short) 4269, (short) 0, (short) 0, (short) 6269, (short) 7019,                   // Geodetic info
+          (short) 0, (short) 0, (short) 26900, (short) 0, (byte) 1, (byte) 23),             // UPS and UTM info
 
     /**
      * North American Datum 1927.
@@ -209,8 +214,8 @@ public enum CommonCRS {
      *   <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,           // Geodetic info
-          (short) 26700, (short) 0, (byte) 1, (byte) 22),                           // UTM info
+    NAD27((short) 4267, (short) 0, (short) 0, (short) 6267, (short) 7008,                   // Geodetic info
+          (short) 0, (short) 0, (short) 26700, (short) 0, (byte) 1, (byte) 22),             // UPS and UTM info
 
     /**
      * European Terrestrial Reference System 1989.
@@ -234,8 +239,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,    // Geodetic info
-           (short) 25800, (short) 0, (byte) 28, (byte) 37),                         // UTM info
+    ETRS89((short) 4258, (short) 4937, (short) 4936, (short) 6258, (short) 7019,            // Geodetic info
+           (short) 0, (short) 0, (short) 25800, (short) 0, (byte) 28, (byte) 37),           // UPS and UTM info
 
     /**
      * European Datum 1950.
@@ -252,8 +257,8 @@ public enum CommonCRS {
      *   <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,            // Geodetic info
-           (short) 23000, (short) 0, (byte) 28, (byte) 38),                         // UTM info
+    ED50((short) 4230, (short) 0, (short) 0, (short) 6230, (short) 7022,                    // Geodetic info
+         (short) 0, (short) 0, (short) 23000, (short) 0, (byte) 28, (byte) 38),             // UPS and UTM info
 
     /**
      * Unspecified datum based upon the GRS 1980 Authalic Sphere. Spheres use a simpler algorithm for
@@ -271,8 +276,8 @@ public enum CommonCRS {
      *
      * @see org.apache.sis.referencing.datum.DefaultEllipsoid#getAuthalicRadius()
      */
-    SPHERE((short) 4047, (short) 0, (short) 0, (short) 6047, (short) 7048,          // Geodetic info
-           (short) 0, (short) 0, (byte) 0, (byte) 0);                               // UTM info
+    SPHERE((short) 4047, (short) 0, (short) 0, (short) 6047, (short) 7048,                  // Geodetic info
+           (short) 0, (short) 0, (short) 0, (short) 0, (byte) 0, (byte) 0);                 // UPS and UTM info
 
     /**
      * The enum for the default CRS.
@@ -314,6 +319,11 @@ public enum CommonCRS {
     final short ellipsoid;
 
     /**
+     * EPSG codes of Universal Polar Stereographic projections, North and South cases.
+     */
+    final short northUPS, southUPS;
+
+    /**
      * EPSG codes of pseudo "UTM zone zero" (North case and South case), or 0 if none.
      */
     final short northUTM, southUTM;
@@ -359,12 +369,21 @@ public enum CommonCRS {
     private transient volatile GeocentricCRS cachedSpherical;
 
     /**
-     * The Universal Transverse Mercator projections, created when first needed.
-     * All accesses to this map shall be synchronized on {@code cachedUTM}.
+     * The Universal Transverse Mercator (UTM) or Universal Polar Stereographic (UPS) projections,
+     * created when first needed. The UPS projections are arbitrarily given zone numbers
+     * {@value #POLAR} and -{@value #POLAR} for North and South poles respectively.
+     *
+     * <p>All accesses to this map shall be synchronized on {@code cachedProjections}.</p>
      *
-     * @see #UTM(double, double)
+     * @see #universal(double, double)
      */
-    private final Map<Integer,ProjectedCRS> cachedUTM;
+    private final Map<Integer,ProjectedCRS> cachedProjections;
+
+    /**
+     * The special zone number used as key in {@link #cachedProjections} for polar stereographic projections.
+     * Must be outside the range of UTM zone numbers.
+     */
+    private static final int POLAR = 90;
 
     /**
      * Creates a new constant for the given EPSG or SIS codes.
@@ -376,18 +395,20 @@ public enum CommonCRS {
      * @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,
-            final short northUTM, final short southUTM, final byte firstZone, final byte lastZone)
+            final short northUPS, final short southUPS, 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.northUPS   = northUPS;
+        this.southUPS   = southUPS;
         this.northUTM   = northUTM;
         this.southUTM   = southUTM;
         this.firstZone  = firstZone;
         this.lastZone   = lastZone;
-        cachedUTM = new HashMap<>();
+        cachedProjections = new HashMap<>();
     }
 
     /**
@@ -407,15 +428,81 @@ 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'.
+    @SuppressWarnings("NestedSynchronizedStatement")    // Safe because cachedProjections never call any method of 'this'.
     synchronized void clear() {
         cached           = null;
         cachedGeo3D      = null;
         cachedNormalized = null;
         cachedGeocentric = null;
-        synchronized (cachedUTM) {
-            cachedUTM.clear();
+        synchronized (cachedProjections) {
+            cachedProjections.clear();
+        }
+    }
+
+    /**
+     * Returns the {@code CommonCRS} enumeration value for the datum of the given CRS.
+     * The given CRS shall comply to the following conditions
+     * (otherwise an {@link IllegalArgumentException} is thrown):
+     *
+     * <ul>
+     *   <li>The {@code crs} is either an instance of {@link SingleCRS},
+     *       or an instance of {@link org.opengis.referencing.crs.CompoundCRS}
+     *       with an {@linkplain CRS#getHorizontalComponent horizontal component}.</li>
+     *   <li>The {@code crs} or the horizontal component of {@code crs} is associated to a {@link GeodeticDatum}.</li>
+     *   <li>The geodetic datum either<ul>
+     *     <li>has the same EPSG code than one of the {@code CommonCRS} enumeration values, or</li>
+     *     <li>has no EPSG code but is {@linkplain Utilities#equalsIgnoreMetadata equal, ignoring metadata},
+     *       to the {@link #datum()} value of one of the {@code CommonCRS} enumeration values.</li>
+     *   </ul></li>
+     * </ul>
+     *
+     * This method is useful for easier creation of various coordinate reference systems through the
+     * {@link #geographic()}, {@link #geocentric()} or other convenience methods when the set of datums
+     * supported by {@code CommonCRS} is known to be sufficient.
+     *
+     * @param  crs  the coordinate reference system for which to get a {@code CommonCRS} value.
+     * @return the {@code CommonCRS} value for the geodetic datum of the given CRS.
+     * @throws IllegalArgumentException if no {@code CommonCRS} value can be found for the given CRS.
+     *
+     * @see #datum()
+     * @since 0.8
+     */
+    public static CommonCRS forDatum(final CoordinateReferenceSystem crs) {
+        final SingleCRS single;
+        if (crs instanceof SingleCRS) {
+            single = (SingleCRS) crs;
+        } else {
+            single = CRS.getHorizontalComponent(crs);
+            if (single == null) {
+                throw new IllegalArgumentException(Resources.format(
+                        Resources.Keys.NonHorizontalCRS_1, IdentifiedObjects.getName(crs, null)));
+            }
         }
+        final Datum datum = single.getDatum();
+        if (datum instanceof GeodeticDatum) {
+            /*
+             * First, try to search using only the EPSG code. This approach avoid initializing unneeded
+             * geodetic objects (such initializations are costly if they require connection to the EPSG
+             * database).
+             */
+            int epsg = 0;
+            final Identifier identifier = IdentifiedObjects.getIdentifier(datum, Citations.EPSG);
+            if (identifier != null) {
+                final String code = identifier.getCode();
+                if (code != null) try {
+                    epsg = Integer.parseInt(code);
+                } catch (NumberFormatException e) {
+                    Logging.recoverableException(Logging.getLogger(Modules.REFERENCING), CommonCRS.class, "forDatum", e);
+                }
+            }
+            for (final CommonCRS c : values()) {
+                if ((epsg != 0) ? c.datum == epsg : Utilities.equalsIgnoreMetadata(c.datum(), datum)) {
+                    return c;
+                }
+            }
+        }
+        throw new IllegalArgumentException(Errors.format(
+                Errors.Keys.UnsupportedDatum_1, IdentifiedObjects.getName(datum, null)));
     }
 
     /**
@@ -727,6 +814,7 @@ public enum CommonCRS {
      *
      * @return the geodetic datum associated to this enum.
      *
+     * @see #forDatum(CoordinateReferenceSystem)
      * @see org.apache.sis.referencing.datum.DefaultGeodeticDatum
      */
     public GeodeticDatum datum() {
@@ -889,90 +977,175 @@ public enum CommonCRS {
     /**
      * 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.
+     *
+     * @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
+     *
+     * @deprecated Generalized by {@link #universal(double, double)},
+     *             which can also return a UPS projection when appropriate.
+     */
+    @Deprecated
+    public ProjectedCRS UTM(final double latitude, final double longitude) {
+        return universal(Math.signum(latitude), longitude);
+    }
+
+    /**
+     * Returns a Universal Transverse Mercator (UTM) or a Universal Polar Stereographic (UPS) 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 class="verbose">
+     *   <li>If the <var>latitude</var> argument is less than 80°S or equal or greater than 84°N,
+     *       then a <cite>Universal Polar Stereographic</cite> projection is created.</li>
+     *   <li>Otherwise a <cite>Universal Transverse Mercator</cite> projection is created as below:
+     *     <ul class="verbose">
+     *       <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 the special cases documented below
+     *           and 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>
+     *       <li>Calculation of UTM zone involves two special cases (if those special cases are not desired,
+     *           they can be avoided by making sure that the given latitude is below 56°N):
+     *         <ul>
+     *           <li>Between 56°N and 64°N, zone 32 is widened to 9° (at the expense of zone 31)
+     *               to accommodate southwest Norway.</li>
+     *           <li>Between 72°N and 84°N, zones 33 and 35 are widened to 12° to accommodate Svalbard.
+     *               To compensate for these 12° wide zones, zones 31 and 37 are widened to 9° and
+     *               zones 32, 34, and 36 are eliminated.</li>
+     *         </ul>
+     *       </li>
+     *     </ul>
+     *   </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>
+     * <div class="note"><b>Tip:</b>
+     * for "straight" UTM zone calculation without any special case (neither Norway, Svalbard or Universal Polar
+     * Stereographic projection), one can replace the {@code latitude} argument by {@code Math.signum(latitude)}.
+     * For using a specific zone number, one can additionally replace the {@code longitude} argument by
+     * {@code zone * 6 - 183}.</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>
+     * <table class="sis">
+     *   <caption>Universal Transverse Mercator (UTM) and Universal Polar Stereographic (UPS) projection parameters</caption>
+     *   <tr>
+     *     <th>Parameter name</th>
+     *     <th>UTM parameter value</th>
+     *     <th>UPS parameter value</th>
+     *   </tr><tr>
+     *     <td>Latitude of natural origin</td>
+     *     <td>0°</td>
+     *     <td>90°N or 90°S depending on the sign of given latitude</td>
+     *   </tr><tr>
+     *     <td>Longitude of natural origin</td>
+     *     <td>Central meridian of the UTM zone containing the given longitude</td>
+     *     <td>0°</td>
+     *   </tr><tr>
+     *     <td>Scale factor at natural origin</td>
+     *     <td>0.9996</td>
+     *     <td>0.994</td>
+     *   </tr><tr>
+     *     <td>False easting</td>
+     *     <td>500 000 metres</td>
+     *     <td>2 000 000 metres</td>
+     *   </tr><tr>
+     *     <td>False northing</td>
+     *     <td>0 (North hemisphere) or 10 000 000 (South hemisphere) metres</td>
+     *     <td>2 000 000 metres</td>
+     *   </tr>
+     * </table>
      *
      * 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.
+     * <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>
      *
-     * @since 0.7
+     * @param  latitude  a latitude in the desired UTM or UPS projection zone.
+     * @param  longitude a longitude in the desired UTM or UPS projection zone.
+     * @return a Universal Transverse Mercator or Polar Stereographic projection for the zone containing the given point.
+     *
+     * @since 0.8
      */
-    public ProjectedCRS UTM(final double latitude, final double longitude) {
+    public ProjectedCRS universal(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.Zoner.UTM.zone(longitude);
+        final boolean isUTM   = latitude >= TransverseMercator.Zoner.SOUTH_BOUNDS
+                             && latitude <  TransverseMercator.Zoner.NORTH_BOUNDS;
+        final int zone = isUTM ? TransverseMercator.Zoner.UTM.zone(latitude, longitude) : POLAR;
         final Integer key = isSouth ? -zone : zone;
         ProjectedCRS crs;
-        synchronized (cachedUTM) {
-            crs = cachedUTM.get(key);
+        synchronized (cachedProjections) {
+            crs = cachedProjections.get(key);
         }
         if (crs == null) {
+            /*
+             * Requested CRS has not been previously created, or the cache has been cleared.
+             * Before to create the CRS explicitely, try to get it from the EPSG database.
+             * Using the EPSG geodetic dataset when possible gives us more information,
+             * like the aliases and area of validity.
+             */
             int code = 0;
-            if (zone >= firstZone && zone <= lastZone) {
+            if (!isUTM) {
+                code = (isSouth ? southUPS : northUPS) & 0xFFFF;
+            } else if (zone >= firstZone && zone <= lastZone) {
                 code = (isSouth ? southUTM : northUTM) & 0xFFFF;
-                if (code != 0) {
-                    code += zone;
-                    final GeodeticAuthorityFactory factory = factory();
-                    if (factory != null) try {
-                        return factory.createProjectedCRS(String.valueOf(code));
-                    } catch (FactoryException e) {
-                        failure(this, "UTM", e, code);
-                    }
+            }
+            if (code != 0) {
+                if (isUTM) code += zone;
+                final GeodeticAuthorityFactory factory = factory();
+                if (factory != null) try {
+                    return factory.createProjectedCRS(String.valueOf(code));
+                } catch (FactoryException e) {
+                    failure(this, "universal", e, code);
                 }
             }
             /*
-             * 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.
+             * At this point we couldn't use the EPSG dataset; we have to create the CRS ourselves.
+             * All constants defined in this enumeration use the same coordinate system (EPSG:4400)
+             * except for the polar regions. We will arbitrarily create the CS only for a frequently
+             * used datum, then 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 (isUTM) {
+                synchronized (DEFAULT.cachedProjections) {
+                    for (final Map.Entry<Integer,ProjectedCRS> entry : DEFAULT.cachedProjections.entrySet()) {
+                        if (Math.abs(entry.getKey()) != POLAR) {
+                            cs = entry.getValue().getCoordinateSystem();
+                            break;
+                        }
+                    }
                 }
             }
+            /*
+             * If we didn't found a Coordinate System for EPSG:4400, or if the CS that we needed was
+             * for another EPSG code (polar cases), delegate to the WGS84 datum or create the CS now.
+             *
+             *   EPSG:4400 — Cartesian 2D CS. Axes: easting, northing (E,N). Orientations: east, north. UoM: m.
+             *   EPSG:1026 — Cartesian 2D CS for UPS north. Axes: E,N. Orientations: E along 90°E meridian, N along 180°E meridian. UoM: m.
+             *   EPSG:1027 — Cartesian 2D CS for UPS south. Axes: E,N. Orientations: E along 90°E, N along 0°E meridians. UoM: m.
+             */
             if (cs == null) {
                 if (this != DEFAULT) {
-                    cs = DEFAULT.UTM(latitude, longitude).getCoordinateSystem();
+                    cs = DEFAULT.universal(latitude, longitude).getCoordinateSystem();
                 } else {
-                    cs = (CartesianCS) StandardDefinitions.createCoordinateSystem(Constants.EPSG_PROJECTED_CS);
+                    cs = (CartesianCS) StandardDefinitions.createCoordinateSystem(
+                            isUTM ? Constants.EPSG_PROJECTED_CS : isSouth ? (short) 1027 : (short) 1026);
                 }
             }
-            crs = StandardDefinitions.createUTM(code, geographic(), latitude, longitude, cs);
+            crs = StandardDefinitions.createUniversal(code, geographic(), isUTM, latitude, longitude, cs);
             final ProjectedCRS other;
-            synchronized (cachedUTM) {
-                other = JDK8.putIfAbsent(cachedUTM, key, crs);
+            synchronized (cachedProjections) {
+                other = JDK8.putIfAbsent(cachedProjections, key, crs);
             }
             if (other != null) {
                 return other;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/EPSGFactoryFallback.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/EPSGFactoryFallback.java?rev=1785863&r1=1785862&r2=1785863&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/EPSGFactoryFallback.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/EPSGFactoryFallback.java [UTF-8] Tue Mar  7 17:44:59 2017
@@ -45,6 +45,7 @@ import org.apache.sis.util.resources.Voc
 import org.apache.sis.util.iso.DefaultNameSpace;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Debug;
+import org.apache.sis.measure.Latitude;
 
 
 /**
@@ -94,8 +95,8 @@ final class EPSGFactoryFallback extends
     }
 
     /**
-     * Returns the EPSG authority with only a modification in the title of emphasing that this is a subset
-     * of EPSG dataset.
+     * Returns the EPSG authority with only a modification in the title
+     * for emphasing that this is a subset of EPSG dataset.
      */
     @Override
     public synchronized Citation getAuthority() {
@@ -138,10 +139,14 @@ final class EPSGFactoryFallback extends
                 add(codes, crs.geographic);
                 add(codes, crs.geo3D);
             }
-            if (projected && (crs.northUTM != 0 || crs.southUTM != 0)) {
-                for (int zone = crs.firstZone; zone <= crs.lastZone; zone++) {
-                    if (crs.northUTM != 0) codes.add(Integer.toString(crs.northUTM + zone));
-                    if (crs.southUTM != 0) codes.add(Integer.toString(crs.southUTM + zone));
+            if (projected) {
+                add(codes, crs.northUPS);
+                add(codes, crs.southUPS);
+                if (crs.northUTM != 0 || crs.southUTM != 0) {
+                    for (int zone = crs.firstZone; zone <= crs.lastZone; zone++) {
+                        if (crs.northUTM != 0) codes.add(Integer.toString(crs.northUTM + zone));
+                        if (crs.southUTM != 0) codes.add(Integer.toString(crs.southUTM + zone));
+                    }
                 }
             }
         }
@@ -246,13 +251,19 @@ final class EPSGFactoryFallback extends
                     final double latitude;
                     int zone;
                     if (crs.northUTM != 0 && (zone = n - crs.northUTM) >= crs.firstZone && zone <= crs.lastZone) {
-                        latitude = +1;
+                        latitude = +1;          // Any north latitude below 56°N (because of Norway exception) is okay
                     } else if (crs.southUTM != 0 && (zone = n - crs.southUTM) >= crs.firstZone && zone <= crs.lastZone) {
-                        latitude = -1;
+                        latitude = -1;          // Any south latitude above 80°S (because of UPS south case) is okay.
+                    } else if (n == crs.northUPS) {
+                        latitude = Latitude.MAX_VALUE;
+                        zone     = 30;                  // Any random UTM zone is okay.
+                    } else if (n == crs.southUPS) {
+                        latitude = Latitude.MIN_VALUE;
+                        zone     = 30;                  // Any random UTM zone is okay.
                     } else {
                         continue;
                     }
-                    return crs.UTM(latitude, TransverseMercator.Zoner.UTM.centralMeridian(zone));
+                    return crs.universal(latitude, TransverseMercator.Zoner.UTM.centralMeridian(zone));
                 }
             }
             if ((kind & (DATUM | CRS)) != 0) {



Mime
View raw message