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 31beb1f10f7dc67b6a6ab6254b192966db3ec0a0
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Feb 24 11:02:20 2022 +0100

    Add formatting support for ESRI WKT "GeogTran" element.
---
 .../apache/sis/io/wkt/GeodeticObjectParser.java    |  4 +-
 .../operation/AbstractCoordinateOperation.java     | 53 ++++++++++++++++++---
 .../sis/io/wkt/GeodeticObjectParserTest.java       |  3 +-
 .../java/org/apache/sis/io/wkt/WKTFormatTest.java  | 55 +++++++++++++++++++---
 4 files changed, 98 insertions(+), 17 deletions(-)

diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
index 3ed1bf7..e47ebdf 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
@@ -2264,8 +2264,8 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
             return null;
         }
         final String name = element.pullString("name");
-        final CoordinateReferenceSystem sourceCRS  = 
parseCoordinateReferenceSystem(element, true);
-        final CoordinateReferenceSystem targetCRS  = 
parseCoordinateReferenceSystem(element, true);
+        final CoordinateReferenceSystem sourceCRS  = 
parseGeodeticCRS(MANDATORY, element, 2, null);
+        final CoordinateReferenceSystem targetCRS  = 
parseGeodeticCRS(MANDATORY, element, 2, null);
         final OperationMethod           method     = parseMethod(element, 
WKTKeywords.Method);
         final Map<String,Object>        properties = 
