You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/05/25 15:22:06 UTC

[sis] branch geoapi-4.0 updated (756a5e0ea3 -> 3110d38dba)

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

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


    from 756a5e0ea3 Avoid start imports of `org.opengis.test.Assert` static methods. This is in anticipation for a fix equivalent to SIS-579 in GeoAPI.
     new 8fc9815c59 Add support for two-dimensional `SphericalCS` as submitted in ISO 19111 amendment 2.
     new a618f9af4d Add clarification about the use of conformal radius. This is a change in documentation and symbols only.
     new 3110d38dba Add an `AbstractEnvelope.isFinite()` convenience method.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/sis/geometry/AbstractEnvelope.java  | 21 ++++++++++++-
 .../apache/sis/io/wkt/GeodeticObjectParser.java    | 14 +++++----
 .../sis/referencing/cs/CoordinateSystems.java      |  4 +--
 .../sis/referencing/cs/DefaultSphericalCS.java     | 32 ++++++++++++++++++--
 .../referencing/factory/GeodeticObjectFactory.java | 35 ++++++++++++++++++++++
 .../referencing/factory/sql/EPSGDataAccess.java    |  1 +
 .../operation/projection/AzimuthalEquidistant.java |  2 +-
 .../operation/projection/Initializer.java          |  6 ++--
 .../referencing/operation/projection/Mercator.java |  7 +++--
 .../operation/projection/NormalizedProjection.java | 11 ++++---
 .../operation/projection/ProjectionVariant.java    |  6 ++++
 .../org/apache/sis/geometry/ArrayEnvelopeTest.java | 15 ++++++++--
 .../org/apache/sis/storage/geotiff/CRSBuilder.java |  4 +--
 .../org/apache/sis/internal/netcdf/CRSBuilder.java | 10 +++++--
 14 files changed, 140 insertions(+), 28 deletions(-)


[sis] 03/03: Add an `AbstractEnvelope.isFinite()` convenience method.

Posted by de...@apache.org.
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 3110d38dbaf5d25be54ecdef367323c13ec292d4
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu May 25 17:21:06 2023 +0200

    Add an `AbstractEnvelope.isFinite()` convenience method.
---
 .../org/apache/sis/geometry/AbstractEnvelope.java   | 21 ++++++++++++++++++++-
 .../org/apache/sis/geometry/ArrayEnvelopeTest.java  | 15 +++++++++++++--
 2 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java b/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
index cb9726a691..ae6f1d59fc 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
@@ -118,7 +118,7 @@ import static org.apache.sis.math.MathFunctions.isNegativeZero;
  * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   0.3
  */
 @XmlTransient
@@ -675,6 +675,25 @@ public abstract class AbstractEnvelope extends FormattableObject implements Enve
         return envelopes;
     }
 
