sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Add a `DefinitionVerifier.compare(…)` method for comparing against an authoritative CRS already available. Contains opportunistic field renaming and localization.
Date Mon, 19 Jul 2021 14:42:50 GMT
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

commit 3709ff1274f2d1d7e11f4f3850df9156cb109fc2
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon Jul 19 15:42:33 2021 +0200

    Add a `DefinitionVerifier.compare(…)` method for comparing against an authoritative
CRS already available.
    Contains opportunistic field renaming and localization.
---
 .../internal/referencing/DefinitionVerifier.java   | 89 ++++++++++++++++++----
 .../internal/referencing/ReferencingUtilities.java | 22 +++++-
 .../main/java/org/apache/sis/referencing/CRS.java  |  4 +-
 .../referencing/DefinitionVerifierTest.java        | 22 +++---
 .../sis/internal/storage/wkt/StoreFormat.java      |  3 +-
 .../org/apache/sis/internal/storage/xml/Store.java |  3 +-
 6 files changed, 110 insertions(+), 33 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DefinitionVerifier.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DefinitionVerifier.java
index 04ddd40..42fcb47 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DefinitionVerifier.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DefinitionVerifier.java
@@ -62,7 +62,7 @@ import org.apache.sis.util.Utilities;
  * but provide a "recommended CRS" field for what we think is the intended CRS.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
  * @since   0.8
  * @module
  */