parseParametersAndClose(element, name, method);
         try {
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
index 80eaa44..334951d 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
@@ -34,6 +34,7 @@ import org.opengis.metadata.Identifier;
 import org.opengis.metadata.extent.Extent;
 import org.opengis.metadata.quality.PositionalAccuracy;
 import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.GeneralDerivedCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.ConcatenatedOperation;
@@ -71,6 +72,7 @@ import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.internal.system.Semaphores;
 import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.io.wkt.Convention;
 
 import static org.apache.sis.util.Utilities.deepEquals;
 
@@ -103,7 +105,7 @@ import static org.apache.sis.util.Utilities.deepEquals;
  * synchronization.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.2
  * @since   0.6
  * @module
  */
@@ -934,6 +936,17 @@ check:      for (int isTarget=0; ; isTarget++) {        // 
0 == source check; 1
     /**
      * Formats this coordinate operation in Well Known Text (WKT) version 2 
format.
      *
+     * <h4>ESRI extension</h4>
+     * Coordinate operations can not be formatted in standard WKT 1 format, 
but an ESRI variant of WKT 1
+     * allows a subset of coordinate operations with the ESRI-specific {@code 
GEOGTRAN} keyword.
+     * To enabled this variant, {@link org.apache.sis.io.wkt.WKTFormat} can be 
configured as below:
+     *
+     * {@preformat java
+     *     format = new WKTFormat(null, null);
+     *     format.setConvention(Convention.WKT1_IGNORE_AXES);
+     *     format.setNameAuthority(Citations.ESRI);
+     * }
+     *
      * @param  formatter  the formatter to use.
      * @return {@code "CoordinateOperation"}.
      *
@@ -943,6 +956,10 @@ check:      for (int isTarget=0; ; isTarget++) {        // 
0 == source check; 1
     protected String formatTo(final Formatter formatter) {
         super.formatTo(formatter);
         formatter.newLine();
+        final CoordinateReferenceSystem sourceCRS = getSourceCRS();
+        final CoordinateReferenceSystem targetCRS = getTargetCRS();
+        final Convention convention = formatter.getConvention();
+        final boolean isWKT1 = (convention.majorVersion() == 1);
         /*
          * If the WKT is a component of a ConcatenatedOperation, do not format 
the source CRS since it is identical
          * to the target CRS of the previous step, or to the source CRS of the 
enclosing "ConcatenatedOperation" if
@@ -954,9 +971,18 @@ check:      for (int isTarget=0; ; isTarget++) {        // 
0 == source check; 1
         final FormattableObject enclosing = formatter.getEnclosingElement(1);
         final boolean isSubOperation = (enclosing instanceof 
PassThroughOperation);
         final boolean isComponent    = (enclosing instanceof 
ConcatenatedOperation);
+        boolean isGeogTran = false;
         if (!isSubOperation && !isComponent) {
-            append(formatter, getSourceCRS(), WKTKeywords.SourceCRS);
-            append(formatter, getTargetCRS(), WKTKeywords.TargetCRS);
+            isGeogTran = isWKT1 && (sourceCRS instanceof GeographicCRS) && 
(targetCRS instanceof GeographicCRS);
+            if (isGeogTran) {
+                // ESRI-specific, similar to WKT 1.
+                formatter.append(WKTUtilities.toFormattable(sourceCRS)); 
formatter.newLine();
+                formatter.append(WKTUtilities.toFormattable(targetCRS)); 
formatter.newLine();
+            } else {
+                // WKT 2 (ISO 19162).
+                append(formatter, sourceCRS, WKTKeywords.SourceCRS);
+                append(formatter, targetCRS, WKTKeywords.TargetCRS);
+            }
         }
         final OperationMethod method = getMethod();
         if (method != null) {
@@ -983,8 +1009,9 @@ check:      for (int isTarget=0; ; isTarget++) {        // 
0 == source check; 1
                  * to mix EPSG or someone else components with their own. Note 
also that we don't apply filtering
                  * on MathTransform WKT neither for more reliable debugging.
                  */
-                final boolean filter = 
WKTUtilities.isEPSG(parameters.getDescriptor(), false) &&   // NOT 
method.getName()
-                        
Constants.EPSG.equalsIgnoreCase(Citations.toCodeSpace(formatter.getNameAuthority()));
+                final boolean filter = isGeogTran ||
+                        (WKTUtilities.isEPSG(parameters.getDescriptor(), 
false) &&   // NOT method.getName()
+                        
Constants.EPSG.equalsIgnoreCase(Citations.toCodeSpace(formatter.getNameAuthority())));
                 formatter.newLine();
                 formatter.indent(+1);
                 for (final GeneralParameterValue param : parameters.values()) {
@@ -995,7 +1022,10 @@ check:      for (int isTarget=0; ; isTarget++) {        
// 0 == source check; 1
                 formatter.indent(-1);
             }
         }
-        if (!isSubOperation && !(this instanceof ConcatenatedOperation)) {
+        /*
+         * Add interpolation CRS if we are formatting a top-level WKT 2 single 
operation.
+         */
+        if (!isSubOperation && !isGeogTran && !(this instanceof 
ConcatenatedOperation)) {
             append(formatter, getInterpolationCRS(), 
WKTKeywords.InterpolationCRS);
             final double accuracy = getLinearAccuracy();
             if (accuracy > 0) {
@@ -1007,7 +1037,16 @@ check:      for (int isTarget=0; ; isTarget++) {        
// 0 == source check; 1
                 });
             }
         }
-        if (formatter.getConvention().majorVersion() == 1) {
+        /*
+         * Verifies if what we wrote is allowed by the standard.
+         */
+        if (isGeogTran) {
+            if (method == null || convention != Convention.WKT1_IGNORE_AXES) {
+                formatter.setInvalidWKT(this, null);
+            }
+            return WKTKeywords.GeogTran;
+        }
+        if (isWKT1) {
             formatter.setInvalidWKT(this, null);
         }
         if (isComponent) {
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
index e092f7f..822a0f0 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
@@ -1095,6 +1095,7 @@ public final strictfp class GeodeticObjectParserTest 
extends TestCase {
      *
      * @throws ParseException if the parsing failed.
      *
+     * @see <a href="https://issues.apache.org/jira/browse/SIS-538";>SIS-538</a>
      * @since 1.2
      */
     @Test
@@ -1116,7 +1117,7 @@ public final strictfp class GeodeticObjectParserTest 
extends TestCase {
                 "    PARAMETER[“Z_Axis_Translation”, 340.8944],\n" +
                 "    PARAMETER[“X_Axis_Rotation”, -8.001],\n" +
                 "    PARAMETER[“Y_Axis_Rotation”, -4.42],\n" +
-                "    PARAMETER[“Z_Axis_Rotation”,-11.821],\n" +
+                "    PARAMETER[“Z_Axis_Rotation”, -11.821],\n" +
                 "    PARAMETER[“Scale_Difference”, 1.0],\n" +
                 "  AUTHORITY[“EPSG”, 1074]]");
 
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.java 
b/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.java
index e81ed67..04c074a 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.java
@@ -36,7 +36,7 @@ import static org.apache.sis.test.Assert.*;
  * Tests {@link WKTFormat}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.2
  * @since   0.5
  * @module
  */
@@ -143,7 +143,7 @@ public final strictfp class WKTFormatTest extends TestCase {
      * @throws ParseException if a parsing failed.
      */
     private void testConsistency() throws ParseException {
-        testConsistency(
+        testConsistency(false,
                 "GEOGCS[“Tokyo”,"
                 + "DATUM[“Tokyo”,"
                 +   "SPHEROID[“Bessel 1841”, 6377397.155, 299.1528128, 
AUTHORITY[“EPSG”,“7004”]],"
@@ -154,7 +154,7 @@ public final strictfp class WKTFormatTest extends TestCase {
                 + "AXIS[“Long”,EAST],"
                 + "AUTHORITY[“EPSG”,“4301”]]");
 
-        testConsistency(
+        testConsistency(false,
                 "GEOGCS[“NTF (Paris)”,"
                 + "DATUM[“Nouvelle_Triangulation_Francaise”,"
                 +   "SPHEROID[“Clarke 1880 (IGN)”, 6378249.2, 
293.466021293627, AUTHORITY[“EPSG”,“7011”]],"
@@ -165,7 +165,7 @@ public final strictfp class WKTFormatTest extends TestCase {
                 + "AXIS[“Long”,EAST],"
                 + "AUTHORITY[“EPSG”,“4807”]]");
 
-        testConsistency(
+        testConsistency(false,
                 "PROJCS[“NAD27 / Texas South Central”,"
                 + "GEOGCS[“NAD27”,"
                 +   "DATUM[“North American Datum 1927”,"
@@ -184,7 +184,7 @@ public final strictfp class WKTFormatTest extends TestCase {
                 + "AXIS[“Y”,NORTH],"
                 + "AXIS[“X”,EAST]]");
 
-        testConsistency(
+        testConsistency(false,
                 "VERT_CS[“mean sea level depth”,"
                 + "VERT_DATUM[“Mean Sea Level”,2005,AUTHORITY[“EPSG”,“5100”]],"
                 + "UNIT[“kilometre”,1000],AXIS[“Z”,DOWN]]");
@@ -201,7 +201,7 @@ public final strictfp class WKTFormatTest extends TestCase {
      * @throws ParseException if a parsing failed.
      */
     private void testConsistencyWithDenormalizedBaseCRS() throws 
ParseException {
-        testConsistency(
+        testConsistency(false,
                 "PROJCS[“NTF (Paris) / France I”,"
                 + "GEOGCS[“NTF (Paris)”,"
                 +   "DATUM[“Nouvelle_Triangulation_Francaise”,"
@@ -225,15 +225,56 @@ public final strictfp class WKTFormatTest extends 
TestCase {
     }
 
     /**
+     * Tests consistency between the parser and the formatter for the 
ESRI-specific "GeogTran" object.
+     *
+     * @throws ParseException if a parsing failed.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/SIS-538";>SIS-538</a>
+     * @since 1.2
+     */
+    @Test
+    @DependsOnMethod("testConsistencyOfWKT1")
+    public void testConsistencyOfGeogTran() throws ParseException {
+        final Symbols symbols = new Symbols(Symbols.SQUARE_BRACKETS);
+        symbols.setPairedQuotes("“”", "\"\"");
+        format = new WKTFormat(null, null);
+        format.setConvention(Convention.WKT1_IGNORE_AXES);
+        format.setNameAuthority(Citations.ESRI);
+        format.setSymbols(symbols);
+        parser = format;
+        testConsistency(true,
+                "GEOGTRAN[“Abidjan 1987 to WGS 1984_20”,\n" +
+                "  GEOGCS[“Abidjan 1987”,\n" +
+                "    DATUM[“D_Abidjan_1987”,\n" +
+                "      SPHEROID[“Clarke_ 880 RGS”, 6378249.145, 293.465]],\n" +
+                "      PRIMEM[“Greenwich”, 0.0],\n" +
+                "    UNIT[“degree”, 0.017453292519943295]],\n" +
+                "  GEOGCS[“WGS 1984”,\n" +
+                "    DATUM[“D_WGS_1984”,\n" +
+                "      SPHEROID[“WGS 1984”, 6378137.0, 298.257223563]],\n" +
+                "      PRIMEM[“Greenwich”, 0.0],\n" +
+                "    UNIT[“degree”, 0.017453292519943295]],\n" +
+                "  METHOD[“Geocentric_Translation”, AUTHORITY[“EPSG”, 
“9603”]],\n" +
+                "    PARAMETER[“X-axis translation”, -123.1],\n" +
+                "    PARAMETER[“Y-axis translation”, 53.2],\n" +
+                "    PARAMETER[“Z-axis translation”, 465.4]]");
+    }
+
+    /**
      * Implementation of {@link #testConsistency()} for a single WKT.
      *
+     * @param  strict  whether to require strict equality of WKT strings.
+     * @param  wkt     the Well-Known Text to parse and reformat.
      * @throws ParseException if the parsing failed.
      */
-    private void testConsistency(final String wkt) throws ParseException {
+    private void testConsistency(final boolean strict, final String wkt) 
throws ParseException {
         final Object expected = parser.parseObject(wkt);
         final String reformat = format.format(expected);
         final Object reparsed = format.parseObject(reformat);
         assertEqualsIgnoreMetadata(expected, reparsed);
+        if (strict) {
+            assertMultilinesEquals(wkt, reformat);
+        }
     }
 
     /**

Reply via email to