You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2015/06/25 12:18:31 UTC

svn commit: r1687467 - in /sis/branches/JDK8/core: sis-metadata/src/main/java/org/apache/sis/internal/metadata/ sis-metadata/src/main/java/org/apache/sis/io/wkt/ sis-referencing/src/main/java/org/apache/sis/referencing/datum/ sis-referencing/src/test/j...

Author: desruisseaux
Date: Thu Jun 25 10:18:31 2015
New Revision: 1687467

URL: http://svn.apache.org/r1687467
Log:
WKT 2: initial support of "GeodeticCRS" element.
This imply the merge of parsing methods for GeographicCRS and GeocentricCRS into a single method.

Modified:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/AbstractDatum.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.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-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java?rev=1687467&r1=1687466&r2=1687467&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java [UTF-8] Thu Jun 25 10:18:31 2015
@@ -93,13 +93,14 @@ public final class WKTKeywords extends S
      * and {@link org.apache.sis.referencing.crs.DefaultGeographicCRS}.
      */
     public static final String
-            BBox        = "BBox",
-            Datum       = "Datum",
-            GeodeticCRS = "GeodeticCRS",
-            BaseGeodCRS = "BaseGeodCRS",
-            GeodCRS     = "GeodCRS",
-            GeogCS      = "GeogCS",
-            GeocCS      = "GeocCS";
+            BBox          = "BBox",
+            Datum         = "Datum",
+            GeodeticDatum = "GeodeticDatum",
+            GeodeticCRS   = "GeodeticCRS",
+            BaseGeodCRS   = "BaseGeodCRS",
+            GeodCRS       = "GeodCRS",
+            GeogCS        = "GeogCS",
+            GeocCS        = "GeocCS";
 
     /**
      * Related to {@link org.apache.sis.referencing.crs.DefaultVerticalCRS}.

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java?rev=1687467&r1=1687466&r2=1687467&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java [UTF-8] Thu Jun 25 10:18:31 2015
@@ -535,7 +535,7 @@ final class Element {
                 if (element.list != null) {
                     for (int i=0; i<keys.length; i++) {
                         if (element.keyword.equalsIgnoreCase(keys[i])) {
-                            keywordIndex = (byte) i;
+                            element.keywordIndex = (byte) i;
                             iterator.remove();
                             return element;
                         }
@@ -604,8 +604,7 @@ final class Element {
     }
 
     /**
-     * Returns the index of the keyword in the array given to the {@link #pullElement(String...)}
-     * or {@link #pullOptionalElement(String...)} method.
+     * Returns the index of the keyword in the array given to the {@link #pullElement(int, String...)} method.
      */
     final int getKeywordIndex() {
         return keywordIndex;

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java?rev=1687467&r1=1687466&r2=1687467&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java [UTF-8] Thu Jun 25 10:18:31 2015
@@ -346,9 +346,8 @@ final class GeodeticObjectParser extends
             throws ParseException
     {
         CoordinateReferenceSystem crs;
-        if ((crs = parseGeographicCRS  (FIRST, element)) == null &&
+        if ((crs = parseGeodeticCRS    (FIRST, element)) == null &&
             (crs = parseProjectedCRS   (FIRST, element)) == null &&
-            (crs = parseGeocentricCRS  (FIRST, element)) == null &&
             (crs = parseVerticalCRS    (FIRST, element)) == null &&
             (crs = parseTimeCRS        (FIRST, element)) == null &&
             (crs = parseEngineeringCRS (FIRST, element)) == null &&
@@ -476,7 +475,7 @@ final class GeodeticObjectParser extends
             while ((element = parent.pullElement(OPTIONAL, WKTKeywords.VerticalExtent)) != null) {
                 final double minimum = element.pullDouble("minimum");
                 final double maximum = element.pullDouble("maximum");
-                Unit<Length> unit = parseDerivedUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+                Unit<Length> unit = parseScaledUnit(element, WKTKeywords.LengthUnit, SI.METRE);
                 element.close(ignoredElements);
                 if (unit   == null) unit   = SI.METRE;
                 if (extent == null) extent = new DefaultExtent();
@@ -537,7 +536,7 @@ final class GeodeticObjectParser extends
      * @todo Authority code is currently ignored. We may consider to create a subclass of
      *       {@link Unit} which implements {@link IdentifiedObject} in a future version.
      */
-    private <Q extends Quantity> Unit<Q> parseDerivedUnit(final Element parent,
+    private <Q extends Quantity> Unit<Q> parseScaledUnit(final Element parent,
             final String keyword, final Unit<Q> baseUnit) throws ParseException
     {
         final Element element = parent.pullElement(OPTIONAL, keyword, WKTKeywords.Unit);
@@ -568,7 +567,7 @@ final class GeodeticObjectParser extends
         final int    index  = element.getKeywordIndex() - 1;
         parseMetadataAndClose(element, name);
         if (index >= 0 && index < BASE_UNITS.length) {
-            return Units.multiply(BASE_UNITS[index - 1], factor);
+            return Units.multiply(BASE_UNITS[index], factor);
         }
         // If we can not infer the base type, we have to rely on the name.
         return parseUnit(name);
@@ -907,7 +906,7 @@ final class GeodeticObjectParser extends
         final Element meridian = element.pullElement(OPTIONAL, WKTKeywords.Meridian);
         if (meridian != null) {
             double angle = meridian.pullDouble("meridian");
-            final Unit<Angle> m = parseDerivedUnit(meridian, WKTKeywords.AngleUnit, SI.RADIAN);
+            final Unit<Angle> m = parseScaledUnit(meridian, WKTKeywords.AngleUnit, SI.RADIAN);
             meridian.close(ignoredElements);
             if (m != null) {
                 angle = m.getConverterTo(NonSI.DEGREE_ANGLE).convert(angle);
@@ -990,7 +989,10 @@ final class GeodeticObjectParser extends
     }
 
     /**
-     * Parses a {@code "PRIMEM"} element. This element has the following pattern:
+     * Parses a {@code "PrimeMeridian"} element. The syntax is given by
+     * <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#53">WKT 2 specification §8.2.2</a>.
+     *
+     * The legacy WKT 1 pattern was:
      *
      * {@preformat text
      *     PRIMEM["<name>", <longitude> {,<authority>}]
@@ -999,8 +1001,10 @@ final class GeodeticObjectParser extends
      * @param  mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
      * @param  parent The parent element.
      * @param  angularUnit The contextual unit.
-     * @return The {@code "PRIMEM"} element as a {@link PrimeMeridian} object.
-     * @throws ParseException if the {@code "PRIMEM"} element can not be parsed.
+     * @return The {@code "PrimeMeridian"} element as a {@link PrimeMeridian} object.
+     * @throws ParseException if the {@code "PrimeMeridian"} element can not be parsed.
+     *
+     * @see org.apache.sis.referencing.datum.DefaultPrimeMeridian#formatTo(Formatter)
      */
     private PrimeMeridian parsePrimeMeridian(final int mode, final Element parent, Unit<Angle> angularUnit)
             throws ParseException
@@ -1026,7 +1030,7 @@ final class GeodeticObjectParser extends
 
     /**
      * Parses an <strong>optional</strong> {@code "TOWGS84"} element.
-     * This element has the following pattern:
+     * This element is specific to WKT 1 and has the following pattern:
      *
      * {@preformat text
      *     TOWGS84[<dx>, <dy>, <dz>, <ex>, <ey>, <ez>, <ppm>]
@@ -1055,7 +1059,10 @@ final class GeodeticObjectParser extends
     }
 
     /**
-     * Parses a {@code "SPHEROID"} element. This element has the following pattern:
+     * Parses an {@code "Ellipsoid"} element. The syntax is given by
+     * <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#52">WKT 2 specification §8.2.1</a>.
+     *
+     * The legacy WKT 1 pattern was:
      *
      * {@preformat text
      *     SPHEROID["<name>", <semi-major axis>, <inverse flattening> {,<authority>}]
@@ -1063,8 +1070,10 @@ final class GeodeticObjectParser extends
      *
      * @param  mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
      * @param  parent The parent element.
-     * @return The {@code "SPHEROID"} element as an {@link Ellipsoid} object.
-     * @throws ParseException if the {@code "SPHEROID"} element can not be parsed.
+     * @return The {@code "Ellipsoid"} element as an {@link Ellipsoid} object.
+     * @throws ParseException if the {@code "Ellipsoid"} element can not be parsed.
+     *
+     * @see org.apache.sis.referencing.datum.DefaultEllipsoid#formatTo(Formatter)
      */
     private Ellipsoid parseEllipsoid(final int mode, final Element parent) throws ParseException {
         final Element element = parent.pullElement(mode, WKTKeywords.Ellipsoid, WKTKeywords.Spheroid);
@@ -1074,12 +1083,17 @@ final class GeodeticObjectParser extends
         final String name          = element.pullString("name");
         final double semiMajorAxis = element.pullDouble("semiMajorAxis");
         double inverseFlattening   = element.pullDouble("inverseFlattening");
-        if (inverseFlattening == 0) {   // OGC convention for a sphere.
-            inverseFlattening = Double.POSITIVE_INFINITY;
+        Unit<Length> unit = parseScaledUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+        if (unit == null) {
+            unit = SI.METRE;
         }
+        final Map<String,?> properties = parseMetadataAndClose(element, name);
         try {
-            return datumFactory.createFlattenedSphere(parseMetadataAndClose(element, name),
-                    semiMajorAxis, inverseFlattening, SI.METRE);
+            if (inverseFlattening == 0) {   // OGC convention for a sphere.
+                return datumFactory.createEllipsoid(properties, semiMajorAxis, semiMajorAxis, unit);
+            } else {
+                return datumFactory.createFlattenedSphere(properties, semiMajorAxis, inverseFlattening, unit);
+            }
         } catch (FactoryException exception) {
             throw element.parseFailed(exception);
         }
@@ -1122,7 +1136,10 @@ final class GeodeticObjectParser extends
     }
 
     /**
-     * Parses a {@code "DATUM"} element. This element has the following pattern:
+     * Parses a {@code "Datum"} (WKT 2) element. The syntax is given by
+     * <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#54">WKT 2 specification §8.2.4</a>.
+     *
+     * The legacy WKT 1 pattern was:
      *
      * {@preformat text
      *     DATUM["<name>", <spheroid> {,<to wgs84>} {,<authority>}]
@@ -1131,21 +1148,28 @@ final class GeodeticObjectParser extends
      * @param  mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
      * @param  parent The parent element.
      * @param  meridian the prime meridian, or {@code null} for Greenwich.
-     * @return The {@code "DATUM"} element as a {@link GeodeticDatum} object.
-     * @throws ParseException if the {@code "DATUM"} element can not be parsed.
+     * @return The {@code "Datum"} element as a {@link GeodeticDatum} object.
+     * @throws ParseException if the {@code "Datum"} element can not be parsed.
+     *
+     * @see org.apache.sis.referencing.datum.DefaultGeodeticDatum#formatTo(Formatter)
      */
     private GeodeticDatum parseDatum(final int mode, final Element parent, PrimeMeridian meridian) throws ParseException {
-        final Element element = parent.pullElement(mode, WKTKeywords.Datum);
+        final Element element = parent.pullElement(mode, WKTKeywords.Datum, WKTKeywords.GeodeticDatum);
         if (element == null) {
             return null;
         }
         final String             name       = element.pullString("name");
         final Ellipsoid          ellipsoid  = parseEllipsoid(MANDATORY, element);
+        final Element            anchor     = element.pullElement(OPTIONAL, WKTKeywords.Anchor);
         final Object             toWGS84    = parseToWGS84(OPTIONAL, element);
         final Map<String,Object> properties = parseMetadataAndClose(element, name);
         if (meridian == null) {
             meridian = referencing.getGreenwich();
         }
+        if (anchor != null) {
+            properties.put(GeodeticDatum.ANCHOR_POINT_KEY, anchor.pullString("anchorDefinition"));
+            anchor.close(ignoredElements);
+        }
         if (toWGS84 != null) {
             properties.put(ReferencingServices.BURSA_WOLF_KEY, toWGS84);
         }
@@ -1173,8 +1197,8 @@ final class GeodeticObjectParser extends
         if (element == null) {
             return null;
         }
-        final String  name  = element.pullString ("name");
-        final int     datum = element.pullInteger("datum");
+        final String name  = element.pullString ("name");
+        final int    datum = element.pullInteger("datum");
         final VerticalDatumType type = VerticalDatumTypes.fromLegacy(datum);
         // TODO: need to find a default value if null.
         try {
@@ -1270,35 +1294,96 @@ final class GeodeticObjectParser extends
     }
 
     /**
-     * Parses a {@code "GEOCCS"} element.
-     * This element has the following pattern:
+     * Parses a {@code "GeodeticCRS"} (WKT 2) element. The syntax is given by
+     * <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#49">WKT 2 specification §8</a>.
+     *
+     * The legacy WKT 1 specification had two elements for this:
+     *
+     * {@preformat text
+     *     GEOGCS["<name>", <datum>, <prime meridian>, <angular unit>  {,<twin axes>} {,<authority>}]
+     * }
+     *
+     * and
      *
      * {@preformat text
-     *     GEOCCS["<name>", <datum>, <prime meridian>, <linear unit>
-     *            {,<axis> ,<axis> ,<axis>} {,<authority>}]
+     *     GEOCCS["<name>", <datum>, <prime meridian>, <linear unit> {,<axis> ,<axis> ,<axis>} {,<authority>}]
      * }
      *
      * @param  mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
      * @param  parent The parent element.
-     * @return The {@code "GEOCCS"} element as a {@link GeocentricCRS} object.
-     * @throws ParseException if the {@code "GEOCCS"} element can not be parsed.
+     * @return The {@code "GeodeticCRS"} element as a {@link GeocentricCRS} object.
+     * @throws ParseException if the {@code "GeodeticCRS"} element can not be parsed.
+     *
+     * @see org.apache.sis.referencing.crs.DefaultGeographicCRS#formatTo(Formatter)
+     * @see org.apache.sis.referencing.crs.DefaultGeocentricCRS#formatTo(Formatter)
      */
-    private GeocentricCRS parseGeocentricCRS(final int mode, final Element parent) throws ParseException {
-        final Element element = parent.pullElement(mode, WKTKeywords.GeocCS);
+    private GeodeticCRS parseGeodeticCRS(final int mode, final Element parent) throws ParseException {
+        final Element element = parent.pullElement(mode,
+                WKTKeywords.GeodeticCRS,    // [0]  WKT 2
+                WKTKeywords.GeodCRS,        // [1]  WKT 2
+                WKTKeywords.BaseGeodCRS,    // [2]  WKT 2
+                WKTKeywords.GeogCS,         // [3]  WKT 1
+                WKTKeywords.GeocCS);        // [4]  WKT 1
         if (element == null) {
             return null;
         }
-        final String        name       = element.pullString("name");
-        final PrimeMeridian meridian   = parsePrimeMeridian(MANDATORY, element, NonSI.DEGREE_ANGLE);
-        final GeodeticDatum datum      = parseDatum(MANDATORY, element, meridian);
-        final Unit<Length>  linearUnit = parseDerivedUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+        final String      csType;
+        final Unit<Angle> angularUnit;
+        final Unit<?>     defaultUnit;
+        final int         dimension;
+        switch (element.getKeywordIndex()) {
+            default: {      // WKT2 element.
+                csType      = null;
+                defaultUnit = parseUnit(element);
+                angularUnit = Units.isAngular(defaultUnit) ? defaultUnit.asType(Angle.class) : NonSI.DEGREE_ANGLE;
+                dimension   = 2;
+                break;
+            }
+            case 3: {       // WKT1 "GeogCS" element.
+                csType      = WKTKeywords.ellipsoidal;
+                angularUnit = parseScaledUnit(element, WKTKeywords.AngleUnit, SI.RADIAN);
+                defaultUnit = angularUnit;
+                dimension   = 2;
+                break;
+            }
+            case 4: {       // WKT1 "GeocCS" element.
+                csType      = WKTKeywords.Cartesian;
+                angularUnit = NonSI.DEGREE_ANGLE;
+                defaultUnit = parseScaledUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+                dimension   = 3;
+                break;
+            }
+        }
+        Object              name     = element.pullString("name");
+        final PrimeMeridian meridian = parsePrimeMeridian(MANDATORY, element, angularUnit);
+        final GeodeticDatum datum    = parseDatum(MANDATORY, element, meridian);
+        if (((String) name).isEmpty()) {
+            /*
+             * GeographicCRS name is a mandatory property, but some invalid WKT with an empty string exist.
+             * In such case, we will use the name of the enclosed datum. Indeed, it is not uncommon to have
+             * the same name for a geographic CRS and its geodetic datum.
+             */
+            name = datum.getName();
+        }
+        final CoordinateSystem cs;
         try {
-            final CoordinateSystem cs = parseCoordinateSystem(element, WKTKeywords.Cartesian, 3, linearUnit, datum);
-            return crsFactory.createGeocentricCRS(parseMetadataAndClose(element, name), datum,
-                    referencing.upgradeGeocentricCS((CartesianCS) cs));
+            cs = parseCoordinateSystem(element, csType, dimension, defaultUnit, datum);
+            final Map<String,?> properties = parseMetadataAndClose(element, name);
+            if (cs instanceof EllipsoidalCS) {
+                return crsFactory.createGeographicCRS(properties, datum, (EllipsoidalCS) cs);
+            }
+            if (cs instanceof CartesianCS) {
+                return crsFactory.createGeocentricCRS(properties, datum,
+                        referencing.upgradeGeocentricCS((CartesianCS) cs));
+            }
+            if (cs instanceof SphericalCS) {
+                return crsFactory.createGeocentricCRS(properties, datum, (SphericalCS) cs);
+            }
         } catch (FactoryException exception) {
             throw element.parseFailed(exception);
         }
+        throw new LocalizedParseException(errorLocale, Errors.Keys.IllegalCoordinateSystem_1,
+                new Object[] {cs.getClass()}, element.offset);
     }
 
     /**
@@ -1319,9 +1404,9 @@ final class GeodeticObjectParser extends
         if (element == null) {
             return null;
         }
-        final String         name       = element.pullString("name");
-        final VerticalDatum  datum      = parseVerticalDatum(MANDATORY, element);
-        final Unit<Length>   linearUnit = parseDerivedUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+        final String        name       = element.pullString("name");
+        final VerticalDatum datum      = parseVerticalDatum(MANDATORY, element);
+        final Unit<Length>  linearUnit = parseScaledUnit(element, WKTKeywords.LengthUnit, SI.METRE);
         try {
             final CoordinateSystem cs = parseCoordinateSystem(element, WKTKeywords.vertical, 1, linearUnit, datum);
             verticalCRS = crsFactory.createVerticalCRS(parseMetadataAndClose(element, name), datum, (VerticalCS) cs);
@@ -1353,7 +1438,7 @@ final class GeodeticObjectParser extends
         }
         final String         name     = element.pullString("name");
         final TemporalDatum  datum    = parseTimeDatum(MANDATORY, element);
-        final Unit<Duration> timeUnit = parseDerivedUnit(element, WKTKeywords.TimeUnit, SI.SECOND);
+        final Unit<Duration> timeUnit = parseScaledUnit(element, WKTKeywords.TimeUnit, SI.SECOND);
         try {
             final CoordinateSystem cs = parseCoordinateSystem(element, WKTKeywords.temporal, 1, timeUnit, datum);
             return crsFactory.createTemporalCRS(parseMetadataAndClose(element, name), datum, (TimeCS) cs);
@@ -1363,43 +1448,6 @@ final class GeodeticObjectParser extends
     }
 
     /**
-     * Parses a {@code "GEOGCS"} element. This element has the following pattern:
-     *
-     * {@preformat text
-     *     GEOGCS["<name>", <datum>, <prime meridian>, <angular unit>  {,<twin axes>} {,<authority>}]
-     * }
-     *
-     * @param  mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
-     * @param  parent The parent element.
-     * @return The {@code "GEOGCS"} element as a {@link GeographicCRS} object.
-     * @throws ParseException if the {@code "GEOGCS"} element can not be parsed.
-     */
-    private GeographicCRS parseGeographicCRS(final int mode, final Element parent) throws ParseException {
-        final Element element = parent.pullElement(mode, WKTKeywords.GeodeticCRS, WKTKeywords.GeodCRS, WKTKeywords.GeogCS);
-        if (element == null) {
-            return null;
-        }
-        Object              name        = element.pullString("name");
-        final Unit<Angle>   angularUnit = parseDerivedUnit(element, WKTKeywords.AngleUnit, SI.RADIAN);
-        final PrimeMeridian meridian    = parsePrimeMeridian(MANDATORY, element, angularUnit);
-        final GeodeticDatum datum       = parseDatum(MANDATORY, element, meridian);
-        if (((String) name).isEmpty()) {
-            /*
-             * GeographicCRS name is a mandatory property, but some invalid WKT with an empty string exist.
-             * In such case, we will use the name of the enclosed datum. Indeed, it is not uncommon to have
-             * the same name for a geographic CRS and its geodetic datum.
-             */
-            name = datum.getName();
-        }
-        try {
-            final CoordinateSystem cs = parseCoordinateSystem(element, WKTKeywords.ellipsoidal, 2, angularUnit, datum);
-            return crsFactory.createGeographicCRS(parseMetadataAndClose(element, name), datum, (EllipsoidalCS) cs);
-        } catch (FactoryException exception) {
-            throw element.parseFailed(exception);
-        }
-    }
-
-    /**
      * Parses a {@code "PROJCS"} element.
      * This element has the following pattern:
      *
@@ -1418,15 +1466,19 @@ final class GeodeticObjectParser extends
         if (element == null) {
             return null;
         }
-        final String        name       = element.pullString("name");
-        final GeographicCRS geoCRS     = parseGeographicCRS(MANDATORY, element);
-        final Unit<Length>  linearUnit = parseDerivedUnit(element, WKTKeywords.LengthUnit, SI.METRE);
-        final boolean  usesCommonUnits = convention.usesCommonUnits;
-        final Conversion    conversion = parseProjection(MANDATORY, element, usesCommonUnits ? SI.METRE : linearUnit,
+        final String      name   = element.pullString("name");
+        final GeodeticCRS geoCRS = parseGeodeticCRS(MANDATORY, element);
+        if (!(geoCRS instanceof GeographicCRS)) {
+            throw new LocalizedParseException(errorLocale, Errors.Keys.IllegalCRSType_1,
+                    new Object[] {geoCRS.getClass()}, element.offset);
+        }
+        final Unit<Length> linearUnit = parseScaledUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+        final boolean usesCommonUnits = convention.usesCommonUnits;
+        final Conversion conversion = parseProjection(MANDATORY, element, usesCommonUnits ? SI.METRE : linearUnit,
                 usesCommonUnits ? NonSI.DEGREE_ANGLE : geoCRS.getCoordinateSystem().getAxis(0).getUnit().asType(Angle.class));
         try {
             final CoordinateSystem cs = parseCoordinateSystem(element, WKTKeywords.Cartesian, 2, linearUnit, geoCRS.getDatum());
-            return crsFactory.createProjectedCRS(parseMetadataAndClose(element, name), geoCRS, conversion, (CartesianCS) cs);
+            return crsFactory.createProjectedCRS(parseMetadataAndClose(element, name), (GeographicCRS) geoCRS, conversion, (CartesianCS) cs);
         } catch (FactoryException exception) {
             throw element.parseFailed(exception);
         }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/AbstractDatum.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/AbstractDatum.java?rev=1687467&r1=1687466&r2=1687467&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/AbstractDatum.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/AbstractDatum.java [UTF-8] Thu Jun 25 10:18:31 2015
@@ -280,8 +280,7 @@ public class AbstractDatum extends Abstr
      *       {@linkplain DefaultTemporalDatum#getOrigin() origin} instead.</li>
      * </ul>
      *
-     * @return Description, possibly including coordinates, of the point or points used to anchor the datum
-     *         to the Earth.
+     * @return Description, possibly including coordinates, of the point or points used to anchor the datum to the Earth.
      */
     @Override
     public InternationalString getAnchorPoint() {

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java?rev=1687467&r1=1687466&r2=1687467&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java [UTF-8] Thu Jun 25 10:18:31 2015
@@ -26,6 +26,7 @@ import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.extent.Extent;
+import org.opengis.referencing.crs.GeodeticCRS;
 import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.datum.PrimeMeridian;
 import org.opengis.referencing.datum.GeodeticDatum;
@@ -535,7 +536,7 @@ public class DefaultGeodeticDatum extend
      * Note that the {@linkplain #getPrimeMeridian() prime meridian} shall be formatted by the caller
      * as a separated element after the geodetic datum (for compatibility with WKT 1).
      *
-     * @return {@code "Datum"}.
+     * @return {@code "Datum"} or {@code "GeodeticDatum"}.
      *
      * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#51">WKT 2 specification §8.2</a>
      */
@@ -544,7 +545,8 @@ public class DefaultGeodeticDatum extend
         super.formatTo(formatter);
         formatter.newLine();
         formatter.append(toFormattable(getEllipsoid()));
-        if (formatter.getConvention().majorVersion() == 1) {
+        final boolean isWKT1 = formatter.getConvention().majorVersion() == 1;
+        if (isWKT1) {
             /*
              * Note that at the different of other datum (in particular vertical datum),
              * WKT of geodetic datum do not have a numerical code for the datum type.
@@ -560,7 +562,19 @@ public class DefaultGeodeticDatum extend
             }
         }
         // For the WKT 2 case, the ANCHOR[…] element is added by Formatter itself.
+
         formatter.newLine(); // For writing the ID[…] element on its own line.
+        if (!isWKT1) {
+            /*
+             * In WKT 2, both "Datum" and "GeodeticDatum" keywords are permitted. The standard recommends
+             * to use "Datum" for simplicity. We will follow this advice when the Datum element is inside
+             * a GeodeticCRS element since the "Geodetic" aspect is more obvious in such case. But if the
+             * Datum appears in another context, then we will use "GeodeticDatum" for clarity.
+             */
+            if (!(formatter.getEnclosingElement(1) instanceof GeodeticCRS)) {
+                return WKTKeywords.GeodeticDatum;
+            }
+        }
         return WKTKeywords.Datum;
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java?rev=1687467&r1=1687466&r2=1687467&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java [UTF-8] Thu Jun 25 10:18:31 2015
@@ -181,6 +181,29 @@ public final strictfp class GeodeticObje
     }
 
     /**
+     * Tests the parsing of a geodetic datum from a WKT 2 string.
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    public void testDatum() throws ParseException {
+        final GeodeticDatum datum = parse(GeodeticDatum.class,
+                "DATUM[“Tananarive 1925”,\n" +
+                "  ELLIPSOID[“International 1924”, 6378.388, 297.0, LENGTHUNIT[“km”, 1000]],\n" +
+                "  ANCHOR[“Tananarive observatory”]]");
+
+        assertNameAndIdentifierEqual("Tananarive 1925", 0, datum);
+        assertEquals("anchor", "Tananarive observatory", String.valueOf(datum.getAnchorPoint()));
+
+        final Ellipsoid ellipsoid = datum.getEllipsoid();
+        assertNameAndIdentifierEqual("International 1924", 0, ellipsoid);
+        assertEquals("unit", SI.KILOMETRE, ellipsoid.getAxisUnit());
+        assertEquals("semiMajor", 6378.388, ellipsoid.getSemiMajorAxis(), STRICT);
+        assertEquals("inverseFlattening", 297, ellipsoid.getInverseFlattening(), STRICT);
+        assertEquals("greenwichLongitude", 0, datum.getPrimeMeridian().getGreenwichLongitude(), STRICT);
+    }
+
+    /**
      * 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).
@@ -188,7 +211,7 @@ public final strictfp class GeodeticObje
      * @throws ParseException if the parsing failed.
      */
     @Test
-    @DependsOnMethod("testAxis")
+    @DependsOnMethod({"testAxis", "testDatum"})
     public void testGeocentricCRS() throws ParseException {
         final GeocentricCRS crs = parse(GeocentricCRS.class,
                 "GEOCCS[“Geocentric”,\n" +
@@ -226,6 +249,7 @@ public final strictfp class GeodeticObje
      * @throws ParseException if the parsing failed.
      */
     @Test
+    @DependsOnMethod({"testAxis", "testDatum"})
     public void testGeographicCRS() throws ParseException {
         verifyGeographicCRS(0, parse(GeographicCRS.class,
                "  GEOGCS[“WGS 84”,\n" +

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java?rev=1687467&r1=1687466&r2=1687467&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java [UTF-8] Thu Jun 25 10:18:31 2015
@@ -184,7 +184,7 @@ public final strictfp class DefaultGeode
     public void testToWKT() {
         final DefaultGeodeticDatum datum = new DefaultGeodeticDatum(WGS84);
         assertWktEquals(
-                "Datum[“WGS84”,\n" +
+                "GeodeticDatum[“WGS84”,\n" +
                 "  Ellipsoid[“WGS84”, 6378137.0, 298.257223563, LengthUnit[“metre”, 1]]]",
                 datum);
     }
@@ -266,13 +266,13 @@ public final strictfp class DefaultGeode
                 datum);
 
         assertWktEquals(Convention.WKT2,
-                "Datum[“World Geodetic System 1984”,\n" +
+                "GeodeticDatum[“World Geodetic System 1984”,\n" +
                 "  Ellipsoid[“WGS 84”, 6378137.0, 298.257223563, LengthUnit[“metre”, 1]],\n" +
                 "  Id[“EPSG”, 6326, URI[“urn:ogc:def:datum:EPSG::6326”]]]",
                 datum);
 
         assertWktEquals(Convention.INTERNAL,
-                "Datum[“World Geodetic System 1984”,\n" +
+                "GeodeticDatum[“World Geodetic System 1984”,\n" +
                 "  Ellipsoid[“WGS 84”, 6378137.0, 298.257223563, Id[“EPSG”, 7030],\n" +
                 "    Remark[“Defining parameters cited in EPSG database.”]],\n" +
                 "  Anchor[“Station coordinates changed by a few centimetres in 1994, 1997, 2002 and 2012.”],\n" +

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=1687467&r1=1687466&r2=1687467&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] Thu Jun 25 10:18:31 2015
@@ -323,11 +323,21 @@ public final class Errors extends Indexe
         public static final short IllegalBitsPattern_1 = 33;
 
         /**
+         * Coordinate reference system can not be of type ‘{0}’.
+         */
+        public static final short IllegalCRSType_1 = 194;
+
+        /**
          * Class ‘{1}’ is illegal. It must be ‘{0}’ or a derived class.
          */
         public static final short IllegalClass_2 = 34;
 
         /**
+         * Coordinate system can not be “{0}”.
+         */
+        public static final short IllegalCoordinateSystem_1 = 193;
+
+        /**
          * The “{1}” pattern can not be applied to formating of objects of type ‘{0}’.
          */
         public static final short IllegalFormatPatternForClass_2 = 35;

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=1687467&r1=1687466&r2=1687467&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] Thu Jun 25 10:18:31 2015
@@ -75,6 +75,8 @@ IllegalArgumentValue_2            = Argu
 IllegalAxisDirection_2            = Coordinate system of class \u2018{0}\u2019 can not have axis in the {1} direction.
 IllegalBitsPattern_1              = Illegal bits pattern: {0}.
 IllegalClass_2                    = Class \u2018{1}\u2019 is illegal. It must be \u2018{0}\u2019 or a derived class.
+IllegalCoordinateSystem_1         = Coordinate system can not be \u201c{0}\u201d.
+IllegalCRSType_1                  = Coordinate reference system can not be of type \u2018{0}\u2019.
 IllegalFormatPatternForClass_2    = The \u201c{1}\u201d pattern can not be applied to formating of objects of type \u2018{0}\u2019.
 IllegalLanguageCode_1             = The \u201c{0}\u201d language is not recognized.
 IllegalMemberType_2               = Member \u201c{0}\u201d can not be associated to type \u201c{1}\u201d.

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=1687467&r1=1687466&r2=1687467&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] Thu Jun 25 10:18:31 2015
@@ -72,6 +72,8 @@ IllegalArgumentValue_2            = L\u2
 IllegalAxisDirection_2            = Les syst\u00e8mes de coordonn\u00e9es de classe \u2018{0}\u2019 ne peuvent pas avoir d\u2019axe dans la direction \u00ab\u202f{1}\u202f\u00bb.
 IllegalBitsPattern_1              = Pattern de bits invalide: {0}.
 IllegalClass_2                    = La classe \u2018{1}\u2019 est ill\u00e9gale. Il doit s\u2019agir d\u2019une classe \u2018{0}\u2019 ou d\u00e9riv\u00e9e.
+IllegalCoordinateSystem_1         = Le syst\u00e8me de coordonn\u00e9es ne peut pas \u00eatre \u00ab\u202f{0}\u202f\u00bb.
+IllegalCRSType_1                  = Le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es ne peut pas \u00eatre de type \u2018{0}\u2019.
 IllegalFormatPatternForClass_2    = Le mod\u00e8le \u00ab\u202f{1}\u202f\u00bb ne peut pas \u00eatre appliqu\u00e9 au formatage d\u2019objets de type \u2018{0}\u2019.
 IllegalLanguageCode_1             = Le code de langue \u00ab\u202f{0}\u202f\u00bb n\u2019est pas reconnu.
 IllegalMemberType_2               = Le membre \u00ab\u202f{0}\u202f\u00bb ne peut pas \u00eatre associ\u00e9 au type \u00ab\u202f{1}\u202f\u00bb.