@@ -80,13 +80,14 @@ public final class DefinitionVerifier {
 
     /**
      * Recommended CRS. May be the instance given to the {@link #withAuthority withAuthority(…)}
method
-     * or an instance created from the authority factory.
+     * or an instance created from the authority factory. May also be {@code null} if all
CRS given to the
+     * {@link #compare(CoordinateReferenceSystem, CoordinateReferenceSystem) compare(…)}
method were null.
      *
      * Note that ISO 19162 said <cite>"Should any attributes or values given in the
cited identifier be in conflict
      * with attributes or values given explicitly in the WKT description, the WKT values
shall prevail."</cite>
      * So we normally do not use this field.
      */
-    public final CoordinateReferenceSystem authoritative;
+    public final CoordinateReferenceSystem recommendation;
 
     /**
      * If {@link #withAuthority withAuthority(…)} produced a localizable warning, the resource
key for creating the
@@ -100,15 +101,16 @@ public final class DefinitionVerifier {
     private Object[] arguments;
 
     /**
-     * The locale, fixed for now but may become configurable in a future version.
+     * The locale for warning messages, or {@code null} for the system default.
      */
-    private static final Locale locale = null;
+    private final Locale locale;
 
     /**
      * Creates the result of a call to {@code withAuthority(…)}.
      */
-    private DefinitionVerifier(final CoordinateReferenceSystem authoritative) {
-        this.authoritative = authoritative;
+    private DefinitionVerifier(final CoordinateReferenceSystem recommendation, final Locale
locale) {
+        this.recommendation = recommendation;
+        this.locale = locale;
     }
 
     /**
@@ -124,7 +126,7 @@ public final class DefinitionVerifier {
     public static void withAuthority(final CoordinateReferenceSystem crs, final String logger,
             final Class<?> classe, final String method) throws FactoryException
     {
-        final DefinitionVerifier verification = DefinitionVerifier.withAuthority(crs, null,
false);
+        final DefinitionVerifier verification = DefinitionVerifier.withAuthority(crs, null,
false, null);
         if (verification != null) {
             final LogRecord record = verification.warning(true);
             if (record != null) {
@@ -141,11 +143,12 @@ public final class DefinitionVerifier {
      * @param  crs      the CRS to compare with the authoritative description.
      * @param  factory  the factory to use for fetching authoritative description, or {@code
null} for the default.
      * @param  lookup   whether this method is allowed to use {@link IdentifiedObjectFinder}.
+     * @param  locale   the locale for warning messages, or {@code null} for the system default.
      * @return verification result, or {@code null} if the given CRS should be used as-is.
      * @throws FactoryException if an error occurred while querying the authority factory.
      */
     public static DefinitionVerifier withAuthority(final CoordinateReferenceSystem crs, final
CRSAuthorityFactory factory,
-            final boolean lookup) throws FactoryException
+            final boolean lookup, final Locale locale) throws FactoryException
     {
         final CoordinateReferenceSystem authoritative;
         final Citation authority = (factory != null) ? factory.getAuthority() : null;
@@ -162,7 +165,7 @@ public final class DefinitionVerifier {
                 authoritative = CRS.forCode(identifier);
             }
         } catch (NoSuchAuthorityCodeException e) {
-            final DefinitionVerifier verifier = new DefinitionVerifier(crs);
+            final DefinitionVerifier verifier = new DefinitionVerifier(crs, locale);
             verifier.arguments = new String[] {e.getLocalizedMessage()};
             return verifier;
         } else if (lookup) {
@@ -189,8 +192,62 @@ public final class DefinitionVerifier {
         }
         /*
          * At this point we found an authoritative description (typically from EPSG database)
for the given CRS.
-         * Verify if the given CRS is equal to the authoritative description, or a variant
of it. The similarity
-         * variable tells us if we have equality (0), mismatch (-), or equality when using
a variant (+).
+         * Verify if the given CRS is equal to the authoritative description, or a variant
of it.
+         */
+        return compare(crs, authoritative, identifier != null, identifier == null, locale);
+    }
+
+    /**
+     * Compares the given CRS with an authoritative definition of that CRS.
+     * Typically, {@code crs} is parsed from a Well-Known Text (WKT) definition while
+     * {@code authoritative} is provided by a geodetic database from an authority code.
+     *
+     * <p>The {@link #recommendation} CRS is set as below:</p>
+     * <ul>
+     *   <li>If one of given CRS is {@code null}, then the other CRS (which may also
be null) is selected.</li>
+     *   <li>Otherwise if {@code crs} is compatible with {@code authority} with only
a change in axis order,
+     *       a CRS derived from {@code authority} but with {@code crs} axis order is silently
selected.</li>
+     *   <li>Otherwise {@code authority} is selected and a {@linkplain #warning(boolean)
warning message} is prepared.</li>
+     * </ul>
+     *
+     * @param  crs            the CRS to compare against an authoritative definition, or
{@code null}.
+     * @param  authoritative  the presumed authoritative definition of the given CRS, or
{@code null}.
+     * @param  locale         the locale for warning messages, or {@code null} for the system
default.
+     * @return verification result (never {@code null}).
+     */
+    public static DefinitionVerifier compare(final CoordinateReferenceSystem crs,
+            final CoordinateReferenceSystem authoritative, final Locale locale)
+    {
+        if (crs == null || authoritative == null) {
+            return new DefinitionVerifier((crs != null) ? crs : authoritative, locale);
+        } else {
+            return compare(crs, authoritative, false, false, locale);
+        }
+    }
+
+    /**
+     * Implementation of {@link #compare(CoordinateReferenceSystem, CoordinateReferenceSystem)}
+     * and final step in {@code forAuthority(…)} methods. The boolean flags control the
behavior
+     * in case of mismatched axis order or full mismatch.
+     *
+     * @param  strictAxisOrder  whether the CRS should comply with authoritative axis order.
+     *                          If {@code true}, mismatched axis order will be reported as
a warning.
+     *                          If {@code false}, they will be silently ignored.
+     * @param  nullIfNoMatch    whether to return {@code null} if CRS do not match.
+     *                          If {@code false}, then this method never return {@code null}.
+     * @return verification result, possibly {@code null} if {@code nullIfNoMatch} is {@code
true}.
+     */
+    private static DefinitionVerifier compare(final CoordinateReferenceSystem crs,
+                                              final CoordinateReferenceSystem authoritative,
+                                              final boolean strictAxisOrder,
+                                              final boolean nullIfNoMatch,
+                                              final Locale locale)
+    {
+        /*
+         * The similarity flag has the following meaning:
+         *   (-) mismatch
+         *   (0) equality
+         *   (+) equality when using a variant
          */
         int similarity = 0;
         final AbstractCRS ca = AbstractCRS.castOrCopy(authoritative);
@@ -198,7 +255,7 @@ public final class DefinitionVerifier {
         while (!variant.equals(crs, ComparisonMode.APPROXIMATE)) {
             if (similarity < VARIANTS.length) {
                 variant = ca.forConvention(VARIANTS[similarity++]);
-            } else if (identifier == null) {
+            } else if (nullIfNoMatch) {
                 return null;        // Mismatched CRS, but our "authoritative" description
was only a guess. Ignore.
             } else {
                 similarity = -1;    // Mismatched CRS and our authoritative description was
not a guess. Need warning.
@@ -213,13 +270,13 @@ public final class DefinitionVerifier {
              *     The coordinate system axes in the given “{0}” description do not conform
to the expected axes
              *     according “{1}” authoritative description.
              */
-            verifier = new DefinitionVerifier(variant);
-            if (identifier != null) {
+            verifier = new DefinitionVerifier(variant, locale);
+            if (strictAxisOrder) {
                 verifier.resourceKey = Resources.Keys.NonConformAxes_2;
                 verifier.arguments   = new String[2];
             }
         } else {
-            verifier = new DefinitionVerifier(authoritative);
+            verifier = new DefinitionVerifier(authoritative, locale);
             if (similarity != 0) {
                 /*
                  * Warning message (from Resources.properties):
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
index 18bc461..fa52f4d 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
@@ -160,6 +160,24 @@ public final class ReferencingUtilities extends Static {
 
     /**
      * Returns the GeoAPI interface implemented by the given object, or the implementation
class
+     * if the interface is unknown. This method can be used when the base type (CRS, CS,
Datum…)
+     * is unknown, for example when preparing an error message. If the base type is known,
then
+     * the method expecting a {@code baseType} argument should be preferred.
+     *
+     * @param  object    the object for which to get the GeoAPI interface, or {@code null}.
+     * @return GeoAPI interface or implementation class of the given object, or {@code null}
if the given object is null.
+     */
+    @SuppressWarnings("unchecked")
+    public static Class<?> getInterface(final Object object) {
+        if (object instanceof AbstractIdentifiedObject) {
+            return ((AbstractIdentifiedObject) object).getInterface();
+        } else {
+            return getInterface(IdentifiedObject.class, (Class) Classes.getClass(object));
+        }
+    }
+
+    /**
+     * Returns the GeoAPI interface implemented by the given object, or the implementation
class
      * if the interface is unknown.
      *
      * @param  <T>       compile-time value of {@code baseType}.
@@ -169,9 +187,9 @@ public final class ReferencingUtilities extends Static {
      */
     public static <T extends IdentifiedObject> Class<? extends T> getInterface(final
Class<T> baseType, final T object) {
         if (object instanceof AbstractIdentifiedObject) {
-             return ((AbstractIdentifiedObject) object).getInterface().asSubclass(baseType);
+            return ((AbstractIdentifiedObject) object).getInterface().asSubclass(baseType);
         } else {
-             return getInterface(baseType, Classes.getClass(object));
+            return getInterface(baseType, Classes.getClass(object));
         }
     }
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
index 3ec7fda..a159731 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
@@ -416,9 +416,9 @@ public final class CRS extends Static {
             final CRSAuthorityFactory factory, final Filter warningFilter) throws FactoryException
     {
         if (crs != null) {
-            final DefinitionVerifier verification = DefinitionVerifier.withAuthority(crs,
factory, true);
+            final DefinitionVerifier verification = DefinitionVerifier.withAuthority(crs,
factory, true, null);
             if (verification != null) {
-                crs = verification.authoritative;
+                crs = verification.recommendation;
                 if (warningFilter != null) {
                     final LogRecord record = verification.warning(false);
                     if (record != null) {
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/DefinitionVerifierTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/DefinitionVerifierTest.java
index 20add80..7262306 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/DefinitionVerifierTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/DefinitionVerifierTest.java
@@ -38,7 +38,7 @@ import static org.junit.Assert.*;
  * Tests {@link DefinitionVerifier}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
  * @since   0.8
  * @module
  */
@@ -52,10 +52,10 @@ public final strictfp class DefinitionVerifierTest extends TestCase {
     @Test
     public void testConformCRS() throws FactoryException {
         final DefaultGeographicCRS crs = HardCodedCRS.WGS84_LATITUDE_FIRST;
-        final DefinitionVerifier ver = DefinitionVerifier.withAuthority(crs, null, false);
+        final DefinitionVerifier ver = DefinitionVerifier.withAuthority(crs, null, false,
null);
         assertNotNull("Should replace by EPSG:4326", ver);
-        assertNotSame("Should replace by EPSG:4326", crs, ver.authoritative);
-        assertSame   ("Should replace by EPSG:4326", CommonCRS.WGS84.geographic(), ver.authoritative);
+        assertNotSame("Should replace by EPSG:4326", crs, ver.recommendation);
+        assertSame   ("Should replace by EPSG:4326", CommonCRS.WGS84.geographic(), ver.recommendation);
         assertNull   ("Should be silent.", ver.warning(true));
     }
 
@@ -68,11 +68,11 @@ public final strictfp class DefinitionVerifierTest extends TestCase {
     @DependsOnMethod("testConformCRS")
     public void testNormalizedCRS() throws FactoryException {
         final DefaultGeographicCRS crs = HardCodedCRS.WGS84;
-        assertNull("No replacement without EPSG code.", DefinitionVerifier.withAuthority(crs,
null, false));
-        final DefinitionVerifier ver = DefinitionVerifier.withAuthority(crs, null, true);
+        assertNull("No replacement without EPSG code.", DefinitionVerifier.withAuthority(crs,
null, false, null));
+        final DefinitionVerifier ver = DefinitionVerifier.withAuthority(crs, null, true,
null);
         assertNotNull("Should replace by normalized CRS", ver);
-        assertNotSame("Should replace by normalized CRS", crs, ver.authoritative);
-        assertSame   ("Should replace by normalized CRS", CommonCRS.WGS84.normalizedGeographic(),
ver.authoritative);
+        assertNotSame("Should replace by normalized CRS", crs, ver.recommendation);
+        assertSame   ("Should replace by normalized CRS", CommonCRS.WGS84.normalizedGeographic(),
ver.recommendation);
         assertNull   ("Should be silent.", ver.warning(true));
     }
 
@@ -90,10 +90,10 @@ public final strictfp class DefinitionVerifierTest extends TestCase {
         DefaultGeographicCRS crs = HardCodedCRS.WGS84;
         crs = new DefaultGeographicCRS(properties, crs.getDatum(), crs.getCoordinateSystem());
 
-        final DefinitionVerifier ver = DefinitionVerifier.withAuthority(crs, null, false);
+        final DefinitionVerifier ver = DefinitionVerifier.withAuthority(crs, null, false,
null);
         assertNotNull("Should replace by normalized CRS", ver);
-        assertNotSame("Should replace by normalized CRS", crs, ver.authoritative);
-        assertSame   ("Should replace by normalized CRS", CommonCRS.WGS84.normalizedGeographic(),
ver.authoritative);
+        assertNotSame("Should replace by normalized CRS", crs, ver.recommendation);
+        assertSame   ("Should replace by normalized CRS", CommonCRS.WGS84.normalizedGeographic(),
ver.recommendation);
 
         final LogRecord warning = ver.warning(true);
         assertNotNull("Should emit a warning.", warning);
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
index 3557b9f..a74cc84 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
@@ -133,7 +133,8 @@ public final class StoreFormat extends WKTFormat {
             log(new LogRecord(Level.WARNING, warnings.toString()));
         }
         if (parsed instanceof CoordinateReferenceSystem) try {
-            final DefinitionVerifier v = DefinitionVerifier.withAuthority((CoordinateReferenceSystem)
parsed, null, false);
+            final DefinitionVerifier v = DefinitionVerifier.withAuthority(
+                    (CoordinateReferenceSystem) parsed, null, false, getLocale());
             if (v != null) {
                 final LogRecord warning = v.warning(false);
                 if (warning != null) log(warning);
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java
index 94504d7..1036f27 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java
@@ -163,7 +163,8 @@ final class Store extends URIDataStore implements Filter {
             throw new DataStoreException(Errors.format(Errors.Keys.CanNotRead_1, getDisplayName()),
e);
         }
         if (object instanceof CoordinateReferenceSystem) try {
-            final DefinitionVerifier v = DefinitionVerifier.withAuthority((CoordinateReferenceSystem)
object, null, false);
+            final DefinitionVerifier v = DefinitionVerifier.withAuthority(
+                    (CoordinateReferenceSystem) object, null, false, getLocale());
             if (v != null) {
                 log(v.warning(false));
             }

Mime
View raw message