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

asf-gitbox-commits 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 1a9bd95d3b feat(Geometry): handle the case where an EWKT/EWKN geometry 
define a 2d crs with a Z value, ellipsoid height is added
1a9bd95d3b is described below

commit 1a9bd95d3bc32e45692619eb35873e236752ffb7
Author: jsorel <[email protected]>
AuthorDate: Wed Sep 16 16:07:25 2026 +0200

    feat(Geometry): handle the case where an EWKT/EWKN geometry define a 2d crs 
with a Z value, ellipsoid height is added
---
 .../org/apache/sis/geometries/adapter/Srid.java    | 120 +++++++++++++++++----
 .../sis/geometries/adapter/WellKnownBinary.java    |   6 ++
 .../geometries/adapter/WellKnownBinaryParser.java  |  39 ++++---
 .../sis/geometries/adapter/WellKnownText.java      |   7 ++
 .../geometries/adapter/WellKnownTextParser.java    |  40 ++++---
 .../geometries/adapter/WellKnownBinaryTest.java    |  39 ++++++-
 .../sis/geometries/adapter/WellKnownTextTest.java  |  36 ++++++-
 7 files changed, 227 insertions(+), 60 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/Srid.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/Srid.java
index e121324b3e..714357fde4 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/Srid.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/Srid.java
@@ -16,11 +16,15 @@
  */
 package org.apache.sis.geometries.adapter;
 
+import java.util.logging.Logger;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.util.logging.Logging;
 import org.opengis.metadata.Identifier;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.util.FactoryException;
 
 
@@ -29,16 +33,34 @@ import org.opengis.util.FactoryException;
  * {@link CoordinateReferenceSystem}. A <abbr>SRID</abbr> is the numeric part 
of an <abbr>EPSG</abbr>
  * code, and is what {@code EWKT} and {@code EWKB} carry in place of a full 
system definition.
  *
