This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 3e68c8972c Improve the warnings when the units of measurement in a WKT 
are inconsistent.
3e68c8972c is described below

commit 3e68c8972c640dfd1548f54e2cbce27c0ac2705b
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Sep 16 18:16:19 2025 +0200

    Improve the warnings when the units of measurement in a WKT are 
inconsistent.
---
 .../main/org/apache/sis/io/wkt/AbstractParser.java | 18 ++---
 .../apache/sis/io/wkt/GeodeticObjectParser.java    | 89 +++++++++++++---------
 .../org/apache/sis/io/wkt/MathTransformParser.java |  6 +-
 .../sis/io/wkt/GeodeticObjectParserTest.java       | 77 ++++++++++++-------
 4 files changed, 114 insertions(+), 76 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java
index 4e70bf62f5..75e08feff8 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java
@@ -221,18 +221,6 @@ abstract class AbstractParser implements Parser {
         return "createFromWKT";
     }
 
-    /**
-     * Logs the given record for a warning that occurred during parsing.
-     * This is used when we cannot use the {@link #warning warning methods},
-     * or when the information is not worth to report as a warning.
-     */
-    final void log(final LogRecord record) {
-        record.setSourceClassName (getPublicFacade());
-        record.setSourceMethodName(getFacadeMethod());
-        record.setLoggerName(Loggers.WKT);
-        LOGGER.log(record);
-    }
-
     /**
      * Creates the object from a WKT string and logs the warnings if any.
      * This method is for implementation of {@code createFromWKT(String)} 
method in SIS factories only.
@@ -269,7 +257,11 @@ abstract class AbstractParser implements Parser {
                         CharSequences.token(wkt, 0) + "[…]", unparsed));
         }
         if (warnings != null) {
-            log(new LogRecord(Level.WARNING, warnings.toString()));
+            final var record = new LogRecord(Level.WARNING, 
warnings.toString());
+            record.setSourceClassName (getPublicFacade());
+            record.setSourceMethodName(getFacadeMethod());
+            record.setLoggerName(Loggers.WKT);
+            LOGGER.log(record);
         }
         return result;
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
index 8120099749..95e05df498 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
@@ -25,8 +25,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
 import java.text.DateFormat;
 import java.text.NumberFormat;
 import java.text.ParsePosition;
@@ -41,11 +39,11 @@ import javax.measure.quantity.Angle;
 import javax.measure.quantity.Length;
 import javax.measure.quantity.Time;
 import javax.measure.format.MeasurementParseException;
+import org.opengis.util.FactoryException;
 import org.opengis.metadata.Identifier;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.ObjectFactory;
-import org.opengis.util.FactoryException;
 import org.opengis.referencing.cs.*;
 import org.opengis.referencing.crs.*;
 import org.opengis.referencing.datum.*;
@@ -118,6 +116,12 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
      */
     private static final String[] ToWGS84 = {"dx", "dy", "dz", "ex", "ey", 
"ez", "ppm"};
 
