sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1783077 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/internal/referencing/ sis-referencing/src/main/java/org/apache/sis/referencing/ sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/ sis-refe...
Date Wed, 15 Feb 2017 09:24:51 GMT
Author: desruisseaux
Date: Wed Feb 15 09:24:50 2017
New Revision: 1783077

URL: http://svn.apache.org/viewvc?rev=1783077&view=rev
Log:
Complete support of MGRS label formatted from arbitrary CRS. It does not include yet the support
of polar cases.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/GazetteerException.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MGRSEncoder.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MGRSEncoderTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
[UTF-8] Wed Feb 15 09:24:50 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/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
[ISO-8859-1] Wed Feb 15 09:24:50 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/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
[ISO-8859-1] Wed Feb 15 09:24:50 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/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
[UTF-8] Wed Feb 15 09:24:50 2017
@@ -24,9 +24,11 @@ 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;
@@ -40,6 +42,7 @@ import org.opengis.referencing.cs.Cartes
 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;
@@ -67,6 +70,7 @@ 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;
@@ -418,28 +422,67 @@ public enum CommonCRS {
     }
 
     /**
-     * Returns the value for the given datum or CRS, or {@code null} if none.
-     * The given object can be either an instance of {@link GeodeticDatum},
-     * or an instance of {@link SingleCRS} associated to a geodetic datum.
+     * 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):
      *
-     * @param  object  the object (datum or CRS) for which to get a {@code CommonCRS} value.
-     * @return the {@code CommonCRS} value for the given geodetic object, or {@code null}
if none.
+     * <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 has the same EPSG code than one of the {@code CommonCRS} enumeration
values,
+     *       or 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>
+     *
+     * This method is useful for easier creation of various coordinate reference systems
through the
+     * {@link #geographic()}, {@link #geocentric()} and other convenience methods when the
set of datum
+     * 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(IdentifiedObject object) {
-        if (object instanceof SingleCRS) {
-            object = ((SingleCRS) object).getDatum();
+    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)));
+            }
         }
-        if (object instanceof GeodeticDatum) {
+        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 (Utilities.equalsIgnoreMetadata(c.datum(), object)) {
+                if ((epsg != 0) ? c.datum == epsg : Utilities.equalsIgnoreMetadata(c.datum(),
datum)) {
                     return c;
                 }
             }
         }
-        return null;
+        throw new IllegalArgumentException(Errors.format(
+                Errors.Keys.UnsupportedDatum_1, IdentifiedObjects.getName(datum, null)));
     }
 
     /**
@@ -751,7 +794,7 @@ public enum CommonCRS {
      *
      * @return the geodetic datum associated to this enum.
      *
-     * @see #forDatum(GeodeticDatum)
+     * @see #forDatum(CoordinateReferenceSystem)
      * @see org.apache.sis.referencing.datum.DefaultGeodeticDatum
      */
     public GeodeticDatum datum() {

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/GazetteerException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/GazetteerException.java?rev=1783077&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/GazetteerException.java
(added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/GazetteerException.java
[UTF-8] Wed Feb 15 09:24:50 2017
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.gazetteer;
+
+import org.opengis.referencing.operation.TransformException;
+
+
+/**
+ * Thrown when a coordinate can not be converted to a geographic identifier, or conversely.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+public class GazetteerException extends TransformException {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = 3607149545794483627L;
+
+    /**
+     * Constructs a new exception with no detail message.
+     */
+    public GazetteerException() {
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message.
+     *
+     * @param  message  the details message, or {@code null} if none.
+     */
+    public GazetteerException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified cause.
+     * The details message is copied from the cause.
+     *
+     * @param  cause  the cause, or {@code null} if none.
+     */
+    public GazetteerException(final Throwable cause) {
+        // Reproduce the behavior of standard Throwable(Throwable) constructor.
+        super((cause != null) ? cause.toString() : null, cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and cause.
+     *
+     * @param  message  the details message, or {@code null} if none.
+     * @param  cause    the cause, or {@code null} if none.
+     */
+    public GazetteerException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/GazetteerException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/GazetteerException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MGRSEncoder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MGRSEncoder.java?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MGRSEncoder.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MGRSEncoder.java
[UTF-8] Wed Feb 15 09:24:50 2017
@@ -19,7 +19,6 @@ package org.apache.sis.referencing.gazet
 import org.opengis.util.FactoryException;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.operation.Projection;
 import org.opengis.referencing.operation.OperationMethod;
@@ -121,7 +120,7 @@ final class MGRSEncoder {
     private static final char EXCLUDE_O = 'O';
 
     /**
-     * The datum of the CRS given at construction time, or {@code null} if unsupported.
+     * The datum to which to transform the coordinate before formatting the MGRS label.
      * Only the datums enumerated in {@link CommonCRS} are currently supported.
      */
     private final CommonCRS datum;
@@ -130,38 +129,66 @@ final class MGRSEncoder {
      * UTM zone of position CRS (negative for South hemisphere), or {@value #NORTH_POLE}
or {@value #SOUTH_POLE}
      * if the CRS is a Universal Polar Stereographic projection, or 0 if the CRS is not a
recognized projection.
      * Note that this is not necessarily the same zone than the one to use for formatting
any given coordinate in
-     * that projected CRS, since the {@link #zone(int, double, char)} method has special
rules for some latitudes.
+     * that projected CRS, since the {@link #zone(double, char)} method has special rules
for some latitudes.
      */
     private final int crsZone;
 
     /**
-     * Coordinate conversion from the position CRS to a CRS of the same type but with normalized
axes.
-     * Axis directions are (East, North) and axis units are metres or degrees, depending
on the CRS type.
+     * Coordinate conversion from the position CRS to a CRS of the same type but with normalized
axes,
+     * or {@code null} if not needed. After conversion, axis directions will be (East, North)
and axis
+     * units will be metres.
      *
-     * <p>This transform should perform only simple operation like swapping axis order
an unit conversions.
+     * <p>This transform should perform only simple operations like swapping axis order
and unit conversions.
      * It should not perform more complex operations that would require to go back to geographic
coordinates.</p>
      */
     private final MathTransform toNormalized;
 
     /**
-     * Coordinate conversion from the <em>normalized</em> position CRS to a geographic
CRS.
+     * Coordinate conversion or transformation from the <em>normalized</em> CRS
to geographic CRS.
      * Axis directions are (North, East) as in EPSG geodetic dataset and axis units are degrees.
+     * This transform is never {@code null}.
+     *
+     * <p>This transform may contain datum change from the position datum to the target
{@link #datum}.</p>
      */
     private final MathTransform toGeographic;
 
     /**
+     * A transform from a position in the CRS given at construction time to a position in
the CRS identified by
+     * {@link #actualZone}. This field is updated only when a given position is not located
in the zone of the
+     * CRS given at construction time.
+     */
+    private MathTransform toActualZone;
+
+    /**
+     * The actual zone where the position to encode is located. Legal values are the same
than {@link #crsZone}.
+     * If non-zero, then this is the zone of the {@link #toActualZone} transform. This field
is updated only when
+     * a given position is not located in the zone of the CRS given at construction time.
+     */
+    private int actualZone;
+
+    /**
      * Creates a new converter from direct positions to MGRS labels.
+     *
+     * @param  datum  the datum to which to transform the coordinate before formatting the
MGRS label,
+     *                or {@code null} for inferring the datum from the given {@code crs}.
+     * @param  crs    the coordinate reference system of the coordinates for which to create
MGRS labels.
+     * @throws IllegalArgumentException if the given CRS has no horizontal component or do
not use one of
+     *         the supported datums.
      */
-    MGRSEncoder(final CoordinateReferenceSystem crs) throws FactoryException, TransformException
{
+    MGRSEncoder(CommonCRS datum, CoordinateReferenceSystem crs) throws FactoryException,
TransformException {
         if (crs == null) {
-            throw new IllegalArgumentException(Errors.format(Errors.Keys.UnspecifiedCRS));
+            throw new GazetteerException(Errors.format(Errors.Keys.UnspecifiedCRS));
+        }
+        CoordinateReferenceSystem horizontal = CRS.getHorizontalComponent(crs);
+        if (horizontal == null) {
+            horizontal = crs;
         }
-        datum = CommonCRS.forDatum(crs);
         if (datum == null) {
-            throw new TransformException("Unsupported datum");      // TODO: localize
+            datum = CommonCRS.forDatum(horizontal);
         }
-        if (crs instanceof ProjectedCRS) {
-            ProjectedCRS  projCRS = (ProjectedCRS) crs;
+        this.datum = datum;
+        if (horizontal instanceof ProjectedCRS) {
+            ProjectedCRS  projCRS = (ProjectedCRS) horizontal;
             Projection projection = projCRS.getConversionFromBase();
             final OperationMethod method = projection.getMethod();
             if (IdentifiedObjects.isHeuristicMatchForName(method, TransverseMercator.NAME))
{
@@ -178,12 +205,13 @@ final class MGRSEncoder {
                  * have the expected orientations and units, then we build a normalized version
of
                  * that CRS and compute the transformation to that CRS.
                  */
-                DefaultProjectedCRS normalized;
-                projCRS = normalized = DefaultProjectedCRS.castOrCopy(projCRS);
-                normalized = normalized.forConvention(AxesConvention.NORMALIZED);
-                if (normalized != projCRS) {
-                    toNormalized = CRS.findOperation(projCRS, normalized, null).getMathTransform();
-                    projection = normalized.getConversionFromBase();
+                final DefaultProjectedCRS userAxisOrder = DefaultProjectedCRS.castOrCopy(projCRS);
+                projCRS = userAxisOrder.forConvention(AxesConvention.NORMALIZED);
+                if (crs != horizontal || projCRS != userAxisOrder) {
+                    toNormalized = CRS.findOperation(crs, projCRS, null).getMathTransform();
+                    projection   = projCRS.getConversionFromBase();
+                    horizontal   = projCRS;
+                    crs          = projCRS;         // Next step in the chain of transformations.
                 } else {
                     toNormalized = null;            // ProjectedCRS (UTM or UPS) is already
normalized.
                 }
@@ -195,19 +223,15 @@ final class MGRSEncoder {
              * longitude (in that order) in degrees. We can get this transform directly from
the
              * projected CRS if its base CRS already has the expected axis orientations and
units.
              */
-            GeographicCRS geographic = projCRS.getBaseCRS();
-            GeographicCRS standard = datum.geographic();
-            if (Utilities.equalsIgnoreMetadata(geographic.getCoordinateSystem(), standard.getCoordinateSystem()))
{
+            if (crs == horizontal && Utilities.equalsIgnoreMetadata(projCRS.getBaseCRS(),
datum.geographic())) {
                 toGeographic = projection.getMathTransform().inverse();
-            } else {
-                toGeographic = CRS.findOperation(projCRS, standard, null).getMathTransform();
+                return;
             }
         } else {
             crsZone      = 0;
             toNormalized = null;
-            toGeographic = null;
-            // TODO
         }
+        toGeographic = CRS.findOperation(crs, datum.geographic(), null).getMathTransform();
     }
 
     /**
@@ -224,6 +248,7 @@ final class MGRSEncoder {
         if (band >= EXCLUDE_I && ++band >= EXCLUDE_O && ++band == 'Y')
{
             band = 'X';         // Because the last latitude band ('X') is 12° height instead
of 8°.
         }
+        assert band >= 'C' && band <= 'X' : band;
         return (char) band;
     }
 
@@ -232,12 +257,12 @@ final class MGRSEncoder {
      * Those zones are normally the same than UTM, except for Norway and
      * Svalbard which have special rules.
      *
-     * @param  zone  the value of {@code TransverseMercator.Zoner.UTM.zone(λ)}.
      * @param  band  the latitude band computed by {@link #latitudeBand(double)}.
      * @param  λ     the longitude for which to compute the UTM zone.
-     * @return the UTM zone for the given longitude.
+     * @return the UTM zone for the given longitude, or 0 if the given longitude is NaN or
infinite.
      */
-    static int zone(int zone, final double λ, final char band) {
+    static int zone(final double λ, final char band) {
+        int zone = TransverseMercator.Zoner.UTM.zone(λ);
         switch (band) {
             /*
              * Zone 32 has been widened to 9° (at the expense of zone 31)
@@ -261,6 +286,10 @@ final class MGRSEncoder {
                 break;
             }
         }
+        if (zone == 0) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.NotANumber_1, "λ"));
+        }
+        assert zone >= 1 && zone <= 60 : zone;
         return zone;
     }
 
@@ -268,82 +297,93 @@ final class MGRSEncoder {
      * Encodes the given position into a MGRS label. It is caller responsibility to ensure
that the
      * position CRS is the same than the CRS specified at this {@code MGRSEncoder} creation
time.
      *
+     * @param  owner     the {@code Coder} which own this {@code MGRSEncoder}.
      * @param  position  the direct position to format as a MGRS label.
      * @param  digits    number of digits to use for formatting the numerical part of a MGRS
label.
-     * @param  buffer    where to format the direct position.
+     * @return the value of {@code buffer.toString()}.
      */
-    void encode(DirectPosition position, final int digits, final StringBuilder buffer) throws
TransformException {
+    String encode(final MilitaryGridReferenceSystem.Coder owner, DirectPosition position,
final int digits)
+            throws FactoryException, TransformException
+    {
+        final StringBuilder buffer = owner.buffer;
         if (toNormalized != null) {
-            position = toNormalized.transform(position, null);
+            owner.normalized = position = toNormalized.transform(position, owner.normalized);
         }
-        final DirectPosition geographic;
-        if (crsZone != 0) {
-            geographic = toGeographic.transform(position, null);
-            final double φ = geographic.getOrdinate(0);
-            if (φ >= UTM_SOUTH_BOUNDS && φ <= UTM_NORTH_BOUNDS) {
-                final boolean isNorth = MathFunctions.isPositive(φ);
-                final char    band    = latitudeBand(φ);
-                final double  λ       = geographic.getOrdinate(1);
-                final int     utmZone = TransverseMercator.Zoner.UTM.zone(λ);
-                final int     zone    = zone(utmZone, λ, band);
-                if ((isNorth ? zone : -zone) != crsZone) try {
-                    final double cl = TransverseMercator.Zoner.UTM.centralMeridian(zone);
-                    position = CRS.findOperation(datum.geographic(), datum.UTM(φ, cl), null)
-                            .getMathTransform().transform(geographic, null);
-                } catch (FactoryException e) {
-                    throw new TransformException(e.toString(), e);
+        final DirectPosition geographic = toGeographic.transform(position, owner.geographic);
+        owner.geographic = geographic;                      // For reuse in next method calls.
+        final double φ = geographic.getOrdinate(0);
+        if (φ >= UTM_SOUTH_BOUNDS && φ <= UTM_NORTH_BOUNDS) {
+            /*
+             * Universal Transverse Mercator (UTM) case.
+             */
+            final char band = latitudeBand(φ);
+            final int  zone = zone(geographic.getOrdinate(1), band);
+            final int  sz   = MathFunctions.isNegative(φ) ? -zone : zone;       // Never
zero.
+            if (sz != crsZone) {
+                if (sz != actualZone) {
+                    double cm    = TransverseMercator.Zoner.UTM.centralMeridian(zone);
+                    actualZone   = 0;   // In case an exception is thrown on the next line.
+                    toActualZone = CRS.findOperation(datum.geographic(), datum.UTM(φ, cm),
null).getMathTransform();
+                    actualZone   = sz;
                 }
-                buffer.append(zone).append(band);
-                if (digits >= 0) {
-                    /*
-                     * Specification said that 100,000-meters columns are lettered from A
through Z (omitting I and O)
-                     * starting at the 180° meridian, proceeding easterly for 18°, and
repeating for each 18° intervals.
-                     * Since a UTM zone is 6° width, a 18° interval is exactly 3 standard
UTM zones (not the zone number
-                     * modified by the zone(…) method). Columns in zone 1 are A-H, zone
2 are J-R (skipping O), zone 3
-                     * are S-Z, then repeating every 3 zones.
-                     */
-                    final double x = position.getOrdinate(0);
-                    final double y = position.getOrdinate(1);
-                    final double cx = Math.floor(x / GRID_SQUARE_SIZE);
-                    final double cy = Math.floor(y / GRID_SQUARE_SIZE);
-                    int col = (int) cx;
-                    if (col < 1 || col > 8) {
-                        /*
-                         * UTM northing values at the equator range from 166021 to 833979
meters approximatively
-                         * (WGS84 ellipsoid). Consequently 'cx' ranges from approximatively
1.66 to 8.34, so 'c'
-                         * should range from 1 to 8.
-                         */
-                        throw new TransformException(Errors.format(Errors.Keys.OutsideDomainOfValidity));
-                    }
-                    switch (utmZone % 3) {                          // First A-H sequence
starts at zone number 1.
-                        case 1: col += ('A' - 1); break;
-                        case 2: col += ('J' - 1); if (col >= EXCLUDE_O) col++; break;
-                        case 0: col += ('S' - 1); break;
-                    }
-                    /*
-                     * Rows in odd  zones are ABCDEFGHJKLMNPQRSTUV
-                     * Rows in even zones are FGHJKLMNPQRSTUVABCDE
-                     * Those 20 letters are repeated in a cycle.
-                     */
-                    int row = (int) cy;
-                    if ((zone & 1) == 0) {
-                        row += ('F' - 'A');
-                    }
-                    row = 'A' + (row % 20);
-                    if (row >= EXCLUDE_I && ++row >= EXCLUDE_O) row++;
-                    buffer.append((char) col).append((char) row);
+                owner.normalized = position = toActualZone.transform(geographic, owner.normalized);
+            }
+            buffer.setLength(0);
+            buffer.append(zone).append(band);
+            if (digits >= 0) {
+                /*
+                 * Specification said that 100,000-meters columns are lettered from A through
Z (omitting I and O)
+                 * starting at the 180° meridian, proceeding easterly for 18°, and repeating
for each 18° intervals.
+                 * Since a UTM zone is 6° width, a 18° interval is exactly 3 standard UTM
zones. Columns in zone 1
+                 * are A-H, zone 2 are J-R (skipping O), zone 3 are S-Z, then repeating every
3 zones.
+                 */
+                final double x = position.getOrdinate(0);
+                final double y = position.getOrdinate(1);
+                final double cx = Math.floor(x / GRID_SQUARE_SIZE);
+                final double cy = Math.floor(y / GRID_SQUARE_SIZE);
+                int col = (int) cx;
+                if (col < 1 || col > 8) {
                     /*
-                     * Numerical location at the given precision.
-                     * The specification requires us to truncate the number, not to round
it.
+                     * UTM northing values at the equator range from 166021 to 833979 meters
approximatively
+                     * (WGS84 ellipsoid). Consequently 'cx' ranges from approximatively 1.66
to 8.34, so 'c'
+                     * should range from 1 to 8.
                      */
-                    if (digits > 0) {
-                        final double precision = MathFunctions.pow10(METRE_PRECISION_DIGITS
- digits);
-                        append(buffer, (int) ((x - cx * GRID_SQUARE_SIZE) / precision), digits);
-                        append(buffer, (int) ((y - cy * GRID_SQUARE_SIZE) / precision), digits);
-                    }
+                    throw new TransformException(Errors.format(Errors.Keys.OutsideDomainOfValidity));
+                }
+                switch (zone % 3) {                          // First A-H sequence starts
at zone number 1.
+                    case 1: col += ('A' - 1); break;
+                    case 2: col += ('J' - 1); if (col >= EXCLUDE_O) col++; break;
+                    case 0: col += ('S' - 1); break;
+                }
+                /*
+                 * Rows in odd  zones are ABCDEFGHJKLMNPQRSTUV
+                 * Rows in even zones are FGHJKLMNPQRSTUVABCDE
+                 * Those 20 letters are repeated in a cycle.
+                 */
+                int row = (int) cy;
+                if ((zone & 1) == 0) {
+                    row += ('F' - 'A');
+                }
+                row = 'A' + (row % 20);
+                if (row >= EXCLUDE_I && ++row >= EXCLUDE_O) row++;
+                buffer.append((char) col).append((char) row);
+                /*
+                 * Numerical location at the given precision.
+                 * The specification requires us to truncate the number, not to round it.
+                 */
+                if (digits > 0) {
+                    final double precision = MathFunctions.pow10(METRE_PRECISION_DIGITS -
digits);
+                    append(buffer, (int) ((x - cx * GRID_SQUARE_SIZE) / precision), digits);
+                    append(buffer, (int) ((y - cy * GRID_SQUARE_SIZE) / precision), digits);
                 }
             }
+        } else {
+            /*
+             * Universal Polar Stereographic (UPS) case.
+             */
+            return null;    // TODO
         }
+        return buffer.toString();
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
[UTF-8] Wed Feb 15 09:24:50 2017
@@ -50,10 +50,11 @@ import org.apache.sis.util.resources.Err
  */
 public class MilitaryGridReferenceSystem {
     /**
-     * The target datum, represented by a {@code CommonCRS} instance.
+     * The datum to which to transform the coordinate before formatting the MGRS label,
+     * or {@code null} for inferring the datum from the CRS associated to each coordinate.
      * Only the datums enumerated in {@link CommonCRS} are currently supported.
      */
-    private final CommonCRS datum;
+    final CommonCRS datum;
 
     /**
      * Creates a new Military Grid Reference System (MGRS) using the WGS84 datum.
@@ -66,10 +67,10 @@ public class MilitaryGridReferenceSystem
      * Creates a new Military Grid Reference System (MGRS) using the specified datum.
      * Only the datums enumerated in {@link CommonCRS} are currently supported.
      *
-     * @param  datum  the target datum as a {@code CommonCRS} enumerated value.
+     * @param  datum  the datum to which to transform coordinates before formatting the MGRS
labels,
+     *                or {@code null} for inferring the datum from the CRS associated to
each coordinate.
      */
     public MilitaryGridReferenceSystem(final CommonCRS datum) {
-        ArgumentChecks.ensureNonNull("datum", datum);
         this.datum = datum;
     }
 
@@ -98,7 +99,7 @@ public class MilitaryGridReferenceSystem
      * @version 0.8
      * @module
      */
-    public static class Coder {
+    public class Coder {
         /**
          * Number of digits to use for formatting the numerical part of a MGRS label.
          */
@@ -110,17 +111,23 @@ public class MilitaryGridReferenceSystem
         private final Map<CoordinateReferenceSystem,MGRSEncoder> encoders;
 
         /**
+         * Temporary positions used by {@link MGRSEncoder} only. References are kept for
avoiding to
+         * recreate those temporary objects for every label to format.
+         */
+        DirectPosition normalized, geographic;
+
+        /**
          * A buffer where to create label, to be reused for each new label.
          */
-        private final StringBuilder buffer;
+        final StringBuilder buffer;
 
         /**
          * Creates a new coder initialized to the default precision.
          */
         protected Coder() {
-            digits    = 5;                          // 1 meter precision.
-            buffer    = new StringBuilder(12);      // Length of "4QFJ12345678" sample value.
-            encoders  = new IdentityHashMap<>();
+            digits   = 5;                          // 1 meter precision.
+            buffer   = new StringBuilder(12);      // Length of "4QFJ12345678" sample value.
+            encoders = new IdentityHashMap<>();
         }
 
         /**
@@ -178,17 +185,17 @@ public class MilitaryGridReferenceSystem
             ArgumentChecks.ensureNonNull("position", position);
             final CoordinateReferenceSystem crs = position.getCoordinateReferenceSystem();
             MGRSEncoder encoder = encoders.get(crs);
-            if (encoder == null) try {
-                encoder = new MGRSEncoder(crs);
-                if (encoders.put(crs, encoder) != null) {
-                    throw new ConcurrentModificationException();            // Opportunistic
check.
+            try {
+                if (encoder == null) {
+                    encoder = new MGRSEncoder(datum, crs);
+                    if (encoders.put(crs, encoder) != null) {
+                        throw new ConcurrentModificationException();            // Opportunistic
check.
+                    }
                 }
-            } catch (FactoryException e) {
-                throw new TransformException(e.toString(), e);
+                return encoder.encode(this, position, digits);
+            } catch (IllegalArgumentException  | FactoryException e) {
+                throw new GazetteerException(e.getLocalizedMessage(), e);
             }
-            buffer.setLength(0);
-            encoder.encode(position, digits, buffer);
-            return buffer.toString();
         }
 
         /**

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java
[UTF-8] Wed Feb 15 09:24:50 2017
@@ -291,4 +291,16 @@ public final strictfp class CommonCRSTes
         assertSame("Expected a cached instance.", crs, CommonCRS.WGS72.UTM(-45, -122));
         assertNotSame("Expected a new instance.", crs, CommonCRS.WGS72.UTM(+45, -122));
     }
+
+    /**
+     * Tests {@link CommonCRS#forDatum(CoordinateReferenceSystem)}.
+     *
+     * @sinc 0.8
+     */
+    @Test
+    @DependsOnMethod("testGeographic")
+    public void testForDatum() {
+        assertSame("WGS84", CommonCRS.WGS84, CommonCRS.forDatum(CommonCRS.WGS84.geographic()));
+        assertSame("WGS72", CommonCRS.WGS72, CommonCRS.forDatum(CommonCRS.WGS72.geographic()));
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MGRSEncoderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MGRSEncoderTest.java?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MGRSEncoderTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MGRSEncoderTest.java
[UTF-8] Wed Feb 15 09:24:50 2017
@@ -20,7 +20,6 @@ import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
-import static org.apache.sis.internal.referencing.provider.TransverseMercator.Zoner.UTM;
 
 
 /**
@@ -62,9 +61,9 @@ public final strictfp class MGRSEncoderT
      */
     @Test
     public void testZone() {
-        assertEquals( "4°E band T", 31, MGRSEncoder.zone(UTM.zone( 4),  4, 'T'));
-        assertEquals( "4°E band V", 32, MGRSEncoder.zone(UTM.zone( 4),  4, 'V'));
-        assertEquals("20°E band W", 34, MGRSEncoder.zone(UTM.zone(20), 20, 'W'));
-        assertEquals("20°E band X", 33, MGRSEncoder.zone(UTM.zone(20), 20, 'X'));
+        assertEquals( "4°E band T", 31, MGRSEncoder.zone( 4, 'T'));
+        assertEquals( "4°E band V", 32, MGRSEncoder.zone( 4, 'V'));
+        assertEquals("20°E band W", 34, MGRSEncoder.zone(20, 'W'));
+        assertEquals("20°E band X", 33, MGRSEncoder.zone(20, 'X'));
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
[UTF-8] Wed Feb 15 09:24:50 2017
@@ -80,7 +80,17 @@ public final strictfp class MilitaryGrid
         position.setCoordinateReferenceSystem(CommonCRS.WGS84.UTM(82, 10));
         position.x =  515537;
         position.y = 9104963;
-//      assertEquals("33XVM2240608183", coder.encode(position));
+        assertEquals("33XVM2240708183", coder.encode(position));
+        /*
+         * Same position as previously tested, but using geographic coordinates.
+         */
+        position.setCoordinateReferenceSystem(CommonCRS.WGS84.geographic());
+        position.x = 82;
+        position.y = 10;
+        assertEquals("33XVM2240608183", coder.encode(position));
+        position.x = -41;
+        position.y = 10;
+        assertEquals("32GNV8410260761", coder.encode(position));
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] Wed Feb 15 09:24:50 2017
@@ -865,6 +865,11 @@ public final class Errors extends Indexe
         public static final short UnspecifiedFormatForClass_1 = 158;
 
         /**
+         * The “{0}” datum is not supported by this operation.
+         */
+        public static final short UnsupportedDatum_1 = 168;
+
+        /**
          * Version {1} of {0} format is not supported.
          */
         public static final short UnsupportedFormatVersion_2 = 159;
@@ -886,7 +891,7 @@ public final class Errors extends Indexe
         public static final short UnsupportedOperation_1 = 162;
 
         /**
-         * The ‘{0}’ type is unsupported.
+         * The ‘{0}’ type is not supported in this context.
          */
         public static final short UnsupportedType_1 = 163;
 

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] Wed Feb 15 09:24:50 2017
@@ -188,7 +188,8 @@ UnsupportedFormatVersion_2        = Vers
 UnsupportedImplementation_1       = Can not handle this instance of \u2018{0}\u2019 because
arbitrary implementations are not yet supported.
 UnsupportedInterpolation_1        = The \u201c{0}\u201d interpolation is unsupported.
 UnsupportedOperation_1            = The \u2018{0}\u2019 operation is unsupported.
-UnsupportedType_1                 = The \u2018{0}\u2019 type is unsupported.
+UnsupportedDatum_1                = The \u201c{0}\u201d datum is not supported by this operation.
+UnsupportedType_1                 = The \u2018{0}\u2019 type is not supported in this context.
 ValueAlreadyDefined_1             = A value is already defined for \u201c{0}\u201d.
 ValueNotGreaterThanZero_2         = Value \u2018{0}\u2019 = {1} is invalid. Expected a number
greater than 0.
 ValueOutOfRange_4                 = Value \u2018{0}\u2019 = {3} is invalid. Expected a value
in the [{1} \u2026 {2}] range.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1783077&r1=1783076&r2=1783077&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] Wed Feb 15 09:24:50 2017
@@ -184,7 +184,8 @@ UnsupportedFormatVersion_2        = La v
 UnsupportedImplementation_1       = Cette instance de \u2018{0}\u2019 ne peut pas \u00eatre
g\u00e9r\u00e9e parce que les impl\u00e9mentations arbitraires ne sont pas encore support\u00e9es.
 UnsupportedInterpolation_1        = L\u2019interpolation \u201c{0}\u201d n\u2019est pas support\u00e9e.
 UnsupportedOperation_1            = L\u2019op\u00e9ration \u2018{0}\u2019 n\u2019est pas
support\u00e9e.
-UnsupportedType_1                 = Le type \u2018{0}\u2019 n\u2019est pas support\u00e9.
+UnsupportedDatum_1                = Le r\u00e9f\u00e9rentiel \u00ab\u202f{0}\u202f\u00bb
n\u2019est pas support\u00e9 par cette op\u00e9ration.
+UnsupportedType_1                 = Le type \u2018{0}\u2019 n\u2019est pas support\u00e9
dans ce contexte.
 ValueAlreadyDefined_1             = Une valeur est d\u00e9j\u00e0 d\u00e9finie pour \u00ab\u202f{0}\u202f\u00bb.
 ValueNotGreaterThanZero_2         = La valeur \u2018{0}\u2019 = {1} n\u2019est pas valide.
On attendait un nombre positif non-nul.
 ValueOutOfRange_4                 = La valeur \u2018{0}\u2019 = {3} est invalide. Une valeur
dans la plage [{1} \u2026 {2}] \u00e9tait attendue.



Mime
View raw message