- * <p>The mapping is the one of the identifier the system already carries, 
never a search for an
- * equivalent definition: {@link #of(CoordinateReferenceSystem)} reads the 
identifier and
- * {@link #forCode(int)} builds the system the authority defines for that 
code. Neither method
- * changes the axis order, so a system read back from a code is the one the 
<abbr>EPSG</abbr>
- * authority defines, which for a geographic system is (<var>latitude</var>, 
<var>longitude</var>)
- * and not the (<var>longitude</var>, <var>latitude</var>) order that some 
databases assume.</p>
+ * <p>Neither method changes the axis order, so a system read back from a code 
is the one the
+ * <abbr>EPSG</abbr> authority defines, which for a geographic system is 
(<var>latitude</var>,
+ * <var>longitude</var>) and not the (<var>longitude</var>, 
<var>latitude</var>) order that some
+ * databases assume.</p>
+ *
+ * <h2>Heights</h2>
+ * A spatial reference identifier usually names a two-dimensional system, 
while the geometry it
+ * describes may have a <var>z</var> ordinate: {@code SRID=4326} on a {@code 
POINT Z} is the usual
+ * way of writing a position above the ellipsoid of <abbr>EPSG</abbr>:4326. 
The two directions
+ * therefore do not map one system onto one code:
+ *
+ * <ul>
+ *   <li>{@link #forCode(int, int)} adds an {@linkplain 
CommonCRS.Vertical#ELLIPSOIDAL ellipsoidal
+ *       height} to a two-dimensional system when the geometry needs three 
dimensions.</li>
+ *   <li>{@link #of(CoordinateReferenceSystem)} looks at the
+ *       {@linkplain CRS#getHorizontalComponent horizontal component} of a 
three-dimensional system
+ *       when the system itself has no code, which is the converse 
operation.</li>
+ * </ul>
  *
  * @author Johann Sorel (Geomatys)
  */
 final class Srid {
+    /**
+     * The logger where to report the absence of the <abbr>EPSG</abbr> 
definitions, which keeps a
+     * geometry from being written with its identifier but does not keep it 
from being written.
+     */
+    private static final Logger LOGGER = 
Logger.getLogger("org.apache.sis.geometries");
+
     /**
      * Value meaning that the coordinate reference system has no spatial 
reference identifier,
      * which is how the extended formats say that they carry no system.
@@ -53,40 +75,94 @@ final class Srid {
 
     /**
      * Returns the spatial reference identifier of the given coordinate 
reference system, or
-     * {@link #UNDEFINED} if it has none. Only the identifiers the system 
already carries are
-     * examined; the <abbr>EPSG</abbr> geodetic dataset is not searched for an 
equivalent
-     * definition, so a system built from axes and a datum has no identifier 
whatever it describes.
+     * {@link #UNDEFINED} if none can be determined.
+     *
+     * <p>Three sources are tried in turn, from the cheapest and most certain 
to the most costly:
+     * the identifier the system itself carries, then the identifier of its
+     * {@linkplain CRS#getHorizontalComponent horizontal component}, then a 
search of the
+     * <abbr>EPSG</abbr> definitions for a system equivalent to that 
horizontal component. The
+     * last one is needed because a three-dimensional system built by adding 
an ellipsoidal height
+     * to a two-dimensional one keeps no identifier — neither the combined 
system nor the
+     * horizontal component extracted back from it carries the code the height 
was added to.</p>
      *
      * @param  crs  the system whose identifier to return, or {@code null}.
      * @return the spatial reference identifier, or {@link #UNDEFINED} if none.
      */
     static int of(final CoordinateReferenceSystem crs) {
-        if (crs != null) {
-            final Identifier id = IdentifiedObjects.getIdentifier(crs, 
Citations.EPSG);
-            if (id != null) try {
-                return Integer.parseInt(id.getCode());
-            } catch (NumberFormatException e) {
-                // An EPSG code which is not a number cannot be written as a 
SRID. Fall through.
+        if (crs == null) {
+            return UNDEFINED;
+        }
+        int srid = declaredCode(crs);
+        if (srid == UNDEFINED) {
+            final SingleCRS horizontal = CRS.getHorizontalComponent(crs);
+            if (horizontal != null && horizontal != crs) {
+                srid = declaredCode(horizontal);
+                if (srid == UNDEFINED) try {
+                    final Integer code = 
IdentifiedObjects.lookupEPSG(horizontal);
+                    if (code != null) {
+                        srid = code;
+                    }
+                } catch (FactoryException e) {
+                    // The definitions are not available. The geometry is 
written without a SRID.
+                    Logging.ignorableException(LOGGER, Srid.class, "of", e);
+                }
             }
         }
+        return srid;
+    }
+
+    /**
+     * Returns the <abbr>EPSG</abbr> code which the given system already 
carries, or
+     * {@link #UNDEFINED} if it carries none. The <abbr>EPSG</abbr> geodetic 
dataset is not
+     * searched, so this method is cheap and never fails.
+     */
+    private static int declaredCode(final CoordinateReferenceSystem crs) {
+        final Identifier id = IdentifiedObjects.getIdentifier(crs, 
Citations.EPSG);
+        if (id != null) try {
+            return Integer.parseInt(id.getCode());
+        } catch (NumberFormatException e) {
+            // An EPSG code which is not a number cannot be written as a SRID.
+        }
         return UNDEFINED;
     }
 
     /**
      * Returns the coordinate reference system which the <abbr>EPSG</abbr> 
authority defines for
-     * the given spatial reference identifier.
+     * the given spatial reference identifier, in the given number of 
dimensions.
      *
-     * @param  srid  the spatial reference identifier to resolve.
-     * @return the coordinate reference system of that identifier.
-     * @throws IllegalArgumentException if the identifier is unknown, or if 
the definitions
-     *         are not available.
+     * <p>A two-dimensional system asked for three dimensions is completed 
with an
+     * {@linkplain CommonCRS.Vertical#ELLIPSOIDAL ellipsoidal height}, which 
is what the
+     * <var>z</var> ordinate of a geometry carrying such an identifier means. 
Any other
+     * disagreement between the two is an error: dropping a dimension would 
silently discard
+     * ordinates.</p>
+     *
+     * @param  srid       the spatial reference identifier to resolve.
+     * @param  dimension  number of ordinates the positions have, 2 or 3.
+     * @return the coordinate reference system of that identifier, of exactly 
{@code dimension}
+     *         dimensions.
+     * @throws IllegalArgumentException if the identifier is unknown, if the 
definitions are not
+     *         available, or if the system cannot describe positions of that 
many ordinates.
      */
-    static CoordinateReferenceSystem forCode(final int srid) {
+    static CoordinateReferenceSystem forCode(final int srid, final int 
dimension) {
+        final CoordinateReferenceSystem crs;
         try {
-            return CRS.forCode("EPSG:" + srid);
+            crs = CRS.forCode("EPSG:" + srid);
         } catch (FactoryException e) {
             throw new IllegalArgumentException("Cannot resolve the spatial 
reference identifier "
                     + srid + " to a coordinate reference system: " + 
e.getMessage(), e);
         }
+        final int actual = crs.getCoordinateSystem().getDimension();
+        if (actual == dimension) {
+            return crs;
+        }
+        if (actual == 2 && dimension == 3) try {
+            return CRS.compound(crs, CommonCRS.Vertical.ELLIPSOIDAL.crs());
+        } catch (FactoryException e) {
+            throw new IllegalArgumentException("Cannot add an ellipsoidal 
height to the coordinate"
+                    + " reference system of the spatial reference identifier " 
+ srid + ": " + e.getMessage(), e);
+        }
+        throw new IllegalArgumentException("The coordinate reference system of 
the spatial reference"
+                + " identifier " + srid + " has " + actual + " dimensions, 
which cannot describe"
+                + " positions of " + dimension + " ordinates.");
     }
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinary.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinary.java
index ffe64a5365..c07baf3adb 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinary.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinary.java
@@ -154,6 +154,12 @@ public final class WellKnownBinary {
          * system it names is used unless the caller passed one of their own to
          * {@link #decode(byte[], CoordinateReferenceSystem)}.</p>
          *
+         * <p>An identifier usually names a two-dimensional system while the 
type code may have the
+         * {@code Z} flag. Such a geometry is read as positions above the 
ellipsoid of that system:
+         * an ellipsoidal height is added to it. The converse holds on 
writing, where the identifier
+         * of the {@linkplain 
org.apache.sis.referencing.CRS#getHorizontalComponent horizontal
+         * component} is written when the three-dimensional system has none of 
its own.</p>
+         *
          * <p>This dialect is a superset of {@link #OGC} on reading: the 
thousands of a type code
          * are still understood, so a plain Well-Known Binary decodes 
unchanged. The converse does
          * not hold — the {@code OGC} dialect rejects the high order bits.</p>
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinaryParser.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinaryParser.java
index 49cc8d46ed..d5034e87d6 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinaryParser.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinaryParser.java
@@ -119,6 +119,12 @@ final class WellKnownBinaryParser {
      */
     private CoordinateReferenceSystem sridCRS;
 
+    /**
+     * Number of dimensions {@link #sridCRS} was resolved for, since a 
two-dimensional identifier
+     * gives a different system depending on whether the positions carry a 
<var>z</var> ordinate.
+     */
+    private int sridDimension;
+
     /**
      * Index in {@link #data} of the next byte to read.
      */
@@ -515,26 +521,27 @@ final class WellKnownBinaryParser {
      */
     private CoordinateReferenceSystem crs() {
         final int dimension = positionDimension();
-        final CoordinateReferenceSystem declared;
-        final String source;
         if (userCRS != null) {
-            declared = userCRS;
-            source = "The given coordinate reference system";
-        } else if (srid != Srid.UNDEFINED) {
-            if (sridCRS == null) {
-                sridCRS = Srid.forCode(srid);
+            final int actual = userCRS.getCoordinateSystem().getDimension();
+            if (actual != dimension) {
+                throw error("The given coordinate reference system has " + 
actual + " dimensions,"
+                        + " but the bytes have " + dimension + " ordinates per 
position");
             }
-            declared = sridCRS;
-            source = "The coordinate reference system of SRID " + srid;
-        } else {
-            return Geometries.getUndefinedCRS(dimension);
+            return userCRS;
         }
-        final int actual = declared.getCoordinateSystem().getDimension();
-        if (actual != dimension) {
-            throw error(source + " has " + actual + " dimensions,"
-                    + " but the bytes have " + dimension + " ordinates per 
position");
+        if (srid != Srid.UNDEFINED) {
+            /*
+             * Srid.forCode(…) returns a system of exactly the requested 
number of dimensions,
+             * adding an ellipsoidal height to a two-dimensional one when the 
type code has the
+             * Z flag. The dimension is therefore part of the cache key.
+             */
+            if (sridCRS == null || sridDimension != dimension) {
+                sridCRS = Srid.forCode(srid, dimension);
+                sridDimension = dimension;
+            }
+            return sridCRS;
         }
-        return declared;
+        return Geometries.getUndefinedCRS(dimension);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownText.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownText.java
index fedd8c6b5b..d143f17f6a 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownText.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownText.java
@@ -146,6 +146,13 @@ public final class WellKnownText {
          * the prefix is an ordinary Well-Known Text. On reading, the prefix 
is optional, and the
          * system it names is used unless the caller passed one of their own to
          * {@link #decode(String, CoordinateReferenceSystem)}.</p>
+         *
+         * <p>An identifier usually names a two-dimensional system while the 
text may carry a
+         * <var>z</var> ordinate, as in {@code SRID=4326;POINT Z (1 2 3)}. 
Such a text is read as a
+         * position above the ellipsoid of that system: an ellipsoidal height 
is added to it. The
+         * converse holds on writing, where the identifier of the
+         * {@linkplain org.apache.sis.referencing.CRS#getHorizontalComponent 
horizontal component}
+         * is written when the three-dimensional system has none of its 
own.</p>
          */
         EWKT
     }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownTextParser.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownTextParser.java
index f3b1e9e96f..0635c2e9ff 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownTextParser.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownTextParser.java
@@ -121,6 +121,12 @@ final class WellKnownTextParser {
      */
     private CoordinateReferenceSystem sridCRS;
 
+    /**
+     * Number of dimensions {@link #sridCRS} was resolved for, since a 
two-dimensional identifier
+     * gives a different system depending on whether the positions carry a 
<var>z</var> ordinate.
+     */
+    private int sridDimension;
+
     /**
      * Index in {@link #text} of the next character to read.
      */
@@ -542,26 +548,28 @@ final class WellKnownTextParser {
      */
     private CoordinateReferenceSystem crs() {
         final int dimension = positionDimension();
-        final CoordinateReferenceSystem declared;
-        final String source;
         if (userCRS != null) {
-            declared = userCRS;
-            source = "The given coordinate reference system";
-        } else if (srid != Srid.UNDEFINED) {
-            if (sridCRS == null) {
-                sridCRS = Srid.forCode(srid);
+            final int actual = userCRS.getCoordinateSystem().getDimension();
+            if (actual != dimension) {
+                throw error("The given coordinate reference system has " + 
actual + " dimensions,"
+                        + " but the text has " + dimension + " ordinates per 
position");
             }
-            declared = sridCRS;
-            source = "The coordinate reference system of SRID " + srid;
-        } else {
-            return Geometries.getUndefinedCRS(dimension);
+            return userCRS;
         }
-        final int actual = declared.getCoordinateSystem().getDimension();
-        if (actual != dimension) {
-            throw error(source + " has " + actual + " dimensions,"
-                    + " but the text has " + dimension + " ordinates per 
position");
+        if (srid != Srid.UNDEFINED) {
+            /*
+             * Srid.forCode(…) returns a system of exactly the requested 
number of dimensions,
+             * adding an ellipsoidal height to a two-dimensional one if the 
text has a Z ordinate.
+             * The dimension is therefore part of the cache key: an EMPTY 
element may ask for one
+             * before a later element establishes the flags.
+             */
+            if (sridCRS == null || sridDimension != dimension) {
+                sridCRS = Srid.forCode(srid, dimension);
+                sridDimension = dimension;
+            }
+            return sridCRS;
         }
-        return declared;
+        return Geometries.getUndefinedCRS(dimension);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/WellKnownBinaryTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/WellKnownBinaryTest.java
index 99c75693d9..b1d865ef13 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/WellKnownBinaryTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/WellKnownBinaryTest.java
@@ -42,6 +42,7 @@ import org.apache.sis.geometries.surface.Triangle;
 import org.apache.sis.maths.NDArrays;
 import org.apache.sis.maths.SampleSystem;
 import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -474,10 +475,10 @@ public final class WellKnownBinaryTest {
         final CoordinateReferenceSystem crs = Geometries.getUndefinedCRS(2);
         assertSame(crs, ewkb.decode(bytes, 
crs).getCoordinateReferenceSystem());
         /*
-         * EPSG:4326 is two dimensional, so a Z geometry carrying it 
contradicts it.
-         * 0xA0000001 is the point type with the Z bit and the identifier bit.
+         * A three dimensional identifier cannot describe a two dimensional 
geometry.
+         * EPSG:4979 is the three dimensional WGS 84, and is 0x1373.
          */
-        assertExtendedMalformed("00" + "A0000001" + "000010E6" + ONE + TWO + 
THREE);
+        assertExtendedMalformed("00" + "20000001" + "00001373" + ONE + TWO);
         /*
          * An identifier which no authority defines. 999999 is 0xF423F.
          */
@@ -489,6 +490,38 @@ public final class WellKnownBinaryTest {
                               + "00" + "20000001" + "00000F11" + ONE + TWO);
     }
 
+    /**
+     * Verifies that a two dimensional identifier on a geometry which has the 
{@code Z} flag gives
+     * a three dimensional system, by adding an ellipsoidal height to the one 
the identifier names,
+     * and that writing such a geometry gives the identifier back.
+     */
+    @Test
+    public void testExtendedEllipsoidalHeight() {
+        final WellKnownBinary ewkb = new 
WellKnownBinary(WellKnownBinary.Flavor.EWKB);
+        /*
+         * 0xA0000001 is the point type with the Z bit and the identifier bit, 
and EPSG:4326 is
+         * two dimensional, so the height is the one that identifier implies.
+         */
+        final byte[] bytes = bytes("00" + "A0000001" + "000010E6" + ONE + TWO 
+ THREE);
+        final Geometry geometry = ewkb.decode(bytes);
+        final CoordinateReferenceSystem crs = 
geometry.getCoordinateReferenceSystem();
+        assertEquals(3, crs.getCoordinateSystem().getDimension(), "Promoted to 
three dimensions");
+        assertNotNull(CRS.getVerticalComponent(crs, true), "Has a height");
+        assertEquals("POINT Z (1 2 3)", wkt.encode(geometry));
+        /*
+         * Writing gives the identifier back even though the three dimensional 
system carries
+         * none of its own, so the bytes round-trip unchanged.
+         */
+        assertArrayEquals(bytes, ewkb.encode(geometry));
+        /*
+         * An identifier which is already three dimensional is used as it is. 
EPSG:4979 is 0x1373.
+         */
+        final Geometry direct = ewkb.decode(bytes("00" + "A0000001" + 
"00001373" + ONE + TWO + THREE));
+        assertEquals("4979", IdentifiedObjects.getIdentifier(
+                direct.getCoordinateReferenceSystem(), 
Citations.EPSG).getCode());
+        assertEquals("00" + "A0000001" + "00001373" + ONE + TWO + THREE, 
hex(ewkb.encode(direct)));
+    }
+
     /**
      * Verifies that the given hexadecimal bytes are rejected by the extended 
dialect.
      */
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/WellKnownTextTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/WellKnownTextTest.java
index 0a3def45dc..7159e53944 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/WellKnownTextTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/WellKnownTextTest.java
@@ -40,6 +40,7 @@ import org.apache.sis.geometries.surface.Triangle;
 import org.apache.sis.maths.NDArrays;
 import org.apache.sis.maths.SampleSystem;
 import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 
@@ -353,10 +354,39 @@ public final class WellKnownTextTest {
         final CoordinateReferenceSystem crs = Geometries.getUndefinedCRS(2);
         assertSame(crs, ewkt.decode("SRID=4326;POINT (1 2)", 
crs).getCoordinateReferenceSystem());
         /*
-         * The system the prefix names must have as many dimensions as the 
text has ordinates.
-         * EPSG:4326 is two dimensional, so a Z geometry contradicts it.
+         * A three dimensional identifier cannot describe a two dimensional 
geometry: dropping the
+         * height would discard nothing here, but the converse of adding one 
is not defined.
+         * EPSG:4979 is the three dimensional WGS 84.
          */
-        assertThrows(IllegalArgumentException.class, () -> 
ewkt.decode("SRID=4326;POINT Z (1 2 3)"));
+        assertThrows(IllegalArgumentException.class, () -> 
ewkt.decode("SRID=4979;POINT (1 2)"));
+    }
+
+    /**
+     * Verifies that a two dimensional identifier on a geometry which has a 
<var>z</var> ordinate
+     * gives a three dimensional system, by adding an ellipsoidal height to 
the one the identifier
+     * names, and that writing such a geometry gives the identifier back.
+     */
+    @Test
+    public void testExtendedEllipsoidalHeight() {
+        final WellKnownText ewkt = new 
WellKnownText(WellKnownText.Flavor.EWKT);
+        final Geometry geometry = ewkt.decode("SRID=4326;POINT Z (1 2 3)");
+        final CoordinateReferenceSystem crs = 
geometry.getCoordinateReferenceSystem();
+        assertEquals(3, crs.getCoordinateSystem().getDimension(), "Promoted to 
three dimensions");
+        assertNotNull(CRS.getVerticalComponent(crs, true), "Has a height");
+        /*
+         * The height is the one the identifier implies, so writing the 
geometry gives the
+         * identifier back even though the three dimensional system carries 
none of its own.
+         */
+        assertEquals("SRID=4326;POINT Z (1 2 3)", ewkt.encode(geometry));
+        assertEquals("SRID=4326;LINESTRING Z (0 0 0, 1 1 1)",
+                     ewkt.encode(ewkt.decode("SRID=4326;LINESTRING Z (0 0 0, 1 
1 1)")));
+        /*
+         * An identifier which is already three dimensional is used as it is.
+         */
+        final Geometry direct = ewkt.decode("SRID=4979;POINT Z (1 2 3)");
+        assertEquals("4979", IdentifiedObjects.getIdentifier(
+                direct.getCoordinateReferenceSystem(), 
Citations.EPSG).getCode());
+        assertEquals("SRID=4979;POINT Z (1 2 3)", ewkt.encode(direct));
     }
 
     /**

Reply via email to