+    /**
+     * Symbol of units to not try to parse. Those symbols are used in the 
<abbr>EPSG</abbr> database
+     * or by {@link org.apache.sis.measure.SexagesimalConverter} but not yet 
accepted by the parser.
+     */
+    private static final String[] UNIT_SYMBOLS_TO_IGNORE = {"DMSH", "DMS", 
"D.MS", "D.M"};
+
     /**
      * During WKT 1 parsing, {@code true} means that {@code PRIMEM} and {@code 
PARAMETER} angular units
      * need to be forced to {@code Units.DEGREE} instead of inferred from the 
context.
@@ -650,50 +654,65 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
         if (element == null) {
             return null;
         }
-        final String name   = element.pullString("name");
-        final double factor = element.pullDouble("factor");
-        Unit<Q> unit   = baseUnit.multiply(completeUnitFactor(baseUnit, 
factor));
-        Unit<?> verify = parseUnitID(element);
+        final String  name   = element.pullString("name");
+        final double  factor = element.pullDouble("factor");
+        final var     unitID = new Object[2];
+        final Unit<?> byID   = parseUnitID(element, unitID);
+        Unit<Q> unit = baseUnit.multiply(completeUnitFactor(baseUnit, factor));
         element.close(ignoredElements);
         /*
-         * Consider the following element: UNIT[“kilometre”, 1000, ID[“EPSG”, 
“9036”]]
-         *
-         *  - if the authority code (“9036”) refers to a unit incompatible 
with `baseUnit` (“metre”), log a warning.
-         *  - otherwise: 1) unconditionally replace the parsed unit (“km”) by 
the unit referenced by the authority code.
-         *               2) if the new unit is not equivalent to the old one 
(i.e. different scale factor), log a warning.
+         * Verify the consistency of the unit defined by scale factor with the 
unit parsed from the name.
+         * If they agree, the parsed unit replaces the declared unit because 
it may fix rounding errors.
          */
