sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1684222 [1/2] - in /sis/branches/JDK6: ./ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ core/sis-metadata/src/main/java/org/apache/sis/io/wkt/ core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ core/sis-metadata/src...
Date Mon, 08 Jun 2015 17:04:10 GMT
Author: desruisseaux
Date: Mon Jun  8 17:04:09 2015
New Revision: 1684222

URL: http://svn.apache.org/r1684222
Log:
Merge WKT tests and bug fixes from the JDK7 branch.

Added:
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisNames.java
      - copied unchanged from r1684221, sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisNames.java
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.txt
      - copied unchanged from r1684221, sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.txt
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.java
      - copied unchanged from r1684221, sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.java
Removed:
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.txt
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.java
Modified:
    sis/branches/JDK6/   (props changed)
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/CharEncodingTest.java
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/CommonCRSTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectVerifier.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultGeographicCRSTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/HardCodedAxes.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/HardCodedCS.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/DefaultConversionTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/CharSequencesTest.java

Propchange: sis/branches/JDK6/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Jun  8 17:04:09 2015
@@ -1,4 +1,4 @@
 /sis/branches/Android:1430670-1480699
-/sis/branches/JDK7:1394913-1683807
-/sis/branches/JDK8:1584960-1683803
+/sis/branches/JDK7:1394913-1684221
+/sis/branches/JDK8:1584960-1684220
 /sis/trunk:1394364-1508466,1519089-1519674

Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -25,6 +25,7 @@ import org.opengis.parameter.ParameterDe
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.crs.DerivedCRS;
+import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CartesianCS;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
@@ -378,6 +379,22 @@ public class ReferencingServices extends
         throw moduleNotFound();
     }
 
+    /**
+     * Suggests an abbreviation for the given axis direction. The unit of measurement may be used
+     * for resolving some ambiguities like whether {@link AxisDirection#EAST} is for "x" (Easting)
+     * or "λ" (Longitude).
+     *
+     * @param name      The axis name for which to suggest an abbreviation.
+     * @param direction The axis direction for which to suggest an abbreviation.
+     * @param unit      The axis unit of measurement, for disambiguation.
+     * @return A suggested abbreviation.
+     *
+     * @since 0.6
+     */
+    public String suggestAbbreviation(final String name, final AxisDirection direction, final Unit<?> unit) {
+        throw moduleNotFound();
+    }
+
     /**
      * Creates the {@code TOWGS84} element during parsing of a WKT version 1. This is an optional operation:
      * this method is allowed to return {@code null} if the "sis-referencing" module is not in the classpath.

Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -50,11 +50,13 @@ import org.opengis.referencing.operation
 import org.apache.sis.measure.Units;
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
 import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.internal.metadata.AxisNames;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.metadata.VerticalDatumTypes;
 import org.apache.sis.internal.metadata.ReferencingServices;
 import org.apache.sis.internal.util.LocalizedParseException;
 import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.iso.Types;
 
@@ -261,7 +263,7 @@ final class GeodeticObjectParser extends
         if (child instanceof Element) {
             keyword = ((Element) child).keyword;
             if (keyword != null) {
-                if (keyword.equalsIgnoreCase(WKTKeywords.Axis))        return parseAxis      (element, SI.METRE, true);
+                if (keyword.equalsIgnoreCase(WKTKeywords.Axis))        return parseAxis      (element, false, SI.METRE, true);
                 if (keyword.equalsIgnoreCase(WKTKeywords.PrimeM))      return parsePrimem    (element, NonSI.DEGREE_ANGLE);
                 if (keyword.equalsIgnoreCase(WKTKeywords.ToWGS84))     return parseToWGS84   (element);
                 if (keyword.equalsIgnoreCase(WKTKeywords.Spheroid))    return parseSpheroid  (element);
@@ -386,15 +388,16 @@ final class GeodeticObjectParser extends
      * to make the parser more tolerant to non-100% compliant WKT. Note that AXIS is really the only element without
      * such AUTHORITY clause and the EPSG database provides authority code for all axis.</div>
      *
-     * @param  parent The parent element.
-     * @param  unit The contextual unit, usually {@code SI.METRE} or {@code SI.RADIAN}.
-     * @param  mandatory {@code true} if the axis is mandatory, or {@code false} if it is optional.
+     * @param  parent       The parent element.
+     * @param  isGeographic {@code true} if the parent element is a geodetic CRS having an ellipsoidal coordinate system.
+     * @param  unit         The contextual unit, usually {@code SI.METRE} or {@code SI.RADIAN}.
+     * @param  mandatory    {@code true} if the axis is mandatory, or {@code false} if it is optional.
      * @return The {@code "AXIS"} element as a {@link CoordinateSystemAxis} object, or {@code null}
      *         if the axis was not required and there is no axis object.
      * @throws ParseException if the {@code "AXIS"} element can not be parsed.
      */
-    private CoordinateSystemAxis parseAxis(final Element parent, final Unit<?> unit, final boolean mandatory)
-            throws ParseException
+    private CoordinateSystemAxis parseAxis(final Element parent, final boolean isGeographic,
+            final Unit<?> unit, final boolean mandatory) throws ParseException
     {
         final Element element;
         if (mandatory) {
@@ -405,11 +408,40 @@ final class GeodeticObjectParser extends
                 return null;
             }
         }
-        final String name = element.pullString("name");
+        String name = CharSequences.trimWhitespaces(element.pullString("name"));
+        if (isGeographic) {
+            /*
+             * The longitude and latitude axis names are explicitly fixed by ISO 19111 to "Geodetic longitude"
+             * and "Geodetic latitude". But ISO 19162 §7.5.3(ii) said that the "Geodetic" part in those names
+             * shall be omitted at WKT formatting time. SIS's DefaultCoordinateSystemAxis.formatTo(Formatter)
+             * method performs this removal, so we apply the reverse operation here.
+             */
+            if (name.equalsIgnoreCase(AxisNames.LATITUDE) || name.equalsIgnoreCase("lat")) {
+                name = AxisNames.GEODETIC_LATITUDE;
+            } else if (name.equalsIgnoreCase(AxisNames.LONGITUDE) || name.equalsIgnoreCase("long") || name.equalsIgnoreCase("lon")) {
+                name = AxisNames.GEODETIC_LONGITUDE;
+            }
+        }
         final Element orientation = element.pullVoidElement("orientation");
         final AxisDirection direction = Types.forCodeName(AxisDirection.class, orientation.keyword, mandatory);
+        /*
+         * According ISO 19162, the abbreviation should be inserted between parenthesis in the name.
+         * Example: "Easting (E)", "Longitude (L)". If we do not find an abbreviation, then we will
+         * have to guess one since abbreviation is a mandatory part of axis.
+         */
+        final String abbreviation;
+        final int start, end = name.length() - 1;
+        if (end > 1 && name.charAt(end) == ')' && (start = name.lastIndexOf('(', end-1)) >= 0) {
+            abbreviation = CharSequences.trimWhitespaces(name.substring(start + 1, end));
+            name = CharSequences.trimWhitespaces(name.substring(0, start));
+            if (name.isEmpty()) {
+                name = abbreviation;
+            }
+        } else {
+            abbreviation = referencing.suggestAbbreviation(name, direction, unit);
+        }
         try {
-            return csFactory.createCoordinateSystemAxis(parseAuthorityAndClose(element, name), name, direction, unit);
+            return csFactory.createCoordinateSystemAxis(parseAuthorityAndClose(element, name), abbreviation, direction, unit);
         } catch (FactoryException exception) {
             throw element.parseFailed(exception);
         }
@@ -418,10 +450,10 @@ final class GeodeticObjectParser extends
     /**
      * Creates an axis with the same name than the abbreviation.
      */
