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 b2facbdd8f 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. b2facbdd8f is described below commit b2facbdd8f658ba112ecd17cf397f55ea5dd7396 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 --- .../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"))); } /**