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 4d0d9d7e3a9912163436485b20cc19a767a7d7a8
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Jan 19 18:50:31 2023 +0100

    Assign GeoTIFF projection 15 to Polar Stereographic variant B instead of A.
    It requires a special case for handling existing GeoTIFF files with wrong 
parameters.
    
    https://issues.apache.org/jira/browse/SIS-572
    https://github.com/opengeospatial/geotiff/issues/110
---
 .../apache/sis/internal/referencing/Resources.java |  5 +++
 .../sis/internal/referencing/Resources.properties  |  1 +
 .../internal/referencing/Resources_fr.properties   |  1 +
 .../referencing/provider/PolarStereographicA.java  | 21 +++++-----
 .../referencing/provider/PolarStereographicB.java  | 26 ++++++++----
 .../org/apache/sis/internal/geotiff/Resources.java |  6 +++
 .../sis/internal/geotiff/Resources.properties      |  1 +
 .../sis/internal/geotiff/Resources_fr.properties   |  1 +
 .../org/apache/sis/storage/geotiff/CRSBuilder.java | 47 +++++++++++++++++++++-
 .../org/apache/sis/storage/geotiff/GeoCodes.java   |  7 ----
 .../apache/sis/storage/geotiff/GeoCodesTest.java   | 11 +++--
 11 files changed, 98 insertions(+), 29 deletions(-)

diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
index 0b79c2e4be..6799a2c5ed 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
@@ -525,6 +525,11 @@ public final class Resources extends IndexedResourceBundle 
{
          */
         public static final short RecursiveCreateCallForCode_2 = 62;
 
+        /**
+         * The only valid entries are ±90° or equivalent in alternative angle 
units.
+         */
+        public static final short RestrictedToPoleLatitudes = 104;
+
         /**
          * Matrix is singular.
          */
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
index 8ad76eb3a2..8455a965a4 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
@@ -40,6 +40,7 @@ NotFormalProjectionParameter_1    = This parameter borrowed 
from the \u201c{0}\u
 NonConformAxes_2                  = The coordinate system axes in the given 
\u201c{0}\u201d description do not conform to the expected axes according 
\u201c{1}\u201d authoritative description.
 NonConformCRS_3                   = The given \u201c{0}\u201d description does 
not conform to the \u201c{1}\u201d authoritative description. \
                                     Differences are found in 
{2,choice,0#conversion method|1#conversion description|2#coordinate 
system|3#datum|4#prime meridian|5#CRS}.
+RestrictedToPoleLatitudes         = The only valid entries are \u00b190\u00b0 
or equivalent in alternative angle units.
 
 #
 # Error messages (to be used in exceptions)
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
index 9d82487e4a..6bb9bd8016 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
@@ -45,6 +45,7 @@ NotFormalProjectionParameter_1    = Ce param\u00e8tre 
emprunt\u00e9 \u00e0 la pr
 NonConformAxes_2                  = Les axes du syst\u00e8me de 
coordonn\u00e9es d\u00e9finis dans \u00ab\u202f{0}\u202f\u00bb ne sont pas 
conformes aux axes attendus d\u2019apr\u00e8s la description officielle de 
\u00ab\u202f{1}\u202f\u00bb.
 NonConformCRS_3                   = La description donn\u00e9e pour 
\u00ab\u202f{0}\u202f\u00bb n\u2019est pas conforme \u00e0 la description 
officielle de \u00ab\u202f{1}\u202f\u00bb. \
                                     Des diff\u00e9rences ont \u00e9t\u00e9 
trouv\u00e9es dans {2,choice,0#la m\u00e9thode de conversion|1#la description 
de la conversion|2#le syst\u00e8me de coordonn\u00e9es|3#le 
r\u00e9f\u00e9rentiel|4#le m\u00e9ridien d\u2019origine|5#le CRS}.
+RestrictedToPoleLatitudes         = Les seules valeurs valides sont 
\u00b190\u00b0 ou \u00e9quivalent dans d\u2019autres unit\u00e9s.
 
 #
 # Error messages (to be used in exceptions)
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
index f6e680027e..d947a7d7b2 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
@@ -23,6 +23,7 @@ import org.opengis.parameter.ParameterDescriptorGroup;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.measure.Latitude;
@@ -30,14 +31,14 @@ import org.apache.sis.measure.Units;
 
 
 /**
- * The provider for <cite>"Polar Stereographic (Variant A)"</cite> projection 
(EPSG:9810).
+ * The provider for <cite>"Polar Stereographic (variant A)"</cite> projection 
(EPSG:9810).
  * Also used for the definition of Universal Polar Stereographic (UPS) 
projection.
  *
  * @author  Rueben Schulz (UBC)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0
  *
- * @see <a 
href="http://geotiff.maptools.org/proj_list/polar_stereographic.html";>GeoTIFF 
parameters for Polar Stereographic</a>
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-572";>SIS-512</a>
  *
  * @since 0.6
  */
@@ -77,7 +78,7 @@ public final class PolarStereographicA extends 
AbstractStereographic {
      *   <li>No default value</li>
      * </ul>
      */
-    public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN = 
LambertConformal1SP.LATITUDE_OF_ORIGIN;
+    public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural 
origin</cite> (λ₀) parameter value.
@@ -119,18 +120,20 @@ public final class PolarStereographicA extends 
AbstractStereographic {
     static final ParameterDescriptorGroup PARAMETERS;
     static {
         final ParameterBuilder builder = builder();
+        LATITUDE_OF_ORIGIN = createMandatoryLatitude(builder
+                .addNamesAndIdentifiers(LambertConformal1SP.LATITUDE_OF_ORIGIN)
+                
.setRemarks(Resources.formatInternational(Resources.Keys.RestrictedToPoleLatitudes)));
+
         LONGITUDE_OF_ORIGIN = createLongitude(builder
                 
.addNamesAndIdentifiers(ObliqueStereographic.LONGITUDE_OF_ORIGIN)
                 .reidentify(Citations.GEOTIFF, "3095")
                 .rename(Citations.GEOTIFF, "StraightVertPoleLong"));
 
         PARAMETERS = builder
-                .addIdentifier(             IDENTIFIER)
-                .addName(                   NAME)
-                .addName(Citations.OGC,     "Polar_Stereographic")
-                .addName(Citations.GEOTIFF, "CT_PolarStereographic")
-                .addName(Citations.PROJ4,   "stere")
-                .addIdentifier(Citations.GEOTIFF, "15")
+                .addIdentifier(IDENTIFIER)
+                .addName(NAME)
+                .addName(Citations.OGC,   "Polar_Stereographic")
+                .addName(Citations.PROJ4, "stere")
                 .createGroupForMapProjection(
                         LATITUDE_OF_ORIGIN,     // Can be only ±90°
                         LONGITUDE_OF_ORIGIN,
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
index a1e94ce593..383bcb6875 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
@@ -24,14 +24,18 @@ import org.apache.sis.parameter.ParameterBuilder;
 
 
 /**
- * The provider for <cite>"Polar Stereographic (Variant B)"</cite> projection 
(EPSG:9829).
+ * The provider for <cite>"Polar Stereographic (variant B)"</cite> projection 
(EPSG:9829).
  * This provider includes a <cite>"Latitude of standard parallel"</cite> 
parameter and
  * determines the hemisphere of the projection from that parameter value.
  *
  * @author  Rueben Schulz (UBC)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
- * @since   0.6
+ * @version 1.4
+ *
+ * @see <a 
href="http://geotiff.maptools.org/proj_list/polar_stereographic.html";>GeoTIFF 
parameters for Polar Stereographic</a>
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-572";>SIS-512</a>
+ *
+ * @since 0.6
  */
 @XmlTransient
 public final class PolarStereographicB extends AbstractStereographic {
@@ -40,6 +44,11 @@ public final class PolarStereographicB extends 
AbstractStereographic {
      */
     private static final long serialVersionUID = 5188231050523249971L;
 
+    /**
+     * The EPSG name for this projection.
+     */
+    public static final String NAME = "Polar Stereographic (variant B)";
+
     /**
      * The EPSG identifier, to be preferred to the name when available.
      */
@@ -128,11 +137,12 @@ public final class PolarStereographicB extends 
AbstractStereographic {
 
         PARAMETERS = builder
                 .addIdentifier(IDENTIFIER)
-                .addName("Polar Stereographic (variant B)")
-                .addName(Citations.S57,  "Polar stereographic")
-                .addName(Citations.S57,  "PST")
-                .addIdentifier(Citations.S57, "11")
-                .addNameAndIdentifier(Citations.PROJ4, 
PolarStereographicA.PARAMETERS)
+                .addName(NAME)
+                .addName      (Citations.S57,     "Polar stereographic")
+                .addName      (Citations.S57,     "PST")
+                .addIdentifier(Citations.S57,     "11")
+                .addName      (Citations.GEOTIFF, "CT_PolarStereographic")
+                .addIdentifier(Citations.GEOTIFF, "15")
                 .createGroupForMapProjection(
                         STANDARD_PARALLEL,
                         LONGITUDE_OF_ORIGIN,
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.java
index 5e1897f5e9..ac6264953b 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.java
@@ -151,6 +151,12 @@ public final class Resources extends IndexedResourceBundle 
{
          */
         public static final short RandomizedProcessApplied = 15;
 
+        /**
+         * Interpreted parameter “{0}” as “{1}” because the former is invalid 
for the “{2}” map
+         * projection.
+         */
+        public static final short ReassignedParameter_3 = 32;
+
         /**
          * The “{0}” GeoTIFF file does not specify the values format.
          */
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.properties
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.properties
index 3eb87da2a6..5c320a471e 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.properties
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.properties
@@ -37,6 +37,7 @@ MissingGeoValue_1                 = No value has been found 
for the \u201c{0}\u2
 MissingValue_2                    = Cannot read TIFF image from 
\u201c{0}\u201d because the \u201c{1}\u201d tag is missing.
 NotTheEpsgValue_5                 = The file defines \u201c{2}\u201d with 
value {3}{4}, but that value should be {1}{4} according parent definition ({0}).
 RandomizedProcessApplied          = A randomized process such as error 
diffusion has been applied to the image data.
+ReassignedParameter_3             = Interpreted parameter \u201c{0}\u201d as 
\u201c{1}\u201d because the former is invalid for the \u201c{2}\u201d map 
projection.
 UndefinedDataFormat_1             = The \u201c{0}\u201d GeoTIFF file does not 
specify the values format.
 UnexpectedListOfValues_2          = A single value was expected for the 
\u201c{0}\u201d key but {1} values have been found.
 UnexpectedParameter_2             = The \u201c{1}\u201d parameter was not 
expected for the \u201c{0}\u201d projection method.
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources_fr.properties
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources_fr.properties
index f668385c12..3193a52fd0 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources_fr.properties
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources_fr.properties
@@ -42,6 +42,7 @@ MissingGeoValue_1                 = Aucune valeur n\u2019a 
\u00e9t\u00e9 trouv\u
 MissingValue_2                    = Ne peut pas lire l\u2019image TIFF 
provenant de \u00ab\u202f{0}\u202f\u00bb car le tag \u00ab\u202f{1}\u202f\u00bb 
est manquant.
 NotTheEpsgValue_5                 = Le fichier d\u00e9finit 
\u00ab\u202f{2}\u202f\u00bb avec la valeur {3}{4}, mais cette valeur devrait 
\u00eatre {1}{4} pour \u00eatre en accord avec la d\u00e9finition du parent {0}.
 RandomizedProcessApplied          = Un processus randomis\u00e9 comme la 
diffusion d\u2019erreur a \u00e9t\u00e9 appliqu\u00e9.
+ReassignedParameter_3             = Le param\u00e8tre 
\u00ab\u202f{0}\u202f\u00bb a \u00e9t\u00e9 interpr\u00e9t\u00e9 comme 
\u00ab\u202f{1}\u202f\u00bb parce que le premier est invalide pour la 
projection \u00ab\u202f{2}\u202f\u00bb.
 UndefinedDataFormat_1             = Le fichier GeoTIFF 
\u00ab\u202f{0}\u202f\u00bb ne sp\u00e9cifie pas le format de ses valeurs.
 UnexpectedListOfValues_2          = Une seule valeur \u00e9tait attendue pour 
la cl\u00e9 \u00ab\u202f{0}\u202f\u00bb, mais on en a trouv\u00e9es {1}.
 UnexpectedParameter_2             = Le param\u00e8tre 
\u00ab\u202f{1}\u202f\u00bb est inattendu pour la m\u00e9thode de projection 
\u00ab\u202f{0}\u202f\u00bb.
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
index cd2a2b459c..2a700e0c6d 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
@@ -63,6 +63,7 @@ import org.apache.sis.internal.referencing.WKTKeywords;
 import org.apache.sis.internal.referencing.NilReferencingObject;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.referencing.ReferencingFactoryContainer;
+import org.apache.sis.internal.referencing.provider.PolarStereographicA;
 import org.apache.sis.internal.referencing.provider.PolarStereographicB;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.util.Strings;
@@ -76,6 +77,7 @@ import org.apache.sis.referencing.cs.AxesConvention;
 import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.crs.DefaultGeographicCRS;
 import org.apache.sis.io.TableAppender;
+import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Characters;
@@ -381,6 +383,31 @@ final class CRSBuilder extends ReferencingFactoryContainer 
{
         warning(Resources.Keys.InvalidGeoValue_2, GeoKeys.name(key), value);
     }
 
+    /**
+     * Moves the value of a projection parameter to a new GeoKey.
+     * This is used for handling erroneous map projection definitions.
+     * A warning is emitted.
+     *
+     * @param  projection  name of the map projection to report in the warning.
+     * @param  oldKey      old map projection key.
+     * @param  newKey      new map projection key, or 0 if none.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/SIS-572";>SIS-572</a>
+     */
+    private void moveParameter(final String projection, final short oldKey, 
final short newKey) {
+        final Object value = geoKeys.remove(oldKey);
+        if (value != null) {
+            final Object name;
+            if (newKey != 0) {
+                geoKeys.put(newKey, value);
+                name = GeoKeys.name(newKey);
+            } else {
+                name = Vocabulary.formatInternational(Vocabulary.Keys.None);
+            }
+            warning(Resources.Keys.ReassignedParameter_3, 
GeoKeys.name(oldKey), name, projection);
+        }
+    }
+
     /**
      * Verifies that a value found in the GeoTIFF file is approximately equal 
to the expected value.
      * This method is invoked when a CRS component is defined both explicitly 
and by EPSG code,
@@ -1264,9 +1291,25 @@ final class CRSBuilder extends 
ReferencingFactoryContainer {
         try {
             switch (Integer.parseInt(code)) {
                 case GeoCodes.PolarStereographic: {
-                    if (geoKeys.containsKey(GeoCodes.StdParallel1)) {
-                        return Constants.EPSG + ':' + 
PolarStereographicB.IDENTIFIER;
+                    /*
+                     * Some GeoTIFF producers wrongly interpreted GeoTIFF 
projection #15
+                     * as "Polar Stereographic (Variant A)" while it should be 
variant B.
+                     * In those files, the "Latitude of true scale" parameter 
is wrongly
+                     * named "Latitude of natural origin" because the former 
is a member
+                     * of variant A while the latter is a member of variant B. 
This code
+                     * does the substitution.
+                     *
+                     * https://issues.apache.org/jira/browse/SIS-572
+                     */
+                    if (geoKeys.containsKey(GeoKeys.StdParallel1)) {
+                        break;      // Assume a valid map projection.
+                    }
+                    Object value = geoKeys.get(GeoKeys.ScaleAtNatOrigin);
+                    if (value instanceof Number && ((Number) 
value).doubleValue() != 1) {
+                        return Constants.EPSG + ':' + 
PolarStereographicA.IDENTIFIER;
                     }
+                    moveParameter(PolarStereographicB.NAME, 
GeoKeys.NatOriginLat, GeoKeys.StdParallel1);
+                    moveParameter(PolarStereographicB.NAME, 
GeoKeys.ScaleAtNatOrigin, (short) 0);
                     break;
                 }
                 // More cases may be added in the future.
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoCodes.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoCodes.java
index 43e188814e..9c977545ee 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoCodes.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoCodes.java
@@ -87,11 +87,4 @@ final class GeoCodes {
      * This is handled as a special case for distinguishing between variants.
      */
     public static final short PolarStereographic = 15;
-
-    /**
-     * The code for standard parallel map projection parameters.
-     * This is used as a sentinel value for distinguishing between
-     * different variants of a map projection.
-     */
-    public static final short StdParallel1 = 3078;
 }
diff --git 
a/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/GeoCodesTest.java
 
b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/GeoCodesTest.java
index c97dd04686..95cfdb46c5 100644
--- 
a/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/GeoCodesTest.java
+++ 
b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/GeoCodesTest.java
@@ -44,11 +44,16 @@ public final class GeoCodesTest extends TestCase {
      */
     @Test
     public void verifyPolarStereographic() throws FactoryException {
-        ParameterDescriptorGroup parameters = parameters("Polar Stereographic 
(Variant A)");
+        ParameterDescriptorGroup parameters = parameters("Polar Stereographic 
(Variant B)");
         assertEquals(GeoCodes.PolarStereographic, parseCode(parameters));
+        /*
+         * Following are testing `GeoKeys` rather than `GeoCodes`,
+         * but we do that as an additional consistency check.
+         */
+        ParameterDescriptorGroup alternative = parameters("Polar Stereographic 
(Variant A)");
+        assertEquals(GeoKeys.StdParallel1, parseCode(parameters 
.descriptor("Latitude of standard parallel")));
+        assertEquals(GeoKeys.NatOriginLat, 
parseCode(alternative.descriptor("Latitude of natural origin")));
 
-        parameters = parameters("Polar Stereographic (Variant B)");
-        assertEquals(GeoCodes.StdParallel1, 
parseCode(parameters.descriptor("Latitude of standard parallel")));
     }
 
     /**

Reply via email to