-    private CoordinateSystemAxis createAxis(final String abbreviation,
+    private CoordinateSystemAxis createAxis(final String name, final String abbreviation,
             final AxisDirection direction, final Unit<?> unit) throws FactoryException
     {
-        return csFactory.createCoordinateSystemAxis(singletonMap(IdentifiedObject.NAME_KEY, abbreviation),
+        return csFactory.createCoordinateSystemAxis(singletonMap(IdentifiedObject.NAME_KEY, name),
                 abbreviation, direction, unit);
     }
 
@@ -662,11 +694,11 @@ final class GeodeticObjectParser extends
         final String           name       = element.pullString("name");
         final EngineeringDatum datum      = parseLocalDatum(element);
         final Unit<Length>     linearUnit = parseUnit(element, SI.METRE);
-        CoordinateSystemAxis   axis       = parseAxis(element, linearUnit, true);
+        CoordinateSystemAxis   axis       = parseAxis(element, false, linearUnit, true);
         final List<CoordinateSystemAxis> list = new ArrayList<CoordinateSystemAxis>();
         do {
             list.add(axis);
-            axis = parseAxis(element, linearUnit, false);
+            axis = parseAxis(element, false, linearUnit, false);
         } while (axis != null);
         final CoordinateSystem cs = referencing.createAbstractCS(list.toArray(new CoordinateSystemAxis[list.size()]));
         try {
@@ -697,11 +729,11 @@ final class GeodeticObjectParser extends
         final Unit<Length>  linearUnit = parseUnit  (element, SI.METRE);
         CartesianCS cs;
         CoordinateSystemAxis axis0, axis1 = null, axis2 = null;
-        axis0 = parseAxis(element, linearUnit, false);
+        axis0 = parseAxis(element, false, linearUnit, false);
         try {
             if (axis0 != null) {
-                axis1 = parseAxis(element, linearUnit, true);
-                axis2 = parseAxis(element, linearUnit, true);
+                axis1 = parseAxis(element, false, linearUnit, true);
+                axis2 = parseAxis(element, false, linearUnit, true);
             }
             final Map<String,?> properties = parseAuthorityAndClose(element, name);
             if (axis0 != null && !isAxisIgnored) {
@@ -733,10 +765,23 @@ final class GeodeticObjectParser extends
         final String         name       = element.pullString("name");
         final VerticalDatum  datum      = parseVertDatum(element);
         final Unit<Length>   linearUnit = parseUnit(element, SI.METRE);
-        CoordinateSystemAxis axis       = parseAxis(element, linearUnit, false);
+        CoordinateSystemAxis axis       = parseAxis(element, false, linearUnit, false);
         try {
             if (axis == null || isAxisIgnored) {
-                axis = createAxis("h", AxisDirection.UP, linearUnit);
+                String sn = "Height", abbreviation = "h";
+                AxisDirection direction = AxisDirection.UP;
+                final VerticalDatumType type = datum.getVerticalDatumType();
+                if (VerticalDatumType.GEOIDAL.equals(type)) {
+                    sn = AxisNames.GRAVITY_RELATED_HEIGHT;
+                    abbreviation = "H";
+                } else if (VerticalDatumType.DEPTH.equals(type)) {
+                    sn = AxisNames.DEPTH;
+                    abbreviation = "D";
+                    direction = AxisDirection.DOWN;
+                } else if (VerticalDatumTypes.ELLIPSOIDAL.equals(type)) {
+                    sn = AxisNames.ELLIPSOIDAL_HEIGHT;
+                }
+                axis = createAxis(sn, abbreviation, direction, linearUnit);
             }
             return crsFactory.createVerticalCRS(parseAuthorityAndClose(element, name), datum,
                     csFactory.createVerticalCS(singletonMap("name", name), axis));
@@ -757,10 +802,10 @@ final class GeodeticObjectParser extends
         final String         name     = element.pullString("name");
         final TemporalDatum  datum    = parseTimeDatum(element);
         final Unit<Duration> timeUnit = parseUnit(element, SI.SECOND);
-        CoordinateSystemAxis axis     = parseAxis(element, timeUnit, false);
+        CoordinateSystemAxis axis     = parseAxis(element, false, timeUnit, false);
         try {
             if (axis == null || isAxisIgnored) {
-                axis = createAxis("t", AxisDirection.UP, timeUnit);
+                axis = createAxis("Time", "t", AxisDirection.FUTURE, timeUnit);
             }
             return crsFactory.createTemporalCRS(parseAuthorityAndClose(element, name), datum,
                     csFactory.createTimeCS(singletonMap("name", name), axis));
@@ -794,15 +839,15 @@ final class GeodeticObjectParser extends
              */
             name = datum.getName();
         }
-        CoordinateSystemAxis axis0 = parseAxis(element, angularUnit, false);
+        CoordinateSystemAxis axis0 = parseAxis(element, true, angularUnit, false);
         CoordinateSystemAxis axis1 = null;
         try {
             if (axis0 != null) {
-                axis1 = parseAxis(element, angularUnit, true);
+                axis1 = parseAxis(element, true, angularUnit, true);
             }
             if (axis0 == null || isAxisIgnored) {
-                axis0 = createAxis("λ", AxisDirection.EAST,  angularUnit);
-                axis1 = createAxis("φ", AxisDirection.NORTH, angularUnit);
+                axis0 = createAxis(AxisNames.GEODETIC_LONGITUDE, "λ", AxisDirection.EAST,  angularUnit);
+                axis1 = createAxis(AxisNames.GEODETIC_LATITUDE,  "φ", AxisDirection.NORTH, angularUnit);
             }
             final Map<String,?> properties = parseAuthorityAndClose(element, name);
             return crsFactory.createGeographicCRS(properties, datum,
@@ -833,15 +878,15 @@ final class GeodeticObjectParser extends
         final Conversion    conversion = parseProjection(element, linearUnit,
                 (convention == Convention.WKT1_COMMON_UNITS) ? NonSI.DEGREE_ANGLE :
                 geoCRS.getCoordinateSystem().getAxis(0).getUnit().asType(Angle.class));
-        CoordinateSystemAxis axis0 = parseAxis(element, linearUnit, false);
+        CoordinateSystemAxis axis0 = parseAxis(element, false, linearUnit, false);
         CoordinateSystemAxis axis1 = null;
         try {
             if (axis0 != null) {
-                axis1 = parseAxis(element, linearUnit, true);
+                axis1 = parseAxis(element, false, linearUnit, true);
             }
             if (axis0 == null || isAxisIgnored) {
-                axis0 = createAxis("x", AxisDirection.EAST,  linearUnit);
-                axis1 = createAxis("y", AxisDirection.NORTH, linearUnit);
+                axis0 = createAxis(AxisNames.EASTING,  "E", AxisDirection.EAST,  linearUnit);
+                axis1 = createAxis(AxisNames.NORTHING, "N", AxisDirection.NORTH, linearUnit);
             }
             final Map<String,?> properties = parseAuthorityAndClose(element, name);
             return crsFactory.createProjectedCRS(properties, geoCRS, conversion,

Modified: sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/CharEncodingTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/CharEncodingTest.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/CharEncodingTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/CharEncodingTest.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -20,6 +20,7 @@ import org.opengis.referencing.cs.Spheri
 import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.apache.sis.internal.metadata.AxisNames;
 import org.apache.sis.test.mock.CoordinateSystemAxisMock;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -41,10 +42,10 @@ public final strictfp class CharEncoding
      */
     @Test
     public void testGetAbbreviation() {
-        assertAbbreviationEquals("B", new Geographic("Geodetic latitude",    "φ"));
-        assertAbbreviationEquals("U", new Geocentric("Geocentric latitude",  "φ′"));
-        assertAbbreviationEquals("L", new Geographic("Geodetic longitude",   "λ"));
-        assertAbbreviationEquals("V", new Geocentric("Geocentric longitude", "θ"));
+        assertAbbreviationEquals("B", new Geographic(AxisNames.GEODETIC_LATITUDE,   "φ"));
+        assertAbbreviationEquals("U", new Geocentric(AxisNames.SPHERICAL_LATITUDE,  "φ′"));
+        assertAbbreviationEquals("L", new Geographic(AxisNames.GEODETIC_LONGITUDE,  "λ"));
+        assertAbbreviationEquals("V", new Geocentric(AxisNames.SPHERICAL_LONGITUDE, "θ"));
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -98,8 +98,7 @@ import org.junit.BeforeClass;
     org.apache.sis.io.wkt.CharEncodingTest.class,
     org.apache.sis.io.wkt.ColorsTest.class,
     org.apache.sis.io.wkt.FormatterTest.class,
-    org.apache.sis.io.wkt.ElementTest.class,
-    org.apache.sis.io.wkt.WKTFormatTest.class
+    org.apache.sis.io.wkt.ElementTest.class
 })
 public final strictfp class MetadataTestSuite extends TestSuite {
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.referencing;
 
+import javax.measure.unit.Unit;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
@@ -23,6 +24,7 @@ import org.apache.sis.internal.util.Util
 import org.apache.sis.util.Characters;
 import org.apache.sis.util.Static;
 import org.apache.sis.util.iso.Types;
+import org.apache.sis.measure.Units;
 
 import static org.opengis.referencing.cs.AxisDirection.*;
 import static org.apache.sis.util.CharSequences.*;
@@ -516,6 +518,77 @@ public final class AxisDirections extend
     }
 
     /**
+     * Returns {@code true} if the given name starts with the given keyword, ignoring case.
+     */
+    private static boolean startsWith(final String name, final String keyword) {
+        return name.regionMatches(true, 0, keyword, 0, keyword.length());
+    }
+
+    /**
+     * Suggests an abbreviation for the given axis direction. The unit of measurement may be used
+     * for resolving some ambiguities like whether {@link AxisDirection#EAST} is for "x" (Easting)
+     * or "λ" (Longitude).
+     *
+     * @param name      The axis name for which to suggest an abbreviation.
+     * @param direction The axis direction for which to suggest an abbreviation.
+     * @param unit      The axis unit of measurement, for disambiguation.
+     * @return A suggested abbreviation.
+     *
+     * @since 0.6
+     */
+    public static String suggestAbbreviation(final String name, final AxisDirection direction, final Unit<?> unit) {
+        if (name.length() == 1) {
+            return name;  // Most common cases are "x", "y", "z", "t", "i" and "j".
+        }
+        if (isCompass(direction)) {
+            /*
+             * NORTH, EAST, SOUTH, WEST and all intercardinal directions (SOUTH_SOUTH_WEST, etc.):
+             * we will use the acronym (e.g. "SE" for SOUTH_EAST), unless the axis is likely to be
+             * a longitude or latitude axis. We detect those later cases by the unit of measurement.
+             */
+            if (!isIntercardinal(direction) && Units.isAngular(unit)) {
+                if (startsWith(name, "Spherical")) {
+                    return NORTH.equals(absolute(direction)) ? "φ′" : "θ";
+                } else {
+                    return NORTH.equals(absolute(direction)) ? "φ" : "λ";
+                }
+            }
+        } else {
+            /*
+             * All cases other than NORTH, SOUTH, etc. The most common direction is UP, in which case the
+             * abbreviation shall be "H" for Gravity-related height and "h" for ellipsoidal height. Those
+             * two names are specified by ISO 19111, but we will use "Gravity" as a criterion because we
+             * use "h" as the fallback for unknown vertical axis.
+             */
+            if (UP.equals(direction)) {
+                return startsWith(name, "Gravity") ? "H" : startsWith(name, "Geocentric") ? "r": "h";
+            } else if (DOWN.equals(direction)) {
+                return "D"; // "Depth"
+            } else if (isGeocentric(direction)) {
+                // For GEOCENTRIC_X, GEOCENTRIC_Y or GEOCENTRIC_Z, just take the last letter.
+                final String dir = direction.name();
+                return dir.substring(dir.length() - 1).trim();
+            }
+            final AxisDirection a = absolute(direction);
+            if (FUTURE.equals(a)) {
+                return "t";
+            } else if (COLUMN_POSITIVE.equals(a)) {
+                return "i";
+            } else if (ROW_POSITIVE.equals(a)) {
+                return "j";
+            } else if (DISPLAY_RIGHT.equals(a)) {
+                return "x";
+            } else if (DISPLAY_UP.equals(a)) {
+                return "y";
+            } else if (OTHER.equals(a)) {
+                return "z";  // Arbitrary abbreviation, may change in any future SIS version.
+            }
+        }
+        final String id = direction.identifier();   // UML identifier, or null if none.
+        return camelCaseToAcronym(id != null ? id : direction.name()).toString().intern();
+    }
+
+    /**
      * Builds a coordinate system name from the given array of axes.
      * This method expects a {@code StringBuilder} pre-filled with the coordinate system name.
      * The axis directions and abbreviations will be appended after the CS name.

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -34,6 +34,7 @@ import org.opengis.referencing.crs.Tempo
 import org.opengis.referencing.crs.VerticalCRS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CartesianCS;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
@@ -545,6 +546,23 @@ public final class ServicesForMetadata e
     }
 
     /**
+     * Suggests an abbreviation for the given axis direction. The unit of measurement may be used
+     * for resolving some ambiguities like whether {@link AxisDirection#EAST} is for "x" (Easting)
+     * or "λ" (Longitude).
+     *
+     * @param name      The axis name for which to suggest an abbreviation.
+     * @param direction The axis direction for which to suggest an abbreviation.
+     * @param unit      The axis unit of measurement, for disambiguation.
+     * @return A suggested abbreviation.
+     *
+     * @since 0.6
+     */
+    @Override
+    public String suggestAbbreviation(final String name, final AxisDirection direction, final Unit<?> unit) {
+        return AxisDirections.suggestAbbreviation(name, direction, unit);
+    }
+
+    /**
      * Creates the {@code TOWGS84} element during parsing of a WKT version 1.
      *
      * @param  values The 7 Bursa-Wolf parameter values.

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -34,6 +34,7 @@ import org.opengis.referencing.cs.Coordi
 import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.VerticalCRS;
+import org.apache.sis.internal.metadata.AxisNames;
 import org.apache.sis.metadata.iso.extent.Extents;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.referencing.datum.DefaultEllipsoid;
@@ -277,7 +278,7 @@ final class StandardDefinitions {
         final AxisDirection dir;
         switch (code) {
             case 108:  // Used in Ellipsoidal 3D.
-            case 106:  name = "Geodetic latitude";
+            case 106:  name = AxisNames.GEODETIC_LATITUDE;
                        abrv = "φ";
                        unit = NonSI.DEGREE_ANGLE;
                        dir  = AxisDirection.NORTH;
@@ -286,7 +287,7 @@ final class StandardDefinitions {
                        rm   = RangeMeaning.EXACT;
                        break;
             case 109:  // Used in Ellipsoidal 3D.
-            case 107:  name = "Geodetic longitude";
+            case 107:  name = AxisNames.GEODETIC_LONGITUDE;
                        abrv = "λ";
                        unit = NonSI.DEGREE_ANGLE;
                        dir  = AxisDirection.EAST;
@@ -294,27 +295,27 @@ final class StandardDefinitions {
                        max  = Longitude.MAX_VALUE;
                        rm   = RangeMeaning.WRAPAROUND;
                        break;
-            case 110:  name = "Ellipsoidal height";
+            case 110:  name = AxisNames.ELLIPSOIDAL_HEIGHT;
                        abrv = "h";
                        dir  = AxisDirection.UP;
                        break;
-            case 114:  name = "Gravity-related height";
+            case 114:  name = AxisNames.GRAVITY_RELATED_HEIGHT;
                        abrv = "H";
                        dir  = AxisDirection.UP;
                        break;
-            case 113:  name = "Depth";
+            case 113:  name = AxisNames.DEPTH;
                        abrv = "D";
                        dir  = AxisDirection.DOWN;
                        break;
-            case 115:  name = "Geocentric X";
+            case 115:  name = AxisNames.GEOCENTRIC_X;
                        abrv = "X";
                        dir  = AxisDirection.GEOCENTRIC_X;
                        break;
-            case 116:  name = "Geocentric Y";
+            case 116:  name = AxisNames.GEOCENTRIC_Y;
                        abrv = "Y";
                        dir  = AxisDirection.GEOCENTRIC_Y;
                        break;
-            case 117:  name = "Geocentric Z";
+            case 117:  name = AxisNames.GEOCENTRIC_Z;
                        abrv = "Z";
                        dir  = AxisDirection.GEOCENTRIC_Z;
                        break;

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -38,6 +38,7 @@ import org.opengis.referencing.cs.Spheri
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.internal.metadata.AxisNames;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.referencing.AxisDirections;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
@@ -143,6 +144,9 @@ public class DefaultCoordinateSystemAxis
      * <p>Keys in this map are names <strong>in lower cases</strong>.
      * Values are any object that allow us to differentiate latitude from longitude.</p>
      *
+     * <p>Similar strings appear in {@link #formatTo(Formatter)} and
+     * {@code org.apache.sis.io.wkt.GeodeticObjectParser.parseAxis(…)}.</p>
+     *
      * @see #isHeuristicMatchForName(String)
      */
     private static final Map<String,Object> ALIASES = new HashMap<String,Object>(12);
@@ -174,8 +178,8 @@ public class DefaultCoordinateSystemAxis
      * @see #isHeuristicMatchForNameXY(String, String)
      */
     private static final String[] ALIASES_XY = {
-        "Easting", "Northing",
-        "Westing", "Southing"
+        AxisNames.EASTING, AxisNames.NORTHING,
+        AxisNames.WESTING, AxisNames.SOUTHING
     };
 
     /**
@@ -526,7 +530,9 @@ public class DefaultCoordinateSystemAxis
 
     /**
      * Returns the meaning of axis value range specified by the {@linkplain #getMinimumValue() minimum}
-     * and {@linkplain #getMaximumValue() maximum} values.
+     * and {@linkplain #getMaximumValue() maximum} values. If there is no minimum and maximum values
+     * (i.e. if those values are {@linkplain Double#NEGATIVE_INFINITY negative infinity} and
+     * {@linkplain Double#POSITIVE_INFINITY positive infinity} respectively), then this method returns {@code null}.
      *
      * @return The meaning of axis value range, or {@code null} if unspecified.
      */
@@ -795,11 +801,13 @@ public class DefaultCoordinateSystemAxis
                 name = IdentifiedObjects.getName(this, null);
             }
             if (name != null && !isInternal) {
-                if (name.equalsIgnoreCase("Geodetic latitude")) {
-                    name = "Latitude";    // ISO 19162 §7.5.3(ii)
-                } else if (name.equalsIgnoreCase("Geodetic longitude")) {
-                    name = "Longitude";
+                if (name.equalsIgnoreCase(AxisNames.GEODETIC_LATITUDE)) {
+                    name = AxisNames.LATITUDE;    // ISO 19162 §7.5.3(ii)
+                } else if (name.equalsIgnoreCase(AxisNames.GEODETIC_LONGITUDE)) {
+                    name = AxisNames.LONGITUDE;
                 }
+                // Note: the converse of this operation is done in
+                // org.apache.sis.io.wkt.GeodeticObjectParser.parseAxis(…).
             }
         }
         /*

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -140,15 +140,23 @@ class AbstractSingleOperation extends Ab
      *
      * <p>This constructor is for {@link DefaultConversion} usage only,
      * in order to create a "real" conversion from a defining conversion.</p>
+     *
+     * @param definition The defining conversion.
+     * @param sourceCRS  The source CRS.
+     * @param targetCRS  The target CRS.
+     * @param transform  The math transform, optionally with the parameters used for creating it.
+     *        Bundled in a {@code Map.Entry} as an ugly workaround for RFE #4093999 in Java bug database
+     *        ("Relax constraint on placement of this()/super() call in constructors").
      */
     AbstractSingleOperation(final SingleOperation definition,
                             final CoordinateReferenceSystem sourceCRS,
                             final CoordinateReferenceSystem targetCRS,
-                            final MathTransform transform)
+                            final Map.Entry<ParameterValueGroup,MathTransform> transform)
     {
-        super(definition, sourceCRS, targetCRS, transform);
+        super(definition, sourceCRS, targetCRS, transform.getValue());
         method = definition.getMethod();
-        parameters = getParameterValues(definition);
+        final ParameterValueGroup p = transform.getKey();
+        parameters = (p != null) ? p : getParameterValues(definition);
     }
 
     /**
@@ -337,8 +345,9 @@ class AbstractSingleOperation extends Ab
      * {@code null} (in conformance with {@link #parameters} contract).
      */
     private static ParameterValueGroup getParameterValues(final SingleOperation operation) {
-        return (operation instanceof AbstractSingleOperation) ?
-                ((AbstractSingleOperation) operation).parameters : operation.getParameterValues();
+        return (operation instanceof AbstractSingleOperation)
+               ? ((AbstractSingleOperation) operation).parameters   // Null if inferred from MathTransform
+               : operation.getParameterValues();
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -17,6 +17,7 @@
 package org.apache.sis.referencing.operation;
 
 import java.util.Map;
+import java.util.AbstractMap;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.measure.converter.ConversionException;
@@ -386,11 +387,19 @@ public class DefaultConversion extends A
 
     /**
      * Creates the math transform to be given to the sub-class constructor.
-     * This method is a workaround for RFE #4093999 in Sun's bug database
+     * This method is a workaround for RFE #4093999 in Java bug database
      * ("Relax constraint on placement of this()/super() call in constructors").
+     *
+     * <p>This method may also return the parameters used for creating the math transform because those parameters
+     * may have been augmented with the semi-major and semi-minor axis lengths.  Since those parameter values have
+     * been set on a clone of {@code definition.getParameterValues()}, those values are lost (ignoring MathTransform
+     * internal) if we do not return them here.</p>
+     *
+     * @return The math transform, optionally with the parameters used for creating it.
+     *         Bundled in a {@code Map.Entry} as an ugly workaround for RFE #4093999.
      */
     @Workaround(library="JDK", version="1.7")
-    private static MathTransform createMathTransform(
+    private static Map.Entry<ParameterValueGroup,MathTransform> createMathTransform(
             final Conversion definition,
             final CoordinateReferenceSystem sourceCRS,
             final CoordinateReferenceSystem targetCRS,
@@ -398,6 +407,7 @@ public class DefaultConversion extends A
     {
         final int interpDim = ReferencingUtilities.getDimension(getInterpolationCRS(definition));
         MathTransform mt = definition.getMathTransform();
+        final Map.Entry<ParameterValueGroup,MathTransform> pair;
         if (mt == null) {
             /*
              * If the user did not specified explicitely a MathTransform, we will need to create it
@@ -412,7 +422,9 @@ public class DefaultConversion extends A
                 throw new IllegalArgumentException(Errors.format(Errors.Keys.UnspecifiedParameterValues));
             }
             mt = factory.createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem());
+            pair = new AbstractMap.SimpleEntry<ParameterValueGroup,MathTransform>(parameters, mt);
         } else {
+            pair = new AbstractMap.SimpleEntry<ParameterValueGroup,MathTransform>(null, mt);
             /*
              * If the user specified explicitely a MathTransform, we may still need to swap or scale axes.
              * If this conversion is a defining conversion (which is usually the case when creating a new
@@ -434,13 +446,15 @@ public class DefaultConversion extends A
                  */
                 mt = swapAndScaleAxes(mt, sourceCRS, mtSource, interpDim, true,  factory);
                 mt = swapAndScaleAxes(mt, mtTarget, targetCRS, interpDim, false, factory);
-                return mt;  // Skip createPassThroughTransform(…) since it was handled by swapAndScaleAxes(…).
+                pair.setValue(mt);
+                return pair;  // Skip createPassThroughTransform(…) since it was handled by swapAndScaleAxes(…).
             }
         }
         if (interpDim != 0) {
             mt = factory.createPassThroughTransform(interpDim, mt, 0);
         }
-        return mt;
+        pair.setValue(mt);
+        return pair;
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -469,6 +469,12 @@ public class DefaultMathTransformFactory
      * The {@code OperationMethod} instance used by this constructor can be obtained by a call to
      * {@link #getLastMethodUsed()}.
      *
+     * <div class="section">Modification of given parameters</div>
+     * If this method needs to set the values of {@code "semi_major"} and {@code "semi_minor"} parameters,
+     * then it sets those values directly on the given {@code parameters} instance – not on a clone – for
+     * allowing the caller to get back the complete parameter values.
+     * However this method only fills missing values, it never modify existing values.
+     *
      * @param  baseCRS    The source coordinate reference system.
      * @param  parameters The parameter values for the transform.
      * @param  derivedCS  The target coordinate system.

Modified: sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -18,6 +18,7 @@ package org.apache.sis.geometry;
 
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
+import org.apache.sis.internal.metadata.AxisNames;
 import org.apache.sis.math.MathFunctions;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
@@ -446,7 +447,7 @@ public strictfp class GeneralEnvelopeTes
         } catch (IllegalArgumentException ex) {
             // This is the expected exception.
             final String message = ex.getMessage();
-            assertTrue(message, message.contains("Geodetic latitude"));
+            assertTrue(message, message.contains(AxisNames.GEODETIC_LATITUDE));
         }
         // Verify that the envelope still have the old values.
         assertEnvelopeEquals(e, 3, -5, -8, 2);
@@ -467,7 +468,7 @@ public strictfp class GeneralEnvelopeTes
         } catch (IllegalArgumentException ex) {
             // This is the expected exception.
             final String message = ex.getMessage();
-            assertTrue(message, message.contains("Geodetic latitude"));
+            assertTrue(message, message.contains(AxisNames.GEODETIC_LATITUDE));
         }
         // Verify that the envelope still have the old values.
         assertEnvelopeEquals(e, 2, -5, 3, 2);
@@ -494,7 +495,7 @@ public strictfp class GeneralEnvelopeTes
         } catch (IllegalStateException ex) {
             // This is the expected exception.
             final String message = ex.getMessage();
-            assertTrue(message, message.contains("Geodetic latitude"));
+            assertTrue(message, message.contains(AxisNames.GEODETIC_LATITUDE));
         }
         /*
          * Verify that the envelope values are unchanged.

Modified: sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -16,6 +16,10 @@
  */
 package org.apache.sis.internal.referencing;
 
+import java.lang.reflect.Field;
+import javax.measure.unit.SI;
+import javax.measure.unit.NonSI;
+import javax.measure.unit.Unit;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
@@ -361,6 +365,65 @@ public final strictfp class AxisDirectio
     }
 
     /**
+     * Tests {@link AxisDirections#suggestAbbreviation(String, AxisDirection, Unit)}.
+     *
+     * @since 0.6
+     */
+    @Test
+    public void testSuggestAbbreviation() {
+        assertEquals("x",   AxisDirections.suggestAbbreviation("x",                      EAST,             SI.METRE));
+        assertEquals("λ",   AxisDirections.suggestAbbreviation("Geodetic longitude",     EAST,          NonSI.DEGREE_ANGLE));
+        assertEquals("φ",   AxisDirections.suggestAbbreviation("Geodetic latitude",      NORTH,         NonSI.DEGREE_ANGLE));
+        assertEquals("θ",   AxisDirections.suggestAbbreviation("Spherical longitude",    EAST,          NonSI.DEGREE_ANGLE));
+        assertEquals("φ′",  AxisDirections.suggestAbbreviation("Spherical latitude",     NORTH,         NonSI.DEGREE_ANGLE));
+        assertEquals("h",   AxisDirections.suggestAbbreviation("Ellipsoidal height",     UP,               SI.METRE));
+        assertEquals("H",   AxisDirections.suggestAbbreviation("Gravity-related height", UP,               SI.METRE));
+        assertEquals("r",   AxisDirections.suggestAbbreviation("Geocentric radius",      UP,               SI.METRE));
+        assertEquals("X",   AxisDirections.suggestAbbreviation("not needed",             GEOCENTRIC_X,     SI.METRE));
+        assertEquals("Y",   AxisDirections.suggestAbbreviation("not needed",             GEOCENTRIC_Y,     SI.METRE));
+        assertEquals("Z",   AxisDirections.suggestAbbreviation("not needed",             GEOCENTRIC_Z,     SI.METRE));
+        assertEquals("D",   AxisDirections.suggestAbbreviation("not needed",             DOWN,             SI.METRE));
+        assertEquals("t",   AxisDirections.suggestAbbreviation("not needed",             FUTURE,        NonSI.DAY));
+        assertEquals("i",   AxisDirections.suggestAbbreviation("not needed",             COLUMN_NEGATIVE,  Unit.ONE));
+        assertEquals("j",   AxisDirections.suggestAbbreviation("not needed",             ROW_NEGATIVE,     Unit.ONE));
+        assertEquals("x",   AxisDirections.suggestAbbreviation("not needed",             DISPLAY_RIGHT,    Unit.ONE));
+        assertEquals("y",   AxisDirections.suggestAbbreviation("not needed",             DISPLAY_DOWN,     Unit.ONE));
+        assertEquals("N",   AxisDirections.suggestAbbreviation("not needed",             NORTH,            SI.METRE));
+        assertEquals("S",   AxisDirections.suggestAbbreviation("not needed",             SOUTH,            SI.METRE));
+        assertEquals("E",   AxisDirections.suggestAbbreviation("not needed",             EAST,             SI.METRE));
+        assertEquals("W",   AxisDirections.suggestAbbreviation("not needed",             WEST,             SI.METRE));
+        assertEquals("NE",  AxisDirections.suggestAbbreviation("not needed",             NORTH_EAST,       SI.METRE));
+        assertEquals("NW",  AxisDirections.suggestAbbreviation("not needed",             NORTH_WEST,       SI.METRE));
+        assertEquals("SE",  AxisDirections.suggestAbbreviation("not needed",             SOUTH_EAST,       SI.METRE));
+        assertEquals("SW",  AxisDirections.suggestAbbreviation("not needed",             SOUTH_WEST,       SI.METRE));
+        assertEquals("SSE", AxisDirections.suggestAbbreviation("not needed",             SOUTH_SOUTH_EAST, SI.METRE));
+        assertEquals("NNW", AxisDirections.suggestAbbreviation("not needed",             NORTH_NORTH_WEST, SI.METRE));
+        assertEquals("ENE", AxisDirections.suggestAbbreviation("not needed",             EAST_NORTH_EAST,  SI.METRE));
+    }
+
+    /**
+     * Verifies that the abbreviations used in {@link HardCodedAxes} constants are consistent with the abbreviations
+     * suggested by {@link AxisDirections#suggestAbbreviation(String, AxisDirection, Unit)}.  Note that a failure in
+     * this verification does not necessarily means that the {@code suggestAbbreviation(…)}. It could also be the
+     * hard-coded constant which need a revision, or we may decide that the different abbreviations are intended and
+     * should not be compared.
+     *
+     * @throws IllegalAccessException should never happen since we inspect only for public fields.
+     *
+     * @since 0.6
+     */
+    @Test
+    public void verifyAbbreviationConsistency() throws IllegalAccessException {
+        for (final Field field : HardCodedAxes.class.getFields()) {
+            if (CoordinateSystemAxis.class.isAssignableFrom(field.getType())) {
+                final CoordinateSystemAxis axis = (CoordinateSystemAxis) field.get(null);
+                assertEquals(field.getName(), axis.getAbbreviation(), AxisDirections.suggestAbbreviation(
+                        axis.getName().getCode(), axis.getDirection(), axis.getUnit()));
+            }
+        }
+    }
+
+    /**
      * Tests {@link AxisDirections#appendTo(StringBuilder, CoordinateSystemAxis[])}.
      *
      * @since 0.6

Modified: sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java?rev=1684222&r1=1684221&r2=1684222&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java [UTF-8] Mon Jun  8 17:04:09 2015
@@ -20,15 +20,22 @@ import java.util.Date;
 import java.util.Iterator;
 import java.text.ParsePosition;
 import java.text.ParseException;
+import javax.measure.unit.SI;
+import javax.measure.unit.NonSI;
+import javax.measure.unit.Unit;
 import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.cs.*;
 import org.opengis.referencing.crs.*;
 import org.opengis.referencing.datum.*;
+import org.opengis.parameter.ParameterValue;
+import org.opengis.parameter.ParameterValueGroup;
+import org.apache.sis.internal.metadata.AxisNames;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.apache.sis.test.ReferencingAssert.*;
 
 
 /**
@@ -39,7 +46,16 @@ import static org.junit.Assert.*;
  * @version 0.6
  * @module
  */
-@DependsOn(MathTransformParserTest.class)
+@DependsOn({
+    MathTransformParserTest.class,
+    org.apache.sis.referencing.crs.DefaultGeocentricCRSTest.class,
+    org.apache.sis.referencing.crs.DefaultGeographicCRSTest.class,
+    org.apache.sis.referencing.crs.DefaultProjectedCRSTest.class,
+    org.apache.sis.referencing.crs.DefaultVerticalCRSTest.class,
+    org.apache.sis.referencing.crs.DefaultTemporalCRSTest.class,
+    org.apache.sis.referencing.crs.DefaultCompoundCRSTest.class,
+    org.apache.sis.internal.referencing.AxisDirectionsTest.class
+})
 public final strictfp class GeodeticObjectParserTest extends TestCase {
     /**
      * The parser to use for the test.
@@ -51,7 +67,7 @@ public final strictfp class GeodeticObje
      *
      * @throws ParseException if an error occurred during the parsing.
      */
-    private Object parse(final String text) throws ParseException {
+    private <T> T parse(final Class<T> type, final String text) throws ParseException {
         if (parser == null) {
             parser = new GeodeticObjectParser();
         }
@@ -59,15 +75,369 @@ public final strictfp class GeodeticObje
         final Object obj = parser.parseObject(text, position);
         assertEquals("errorIndex", -1, position.getErrorIndex());
         assertEquals("index", text.length(), position.getIndex());
-        return obj;
+        assertInstanceOf("GeodeticObjectParser.parseObject", type, obj);
+        return type.cast(obj);
     }
 
     /**
-     * Asserts that the name of the given object is equals to the given string.
+     * Uses a new parser for the given convention.
      */
-    private static void assertNameEquals(final String expected, final IdentifiedObject object) {
+    private void setConvention(final Convention convention, final boolean isAxisIgnored) {
+        final GeodeticObjectParser p = parser;
+        parser = new GeodeticObjectParser(p.symbols, convention, isAxisIgnored, p.errorLocale, null);
+    }
+
+    /**
+     * Asserts that the name and (optionally) the EPSG identifier of the given object
+     * are equal to the given strings.
+     *
+     * @param name The expected name.
+     * @param epsg The expected EPSG identifier, or {@code 0} if the object shall have no identifier.
+     */
+    static void assertNameAndIdentifierEqual(final String name, final int epsg, final IdentifiedObject object) {
         final String message = object.getClass().getSimpleName();
-        assertEquals(message, expected, object.getName().getCode());
+        assertEquals(message, name, object.getName().getCode());
+        assertEpsgIdentifierEquals(epsg, object.getIdentifiers());
+    }
+
+    /**
+     * Asserts that the given axis is a longitude axis. This method expects the name to be
+     * <cite>"Geodetic longitude"</cite> even if the WKT string contained a different name,
+     * because {@link GeodeticObjectParser} should have done the replacement.
+     */
+    private static void assertLongitudeAxisEquals(final CoordinateSystemAxis axis) {
+        assertAxisEquals(AxisNames.GEODETIC_LONGITUDE, "λ", AxisDirection.EAST, -180, +180, NonSI.DEGREE_ANGLE, RangeMeaning.WRAPAROUND, axis);
+    }
+
+    /**
+     * Asserts that the given axis is a latitude axis. This method expects the name to be
+     * <cite>"Geodetic latitude"</cite> even if the WKT string contained a different name,
+     * because {@link GeodeticObjectParser} should have done the replacement.
+     */
+    private static void assertLatitudeAxisEquals(final CoordinateSystemAxis axis) {
+        assertAxisEquals(AxisNames.GEODETIC_LATITUDE, "φ", AxisDirection.NORTH, -90, +90, NonSI.DEGREE_ANGLE, RangeMeaning.EXACT, axis);
+    }
+
+    /**
+     * Asserts the given axis is an axis of the given name without minimum and maximum values.
+     */
+    private static void assertUnboundedAxisEquals(final String name, final String abbreviation,
+            final AxisDirection direction, final Unit<?> unit, final CoordinateSystemAxis axis)
+    {
+        assertAxisEquals(name, abbreviation, direction, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, unit, null, axis);
+    }
+
+    /**
+     * Verifies that the axes of the coordinate system are those of a projected CRS, in metres.
+     */
+    private static void verifyProjectedCS(final CartesianCS cs) {
+        assertEquals("dimension", 2, cs.getDimension());
+        assertUnboundedAxisEquals("Easting",  "E", AxisDirection.EAST,  SI.METRE, cs.getAxis(0));
+        assertUnboundedAxisEquals("Northing", "N", AxisDirection.NORTH, SI.METRE, cs.getAxis(1));
+    }
+
+    /**
+     * Tests the parsing of a geocentric CRS from a WKT 1 string. The parser
+     * shall replace the OGC 01-009 axis directions (OTHER, EAST, NORTH) by
+     * ISO 19111 axis directions (Geocentric X, Geocentric Y, Geocentric Z).
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    public void testGeocentricCRS() throws ParseException {
+        final GeocentricCRS crs = parse(GeocentricCRS.class,
+                "GEOCCS[“Geocentric”,\n" +
+                "  DATUM[“World Geodetic System 1984”,\n" +
+                "    SPHEROID[“WGS84”, 6378137.0, 298.257223563, AUTHORITY[“EPSG”, “7030”]],\n" +
+                "    AUTHORITY[“EPSG”, “6326”]],\n" +
+                "    PRIMEM[“Greenwich”, 0.0, AUTHORITY[“EPSG”, “8901”]],\n" +
+                "  UNIT[“metre”, 1.0],\n" +
+                "  AXIS[“X”, OTHER],\n" +
+                "  AXIS[“Y”, EAST],\n" +
+                "  AXIS[“Z”, NORTH]]");
+
+        assertNameAndIdentifierEqual("Geocentric", 0, crs);
+
+        final GeodeticDatum datum = crs.getDatum();
+        assertNameAndIdentifierEqual("World Geodetic System 1984", 6326, datum);
+        assertNameAndIdentifierEqual("Greenwich", 8901, datum.getPrimeMeridian());
+
+        final Ellipsoid ellipsoid = datum.getEllipsoid();
+        assertNameAndIdentifierEqual("WGS84", 7030, ellipsoid);
+        assertEquals("semiMajor", 6378137, ellipsoid.getSemiMajorAxis(), STRICT);
+        assertEquals("inverseFlattening", 298.257223563, ellipsoid.getInverseFlattening(), STRICT);
+
+        // Verify that the OGC 01-009 axes have been relaced by ISO 19111 axes.
+        final CartesianCS cs = (CartesianCS) crs.getCoordinateSystem();
+        assertEquals("dimension", 3, cs.getDimension());
+        assertUnboundedAxisEquals(AxisNames.GEOCENTRIC_X, "X", AxisDirection.GEOCENTRIC_X, SI.METRE, cs.getAxis(0));
+        assertUnboundedAxisEquals(AxisNames.GEOCENTRIC_Y, "Y", AxisDirection.GEOCENTRIC_Y, SI.METRE, cs.getAxis(1));
+        assertUnboundedAxisEquals(AxisNames.GEOCENTRIC_Z, "Z", AxisDirection.GEOCENTRIC_Z, SI.METRE, cs.getAxis(2));
+    }
+
+    /**
+     * Tests the parsing of a geographic CRS from a WKT 1 string using (longitude, latitude) axis order.
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    public void testGeographicCRS() throws ParseException {
+        verifyGeographicCRS(0, parse(GeographicCRS.class,
+               "  GEOGCS[“WGS 84”,\n" +
+               "    DATUM[“World Geodetic System 1984”,\n" +
+               "      SPHEROID[“WGS84”, 6378137.0, 298.257223563]],\n" +
+               "      PRIMEM[“Greenwich”, 0.0],\n" +
+               "    UNIT[“degree”, 0.017453292519943295],\n" +
+               "    AXIS[“Longitude”, EAST],\n" +
+               "    AXIS[“Latitude”, NORTH]]"));
+    }
+
+    /**
+     * Tests the parsing of a geographic CRS from a WKT 1 string using (latitude, longitude) axis order.
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    @DependsOnMethod("testGeographicCRS")
+    public void testAxisSwapping() throws ParseException {
+        verifyGeographicCRS(1, parse(GeographicCRS.class,
+               "  GEOGCS[“WGS 84”,\n" +
+               "    DATUM[“World Geodetic System 1984”,\n" +
+               "      SPHEROID[“WGS84”, 6378137.0, 298.257223563]],\n" +
+               "      PRIMEM[“Greenwich”, 0.0],\n" +
+               "    UNIT[“degree”, 0.017453292519943295],\n" +
+               "    AXIS[“Latitude”, NORTH],\n" +
+               "    AXIS[“Longitude”, EAST]]"));
+    }
+
+    /**
+     * Tests the parsing of a geographic CRS from a WKT 1 string that does not declare explicitly the axes.
+     *
+     * @throws ParseException if the parsing failed.
+     *
+     * @see #testImplicitAxesInSeconds()
+     */
+    @Test
+    @DependsOnMethod("testGeographicCRS")
+    public void testImplicitAxes() throws ParseException {
+        verifyGeographicCRS(0, parse(GeographicCRS.class,
+               "  GEOGCS[“WGS 84”,\n" +
+               "    DATUM[“World Geodetic System 1984”,\n" +
+               "      SPHEROID[“WGS84”, 6378137.0, 298.257223563]],\n" +
+               "      PRIMEM[“Greenwich”, 0.0],\n" +
+               "    UNIT[“degree”, 0.017453292519943295]]"));
+    }
+
+    /**
+     * Implementation of {@link #testGeographicCRS()} and {@link #testAxisSwapping()}.
+     * This test expects no {@code AUTHORITY} element on any component.
+     *
+     * @param swap 1 if axes are expected to be swapped, or 0 otherwise.
+     */
+    private void verifyGeographicCRS(final int swap, final GeographicCRS crs) throws ParseException {
+        assertNameAndIdentifierEqual("WGS 84", 0, crs);
+
+        final GeodeticDatum datum = crs.getDatum();
+        assertNameAndIdentifierEqual("World Geodetic System 1984", 0, datum);
+        assertNameAndIdentifierEqual("Greenwich", 0, datum.getPrimeMeridian());
+
+        final Ellipsoid ellipsoid = datum.getEllipsoid();
+        assertNameAndIdentifierEqual("WGS84", 0, ellipsoid);
+        assertEquals("semiMajor", 6378137, ellipsoid.getSemiMajorAxis(), STRICT);
+        assertEquals("inverseFlattening", 298.257223563, ellipsoid.getInverseFlattening(), STRICT);
+
+        final EllipsoidalCS cs = crs.getCoordinateSystem();
+        assertEquals("dimension", 2, cs.getDimension());
+        assertLongitudeAxisEquals(cs.getAxis(0 ^ swap));
+        assertLatitudeAxisEquals (cs.getAxis(1 ^ swap));
+    }
+
+    /**
+     * Tests the parsing of a projected CRS from a WKT 1 string.
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    @DependsOnMethod("testGeographicCRS")
+    public void testProjectedCRS() throws ParseException {
+        final ProjectedCRS crs = parse(ProjectedCRS.class,
+                "PROJCS[“Mercator test”,\n" +
+                "  GEOGCS[“WGS 84”,\n" +
+                "    DATUM[“World Geodetic System 1984”,\n" +
+                "      SPHEROID[“WGS84”, 6378137.0, 298.257223563]],\n" +
+                "      PRIMEM[“Greenwich”, 0.0],\n" +
+                "    UNIT[“degree”, 0.017453292519943295],\n" +
+                "    AXIS[“Longitude”, EAST],\n" +
+                "    AXIS[“Latitude”, NORTH]],\n" +
+                "  PROJECTION[“Mercator_1SP”],\n" +
+                "  PARAMETER[“central_meridian”, -20.0],\n" +
+                "  PARAMETER[“scale_factor”, 1.0],\n" +
+                "  PARAMETER[“false_easting”, 500000.0],\n" +
+                "  PARAMETER[“false_northing”, 0.0],\n" +
+                "  UNIT[“metre”, 1.0],\n" +
+                "  AXIS[“Easting”, EAST],\n" +
+                "  AXIS[“Northing”, NORTH]]");
+
+        assertNameAndIdentifierEqual("Mercator test", 0, crs);
+        verifyProjectedCS(crs.getCoordinateSystem());
+        verifyGeographicCRS(0, crs.getBaseCRS());
+
+        final GeodeticDatum datum = crs.getDatum();
+        assertNameAndIdentifierEqual("World Geodetic System 1984", 0, datum);
+        assertNameAndIdentifierEqual("Greenwich", 0, datum.getPrimeMeridian());
+
+        final Ellipsoid ellipsoid = datum.getEllipsoid();
+        assertNameAndIdentifierEqual("WGS84", 0, ellipsoid);
+        assertEquals("semiMajor", 6378137, ellipsoid.getSemiMajorAxis(), STRICT);
+        assertEquals("inverseFlattening", 298.257223563, ellipsoid.getInverseFlattening(), STRICT);
+
+        assertEquals("Mercator (variant A)", crs.getConversionFromBase().getMethod().getName().getCode());
+        final ParameterValueGroup param = crs.getConversionFromBase().getParameterValues();
+        assertEquals("semi_major",   6378137.0, param.parameter("semi_major"      ).doubleValue(), STRICT);
+        assertEquals("semi_minor",   6356752.3, param.parameter("semi_minor"      ).doubleValue(), 0.1);
+        assertEquals("central_meridian", -20.0, param.parameter("central_meridian").doubleValue(), STRICT);
+        assertEquals("scale_factor",       1.0, param.parameter("scale_factor"    ).doubleValue(), STRICT);
+        assertEquals("false_easting", 500000.0, param.parameter("false_easting"   ).doubleValue(), STRICT);
+        assertEquals("false_northing",     0.0, param.parameter("false_northing"  ).doubleValue(), STRICT);
+    }
+
+    /**
+     * Tests parsing of a CRS using a prime meridian other than Greenwich. The result depends on whether
+     * the parsing is standard compliant or if the parsing use {@link Convention#WKT1_COMMON_UNITS}.
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    @DependsOnMethod("testGeographicCRS")
+    public void testNonGreenwichMeridian() throws ParseException {
+        String wkt = "GEOGCS[“NTF (Paris)”,\n" +
+                     "  DATUM[“Nouvelle Triangulation Française (Paris)”,\n" +
+                     "    SPHEROID[“Clarke 1880 (IGN)”, 6378249.2, 293.4660212936269]],\n" +
+                     "    PRIMEM[“Paris”, 2.5969213, AUTHORITY[“EPSG”, “8903”]],\n" +
+                     "  UNIT[“grade”, 0.015707963267948967],\n" +
+                     "  AXIS[“Latitude”, NORTH],\n" +
+                     "  AXIS[“Longitude”, EAST]]";
+
+        GeographicCRS crs = parse(GeographicCRS.class, wkt);
+        assertNameAndIdentifierEqual("NTF (Paris)", 0, crs);
+        PrimeMeridian pm = verifyNTF(crs.getDatum());
+        assertEquals("angularUnit", NonSI.GRADE, pm.getAngularUnit());
+        assertEquals("greenwichLongitude", 2.5969213, pm.getGreenwichLongitude(), STRICT);
+        EllipsoidalCS cs = crs.getCoordinateSystem();
+        assertEquals("dimension", 2, cs.getDimension());
+        assertAxisEquals(AxisNames.GEODETIC_LATITUDE,  "φ", AxisDirection.NORTH, -100, +100, NonSI.GRADE, RangeMeaning.EXACT,      cs.getAxis(0));
+        assertAxisEquals(AxisNames.GEODETIC_LONGITUDE, "λ", AxisDirection.EAST,  -200, +200, NonSI.GRADE, RangeMeaning.WRAPAROUND, cs.getAxis(1));
+        /*
+         * Parse again using Convention.WKT1_COMMON_UNITS and ignoring AXIS[…] elements (but not the UNIT[…] element,
+         * which still apply to the axes). When using this convention, the parser does not apply the angular units to
+         * the Greenwich longitude value in PRIMEM[…] elements. Instead, the longitude is unconditionally interpreted
+         * as a value in degrees, which is why we convert "2.5969213" grade to "2.33722917" degrees in the WKT before
+         * to run the test (but we do NOT change the UNIT[…] element since the purpose of this test is to verify that
+         * those units are ignored).
+         *
+         * This is a violation of both OGC 01-009 and ISO 19162 standards, but this is what GDAL does.
+         * So we allow this interpretation in Convention.WKT1_COMMON_UNITS for compatibility reasons.
+         */
+        wkt = wkt.replace("2.5969213", "2.33722917");   // Convert unit in prime meridian.
+        setConvention(Convention.WKT1_COMMON_UNITS, true);
+        crs = parse(GeographicCRS.class, wkt);
+        assertNameAndIdentifierEqual("NTF (Paris)", 0, crs);
+        pm = verifyNTF(crs.getDatum());
+        assertEquals("angularUnit", NonSI.DEGREE_ANGLE, pm.getAngularUnit());
+        assertEquals("greenwichLongitude", 2.33722917, pm.getGreenwichLongitude(), STRICT);
+        cs = crs.getCoordinateSystem();
+        assertEquals("dimension", 2, cs.getDimension());
+        assertAxisEquals(AxisNames.GEODETIC_LONGITUDE, "λ", AxisDirection.EAST,  -200, +200, NonSI.GRADE, RangeMeaning.WRAPAROUND, cs.getAxis(0));
+        assertAxisEquals(AxisNames.GEODETIC_LATITUDE,  "φ", AxisDirection.NORTH, -100, +100, NonSI.GRADE, RangeMeaning.EXACT,      cs.getAxis(1));
+    }
+
+    /**
+     * Tests the parsing of a projected CRS using parameters in grades instead than degrees.
+     * The grad units are also used for the prime meridian.
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    @DependsOnMethod({"testNonGreenwichMeridian", "testProjectedCRS"})
+    public void testGradUnitInParameters() throws ParseException {
+        String wkt = "PROJCS[“NTF (Paris) / Lambert zone II”," +
+                     "  GEOGCS[“NTF (Paris)”," +
+                     "    DATUM[“Nouvelle Triangulation Française (Paris)”," +
+                     "      SPHEROID[“Clarke 1880 (IGN)”, 6378249.2, 293.4660212936269]," +
+                     "      TOWGS84[-168,-60,320,0,0,0,0]]," +
+                     "    PRIMEM[“Paris”, 2.5969213, AUTHORITY[“EPSG”, “8903”]]," +  // In grads.
+                     "    UNIT[“grad”, 0.01570796326794897]]," +
+                     "  PROJECTION[“Lambert Conformal Conic (1SP)”]," +  // Intentional swapping of "Conformal" and "Conic".
+                     "  PARAMETER[“latitude_of_origin”, 52.0]," +        // In grads.
+                     "  PARAMETER[“scale_factor”, 0.99987742]," +
+                     "  PARAMETER[“false_easting”, 600000]," +
+                     "  PARAMETER[“false_northing”, 2200000]," +
+                     "  UNIT[“metre”,1]]";
+
+        ProjectedCRS crs = parse(ProjectedCRS.class, wkt);
+        assertNameAndIdentifierEqual("NTF (Paris) / Lambert zone II", 0, crs);
+        verifyProjectedCS(crs.getCoordinateSystem());
+        PrimeMeridian pm = verifyNTF(crs.getDatum());
+        assertEquals("angularUnit", NonSI.GRADE, pm.getAngularUnit());
+        assertEquals("greenwichLongitude", 2.5969213, pm.getGreenwichLongitude(), STRICT);
+        ParameterValue<?> param = verifyNTF(crs.getConversionFromBase().getParameterValues());
+        assertEquals("angularUnit", NonSI.GRADE, param.getUnit());
+        assertEquals("latitude_of_origin",  52.0, param.doubleValue(), STRICT);
+        /*
+         * Parse again using Convention.WKT1_COMMON_UNITS and ignoring AXIS[…] elements.
+         * See the comment in testNonGreenwichMeridian for a discussion. The new aspect
+         * tested by this method is that the unit should be ignored for the parameters
+         * in addition to the prime meridian.
+         */
+        wkt = wkt.replace("2.5969213", "2.33722917");   // Convert unit in prime meridian.
+        wkt = wkt.replace("52.0", "46.8");              // Convert unit in “latitude_of_origin” parameter.
+        setConvention(Convention.WKT1_COMMON_UNITS, true);
+        crs = parse(ProjectedCRS.class, wkt);
+        assertNameAndIdentifierEqual("NTF (Paris) / Lambert zone II", 0, crs);
+        verifyProjectedCS(crs.getCoordinateSystem());
+        pm = verifyNTF(crs.getDatum());
+        assertEquals("angularUnit", NonSI.DEGREE_ANGLE, pm.getAngularUnit());
+        assertEquals("greenwichLongitude", 2.33722917, pm.getGreenwichLongitude(), STRICT);
+        param = verifyNTF(crs.getConversionFromBase().getParameterValues());
+        assertEquals("angularUnit", NonSI.DEGREE_ANGLE, param.getUnit());
+        assertEquals("latitude_of_origin",  46.8, param.doubleValue(), STRICT);
+    }
+
+    /**
+     * Verifies the properties of a datum which is expected to be “Nouvelle Triangulation Française (Paris)”.
+     * This is used by the methods in this class which test a CRS using less frequently used units and prime
+     * meridian.
+     *
+     * @return The prime meridian, to be verified by the caller because the unit of measurement depends on the test.
+     */
+    private static PrimeMeridian verifyNTF(final GeodeticDatum datum) {
+        assertNameAndIdentifierEqual("Nouvelle Triangulation Française (Paris)", 0, datum);
+
+        final Ellipsoid ellipsoid = datum.getEllipsoid();
+        assertNameAndIdentifierEqual("Clarke 1880 (IGN)", 0, ellipsoid);
+        assertEquals("semiMajor", 6378249.2, ellipsoid.getSemiMajorAxis(), STRICT);
+        assertEquals("inverseFlattening", 293.4660212936269, ellipsoid.getInverseFlattening(), STRICT);
+
+        final PrimeMeridian pm = datum.getPrimeMeridian();
+        assertNameAndIdentifierEqual("Paris", 8903, pm);
+        return pm;
+    }
+
+    /**
+     * Verifies the parameter values for a projected CRS which is expected to be “NTF (Paris) / Lambert zone II”.
+     * This is used by the methods in this class which test a CRS using less frequently used units and prime meridian.
+     *
+     * @return The latitude of origin, to be verified by the caller because the unit of measurement depends on the test.
+     */
+    private static ParameterValue<?> verifyNTF(final ParameterValueGroup param) {
+        assertEquals("Lambert Conic Conformal (1SP)", param.getDescriptor().getName().getCode());
+        assertEquals("semi_major",     6378249.2, param.parameter("semi_major"      ).doubleValue(), STRICT);
+        assertEquals("semi_minor",     6356515.0, param.parameter("semi_minor"      ).doubleValue(), 1E-12);
+        assertEquals("central_meridian",     0.0, param.parameter("central_meridian").doubleValue(), STRICT);
+        assertEquals("scale_factor",  0.99987742, param.parameter("scale_factor"    ).doubleValue(), STRICT);
+        assertEquals("false_easting",   600000.0, param.parameter("false_easting"   ).doubleValue(), STRICT);
+        assertEquals("false_northing", 2200000.0, param.parameter("false_northing"  ).doubleValue(), STRICT);
+        return param.parameter("latitude_of_origin");
     }
 
     /**
@@ -76,8 +446,9 @@ public final strictfp class GeodeticObje
      * @throws ParseException if the parsing failed.
      */
     @Test
+    @DependsOnMethod("testGeographicCRS")
     public void testCompoundCRS() throws ParseException {
-        final CompoundCRS crs = (CompoundCRS) parse(
+        final CompoundCRS crs = parse(CompoundCRS.class,
                 "COMPD_CS[“WGS 84 + height + time”,\n" +
                 "  GEOGCS[“WGS 84”,\n" +
                 "    DATUM[“World Geodetic System 1984”,\n" +
@@ -96,55 +467,141 @@ public final strictfp class GeodeticObje
                 "    Axis[“Time”, FUTURE]]]");
 
         // CompoundCRS parent
-        assertNameEquals("WGS 84 + height + time", crs);
+        assertNameAndIdentifierEqual("WGS 84 + height + time", 0, crs);
         final Iterator<CoordinateReferenceSystem> components = crs.getComponents().iterator();
 
         // GeographicCRS child
-        final GeographicCRS geoCRS    = (GeographicCRS) components.next();
-        final GeodeticDatum geoDatum  = geoCRS.getDatum();
-        final Ellipsoid     ellipsoid = geoDatum.getEllipsoid();
-        assertNameEquals("WGS 84", geoCRS);
-        assertNameEquals("World Geodetic System 1984", geoDatum);
-        assertNameEquals("WGS84", ellipsoid);
-        assertNameEquals("Greenwich", geoDatum.getPrimeMeridian());
-        assertEquals("semiMajor", 6378137, ellipsoid.getSemiMajorAxis(), STRICT);
-        assertEquals("inverseFlattening", 298.257223563, ellipsoid.getInverseFlattening(), STRICT);
+        verifyGeographicCRS(0, (GeographicCRS) components.next());
 
         // VerticalCRS child
         final VerticalCRS vertCRS = (VerticalCRS) components.next();
-        assertNameEquals("Gravity-related height", vertCRS);
-        assertNameEquals("Mean Sea Level", vertCRS.getDatum());
+        assertNameAndIdentifierEqual("Gravity-related height", 0, vertCRS);
+        assertNameAndIdentifierEqual("Mean Sea Level", 0, vertCRS.getDatum());
 
         // TemporalCRS child
         final TemporalCRS   timeCRS   = (TemporalCRS) components.next();
         final TemporalDatum timeDatum = timeCRS.getDatum();
-        assertNameEquals("Time", timeCRS);
-        assertNameEquals("Modified Julian", timeDatum);
+        assertNameAndIdentifierEqual("Time", 0, timeCRS);
+        assertNameAndIdentifierEqual("Modified Julian", 0, timeDatum);
         assertEquals("epoch", new Date(-40587 * (24*60*60*1000L)), timeDatum.getOrigin());
 
         // No more CRS.
         assertFalse(components.hasNext());
+
+        // Axes: we verify only the CompoundCRS ones, which should include all others.
+        final CoordinateSystem cs = crs.getCoordinateSystem();
+        assertEquals("dimension", 4, cs.getDimension());
+        assertLongitudeAxisEquals(cs.getAxis(0));
+        assertLatitudeAxisEquals (cs.getAxis(1));
+        assertUnboundedAxisEquals("Gravity-related height", "H", AxisDirection.UP, SI.METRE, cs.getAxis(2));
+        assertUnboundedAxisEquals("Time", "t", AxisDirection.FUTURE, NonSI.DAY, cs.getAxis(3));
     }
 
     /**
-     * Tests integration in {@link WKTFormat#parse(CharSequence, ParsePosition)}.
-     * This method tests only a simple WKT because it is not the purpose of this
-     * method to test the parser itself. We only want to tests its integration in
-     * the {@link WKTFormat} class.
+     * Tests parsing geographic CRS with implicit axes in seconds instead of degrees.
      *
      * @throws ParseException if the parsing failed.
+     *
+     * @see #testImplicitAxes()
      */
     @Test
-    @DependsOnMethod("testCompoundCRS")
-    public void testWKTFormat() throws ParseException {
-        final WKTFormat format = new WKTFormat(null, null);
-        final VerticalCRS crs = (VerticalCRS) format.parseObject(
-                "VERT_CS[“Gravity-related height”,\n" +
-                "  VERT_DATUM[“Mean Sea Level”, 2005],\n" +
-                "  UNIT[“metre”, 1],\n" +
-                "  AXIS[“Gravity-related height”, UP]]");
+    @DependsOnMethod("testImplicitAxes")
+    public void testImplicitAxesInSeconds() throws ParseException {
+        final GeographicCRS crs = parse(GeographicCRS.class,
+                "GEOGCS[“NAD83 / NFIS Seconds”," +
+                "DATUM[“North_American_Datum_1983”,\n" +
+                "SPHEROID[“GRS 1980”, 6378137, 298.257222101]],\n" +
+                "PRIMEM[“Greenwich”, 0],\n" +
+                "UNIT[“Decimal_Second”, 4.84813681109536e-06],\n" +
+                "AUTHORITY[“EPSG”, “100001”]]");
+
+        assertNameAndIdentifierEqual("NAD83 / NFIS Seconds", 100001, crs);
+
+        final GeodeticDatum datum = crs.getDatum();
+        assertNameAndIdentifierEqual("North_American_Datum_1983", 0, datum);
+        assertNameAndIdentifierEqual("Greenwich", 0, datum.getPrimeMeridian());
+
+        final Ellipsoid ellipsoid = datum.getEllipsoid();
+        assertNameAndIdentifierEqual("GRS 1980", 0, ellipsoid);
+        assertEquals("semiMajor", 6378137, ellipsoid.getSemiMajorAxis(), STRICT);
+        assertEquals("inverseFlattening", 298.257222101, ellipsoid.getInverseFlattening(), STRICT);
 
-        assertNameEquals("Gravity-related height", crs);
-        assertNameEquals("Mean Sea Level", crs.getDatum());
+        final EllipsoidalCS cs = crs.getCoordinateSystem();
+        final double secondsIn90 = 90*60*60;
+        CoordinateSystemAxis axis = cs.getAxis(0);
+        assertEquals("name", AxisNames.GEODETIC_LONGITUDE, axis.getName().getCode());
+        assertEquals("abbreviation", "λ",                  axis.getAbbreviation());
+        assertEquals("direction",    AxisDirection.EAST,   axis.getDirection());
+        assertEquals("minimumValue", -secondsIn90*2,       axis.getMinimumValue(), 1E-9);
+        assertEquals("maximumValue", +secondsIn90*2,       axis.getMaximumValue(), 1E-9);
+
+        axis = cs.getAxis(1);
+        assertEquals("name", AxisNames.GEODETIC_LATITUDE,  axis.getName().getCode());
+        assertEquals("abbreviation", "φ",                  axis.getAbbreviation());
+        assertEquals("direction",    AxisDirection.NORTH,  axis.getDirection());
+        assertEquals("minimumValue", -secondsIn90,         axis.getMinimumValue(), 1E-9);
+        assertEquals("maximumValue", +secondsIn90,         axis.getMaximumValue(), 1E-9);
+    }
+
+    /**
+     * Tests parsing a WKT with a missing Geographic CRS name.
+     * This should be considered invalid, but happen in practice.
+     *
+     * <p>The WKT tested in this method contains also some other oddities compared to the usual WKT:</p>
+     * <ul>
+     *   <li>The prime meridian is declared in the {@code "central_meridian"} projection parameter instead
+     *       than in the {@code PRIMEM[…]} element.</li>
+     *   <li>Some elements are not in the usual order.</li>
+     * </ul>
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    @DependsOnMethod("testProjectedCRS")
+    public void testWithMissingName() throws ParseException {
+        final ProjectedCRS crs = parse(ProjectedCRS.class,
+                "PROJCS[“FRANCE/NTF/Lambert III”," +
+                "GEOGCS[“”," + // Missing name (the purpose of this test).
+                "DATUM[“NTF=GR3DF97A”,TOWGS84[-168, -60, 320, 0, 0, 0, 0] ," + // Intentionally misplaced coma.
+                "SPHEROID[“Clarke 1880 (IGN)”,6378249.2,293.4660212936269]]," +
+                "PRIMEM[“Greenwich”,0],UNIT[“Degrees”,0.0174532925199433]," +
+                "AXIS[“Long”,East],AXIS[“Lat”,North]]," +
+                "PROJECTION[“Lambert_Conformal_Conic_1SP”]," +
+                "PARAMETER[“latitude_of_origin”,44.1]," +
+                "PARAMETER[“central_meridian”,2.33722917]," +   // Paris prime meridian.
+                "PARAMETER[“scale_factor”,0.999877499]," +
+                "PARAMETER[“false_easting”,600000]," +
+                "PARAMETER[“false_northing”,200000]," +
+                "UNIT[“Meter”,1]," +
+                "AXIS[“Easting”,East],AXIS[“Northing”,North]]");
+
+        assertNameAndIdentifierEqual("FRANCE/NTF/Lambert III", 0, crs);
+        verifyProjectedCS(crs.getCoordinateSystem());
+        final GeographicCRS geoCRS = crs.getBaseCRS();
+        assertNameAndIdentifierEqual("NTF=GR3DF97A", 0, geoCRS);    // Inherited the datum name.
+
+        final GeodeticDatum datum = geoCRS.getDatum();
+        assertNameAndIdentifierEqual("NTF=GR3DF97A", 0, datum);
+        assertNameAndIdentifierEqual("Greenwich", 0, datum.getPrimeMeridian());
+
+        final Ellipsoid ellipsoid = datum.getEllipsoid();
+        assertNameAndIdentifierEqual("Clarke 1880 (IGN)", 0, ellipsoid);
+        assertEquals("semiMajor", 6378249.2, ellipsoid.getSemiMajorAxis(), STRICT);
+        assertEquals("inverseFlattening", 293.4660212936269, ellipsoid.getInverseFlattening(), STRICT);
+
+        final EllipsoidalCS cs = geoCRS.getCoordinateSystem();
+        assertEquals("dimension", 2, cs.getDimension());
+        assertLongitudeAxisEquals(cs.getAxis(0));
+        assertLatitudeAxisEquals (cs.getAxis(1));
+
+        final ParameterValueGroup param = crs.getConversionFromBase().getParameterValues();
+        assertEquals("Lambert Conic Conformal (1SP)", param.getDescriptor().getName().getCode());
+        assertEquals("semi_major",        6378249.2, param.parameter("semi_major"        ).doubleValue(), STRICT);
+        assertEquals("semi_minor",        6356515.0, param.parameter("semi_minor"        ).doubleValue(), 1E-12);
+        assertEquals("latitude_of_origin",     44.1, param.parameter("latitude_of_origin").doubleValue(), STRICT);
+        assertEquals("central_meridian", 2.33722917, param.parameter("central_meridian"  ).doubleValue(), STRICT);
+        assertEquals("scale_factor",    0.999877499, param.parameter("scale_factor"      ).doubleValue(), STRICT);
+        assertEquals("false_easting",      600000.0, param.parameter("false_easting"     ).doubleValue(), STRICT);
+        assertEquals("false_northing",     200000.0, param.parameter("false_northing"    ).doubleValue(), STRICT);
     }
 }



Mime
View raw message