+    /**
+     * Determines whether or not this envelope contains only finite values.
+     * More specifically this method returns {@code false} if at least one
+     * coordinate value is NaN or infinity, and {@code true} otherwise.
+     * Note that a finite envelope may be {@linkplain #isEmpty() empty}.
+     *
+     * @return {@code true} if this envelope contains only finite coordinate values.
+     *
+     * @since 1.4
+     */
+    public boolean isFinite() {
+        for (int i = getDimension(); --i >= 0;) {
+            if (!Double.isFinite(getLower(i)) || !Double.isFinite(getUpper(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * Determines whether or not this envelope is empty. An envelope is empty if it has zero
      * {@linkplain #getDimension() dimension}, or if the {@linkplain #getSpan(int) span} of
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/geometry/ArrayEnvelopeTest.java b/core/sis-referencing/src/test/java/org/apache/sis/geometry/ArrayEnvelopeTest.java
index f374565cc9..5e916b7c8d 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/geometry/ArrayEnvelopeTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/geometry/ArrayEnvelopeTest.java
@@ -28,14 +28,25 @@ import static org.apache.sis.referencing.crs.HardCodedCRS.WGS84;
 
 /**
  * Unit tests for class {@link ArrayEnvelope}.
- * This is the base class of {@link GeneralEnvelope} and {@link ImmutableEnvelope}.
+ * The latter is the base class of {@link GeneralEnvelope} and {@link ImmutableEnvelope}.
  *
  * @author  Michael Hausegger
- * @version 1.0
+ * @version 1.4
  * @since   0.8
  */
 @DependsOn(AbstractEnvelopeTest.class)
 public final class ArrayEnvelopeTest extends TestCase {
+    /**
+     * Tests {@link ArrayEnvelope#isFinite()}.
+     */
+    @Test
+    public void testIsFinite() {
+        ArrayEnvelope envelope = new ArrayEnvelope(new double[] {10, 356.683168});
+        assertTrue(envelope.isFinite());
+        envelope.coordinates[0] = Double.NaN;
+        assertFalse(envelope.isFinite());
+    }
+
     /**
      * Tests {@link ArrayEnvelope#isEmpty()}.
      */


[sis] 02/03: Add clarification about the use of conformal radius. This is a change in documentation and symbols only.

Posted by de...@apache.org.
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 a618f9af4d7219e83a9802f5cf7083997d0c85cd
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu May 25 16:57:55 2023 +0200

    Add clarification about the use of conformal radius.
    This is a change in documentation and symbols only.
---
 .../operation/projection/AzimuthalEquidistant.java            |  2 +-
 .../sis/referencing/operation/projection/Initializer.java     |  6 +++---
 .../apache/sis/referencing/operation/projection/Mercator.java |  7 ++++---
 .../operation/projection/NormalizedProjection.java            | 11 +++++++----
 .../referencing/operation/projection/ProjectionVariant.java   |  6 ++++++
 5 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistant.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistant.java
index f964505823..8deb5c7306 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistant.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistant.java
@@ -62,7 +62,7 @@ import static org.apache.sis.internal.referencing.provider.ModifiedAzimuthalEqui
  *
  * This base class is aimed to provide the general case valid for all distances;
  * the fact that current version uses spherical formulas should be considered as an implementation limitation
- * that may change in future version. Subclasses are specialization for more restricted areas.
+ * that may change in future version. Subclasses are specializations for more restricted areas.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Maxime Gavens (Geomatys)
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
index 6fe0baa6b1..675360609b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
@@ -166,8 +166,8 @@ final class Initializer {
             } else {
                 eccentricitySquared = DoubleDouble.ONE.subtract(DoubleDouble.of(b, true).divide(k).square());
             }
-            final ParameterDescriptor<? extends Number> radius = roles.get(ParameterRole.LATITUDE_OF_CONFORMAL_SPHERE_RADIUS);
-            if (radius != null) {
+            final ParameterDescriptor<? extends Number> φr = roles.get(ParameterRole.LATITUDE_OF_CONFORMAL_SPHERE_RADIUS);
+            if (φr != null) {
                 /*
                  * EPSG said: R is the radius of the sphere and will normally be one of the CRS parameters.
                  * If the figure of the earth used is an ellipsoid rather than a sphere then R should be calculated
@@ -184,7 +184,7 @@ final class Initializer {
                  *     final double sinφ = sin(toRadians(parameters.doubleValue(radius)));
                  *     k = b / (1 - eccentricitySquared * (sinφ*sinφ));
                  */
-                k = rν2(sin(toRadians(parameters.doubleValue(radius))));
+                k = rν2(sin(toRadians(parameters.doubleValue(φr))));
                 k = DoubleDouble.of(b, true).divide(k);
             }
         }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
index e56fb54648..264d17576b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
@@ -207,9 +207,10 @@ public class Mercator extends ConformalProjection {
             case SPHERICAL: {
                 /*
                  * According to EPSG guide, the latitude of conformal sphere radius should be the latitude of origin.
-                 * However, that origin is fixed to 0° by EPSG guide, which makes radius calculation ineffective when
-                 * using the official parameters. We could fallback on the standard parallel (φ1) if φ0 is not set,
-                 * but for now we wait to see for real cases. Some arguments that may be worth consideration:
+                 * However, that origin is fixed to 0° by EPSG guidance notes, which makes this radius equals to the
+                 * semi-minor axis length. We could allow more flexibility by using the standard parallel (φ1) if φ0
+                 * is not set, but for now we wait to see for real cases.
+                 * Some arguments that may be worth consideration:
                  *
                  *   - The standard parallel is not an EPSG parameter for Spherical case.
                  *   - Users who set the standard parallel anyway may expect that latitude to be used for radius
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
index 754e4a7bcb..ae65fc71e7 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
@@ -284,10 +284,13 @@ public abstract class NormalizedProjection extends AbstractMathTransform2D imple
          *
          * <div style="text-align:center">{@include ../transform/formulas.html#DenormalizeCartesian}</div>
          *
-         * <p>This enumeration shall be used <strong>only</strong> when the user requested explicitly spherical
-         * formulas, for example the <cite>"Mercator (Spherical)"</cite> projection (EPSG:1026), but the figure
-         * of the Earth may be an ellipsoid rather than a sphere. In the majority of cases, this enumeration should
-         * not be used.</p>
+         * <h4>When to use</h4>
+         * This enumeration value should be used only when the user requested explicitly the spherical formulas
+         * of a conformal projection, for example the <cite>"Mercator (Spherical)"</cite> projection (EPSG:1026),
+         * but the figure of the Earth may be an ellipsoid rather than a sphere.
+         * This enumeration value can also be used for other kinds of projection except Equal Area, in which case
+         * the {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid#getAuthalicRadius() authalic radius}
+         * is preferred. In the majority of cases, this enumeration value can be ignored.
          */
         LATITUDE_OF_CONFORMAL_SPHERE_RADIUS,
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionVariant.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionVariant.java
index 518be5934b..3feb162cd9 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionVariant.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionVariant.java
@@ -49,7 +49,13 @@ interface ProjectionVariant {
      * This method can be overridden for handling authalic radius, but not conformance sphere radius.
      * The latter is handled by {@link NormalizedProjection.ParameterRole#LATITUDE_OF_CONFORMAL_SPHERE_RADIUS}.
      *
+     * <h4>When to use</h4>
+     * Authalic radius are used with Equal Area projections.
+     * For other kinds of projection, the radius of conformal sphere is preferred.
+     *
      * @return whether this variant is a spherical variant using authalic radius.
+     *
+     * @see NormalizedProjection.ParameterRole#LATITUDE_OF_CONFORMAL_SPHERE_RADIUS
      */
     default boolean useAuthalicRadius() {
         return false;


[sis] 01/03: Add support for two-dimensional `SphericalCS` as submitted in ISO 19111 amendment 2.

Posted by de...@apache.org.
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 8fc9815c594c47d497d5fabd5faaf320a45a49f1
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu May 25 15:49:09 2023 +0200

    Add support for two-dimensional `SphericalCS` as submitted in ISO 19111 amendment 2.
---
 .../apache/sis/io/wkt/GeodeticObjectParser.java    | 14 +++++----
 .../sis/referencing/cs/CoordinateSystems.java      |  4 +--
 .../sis/referencing/cs/DefaultSphericalCS.java     | 32 ++++++++++++++++++--
 .../referencing/factory/GeodeticObjectFactory.java | 35 ++++++++++++++++++++++
 .../referencing/factory/sql/EPSGDataAccess.java    |  1 +
 .../org/apache/sis/storage/geotiff/CRSBuilder.java |  4 +--
 .../org/apache/sis/internal/netcdf/CRSBuilder.java | 10 +++++--
 7 files changed, 86 insertions(+), 14 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 5bae69b55b..701a429280 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
@@ -99,7 +99,7 @@ import static java.util.Collections.singletonMap;
  * @author  Rémi Eve (IRD)
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Johann Sorel (Geomatys)
- * @version 1.3
+ * @version 1.4
  * @since   0.6
  */
 class GeodeticObjectParser extends MathTransformParser implements Comparator<CoordinateSystemAxis> {
@@ -927,6 +927,14 @@ class GeodeticObjectParser extends MathTransformParser implements Comparator<Coo
                 dimension = (axes.length < 2) ? 2 : 3;                      // For error message.
                 break;
             }
+            case WKTKeywords.spherical: {
+                switch (axes.length) {
+                    case 2: return csFactory.createSphericalCS(csProperties, axes[0], axes[1]);
+                    case 3: return csFactory.createSphericalCS(csProperties, axes[0], axes[1], axes[2]);
+                }
+                dimension = (axes.length < 2) ? 2 : 3;                      // For error message.
+                break;
+            }
             case WKTKeywords.Cartesian: {
                 switch (axes.length) {
                     case 2: return csFactory.createCartesianCS(csProperties, axes[0], axes[1]);
@@ -963,10 +971,6 @@ class GeodeticObjectParser extends MathTransformParser implements Comparator<Coo
                 if (axes.length != (dimension = 3)) break;
                 return csFactory.createCylindricalCS(csProperties, axes[0], axes[1], axes[2]);
             }
-            case WKTKeywords.spherical: {
-                if (axes.length != (dimension = 3)) break;
-                return csFactory.createSphericalCS(csProperties, axes[0], axes[1], axes[2]);
-            }
             case WKTKeywords.parametric: {
                 if (axes.length != (dimension = 1)) break;
                 return csFactory.createParametricCS(csProperties, axes[0]);
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java
index 722da90e42..09debdf369 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java
@@ -520,7 +520,7 @@ next:   for (final CoordinateSystem cs : targets) {
      */
     public static CoordinateSystem replaceLinearUnit(final CoordinateSystem cs, final Unit<Length> newUnit) {
         ArgumentChecks.ensureNonNull("newUnit", newUnit);
-        return CoordinateSystems.replaceAxes(cs, new AxisFilter() {
+        return replaceAxes(cs, new AxisFilter() {
             @Override public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> unit) {
                 return Units.isLinear(unit) ? newUnit : unit;
             }
@@ -551,7 +551,7 @@ next:   for (final CoordinateSystem cs : targets) {
      */
     public static CoordinateSystem replaceAngularUnit(final CoordinateSystem cs, final Unit<javax.measure.quantity.Angle> newUnit) {
         ArgumentChecks.ensureNonNull("newUnit", newUnit);
-        return CoordinateSystems.replaceAxes(cs, new AxisFilter() {
+        return replaceAxes(cs, new AxisFilter() {
             @Override public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> unit) {
                 return Units.isAngular(unit) ? newUnit : unit;
             }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultSphericalCS.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultSphericalCS.java
index d9a14ba01d..f82fc34bce 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultSphericalCS.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultSphericalCS.java
@@ -28,7 +28,8 @@ import org.apache.sis.measure.Units;
 
 
 /**
- * A 3-dimensional coordinate system with one distance measured from the origin and two angular coordinates.
+ * A 2- or 3-dimensional coordinate system with one distance measured from the origin and two angular coordinates.
+ * In the two-dimensional case, the radius is omitted and may be implicitly an ellipsoid surface.
  * Not to be confused with an {@linkplain DefaultEllipsoidalCS ellipsoidal coordinate system}
  * based on an ellipsoid "degenerated" into a sphere.
  *
@@ -125,6 +126,27 @@ public class DefaultSphericalCS extends AbstractCS implements SphericalCS {
         super(properties, axis0, axis1, axis2);
     }
 
+    /**
+     * Constructs a two-dimensional coordinate system from a set of properties.
+     * The given axes shall be angular measurements, without radius.
+     * The properties map is given unchanged to the
+     * {@linkplain AbstractCS#AbstractCS(Map,CoordinateSystemAxis[]) super-class constructor}.
+     *
+     * @param  properties  the properties to be given to the identified object.
+     * @param  axis0       the first  axis (e.g. “Spherical latitude”).
+     * @param  axis1       the second axis (e.g. “Spherical longitude”).
+     *
+     * @see org.apache.sis.referencing.factory.GeodeticObjectFactory#createSphericalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)
+     *
+     * @since 1.4
+     */
+    public DefaultSphericalCS(final Map<String,?>   properties,
+                              final CoordinateSystemAxis axis0,
+                              final CoordinateSystemAxis axis1)
+    {
+        super(properties, axis0, axis1);
+    }
+
     /**
      * Creates a new coordinate system with the same values than the specified one.
      * This copy constructor provides a way to convert an arbitrary implementation into a SIS one
@@ -202,9 +224,15 @@ public class DefaultSphericalCS extends AbstractCS implements SphericalCS {
      * Returns a coordinate system with different axes.
      */
     @Override
+    @SuppressWarnings("fallthrough")
     final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) {
         switch (axes.length) {
-            case 2: return new DefaultPolarCS(properties, axes);
+            case 2: {
+                if (Units.isLinear(axes[0].getUnit()) || Units.isLinear(axes[1].getUnit())) {
+                    return new DefaultPolarCS(properties, axes);
+                }
+                // Fall through
+            }
             case 3: return new DefaultSphericalCS(properties, axes);
             default: throw unexpectedDimension(properties, axes, 2);
         }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
index 5d721fdbc5..f53cf7812e 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
@@ -479,6 +479,41 @@ public class GeodeticObjectFactory extends AbstractFactory implements CRSFactory
         return unique("createSphericalCS", cs);
     }
 
+    /**
+     * Creates a spherical coordinate system without radius.
+     * This coordinate system can be used with geocentric, engineering and derived CRS.
+     *
+     * <h4>Dependencies</h4>
+     * The components needed by this method can be created by the following methods:
+     * <ol>
+     *   <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
+     * </ol>
+     *
+     * The default implementation creates a {@link DefaultSphericalCS} instance.
+     *
+     * @param  properties  name and other properties to give to the new object.
+     * @param  axis0       the first  axis (e.g. “Spherical latitude”).
+     * @param  axis1       the second axis (e.g. “Spherical longitude”).
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see DefaultSphericalCS#DefaultSphericalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)
+     *
+     * @since 1.4
+     */
+    @Override
+    public SphericalCS createSphericalCS(final Map<String,?> properties,
+            final CoordinateSystemAxis axis0,
+            final CoordinateSystemAxis axis1) throws FactoryException
+    {
+        final DefaultSphericalCS cs;
+        try {
+            cs = new DefaultSphericalCS(complete(properties), axis0, axis1);
+        } catch (IllegalArgumentException exception) {
+            throw new InvalidGeodeticParameterException(exception);
+        }
+        return unique("createSphericalCS", cs);
+    }
+
     /**
      * Creates a geographic coordinate reference system. It can be (<var>latitude</var>, <var>longitude</var>)
      * or (<var>longitude</var>, <var>latitude</var>), optionally with an ellipsoidal height.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
index 79894aaf20..5f0eb61e0e 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
@@ -2197,6 +2197,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                     }
                     case WKTKeywords.spherical: {
                         switch (dimension) {
+                            case 2: cs = csFactory.createSphericalCS(properties, axes[0], axes[1]); break;
                             case 3: cs = csFactory.createSphericalCS(properties, axes[0], axes[1], axes[2]); break;
                         }
                         break;
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 2a700e0c6d..f19ab41b14 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
@@ -1067,7 +1067,7 @@ final class CRSBuilder extends ReferencingFactoryContainer {
     private void verify(final GeographicCRS crs, final Unit<Angle> angularUnit) throws FactoryException {
         /*
          * Note: current createUnit(…) implementation does not allow us to distinguish whether METRE ou DEGREE units
-         * were specified in the GeoTIFF file or if we got the default values. We do not compare units of that reason.
+         * were specified in the GeoTIFF file or if we got the default values. We do not compare units for that reason.
          */
         final Unit<Length> linearUnit = createUnit(GeoKeys.GeogLinearUnits, GeoKeys.GeogLinearUnitSize, Length.class, Units.METRE);
         final GeodeticDatum datum = crs.getDatum();
@@ -1134,7 +1134,7 @@ final class CRSBuilder extends ReferencingFactoryContainer {
     private void verify(final GeocentricCRS crs) throws FactoryException {
         /*
          * Note: current createUnit(…) implementation does not allow us to distinguish whether METRE ou DEGREE units
-         * were specified in the GeoTIFF file or if we got the default values. We do not compare units of that reason.
+         * were specified in the GeoTIFF file or if we got the default values. We do not compare units for that reason.
          */
         final Unit<Length> linearUnit = createUnit(GeoKeys.GeogLinearUnits, GeoKeys.GeogLinearUnitSize, Length.class, Units.METRE);
         final Unit<Angle> angularUnit = createUnit(GeoKeys.AngularUnits, GeoKeys.AngularUnitSize, Angle.class, Units.DEGREE);
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
index d62c4b041b..97d0ca7164 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
@@ -84,7 +84,7 @@ import org.apache.sis.measure.Units;
  * while {@link DataStoreException} is handled as a fatal error. Warnings are stored in {@link #warnings} field.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
  * @since   1.0
  */
 abstract class CRSBuilder<D extends Datum, CS extends CoordinateSystem> {
@@ -677,12 +677,16 @@ previous:   for (int i=components.size(); --i >= 0;) {
         }
 
         /**
-         * Creates the three-dimensional {@link SphericalCS} from given axes. This method is invoked only
+         * Creates the two- or three-dimensional {@link SphericalCS} from given axes. This method is invoked only
          * if {@link #setPredefinedComponents(Decoder)} failed to assign a CS or if {@link #build(Decoder, boolean)}
          * found that the {@link #coordinateSystem} does not have compatible axes.
          */
         @Override void createCS(CSFactory factory, Map<String,?> properties, CoordinateSystemAxis[] axes) throws FactoryException {
-            coordinateSystem = factory.createSphericalCS(properties, axes[0], axes[1], axes[2]);
+            if (axes.length > 2) {
+                coordinateSystem = factory.createSphericalCS(properties, axes[0], axes[1], axes[2]);
+            } else {
+                coordinateSystem = factory.createSphericalCS(properties, axes[0], axes[1]);
+            }
         }
 
         /**