-        if (verify != null) {
-            if (!baseUnit.getSystemUnit().equals(verify.getSystemUnit())) {
-                warning(parent, element, 
Errors.formatInternational(Errors.Keys.InconsistentUnitsForCS_1, verify), null);
-            } else if (Math.abs(unit.getConverterTo(unit = (Unit<Q>) 
verify).convert(1) - 1) > Numerics.COMPARISON_THRESHOLD) {
-                warning(parent, element, 
Errors.formatInternational(Errors.Keys.UnexpectedScaleFactorForUnit_2, verify, 
factor), null);
+        if (!ArraysExt.containsIgnoreCase(UNIT_SYMBOLS_TO_IGNORE, name)) try {
+            final Unit<?> parsed = parseUnit(name);
+            if (verifyUnitConsistency(unit, parsed)) {
+                unit = (Unit<Q>) parsed;
             } else {
-                verify = null;                                          // 
Means to perform additional verifications.
+                warning(parent, element, 
Errors.formatInternational(Errors.Keys.UnexpectedScaleFactorForUnit_2, name, 
factor), null);
             }
+        } catch (IncommensurableException e) {
+            unitID[0] = WKTKeywords.Unit;
+            unitID[1] = name;
+            throw new UnparsableObjectException(errorLocale, 
Errors.Keys.InconsistentAttribute_2, unitID, element.offset).initCause(e);
+        } catch (MeasurementParseException e) {
+            warning(parent, element, 
Errors.formatInternational(Errors.Keys.UnknownUnit_1, name), e);
         }
         /*
-         * Above block verified the ID[“EPSG”, “9036”] authority code. Now 
verify the unit parsed from the “km” symbol.
-         * This is only a verification; we will not replace the unit by the 
parsed one (i.e. authority code or scale
-         * factor have precedence over the unit symbol).
+         * Verify the consistency of the defined unit with the unit specified 
by authority code.
+         * If they disagree, a warning is logged and the unit defined by scale 
factor is kept.
+         * If they agree, the unit specified by authority code replaces the 
unit defined by
+         * scale factor, because the latter is often written in WKT with 
rounding errors.
          */
-        if (verify == null) {
+        if (byID != null && byID != unit) {
+            IncommensurableException cause = null;
             try {
-                verify = parseUnit(name);
-            } catch (MeasurementParseException e) {
-                log(new LogRecord(Level.FINE, e.toString()));
-            }
-            if (verify != null) try {
-                if (Math.abs(verify.getConverterToAny(unit).convert(1) - 1) > 
Numerics.COMPARISON_THRESHOLD) {
-                    warning(parent, element, 
Errors.formatInternational(Errors.Keys.UnexpectedScaleFactorForUnit_2, verify, 
factor), null);
+                if (verifyUnitConsistency(unit, byID)) {
+                    return (Unit<Q>) byID;
+                }
+                if (Units.isAngular(unit) && verifyUnitConsistency(unit, 
Units.DEGREE)) {
+                    unit = (Unit<Q>) Units.DEGREE;      // Replace DMS by 
decimal degrees.
                 }
             } catch (IncommensurableException e) {
-                throw new UnparsableObjectException(errorLocale, 
Errors.Keys.InconsistentUnitsForCS_1,
-                        new Object[] {verify}, element.offset).initCause(e);
+                cause = e;
             }
+            unitID[1] = Constants.EPSG + ':' + unitID[0];
+            unitID[0] = WKTKeywords.Unit + "[\"" + name + "\"]." + 
WKTKeywords.Id;
+            warning(parent, element, 
Errors.formatInternational(Errors.Keys.InconsistentAttribute_2, unitID), cause);
         }
         return unit;
     }
 
+    /**
+     * Verifies if the two given units are equivalent, within a small 
tolerance factor.
+     * A tolerance factor is applied because scale factors are often written 
in WKT with
+     * some rounding errors.
+     */
+    private static boolean verifyUnitConsistency(Unit<?> unit, Unit<?> 
expected) throws IncommensurableException {
+        // Use 1.5 instead of 1.0 because identification of DMS units requires 
a number with a fractional part.
+        return Math.abs(expected.getConverterToAny(unit).convert(1.5) - 1.5) 
<= 1.5*Numerics.COMPARISON_THRESHOLD;
+    }
+
     /**
      * Parses a {@code "CS"} element followed by all {@code "AXIS"} elements.
      * This element has the following pattern (simplified):
@@ -1418,7 +1437,7 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
         if (wrapper != null) {
             properties = parseMetadataAndClose(parent, name, method);
             /*
-             * DEPARTURE FROM ISO 19162: the specification in §9.3.2 said:
+             * DEPARTURE FROM THE SPECIFICATION: ISO 19162:2015 §9.3.2 said:
              *
              *     "If an identifier is provided as an attribute within the 
<map projection conversion> object,
              *     because it is expected to describe a complete collection of 
zone name, method, parameters and
@@ -1923,7 +1942,7 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
             default: {
                 /*
                  * WKT 2 "GeodeticCRS" element.
-                 * The specification in §8.2.2 (ii) said:
+                 * ISO 19162:2015 §8.2.2 (ii) said:
                  *
                  *     "If the subtype of the geodetic CRS to which the prime 
meridian is an attribute
                  *     is geographic, the prime meridian’s <irm longitude> 
value shall be given in the
@@ -2017,7 +2036,7 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
                 return crsFactory.createDerivedCRS(properties, baseCRS, 
fromBase, cs);
             }
             /*
-             * The specification in §8.2.2 (ii) said:
+             * ISO 19162:2015 §8.2.2 (ii) said:
              *
              *     "(snip) the prime meridian’s <irm longitude> value shall be 
given in the
              *     same angular units as those for the horizontal axes of the 
geographic CRS."
@@ -2340,7 +2359,7 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
                  * all parameters that do not specify explicitly a LengthUnit. 
If no such crs-wide unit was
                  * specified, then the default will be degrees.
                  *
-                 * More specifically §9.3.4 in the specification said about 
the default units:
+                 * More specifically ISO 19162:2015 §9.3.4 said about the 
default units:
                  *
                  *    - lengths shall be given in the unit for the projected 
CRS axes.
                  *    - angles shall be given in the unit for the base 
geographic CRS of the projected CRS.
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/MathTransformParser.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/MathTransformParser.java
index d2712e0309..2f69b0fa00 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/MathTransformParser.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/MathTransformParser.java
@@ -216,15 +216,17 @@ class MathTransformParser extends AbstractParser {
      * {@code UNIT} elements are rare, so risk of conflicts should be low.
      *
      * @param  parent  the parent {@code "UNIT"} element.
+     * @param  unitID  where to store the authority code for caller's 
information, or {@code null} if none.
      * @return the unit from the identifier code, or {@code null} if none.
      * @throws ParseException if the {@code "ID"} cannot be parsed.
      */
-    final Unit<?> parseUnitID(final Element parent) throws ParseException {
+    final Unit<?> parseUnitID(final Element parent, final Object[] unitID) 
throws ParseException {
         final Element element = parent.pullElement(OPTIONAL, ID_KEYWORDS);
         if (element != null) {
             final String codeSpace = element.pullString("codeSpace");
             final Object code      = element.pullObject("code");            // 
Accepts Integer as well as String.
             element.close(ignoredElements);
+            if (unitID != null) unitID[0] = code;
             if (Constants.EPSG.equalsIgnoreCase(codeSpace)) try {
                 final int n;
                 if (Numbers.isInteger(code.getClass())) {
@@ -257,7 +259,7 @@ class MathTransformParser extends AbstractParser {
         }
         final int     index  = element.getKeywordIndex() - 1;
         final String  name   = element.pullString("name");
-        final Unit<?> unit   = parseUnitID(element);
+        final Unit<?> unit   = parseUnitID(element, null);
         final Unit<?> base   = (index >= 0 && index < BASE_UNITS.length) ? 
BASE_UNITS[index] : null;
         /*
          * The conversion factor form base unit is mandatory, except for 
temporal units
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
index 6fd24bb1a2..27baa1a342 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
@@ -91,7 +91,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
      * Parses the given text. It is caller's responsibility to verify if some 
warnings have been emitted.
      *
      * @param  type  the expected object type.
-     * @param  text  the WKT string to parse.
+     * @param  text  the <abbr>WKT</abbr> string to parse.
      * @return the parsed object.
      * @throws ParseException if an error occurred during the parsing.
      */
@@ -99,7 +99,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
         if (parser == null) {
             newParser(Convention.DEFAULT);
         }
-        final ParsePosition position = new ParsePosition(0);
+        final var position = new ParsePosition(0);
         final Object obj = parser.createFromWKT(text, position);
         assertEquals(-1, position.getErrorIndex(), "errorIndex");
         assertEquals(text.length(), position.getIndex(), "index");
@@ -107,10 +107,10 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Parses the given text and ensure that no warnings have been emitted.
+     * Parses the given text and ensures that no warnings have been emitted.
      *
      * @param  type  the expected object type.
-     * @param  text  the WKT string to parse.
+     * @param  text  the <abbr>WKT</abbr> string to parse.
      * @return the parsed object.
      * @throws ParseException if an error occurred during the parsing.
      */
@@ -145,7 +145,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
 
     /**
      * Asserts that the given axis is a longitude axis. This method expects 
the name to be
-     * <q>Geodetic longitude</q> even if the WKT string contained a different 
name,
+     * <q>Geodetic longitude</q> even if the <abbr>WKT</abbr> string contained 
a different name,
      * because {@link GeodeticObjectParser} should have done the replacement.
      */
     private static void assertLongitudeAxisEquals(final CoordinateSystemAxis 
axis) {
@@ -154,7 +154,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
 
     /**
      * Asserts that the given axis is a latitude axis. This method expects the 
name to be
-     * <q>Geodetic latitude</q> even if the WKT string contained a different 
name,
+     * <q>Geodetic latitude</q> even if the <abbr>WKT</abbr> string contained 
a different name,
      * because {@link GeodeticObjectParser} should have done the replacement.
      */
     private static void assertLatitudeAxisEquals(final CoordinateSystemAxis 
axis) {
@@ -231,7 +231,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a geodetic reference frame from a WKT 2 string.
+     * Tests the parsing of a geodetic reference frame from a <abbr>WKT</abbr> 
2 string.
      *
      * @throws ParseException if the parsing failed.
      */
@@ -254,7 +254,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a geocentric CRS from a WKT 1 string. The parser
+     * Tests the parsing of a geocentric CRS from a <abbr>WKT</abbr> 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).
      *
@@ -293,7 +293,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a geographic CRS from a WKT 1 string using 
(longitude, latitude) axis order.
+     * Tests the parsing of a geographic CRS from a <abbr>WKT</abbr> 1 string 
using (longitude, latitude) axis order.
      *
      * @throws ParseException if the parsing failed.
      */
@@ -310,7 +310,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a geographic CRS from a WKT 1 string using 
(latitude, longitude) axis order.
+     * Tests the parsing of a geographic CRS from a <abbr>WKT</abbr> 1 string 
using (latitude, longitude) axis order.
      *
      * @throws ParseException if the parsing failed.
      */
@@ -327,7 +327,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a geographic CRS from a WKT 1 string that does not 
declare explicitly the axes.
+     * Tests the parsing of a geographic CRS from a <abbr>WKT</abbr> 1 string 
that does not declare explicitly the axes.
      *
      * @throws ParseException if the parsing failed.
      *
@@ -357,7 +357,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
                 "DATUM[“North_American_Datum_1983”,\n" +
                 "SPHEROID[“GRS 1980”, 6378137, 298.257222101]],\n" +
                 "PRIMEM[“Greenwich”, 0],\n" +
-                "UNIT[“Decimal_Second”, 4.84813681109536e-06],\n" +
+                "UNIT[“arc-second”, 4.84813681109536e-06],\n" +
                 "AUTHORITY[“EPSG”, “100001”]]");
 
         assertNameAndIdentifierEqual("NAD83 / NFIS Seconds", 100001, crs);
@@ -566,7 +566,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a projected CRS from a WKT 1 string.
+     * Tests the parsing of a projected CRS from a <abbr>WKT</abbr> 1 string.
      *
      * @throws ParseException if the parsing failed.
      */
@@ -614,7 +614,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a projected CRS from a WKT 1 string with authority 
and Bursa-Wolf parameters.
+     * Tests the parsing of a projected CRS from a <abbr>WKT</abbr> 1 string 
with authority and Bursa-Wolf parameters.
      *
      * @throws ParseException if the parsing failed.
      */
@@ -885,7 +885,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests parsing a WKT with a missing Geographic CRS name.
+     * Tests parsing a <abbr>WKT</abbr> with a missing Geographic 
<abbr>CRS</abbr> name.
      * This should be considered invalid, but happen in practice.
      *
      * <p>The WKT tested in this method also contains some other oddities 
compared to the usual WKT:</p>
@@ -1071,7 +1071,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a derived CRS from a WKT 2 string.
+     * Tests the parsing of a derived CRS from a <abbr>WKT</abbr> 2 string.
      * Note: this test uses an example from an old <abbr>EPSG</abbr>
      * geodetic dataset which is no longer present in more recent versions.
      *
@@ -1120,7 +1120,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of an engineering CRS from a WKT 2 string.
+     * Tests the parsing of an engineering CRS from a <abbr>WKT</abbr> 2 
string.
      *
      * @throws ParseException if the parsing failed.
      */
@@ -1148,7 +1148,8 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a compound CRS from a WKT 1 string, except the 
time dimension which is WKT 2.
+     * Tests the parsing of a compound CRS from a <abbr>WKT</abbr> 1 string,
+     * except the time dimension which is <abbr>WKT</abbr> 2.
      *
      * @throws ParseException if the parsing failed.
      */
@@ -1204,8 +1205,8 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Tests the parsing of a compound CRS from a WKT 1 string with 
ellipsoidal height and its conversion
-     * to a three-dimensional geographic CRS.
+     * Tests the parsing of a compound CRS from a <abbr>WKT</abbr> 1 string 
with ellipsoidal height
+     * and its conversion to a three-dimensional geographic <abbr>CRS</abbr>.
      *
      * @throws ParseException if the parsing failed.
      *
@@ -1271,7 +1272,7 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
     }
 
     /**
-     * Ensures that parsing a WKT with wrong units throws an exception.
+     * Ensures that parsing a <abbr>WKT</abbr> with wrong units throws an 
exception.
      */
     @Test
     public void testIncompatibleUnits() {
@@ -1280,13 +1281,37 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
                     "  DATUM[“North American Datum 1983”,\n" +
                     "    SPHEROID[“GRS 1980”, 6378137.0, 298.257222]],\n" +
                     "  PRIMEM[“Greenwich”, 0],\n" +
-                    "  UNIT[“kilometre”, 1000]]"));                            
          // Wrong unit);
+                    "  UNIT[“kilometre”, 1000]]"));     // Wrong unit
 
-        assertMessageContains(exception, "km");
+        assertMessageContains(exception, "kilometre");
     }
 
     /**
-     * Tests the production of a warning messages when the WKT contains 
unknown elements.
+     * Ensures that parsing a <abbr>WKT</abbr> with an <abbr>EPSG</abbr> code 
inconsistent with the unit definition.
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    public void testInconsistentUnitAuthorityCode() throws ParseException {
+        final GeographicCRS crs = parseIgnoreWarnings(GeographicCRS.class,
+               "GEOGCS[“WGS 84”,\n" +
+               "  DATUM[“World Geodetic System 1984”,\n" +
+               "    SPHEROID[“WGS84”, 6378137.0, 298.257223563]],\n" +
+               "  PRIMEM[“Greenwich”, 0],\n" +
+               "  UNIT[“degree”, 0.0174532925199433, AUTHORITY[“EPSG”, 
“9108”]]]");   // DMSH unit.
+
+        verifyGeographicCRS(0, crs);
+        final Warnings warnings = parser.getAndClearWarnings(crs);
+        assertNotNull(warnings, "warnings");
+        assertTrue(warnings.getExceptions().isEmpty());
+        assertEquals("WGS 84", warnings.getRootElement());
+        final String message = warnings.toString(Locale.US);
+        assertTrue(message.contains("EPSG:9108"), message);
+        assertTrue(message.contains("Unit[\"degree\"].Id"), message);
+    }
+
+    /**
+     * Tests the production of a warning messages when the <abbr>WKT</abbr> 
contains unknown elements.
      *
      * @throws ParseException if the parsing failed.
      */
@@ -1310,14 +1335,14 @@ public final class GeodeticObjectParserTest extends 
EPSGDependentTestCase {
         assertArrayEquals(new String[] {"SPHEROID"},                 
warnings.getUnknownElementLocations("Ext2").toArray());
 
         assertMultilinesEquals("Parsing of “WGS 84” done, but some elements 
were ignored.\n" +
-                               " • Unexpected scale factor 0.01746 for unit of 
measurement “°”.\n" +
+                               " • Unexpected scale factor 0.01746 for unit of 
measurement “degree”.\n" +
                                " • The text contains unknown elements:\n" +
                                "    ‣ “Intruder” in PRIMEM, GEOGCS.\n" +
                                "    ‣ “Ext1” in SPHEROID.\n" +
                                "    ‣ “Ext2” in SPHEROID.", 
warnings.toString(Locale.US));
 
         assertMultilinesEquals("La lecture de « WGS 84 » a été faite, mais en 
ignorant certains éléments.\n" +
-                               " • Le facteur d’échelle 0,01746 est inattendu 
pour l’unité de mesure « ° ».\n" +
+                               " • Le facteur d’échelle 0,01746 est inattendu 
pour l’unité de mesure « degree ».\n" +
                                " • Le texte contient des éléments inconnus 
:\n" +
                                "    ‣ « Intruder » dans PRIMEM, GEOGCS.\n" +
                                "    ‣ « Ext1 » dans SPHEROID.\n" +

Reply via email to