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

commit 33275b4dd4e84b7dc92f32d104a6edd21a2ebbcc
Author: jsorel <[email protected]>
AuthorDate: Wed Oct 14 10:22:32 2026 +0200

    feat(Geometry): separate Capsule,Cylinder,Sphere,Ellipsoid as interface and 
default implementation
---
 .../org/apache/sis/geometries/GeometryFactory.java |  60 ++++-
 .../main/org/apache/sis/geometries/Surface.java    |   6 +-
 .../shared/DefaultCapsule.java}                    | 137 ++--------
 .../internal/shared/DefaultCylinder.java           | 153 ++++++++++++
 .../shared/DefaultEllipsoid.java}                  | 138 ++--------
 .../shared/DefaultSphere.java}                     | 122 +--------
 .../solid/polyhedron/AbstractPolyhedron.java       |   3 +-
 .../org/apache/sis/geometries/surface/Capsule.java | 184 ++++----------
 .../apache/sis/geometries/surface/Cylinder.java    | 192 +++++---------
 .../apache/sis/geometries/surface/Ellipsoid.java   | 277 +++------------------
 .../org/apache/sis/geometries/surface/Sphere.java  | 219 +++-------------
 .../geometries/spherical/GreatCircleArcTest.java   |  16 +-
 .../spherical/SphericalConvexPolygonTest.java      |   4 +-
 .../spherical/SphericalTriangleTest.java           |  24 +-
 .../surface/ParametricCurveSurfaceTest.java        |   3 +-
 15 files changed, 473 insertions(+), 1065 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryFactory.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryFactory.java
index 3b7cca3a4c..cbb8e18d00 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryFactory.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryFactory.java
@@ -61,6 +61,7 @@ import 
org.apache.sis.geometries.internal.shared.DefaultBSplineCurve;
 import org.apache.sis.geometries.internal.shared.DefaultBSplineSurface;
 import org.apache.sis.geometries.internal.shared.DefaultBezier;
 import org.apache.sis.geometries.internal.shared.DefaultBilinearGrid;
+import org.apache.sis.geometries.internal.shared.DefaultCapsule;
 import org.apache.sis.geometries.internal.shared.DefaultCircle;
 import org.apache.sis.geometries.internal.shared.DefaultCircularString;
 import org.apache.sis.geometries.internal.shared.DefaultClothoid;
@@ -68,6 +69,8 @@ import 
org.apache.sis.geometries.internal.shared.DefaultCompoundCurve;
 import org.apache.sis.geometries.internal.shared.DefaultConic;
 import org.apache.sis.geometries.internal.shared.DefaultCubicSpline;
 import org.apache.sis.geometries.internal.shared.DefaultCurvePolygon;
+import org.apache.sis.geometries.internal.shared.DefaultCylinder;
+import org.apache.sis.geometries.internal.shared.DefaultEllipsoid;
 import org.apache.sis.geometries.internal.shared.DefaultEllipticArc;
 import org.apache.sis.geometries.internal.shared.DefaultEmpty;
 import org.apache.sis.geometries.internal.shared.DefaultGeodesic;
@@ -90,9 +93,12 @@ import 
org.apache.sis.geometries.internal.shared.DefaultReversedCurve;
 import org.apache.sis.geometries.internal.shared.DefaultReversedSurface;
 import org.apache.sis.geometries.internal.shared.DefaultRhumb;
 import org.apache.sis.geometries.internal.shared.DefaultSolid;
+import org.apache.sis.geometries.internal.shared.DefaultSphere;
 import org.apache.sis.geometries.internal.shared.DefaultSpiral;
 import org.apache.sis.geometries.internal.shared.DefaultTriangle;
 import org.apache.sis.geometries.internal.shared.DefaultTriangulatedSurface;
+import org.apache.sis.geometries.surface.Capsule;
+import org.apache.sis.geometries.surface.Cylinder;
 import org.apache.sis.geometries.surface.Ellipsoid;
 import org.apache.sis.geometries.solid.Polyhedron;
 import org.apache.sis.geometries.surface.Sphere;
@@ -626,7 +632,7 @@ public final class GeometryFactory {
      * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly one position.
      */
     public Sphere createSphere(DataPoints points, double radius) {
-        return new Sphere(points, radius);
+        return new DefaultSphere(points, radius);
     }
 
     /**
@@ -643,7 +649,57 @@ public final class GeometryFactory {
      *         or if the number of semi-axes is not the number of dimensions.
      */
     public Ellipsoid createEllipsoid(DataPoints points, double... semiAxes) {
-        return new Ellipsoid(points, semiAxes);
+        return new DefaultEllipsoid(points, semiAxes);
+    }
+
+    /**
+     * Creates a capsule whose capping spheres are centered on the two given 
positions.
+     * The reference system of the capsule is the one of those positions.
+     *
+     * @param  bottom  the center of the bottom sphere, not null.
+     * @param  top     the center of the top sphere, not null.
+     * @return the capsule between the two given positions.
+     */
+    public Capsule createCapsule(Vector<?> bottom, Vector<?> top) {
+        return new DefaultCapsule(bottom, top);
+    }
+
+    /**
+     * Creates a capsule whose capping spheres are centered on the two 
positions of the given
+     * {@code DataPoints}. The {@code DataPoints} is taken as-is, so the 
caller may give the
+     * capsule the attributes carried by it.
+     *
+     * @param  points  the centers of the bottom and top spheres, in that 
order.
+     * @return the capsule between the two positions of the given {@code 
DataPoints}.
+     * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly two positions.
+     */
+    public Capsule createCapsule(DataPoints points) {
+        return new DefaultCapsule(points);
+    }
+
+    /**
+     * Creates a cylinder along the axis joining the two given positions.
+     * The reference system of the cylinder is the one of those positions.
+     *
+     * @param  bottom  the center of the bottom circle, not null.
+     * @param  top     the center of the top circle, not null.
+     * @return the cylinder between the two given positions.
+     */
+    public Cylinder createCylinder(Vector<?> bottom, Vector<?> top) {
+        return new DefaultCylinder(bottom, top);
+    }
+
+    /**
+     * Creates a cylinder along the axis joining the two positions of the 
given {@code DataPoints}.
+     * The {@code DataPoints} is taken as-is, so the caller may give the 
cylinder the attributes
+     * carried by it.
+     *
+     * @param  points  the centers of the bottom and top circles, in that 
order.
+     * @return the cylinder between the two positions of the given {@code 
DataPoints}.
+     * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly two positions.
+     */
+    public Cylinder createCylinder(DataPoints points) {
+        return new DefaultCylinder(points);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Surface.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Surface.java
index 63c7b075c2..c9e94c5880 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Surface.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Surface.java
@@ -22,7 +22,9 @@ import static org.opengis.annotation.Specification.ISO_19107;
 import org.opengis.annotation.UML;
 import org.opengis.geometry.DirectPosition;
 import org.apache.sis.geometries.internal.shared.DefaultReversedSurface;
+import org.apache.sis.geometries.surface.Capsule;
 import org.apache.sis.geometries.surface.CurvePolygon;
+import org.apache.sis.geometries.surface.Cylinder;
 import org.apache.sis.geometries.surface.ParametricCurveSurface;
 import org.apache.sis.geometries.surface.Polygon;
 import org.apache.sis.geometries.surface.PolyhedralSurface;
@@ -66,7 +68,9 @@ import org.apache.sis.maths.Vector;
  */
 @UML(identifier="Surface", specification=ISO_19107)
 public sealed interface Surface extends Orientable
-        permits CurvePolygon,
+        permits Capsule,
+                CurvePolygon,
+                Cylinder,
                 ParametricCurveSurface,
                 Polygon,
                 PolyhedralSurface,
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Capsule.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCapsule.java
similarity index 51%
copy from 
incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Capsule.java
copy to 
incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCapsule.java
index 766182019c..1ffdcb304e 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Capsule.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCapsule.java
@@ -14,36 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.geometries.surface;
+package org.apache.sis.geometries.internal.shared;
 
 import java.util.Objects;
-import static org.opengis.annotation.Specification.ISO_12113;
-import org.opengis.annotation.UML;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.apache.sis.geometries.BBox;
+import javax.measure.Quantity;
 import org.apache.sis.geometries.DataPoints;
-import org.apache.sis.geometries.DataPointsType;
 import org.apache.sis.geometries.Geometry;
 import org.apache.sis.geometries.GeometryFactory;
-import org.apache.sis.geometries.GeometryType;
-import org.apache.sis.geometries.internal.shared.AbstractGeometry;
-import org.apache.sis.maths.DataType;
+import org.apache.sis.geometries.surface.Capsule;
 import org.apache.sis.maths.NDArrays;
 import org.apache.sis.maths.ReadOnly;
-import org.apache.sis.maths.Vectors;
 import org.apache.sis.maths.Vector;
 
 
 /**
- * A capsule (cylinder with hemispherical ends) defined by two "capping" 
spheres centered on two
- * positions, with potentially different radii.
- *
+ * Default implementation of a capsule, storing the centers of its two capping 
spheres
+ * in a {@code DataPoints}.
  *
  * @author Johann Sorel (Geomatys)
- * @spec ISO_12113 KHR_implicit_shapes extension Capsule
  */
-@UML(identifier="Capsule", specification=ISO_12113)
-public final class Capsule extends AbstractGeometry{
+public non-sealed class DefaultCapsule extends AbstractGeometry implements 
Capsule {
 
     /**
      * Must contain two points : the center of the bottom sphere, then the 
center of the top one.
@@ -60,7 +50,7 @@ public final class Capsule extends AbstractGeometry{
      * @param  bottom  the center of the bottom sphere, not null.
      * @param  top     the center of the top sphere, not null.
      */
-    public Capsule(Vector<?> bottom, Vector<?> top) {
+    public DefaultCapsule(Vector<?> bottom, Vector<?> top) {
         points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(bottom.getSampleSystem(), 
bottom.getDataType(), 2));
         points.setPosition(0, bottom);
         points.setPosition(1, top);
@@ -74,7 +64,7 @@ public final class Capsule extends AbstractGeometry{
      * @param  points  the centers of the bottom and top spheres, in that 
order.
      * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly two positions.
      */
-    public Capsule(DataPoints points) {
+    public DefaultCapsule(DataPoints points) {
         if (points.size() != 2) {
             throw new IllegalArgumentException("Capsule sequence must contain 
two points");
         }
@@ -82,134 +72,53 @@ public final class Capsule extends AbstractGeometry{
     }
 
     @Override
-    public GeometryType getGeometryType() {
-        return GeometryType.CAPSULE;
-    }
-
     public DataPoints getDataPoints() {
         return points;
     }
 
-    /**
-     * A capsule is a surface.
-     * @return 2
-     */
     @Override
-    public int getTopologicDimension() {
-        return 2;
-    }
-
-    /**
-     * @return the center of the bottom sphere
-     */
     public ReadOnly.Vector<?> getBottom() {
         return points.getPosition(0);
     }
 
-    /**
-     * @param position new center of the bottom sphere
-     */
+    @Override
     public void setBottom(ReadOnly.Vector<?> position) {
         points.setPosition(0, position);
     }
 
-    /**
-     * @return the center of the top sphere
-     */
+    @Override
     public ReadOnly.Vector<?> getTop() {
         return points.getPosition(1);
     }
 
-    /**
-     * @param position new center of the top sphere
-     */
+    @Override
     public void setTop(ReadOnly.Vector<?> position) {
         points.setPosition(1, position);
     }
 
-    /**
-     * Returns the vector going from the center of the bottom sphere to the 
center of the top one.
-     * It is built on doubles whatever the type of the positions, the 
direction and the length of
-     * an axis being real values even when the positions they are derived from 
are integers.
-     */
-    private Vector<?> getAxis() {
-        final ReadOnly.Vector<?> bottom = getBottom();
-        final Vector<?> axis = Vectors.create(bottom.getSampleSystem(), 
DataType.DOUBLE);
-        axis.set(getTop());
-        axis.subtract(bottom);
-        return axis;
-    }
-
-    /**
-     * The height is the length of the axis going from the center of the 
bottom sphere to the
-     * center of the top one, so it is not set but derived from the two 
positions this capsule
-     * is built on. It does not include the two hemispherical ends.
-     *
-     * @return the capsule height
-     */
-    public double getHeight() {
-        return getAxis().length();
-    }
-
-    /**
-     * @return capsule top sphere radius
-     */
+    @Override
     public double getRadiusTop() {
         return radiusTop;
     }
 
-    /**
-     * @param radius new capsule top radius
-     */
+    @Override
     public void setRadiusTop(double radius) {
         this.radiusTop = radius;
     }
 
-    /**
-     * @return capsule bottom sphere radius
-     */
+    @Override
     public double getRadiusBottom() {
         return radiusBottom;
     }
 
-    /**
-     * @param radius new capsule bottom radius
-     */
+    @Override
     public void setRadiusBottom(double radius) {
         this.radiusBottom = radius;
     }
 
     @Override
-    public boolean isEmpty() {
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>A capsule is the convex hull of its two capping spheres, so it 
reaches the whole radius
-     * of each of them on every axis. Contrarily to a {@linkplain Cylinder 
cylinder}, whose flat
-     * ends spread less than their radius on the axis they face, a capsule is 
therefore bounded
-     * by the union of the two boxes bounding its spheres.</p>
-     */
-    @Override
-    public BBox getEnvelope() {
-        final ReadOnly.Vector<?> bottom = getBottom();
-        final ReadOnly.Vector<?> top = getTop();
-        final int dim = points.getDimension();
-        final BBox bbox = new BBox(dim);
-        for (int i=0;i<dim;i++){
-            final double b = bottom.get(i);
-            final double t = top.get(i);
-            bbox.setRange(i, Math.min(b - radiusBottom, t - radiusTop),
-                             Math.max(b + radiusBottom, t + radiusTop));
-        }
-        return bbox;
-    }
-
-    @Override
-    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
-        return points.getCoordinateReferenceSystem();
+    public Quantity<?> getArea() {
+        throw new UnsupportedOperationException("Not supported yet.");
     }
 
     @Override
@@ -217,16 +126,6 @@ public final class Capsule extends AbstractGeometry{
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
-    @Override
-    public void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) 
throws IllegalArgumentException {
-        points.setCoordinateReferenceSystem(crs);
-    }
-
-    @Override
-    public DataPointsType getDataPointsType() {
-        return points.getType();
-    }
-
     @Override
     public boolean equals(Object obj) {
         if (obj == null) {
@@ -235,7 +134,7 @@ public final class Capsule extends AbstractGeometry{
         if (getClass() != obj.getClass()) {
             return false;
         }
-        final Capsule other = (Capsule) obj;
+        final DefaultCapsule other = (DefaultCapsule) obj;
         if (!Objects.equals(this.points, other.points)) {
             return false;
         }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCylinder.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCylinder.java
new file mode 100644
index 0000000000..b7405ea923
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCylinder.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.geometries.internal.shared;
+
+import java.util.Objects;
+import javax.measure.Quantity;
+import org.apache.sis.geometries.DataPoints;
+import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryFactory;
+import org.apache.sis.geometries.surface.Cylinder;
+import org.apache.sis.maths.NDArrays;
+import org.apache.sis.maths.ReadOnly;
+import org.apache.sis.maths.Vector;
+
+
+/**
+ * Default implementation of a cylinder, storing the centers of its two end 
circles
+ * in a {@code DataPoints}.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public non-sealed class DefaultCylinder extends AbstractGeometry implements 
Cylinder {
+
+    /**
+     * Must contain two points : the center of the bottom circle, then the 
center of the top one.
+     */
+    private final DataPoints points;
+
+    private double radiusTop = 1.0;
+    private double radiusBottom = 1.0;
+
+    /**
+     * Creates a cylinder along the axis joining the two given positions.
+     * The reference system of the cylinder is the one of those positions.
+     *
+     * @param  bottom  the center of the bottom circle, not null.
+     * @param  top     the center of the top circle, not null.
+     */
+    public DefaultCylinder(Vector<?> bottom, Vector<?> top) {
+        points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(bottom.getSampleSystem(), 
bottom.getDataType(), 2));
+        points.setPosition(0, bottom);
+        points.setPosition(1, top);
+    }
+
+    /**
+     * Creates a cylinder along the axis joining the two positions of the 
given {@code DataPoints}.
+     * The {@code DataPoints} is taken as-is, so the caller may give the 
cylinder the attributes
+     * carried by that {@code DataPoints}.
+     *
+     * @param  points  the centers of the bottom and top circles, in that 
order.
+     * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly two positions.
+     */
+    public DefaultCylinder(DataPoints points) {
+        if (points.size() != 2) {
+            throw new IllegalArgumentException("Cylinder sequence must contain 
two points");
+        }
+        this.points = points;
+    }
+
+    @Override
+    public DataPoints getDataPoints() {
+        return points;
+    }
+
+    @Override
+    public ReadOnly.Vector<?> getBottom() {
+        return points.getPosition(0);
+    }
+
+    @Override
+    public void setBottom(ReadOnly.Vector<?> position) {
+        points.setPosition(0, position);
+    }
+
+    @Override
+    public ReadOnly.Vector<?> getTop() {
+        return points.getPosition(1);
+    }
+
+    @Override
+    public void setTop(ReadOnly.Vector<?> position) {
+        points.setPosition(1, position);
+    }
+
+    @Override
+    public double getRadiusTop() {
+        return radiusTop;
+    }
+
+    @Override
+    public void setRadiusTop(double radius) {
+        this.radiusTop = radius;
+    }
+
+    @Override
+    public double getRadiusBottom() {
+        return radiusBottom;
+    }
+
+    @Override
+    public void setRadiusBottom(double radius) {
+        this.radiusBottom = radius;
+    }
+
+    @Override
+    public Quantity<?> getArea() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public Geometry getBoundary() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final DefaultCylinder other = (DefaultCylinder) obj;
+        if (!Objects.equals(this.points, other.points)) {
+            return false;
+        }
+        return Double.doubleToLongBits(this.radiusTop) == 
Double.doubleToLongBits(other.radiusTop)
+            && Double.doubleToLongBits(this.radiusBottom) == 
Double.doubleToLongBits(other.radiusBottom);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 89 * hash + Objects.hashCode(this.points);
+        hash = 89 * hash + Double.hashCode(this.radiusTop);
+        hash = 89 * hash + Double.hashCode(this.radiusBottom);
+        return hash;
+    }
+}
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Ellipsoid.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
similarity index 63%
copy from 
incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Ellipsoid.java
copy to 
incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
index 2583f181c5..8a427c31e2 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Ellipsoid.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
@@ -14,58 +14,34 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.geometries.surface;
+package org.apache.sis.geometries.internal.shared;
 
 import java.util.Arrays;
 import java.util.List;
 import javax.measure.Quantity;
-import static org.opengis.annotation.Specification.ISO_19107;
-import org.opengis.annotation.UML;
 import org.opengis.geometry.DirectPosition;
-import org.opengis.geometry.Envelope;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.apache.sis.geometries.BBox;
 import org.apache.sis.geometries.Curve;
 import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.Geometries;
 import org.apache.sis.geometries.Geometry;
 import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.GeometryType;
-import org.apache.sis.geometries.SurfaceInterpolation;
-import org.apache.sis.geometries.DataPointsType;
-import org.apache.sis.geometries.internal.shared.AbstractGeometry;
-import org.apache.sis.geometries.surface.ParametricCurveSurface;
+import org.apache.sis.geometries.surface.Ellipsoid;
 import org.apache.sis.maths.DataType;
 import org.apache.sis.maths.NDArrays;
 import org.apache.sis.maths.ReadOnly;
 import org.apache.sis.maths.SampleSystem;
-import org.apache.sis.maths.Vector;
 import org.apache.sis.util.ArgumentChecks;
 
 
 /**
- * An ellipsoid geometry defined by a center and one semi-axis length per axis 
of the coordinate
- * system.
- *
- * <p>As a conic surface, an ellipsoid is a parametric curve surface given as 
a family of ellipses
- * whose positions vary along the axis of the ellipsoid. Its horizontal curves 
are therefore lines
- * of constant latitude and its vertical curves lines of constant 
longitude.</p>
- *
- * <p>Constraints:</p>
- * <ul>
- *   <li>The up-normal is the outward normal when the control points are 
ordered by increasing
- *       longitude and increasing latitude.</li>
- *   <li>There is one semi-axis per axis of the coordinate system, and each of 
them is positive.</li>
- * </ul>
- *
- * <p>Note: this geometry is not the ellipsoid used as a geometric reference 
surface
- * (ISO 19107:2019 - 6.2.3.4), which {@link 
org.opengis.referencing.datum.Ellipsoid} describes.
- * That one is a property of the coordinate reference system; this one is a 
geometry expressed
- * in such a system.</p>
+ * Default implementation of an ellipsoid, storing its center in a {@code 
DataPoints}
+ * and its semi-axes in an array.
  *
  * @author Johann Sorel (Geomatys)
  */
-public final class Ellipsoid extends AbstractGeometry implements 
ParametricCurveSurface {
+public non-sealed class DefaultEllipsoid extends AbstractGeometry implements 
Ellipsoid {
 
     /**
      * Must contain one point: the center of the ellipsoid.
@@ -81,14 +57,14 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
     /**
      * @param dimension number of dimensions of the ellipsoid, must be 
positive.
      */
-    public Ellipsoid(int dimension) {
+    public DefaultEllipsoid(int dimension) {
         this(Geometries.getUndefinedCRS(dimension));
     }
 
     /**
      * @param crs ellipsoid coordinate system, not null.
      */
-    public Ellipsoid(CoordinateReferenceSystem crs) {
+    public DefaultEllipsoid(CoordinateReferenceSystem crs) {
         points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(SampleSystem.of(crs), 
DataType.DOUBLE, 1));
         semiAxes = unitSemiAxes(crs.getCoordinateSystem().getDimension());
     }
@@ -98,7 +74,7 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
      * @param semiAxes  length of the semi-axis along each axis, one value per 
dimension.
      *                  Each of them must be positive.
      */
-    public Ellipsoid(int dimension, double... semiAxes) {
+    public DefaultEllipsoid(int dimension, double... semiAxes) {
         this(Geometries.getUndefinedCRS(dimension), semiAxes);
     }
 
@@ -107,7 +83,7 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
      * @param semiAxes  length of the semi-axis along each axis, one value per 
dimension.
      *                  Each of them must be positive.
      */
-    public Ellipsoid(CoordinateReferenceSystem crs, double... semiAxes) {
+    public DefaultEllipsoid(CoordinateReferenceSystem crs, double... semiAxes) 
{
         points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(SampleSystem.of(crs), 
DataType.DOUBLE, 1));
         this.semiAxes = 
checkedSemiAxes(crs.getCoordinateSystem().getDimension(), semiAxes);
     }
@@ -120,7 +96,7 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
      * @param  points  the center of the ellipsoid.
      * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly one position.
      */
-    public Ellipsoid(DataPoints points) {
+    public DefaultEllipsoid(DataPoints points) {
         this.points = checkedPoints(points);
         semiAxes = unitSemiAxes(points.getDimension());
     }
@@ -135,7 +111,7 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
      * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly one position,
      *         or if the number of semi-axes is not the number of dimensions.
      */
-    public Ellipsoid(DataPoints points, double... semiAxes) {
+    public DefaultEllipsoid(DataPoints points, double... semiAxes) {
         this.points = checkedPoints(points);
         this.semiAxes = checkedSemiAxes(points.getDimension(), semiAxes);
     }
@@ -181,125 +157,43 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
         }
     }
 
-    @Override
-    public GeometryType getGeometryType() {
-        return GeometryType.ELLIPSOID;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return false;
-    }
-
     @Override
     public Quantity<?> getArea() {
         throw new UnsupportedOperationException("Not supported.");
     }
 
     @Override
-    public void setCoordinateReferenceSystem(CoordinateReferenceSystem cs) 
throws IllegalArgumentException {
-        if (cs.getCoordinateSystem().getDimension() != 
getCoordinateReferenceSystem().getCoordinateSystem().getDimension()) {
-            throw new IllegalArgumentException("New CRS dimension must be the 
same as previous CRS");
-        }
-        points.setCoordinateReferenceSystem(cs);
-    }
-
-    @Override
-    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
-        return points.getCoordinateReferenceSystem();
-    }
-
-    @Override
-    public DataPointsType getDataPointsType() {
-        return points.getType();
-    }
-
-    /**
-     * Returns {@link SurfaceInterpolation#ELLIPTICAL}: this surface is a 
section of an ellipsoid.
-     *
-     * @see ISO 19107:2019 - 8.5.2
-     */
-    @UML(identifier="interpolation", specification=ISO_19107)
-    @Override
-    public List<SurfaceInterpolation> getInterpolation() {
-        return List.of(SurfaceInterpolation.ELLIPTICAL);
-    }
-
-    /**
-     * Returns a copy of the semi-axis lengths, one per axis of the coordinate 
system.
-     * Writing in the returned array does not reshape this ellipsoid;
-     * use {@link #setSemiAxes(double...)} for that.
-     *
-     * @return length of the semi-axis along each axis.
-     */
     public double[] getSemiAxes() {
         return semiAxes.clone();
     }
 
-    /**
-     * @param  dimension  index of the axis along which to measure the 
semi-axis.
-     * @return length of the semi-axis along the given axis.
-     */
+    @Override
     public double getSemiAxis(int dimension) {
         return semiAxes[dimension];
     }
 
-    /**
-     * @param semiAxes new semi-axis lengths, one per dimension. Each of them 
must be positive.
-     * @throws IllegalArgumentException if the number of semi-axes is not the 
number of dimensions.
-     */
+    @Override
     public void setSemiAxes(double... semiAxes) {
         ensureValidSemiAxes(this.semiAxes.length, semiAxes);
         System.arraycopy(semiAxes, 0, this.semiAxes, 0, this.semiAxes.length);
     }
 
-    /**
-     * @param dimension index of the axis along which to set the semi-axis.
-     * @param length    new semi-axis length, must be positive.
-     */
+    @Override
     public void setSemiAxis(int dimension, double length) {
         ArgumentChecks.ensurePositive("length", length);
         semiAxes[dimension] = length;
     }
 
-    /**
-     * Returns a copy of the ellipsoid center. Writing in the returned tuple 
does not move this
-     * ellipsoid; use {@link #setCenter(Tuple)} for that.
-     *
-     * @return ellipsoid center.
-     */
+    @Override
     public ReadOnly.Vector<?> getCenter() {
         return points.getPosition(0);
     }
 
-    /**
-     * @param position new center of the ellipsoid
-     */
+    @Override
     public void setCenter(ReadOnly.Vector<?> position) {
         points.setPosition(0, position);
     }
 
-    /**
-     * {@inheritDoc }
-     *
-     * <p>An ellipsoid reaches its semi-axis away from its center on each 
axis, so the smallest box
-     * containing it is the box of that extent around the center.</p>
-     */
-    @Override
-    public Envelope getEnvelope() {
-        final Vector<?> center = getCenter().copy();
-        final BBox env = new BBox(center, center);
-        env.setCoordinateReferenceSystem(getCoordinateReferenceSystem());
-        for (int i = 0, n = getDimension(); i < n; i++) {
-            final double c = center.get(i);
-            final double a = semiAxes[i];
-            if (a > 0) {
-                env.setRange(i, c-a, c+a);
-            }
-        }
-        return env;
-    }
-
     @Override
     public Geometry getBoundary() {
         throw new UnsupportedOperationException("Not supported yet.");
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Sphere.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSphere.java
similarity index 58%
copy from 
incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Sphere.java
copy to 
incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSphere.java
index 81ecd4f55c..ef1e4d6984 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Sphere.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSphere.java
@@ -14,61 +14,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.geometries.surface;
+package org.apache.sis.geometries.internal.shared;
 
 import java.util.List;
 import javax.measure.Quantity;
-import static org.opengis.annotation.Specification.ISO_19107;
-import org.opengis.annotation.UML;
 import org.opengis.geometry.DirectPosition;
-import org.opengis.geometry.Envelope;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.apache.sis.geometries.BBox;
 import org.apache.sis.geometries.Curve;
 import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.Geometries;
 import org.apache.sis.geometries.Geometry;
 import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.GeometryType;
-import org.apache.sis.geometries.SurfaceInterpolation;
-import org.apache.sis.geometries.DataPointsType;
-import org.apache.sis.geometries.internal.shared.AbstractGeometry;
-import org.apache.sis.geometries.surface.ParametricCurveSurface;
+import org.apache.sis.geometries.surface.Sphere;
 import org.apache.sis.maths.DataType;
 import org.apache.sis.maths.NDArrays;
 import org.apache.sis.maths.ReadOnly;
 import org.apache.sis.maths.SampleSystem;
-import org.apache.sis.maths.Vector;
 import org.apache.sis.util.ArgumentChecks;
 
 
 /**
- * A sphere geometry defined by a center and a radius.
- *
- * <p>As a conic surface, a sphere is a parametric curve surface given as a 
family of circles whose
- * positions vary linearly along the axis of the sphere and whose radius 
varies with the cosine of
- * the central angle. Its horizontal curves are therefore lines of constant 
latitude and its vertical
- * curves lines of constant longitude.</p>
- *
- * <p>Constraints:</p>
- * <ul>
- *   <li>The up-normal is the outward normal when the control points are 
ordered by increasing
- *       longitude and increasing latitude.</li>
- *   <li>The radius is positive.</li>
- * </ul>
- *
- * <p>Difference with ISO 19107, which defines a sphere in a 3-dimensional 
space: even if it is
- * called a Sphere this class can handle 2 to N dimensions.</p>
- *
- * <p>Note: ISO 19107 classifies this geometry as a conic surface. It should 
not be confused with
- * the sphere used as a geometric reference surface (ISO 19107:2019 - 
6.2.3.4).</p>
+ * Default implementation of a sphere, storing its center in a {@code 
DataPoints}.
  *
  * @author Johann Sorel (Geomatys)
- *
- * @see ISO 19107:2019 - 8.5.1, 8.5.2
  */
-@UML(identifier="Sphere", specification=ISO_19107)
-public final class Sphere extends AbstractGeometry implements 
ParametricCurveSurface {
+public non-sealed class DefaultSphere extends AbstractGeometry implements 
Sphere {
 
     /**
      * Must contain one point: the center of the sphere.
@@ -80,14 +51,14 @@ public final class Sphere extends AbstractGeometry 
implements ParametricCurveSur
     /**
      * @param dimension number of dimensions of the sphere, must be positive.
      */
-    public Sphere(int dimension) {
+    public DefaultSphere(int dimension) {
         this(Geometries.getUndefinedCRS(dimension));
     }
 
     /**
      * @param crs sphere coordinate system, not null.
      */
-    public Sphere(CoordinateReferenceSystem crs) {
+    public DefaultSphere(CoordinateReferenceSystem crs) {
         points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(SampleSystem.of(crs), 
DataType.DOUBLE, 1));
     }
 
@@ -95,7 +66,7 @@ public final class Sphere extends AbstractGeometry implements 
ParametricCurveSur
      * @param dimension number of dimensions of the sphere, must be positive.
      * @param radius radius new sphere radius, must be positive.
      */
-    public Sphere(int dimension, double radius) {
+    public DefaultSphere(int dimension, double radius) {
         this(Geometries.getUndefinedCRS(dimension), radius);
     }
 
@@ -103,7 +74,7 @@ public final class Sphere extends AbstractGeometry 
implements ParametricCurveSur
      * @param crs sphere coordinate system, not null.
      * @param radius radius new sphere radius, must be positive.
      */
-    public Sphere(CoordinateReferenceSystem crs, double radius) {
+    public DefaultSphere(CoordinateReferenceSystem crs, double radius) {
         this(crs);
         this.radius = radius;
     }
@@ -116,7 +87,7 @@ public final class Sphere extends AbstractGeometry 
implements ParametricCurveSur
      * @param  points  the center of the sphere.
      * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly one position.
      */
-    public Sphere(DataPoints points) {
+    public DefaultSphere(DataPoints points) {
         if (points.size() != 1) {
             throw new IllegalArgumentException("Sphere sequence must contain 
one point");
         }
@@ -130,104 +101,37 @@ public final class Sphere extends AbstractGeometry 
implements ParametricCurveSur
      * @param  radius  new sphere radius, must be positive.
      * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly one position.
      */
-    public Sphere(DataPoints points, double radius) {
+    public DefaultSphere(DataPoints points, double radius) {
         this(points);
         this.radius = radius;
     }
 
-    @Override
-    public GeometryType getGeometryType() {
-        return GeometryType.SPHERE;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return false;
-    }
-
     @Override
     public Quantity<?> getArea() {
         throw new UnsupportedOperationException("Not supported.");
     }
 
     @Override
-    public void setCoordinateReferenceSystem(CoordinateReferenceSystem cs) 
throws IllegalArgumentException {
-        if (cs.getCoordinateSystem().getDimension() != 
getCoordinateReferenceSystem().getCoordinateSystem().getDimension()) {
-            throw new IllegalArgumentException("New CRS dimension must be the 
same as previous CRS");
-        }
-        points.setCoordinateReferenceSystem(cs);
-    }
-
-    @Override
-    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
-        return points.getCoordinateReferenceSystem();
-    }
-
-    @Override
-    public DataPointsType getDataPointsType() {
-        return points.getType();
-    }
-
-    /**
-     * Returns {@link SurfaceInterpolation#SPHERICAL}: this surface is a 
section of a sphere.
-     *
-     * @see ISO 19107:2019 - 8.5.2
-     */
-    @UML(identifier="interpolation", specification=ISO_19107)
-    @Override
-    public List<SurfaceInterpolation> getInterpolation() {
-        return List.of(SurfaceInterpolation.SPHERICAL);
-    }
-
-    /**
-     * @return radius of the sphere.
-     */
     public double getRadius() {
         return radius;
     }
 
-    /**
-     * @param radius new sphere radius, must be positive.
-     */
+    @Override
     public void setRadius(double radius) {
         ArgumentChecks.ensurePositive("radius", radius);
         this.radius = radius;
     }
 
-    /**
-     * Returns a copy of the sphere center. Writing in the returned tuple does 
not move this
-     * sphere; use {@link #setCenter(Tuple)} for that.
-     *
-     * @return sphere center.
-     */
+    @Override
     public ReadOnly.Vector<?> getCenter() {
         return points.getPosition(0);
     }
 
-    /**
-     * @param position new center of the sphere
-     */
+    @Override
     public void setCenter(ReadOnly.Vector<?> position) {
         points.setPosition(0, position);
     }
 
-    /**
-     * {@inheritDoc }
-     */
-    @Override
-    public Envelope getEnvelope() {
-        final Vector<?> center = getCenter().copy();
-        final BBox env = new BBox(center, center);
-        env.setCoordinateReferenceSystem(getCoordinateReferenceSystem());
-        if (radius > 0) {
-            for (int i = 0, n = getDimension(); i < n; i++) {
-                double c = center.get(i);
-                env.setRange(i, c-radius, c+radius);
-            }
-        }
-        return env;
-    }
-
     @Override
     public Geometry getBoundary() {
         throw new UnsupportedOperationException("Not supported yet.");
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/polyhedron/AbstractPolyhedron.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/polyhedron/AbstractPolyhedron.java
index 5f87692628..0f2a763c2c 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/polyhedron/AbstractPolyhedron.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/polyhedron/AbstractPolyhedron.java
@@ -26,6 +26,7 @@ import 
org.apache.sis.geometries.internal.shared.AbstractGeometry;
 import org.apache.sis.geometries.solid.Polyhedron;
 import org.apache.sis.geometries.spherical.SphericalConvexPolygon;
 import org.apache.sis.geometries.surface.Polygon;
+import org.apache.sis.geometries.internal.shared.DefaultSphere;
 import org.apache.sis.geometries.surface.Sphere;
 import org.apache.sis.maths.Array;
 import org.apache.sis.maths.NDArrays;
@@ -192,7 +193,7 @@ public abstract sealed class AbstractPolyhedron extends 
AbstractGeometry impleme
      * @return matching face index, or -1 if none matched
      */
     protected static int nearestFace(Array vertices, int[][] faces, 
ReadOnly.Vector<?> unitVector) {
-        final Sphere sphere = new Sphere(3);
+        final Sphere sphere = new DefaultSphere(3);
         for (int f = 0; f < faces.length; f++) {
             final Array polyArray = NDArrays.subset(vertices, faces[f]);
             final SphericalConvexPolygon face = new 
SphericalConvexPolygon(sphere, polyArray);
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Capsule.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Capsule.java
index 766182019c..5ea62d8613 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Capsule.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Capsule.java
@@ -16,19 +16,15 @@
  */
 package org.apache.sis.geometries.surface;
 
-import java.util.Objects;
 import static org.opengis.annotation.Specification.ISO_12113;
 import org.opengis.annotation.UML;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.geometries.BBox;
-import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.DataPointsType;
-import org.apache.sis.geometries.Geometry;
-import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.GeometryType;
-import org.apache.sis.geometries.internal.shared.AbstractGeometry;
+import org.apache.sis.geometries.Surface;
+import org.apache.sis.geometries.internal.shared.DefaultCapsule;
 import org.apache.sis.maths.DataType;
-import org.apache.sis.maths.NDArrays;
 import org.apache.sis.maths.ReadOnly;
 import org.apache.sis.maths.Vectors;
 import org.apache.sis.maths.Vector;
@@ -38,150 +34,89 @@ import org.apache.sis.maths.Vector;
  * A capsule (cylinder with hemispherical ends) defined by two "capping" 
spheres centered on two
  * positions, with potentially different radii.
  *
+ * <p>Constraints:</p>
+ * <ul>
+ *   <li>The {@linkplain #getDataPoints() data points} hold exactly two 
positions:
+ *       the center of the bottom sphere, then the center of the top one.</li>
+ * </ul>
  *
  * @author Johann Sorel (Geomatys)
  * @spec ISO_12113 KHR_implicit_shapes extension Capsule
  */
 @UML(identifier="Capsule", specification=ISO_12113)
-public final class Capsule extends AbstractGeometry{
+public sealed interface Capsule extends Surface
+        permits DefaultCapsule
+{
 
     /**
-     * Must contain two points : the center of the bottom sphere, then the 
center of the top one.
+     * Returns {@link GeometryType#CAPSULE}.
      */
-    private final DataPoints points;
-
-    private double radiusTop = 1.0;
-    private double radiusBottom = 1.0;
-
-    /**
-     * Creates a capsule whose capping spheres are centered on the two given 
positions.
-     * The reference system of the capsule is the one of those positions.
-     *
-     * @param  bottom  the center of the bottom sphere, not null.
-     * @param  top     the center of the top sphere, not null.
-     */
-    public Capsule(Vector<?> bottom, Vector<?> top) {
-        points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(bottom.getSampleSystem(), 
bottom.getDataType(), 2));
-        points.setPosition(0, bottom);
-        points.setPosition(1, top);
-    }
-
-    /**
-     * Creates a capsule whose capping spheres are centered on the two 
positions of the given
-     * {@code DataPoints}. The {@code DataPoints} is taken as-is, so the 
caller may give the
-     * capsule the attributes carried by that {@code DataPoints}.
-     *
-     * @param  points  the centers of the bottom and top spheres, in that 
order.
-     * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly two positions.
-     */
-    public Capsule(DataPoints points) {
-        if (points.size() != 2) {
-            throw new IllegalArgumentException("Capsule sequence must contain 
two points");
-        }
-        this.points = points;
-    }
-
     @Override
-    public GeometryType getGeometryType() {
+    default GeometryType getGeometryType() {
         return GeometryType.CAPSULE;
     }
 
-    public DataPoints getDataPoints() {
-        return points;
-    }
-
     /**
-     * A capsule is a surface.
-     * @return 2
+     * Returns {@code false}: a capsule always has its two sphere centers.
      */
     @Override
-    public int getTopologicDimension() {
-        return 2;
+    default boolean isEmpty() {
+        return false;
     }
 
     /**
      * @return the center of the bottom sphere
      */
-    public ReadOnly.Vector<?> getBottom() {
-        return points.getPosition(0);
-    }
+    ReadOnly.Vector<?> getBottom();
 
     /**
      * @param position new center of the bottom sphere
      */
-    public void setBottom(ReadOnly.Vector<?> position) {
-        points.setPosition(0, position);
-    }
+    void setBottom(ReadOnly.Vector<?> position);
 
     /**
      * @return the center of the top sphere
      */
-    public ReadOnly.Vector<?> getTop() {
-        return points.getPosition(1);
-    }
+    ReadOnly.Vector<?> getTop();
 
     /**
      * @param position new center of the top sphere
      */
-    public void setTop(ReadOnly.Vector<?> position) {
-        points.setPosition(1, position);
-    }
-
-    /**
-     * Returns the vector going from the center of the bottom sphere to the 
center of the top one.
-     * It is built on doubles whatever the type of the positions, the 
direction and the length of
-     * an axis being real values even when the positions they are derived from 
are integers.
-     */
-    private Vector<?> getAxis() {
-        final ReadOnly.Vector<?> bottom = getBottom();
-        final Vector<?> axis = Vectors.create(bottom.getSampleSystem(), 
DataType.DOUBLE);
-        axis.set(getTop());
-        axis.subtract(bottom);
-        return axis;
-    }
-
-    /**
-     * The height is the length of the axis going from the center of the 
bottom sphere to the
-     * center of the top one, so it is not set but derived from the two 
positions this capsule
-     * is built on. It does not include the two hemispherical ends.
-     *
-     * @return the capsule height
-     */
-    public double getHeight() {
-        return getAxis().length();
-    }
+    void setTop(ReadOnly.Vector<?> position);
 
     /**
      * @return capsule top sphere radius
      */
-    public double getRadiusTop() {
-        return radiusTop;
-    }
+    double getRadiusTop();
 
     /**
      * @param radius new capsule top radius
      */
-    public void setRadiusTop(double radius) {
-        this.radiusTop = radius;
-    }
+    void setRadiusTop(double radius);
 
     /**
      * @return capsule bottom sphere radius
      */
-    public double getRadiusBottom() {
-        return radiusBottom;
-    }
+    double getRadiusBottom();
 
     /**
      * @param radius new capsule bottom radius
      */
-    public void setRadiusBottom(double radius) {
-        this.radiusBottom = radius;
-    }
+    void setRadiusBottom(double radius);
 
-    @Override
-    public boolean isEmpty() {
-        return false;
+    /**
+     * The height is the length of the axis going from the center of the 
bottom sphere to the
+     * center of the top one, so it is not set but derived from the two 
positions this capsule
+     * is built on. It does not include the two hemispherical ends.
+     *
+     * @return the capsule height
+     */
+    default double getHeight() {
+        final ReadOnly.Vector<?> bottom = getBottom();
+        final Vector<?> axis = Vectors.create(bottom.getSampleSystem(), 
DataType.DOUBLE);
+        axis.set(getTop());
+        axis.subtract(bottom);
+        return axis.length();
     }
 
     /**
@@ -193,10 +128,12 @@ public final class Capsule extends AbstractGeometry{
      * by the union of the two boxes bounding its spheres.</p>
      */
     @Override
-    public BBox getEnvelope() {
+    default BBox getEnvelope() {
         final ReadOnly.Vector<?> bottom = getBottom();
         final ReadOnly.Vector<?> top = getTop();
-        final int dim = points.getDimension();
+        final double radiusBottom = getRadiusBottom();
+        final double radiusTop = getRadiusTop();
+        final int dim = getDataPoints().getDimension();
         final BBox bbox = new BBox(dim);
         for (int i=0;i<dim;i++){
             final double b = bottom.get(i);
@@ -208,47 +145,18 @@ public final class Capsule extends AbstractGeometry{
     }
 
     @Override
-    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
-        return points.getCoordinateReferenceSystem();
-    }
-
-    @Override
-    public Geometry getBoundary() {
-        throw new UnsupportedOperationException("Not supported yet.");
+    default CoordinateReferenceSystem getCoordinateReferenceSystem() {
+        return getDataPoints().getCoordinateReferenceSystem();
     }
 
     @Override
-    public void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) 
throws IllegalArgumentException {
-        points.setCoordinateReferenceSystem(crs);
+    default void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) 
throws IllegalArgumentException {
+        getDataPoints().setCoordinateReferenceSystem(crs);
     }
 
     @Override
-    public DataPointsType getDataPointsType() {
-        return points.getType();
+    default DataPointsType getDataPointsType() {
+        return getDataPoints().getType();
     }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final Capsule other = (Capsule) obj;
-        if (!Objects.equals(this.points, other.points)) {
-            return false;
-        }
-        return Double.doubleToLongBits(this.radiusTop) == 
Double.doubleToLongBits(other.radiusTop)
-            && Double.doubleToLongBits(this.radiusBottom) == 
Double.doubleToLongBits(other.radiusBottom);
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 3;
-        hash = 89 * hash + Objects.hashCode(this.points);
-        hash = 89 * hash + Double.hashCode(this.radiusTop);
-        hash = 89 * hash + Double.hashCode(this.radiusBottom);
-        return hash;
-    }
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Cylinder.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Cylinder.java
index f7355d8c04..c36befb43b 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Cylinder.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Cylinder.java
@@ -16,19 +16,17 @@
  */
 package org.apache.sis.geometries.surface;
 
-import java.util.Objects;
+import java.util.List;
 import static org.opengis.annotation.Specification.ISO_12113;
 import org.opengis.annotation.UML;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.geometries.BBox;
-import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.DataPointsType;
-import org.apache.sis.geometries.Geometry;
-import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.GeometryType;
-import org.apache.sis.geometries.internal.shared.AbstractGeometry;
+import org.apache.sis.geometries.Surface;
+import org.apache.sis.geometries.SurfaceInterpolation;
+import org.apache.sis.geometries.internal.shared.DefaultCylinder;
 import org.apache.sis.maths.DataType;
-import org.apache.sis.maths.NDArrays;
 import org.apache.sis.maths.ReadOnly;
 import org.apache.sis.maths.Vectors;
 import org.apache.sis.maths.Vector;
@@ -43,95 +41,87 @@ import org.apache.sis.maths.Vector;
  *
  * ISO 19107 : classified as a conic surface
  *
+ * <p>Constraints:</p>
+ * <ul>
+ *   <li>The {@linkplain #getDataPoints() data points} hold exactly two 
positions:
+ *       the center of the bottom circle, then the center of the top one.</li>
+ * </ul>
+ *
  * @author Johann Sorel (Geomatys)
  * @spec ISO_19107 section 8.5.4  Cylinder
  * @spec ISO_19107 section 8.5.3  Cone
  * @spec ISO_12113 KHR_implicit_shapes extension Cylinder
  */
 @UML(identifier="Cylinder", specification=ISO_12113)
-public final class Cylinder extends AbstractGeometry{
+public sealed interface Cylinder extends Surface
+        permits DefaultCylinder
+{
 
     /**
-     * Must contain two points : the center of the bottom circle, then the 
center of the top one.
+     * Returns {@link GeometryType#CYLINDER}.
      */
-    private final DataPoints points;
-
-    private double radiusTop = 1.0;
-    private double radiusBottom = 1.0;
-
-    /**
-     * Creates a cylinder along the axis joining the two given positions.
-     * The reference system of the cylinder is the one of those positions.
-     *
-     * @param  bottom  the center of the bottom circle, not null.
-     * @param  top     the center of the top circle, not null.
-     */
-    public Cylinder(Vector<?> bottom, Vector<?> top) {
-        points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(bottom.getSampleSystem(), 
bottom.getDataType(), 2));
-        points.setPosition(0, bottom);
-        points.setPosition(1, top);
+    @Override
+    default GeometryType getGeometryType() {
+        return GeometryType.CYLINDER;
     }
 
     /**
-     * Creates a cylinder along the axis joining the two positions of the 
given {@code DataPoints}.
-     * The {@code DataPoints} is taken as-is, so the caller may give the 
cylinder the attributes
-     * carried by that {@code DataPoints}.
+     * Returns {@link SurfaceInterpolation#CONIC}: ISO 19107 classifies 
cylinders and cones as conic surfaces.
      *
-     * @param  points  the centers of the bottom and top circles, in that 
order.
-     * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly two positions.
+     * @see ISO 19107:2019 - 8.5.3, 8.5.4
      */
-    public Cylinder(DataPoints points) {
-        if (points.size() != 2) {
-            throw new IllegalArgumentException("Cylinder sequence must contain 
two points");
-        }
-        this.points = points;
-    }
-
     @Override
-    public GeometryType getGeometryType() {
-        return GeometryType.CYLINDER;
+    default List<SurfaceInterpolation> getInterpolation() {
+        return List.of(SurfaceInterpolation.CONIC);
     }
 
     /**
-     * A cylinder is a surface.
-     * @return 2
+     * Returns {@code false}: a cylinder always has its two circle centers.
      */
     @Override
-    public int getTopologicDimension() {
-        return 2;
-    }
-
-    public DataPoints getDataPoints() {
-        return points;
+    default boolean isEmpty() {
+        return false;
     }
 
     /**
      * @return the center of the bottom circle
      */
-    public ReadOnly.Vector<?> getBottom() {
-        return points.getPosition(0);
-    }
+    ReadOnly.Vector<?> getBottom();
 
     /**
      * @param position new center of the bottom circle
      */
-    public void setBottom(ReadOnly.Vector<?> position) {
-        points.setPosition(0, position);
-    }
+    void setBottom(ReadOnly.Vector<?> position);
 
     /**
      * @return the center of the top circle
      */
-    public ReadOnly.Vector<?> getTop() {
-        return points.getPosition(1);
-    }
+    ReadOnly.Vector<?> getTop();
 
     /**
      * @param position new center of the top circle
      */
-    public void setTop(ReadOnly.Vector<?> position) {
-        points.setPosition(1, position);
-    }
+    void setTop(ReadOnly.Vector<?> position);
+
+    /**
+     * @return cylinder top circle radius
+     */
+    double getRadiusTop();
+
+    /**
+     * @param radius new cylinder top radius
+     */
+    void setRadiusTop(double radius);
+
+    /**
+     * @return cylinder bottom circle radius
+     */
+    double getRadiusBottom();
+
+    /**
+     * @param radius new cylinder bottom radius
+     */
+    void setRadiusBottom(double radius);
 
     /**
      * Returns the vector going from the center of the bottom circle to the 
center of the top one.
@@ -153,50 +143,17 @@ public final class Cylinder extends AbstractGeometry{
      *
      * @return the cylinder height
      */
-    public double getHeight() {
+    default double getHeight() {
         return getAxis().length();
     }
 
-    /**
-     * @return cylinder top circle radius
-     */
-    public double getRadiusTop() {
-        return radiusTop;
-    }
-
-    /**
-     * @param radius new cylinder top radius
-     */
-    public void setRadiusTop(double radius) {
-        this.radiusTop = radius;
-    }
-
-    /**
-     * @return cylinder bottom circle radius
-     */
-    public double getRadiusBottom() {
-        return radiusBottom;
-    }
-
-    /**
-     * @param radius new cylinder bottom radius
-     */
-    public void setRadiusBottom(double radius) {
-        this.radiusBottom = radius;
-    }
-
     /**
      * A cylinder becomes a cone when the top or bottom radius is set to 0.0.
      *
      * @return true if cylinder is a cone, top or bottom radius is 0.0.
      */
-    public boolean isCone() {
-        return radiusBottom == 0.0 || radiusTop == 0.0;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return false;
+    default boolean isCone() {
+        return getRadiusBottom() == 0.0 || getRadiusTop() == 0.0;
     }
 
     /**
@@ -209,12 +166,14 @@ public final class Cylinder extends AbstractGeometry{
      * has no axis to speak of, and is then bounded by its radii on every 
axis.</p>
      */
     @Override
-    public BBox getEnvelope() {
+    default BBox getEnvelope() {
         final ReadOnly.Vector<?> bottom = getBottom();
         final ReadOnly.Vector<?> top = getTop();
+        final double radiusBottom = getRadiusBottom();
+        final double radiusTop = getRadiusTop();
         final Vector<?> axis = getAxis();
         final double height = axis.length();
-        final int dim = points.getDimension();
+        final int dim = getDataPoints().getDimension();
         final BBox bbox = new BBox(dim);
         for (int i=0;i<dim;i++){
             final double b = bottom.get(i);
@@ -233,47 +192,18 @@ public final class Cylinder extends AbstractGeometry{
     }
 
     @Override
-    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
-        return points.getCoordinateReferenceSystem();
-    }
-
-    @Override
-    public Geometry getBoundary() {
-        throw new UnsupportedOperationException("Not supported yet.");
+    default CoordinateReferenceSystem getCoordinateReferenceSystem() {
+        return getDataPoints().getCoordinateReferenceSystem();
     }
 
     @Override
-    public void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) 
throws IllegalArgumentException {
-        points.setCoordinateReferenceSystem(crs);
+    default void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) 
throws IllegalArgumentException {
+        getDataPoints().setCoordinateReferenceSystem(crs);
     }
 
     @Override
-    public DataPointsType getDataPointsType() {
-        return points.getType();
+    default DataPointsType getDataPointsType() {
+        return getDataPoints().getType();
     }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final Cylinder other = (Cylinder) obj;
-        if (!Objects.equals(this.points, other.points)) {
-            return false;
-        }
-        return Double.doubleToLongBits(this.radiusTop) == 
Double.doubleToLongBits(other.radiusTop)
-            && Double.doubleToLongBits(this.radiusBottom) == 
Double.doubleToLongBits(other.radiusBottom);
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 3;
-        hash = 89 * hash + Objects.hashCode(this.points);
-        hash = 89 * hash + Double.hashCode(this.radiusTop);
-        hash = 89 * hash + Double.hashCode(this.radiusBottom);
-        return hash;
-    }
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Ellipsoid.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Ellipsoid.java
index 2583f181c5..653f04c508 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Ellipsoid.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Ellipsoid.java
@@ -16,31 +16,18 @@
  */
 package org.apache.sis.geometries.surface;
 
-import java.util.Arrays;
 import java.util.List;
-import javax.measure.Quantity;
 import static org.opengis.annotation.Specification.ISO_19107;
 import org.opengis.annotation.UML;
-import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.geometries.BBox;
-import org.apache.sis.geometries.Curve;
-import org.apache.sis.geometries.DataPoints;
-import org.apache.sis.geometries.Geometries;
-import org.apache.sis.geometries.Geometry;
-import org.apache.sis.geometries.GeometryFactory;
+import org.apache.sis.geometries.DataPointsType;
 import org.apache.sis.geometries.GeometryType;
 import org.apache.sis.geometries.SurfaceInterpolation;
-import org.apache.sis.geometries.DataPointsType;
-import org.apache.sis.geometries.internal.shared.AbstractGeometry;
-import org.apache.sis.geometries.surface.ParametricCurveSurface;
-import org.apache.sis.maths.DataType;
-import org.apache.sis.maths.NDArrays;
+import org.apache.sis.geometries.internal.shared.DefaultEllipsoid;
 import org.apache.sis.maths.ReadOnly;
-import org.apache.sis.maths.SampleSystem;
 import org.apache.sis.maths.Vector;
-import org.apache.sis.util.ArgumentChecks;
 
 
 /**
@@ -65,155 +52,26 @@ import org.apache.sis.util.ArgumentChecks;
  *
  * @author Johann Sorel (Geomatys)
  */
-public final class Ellipsoid extends AbstractGeometry implements 
ParametricCurveSurface {
-
-    /**
-     * Must contain one point: the center of the ellipsoid.
-     */
-    private final DataPoints points;
-
-    /**
-     * Length of the semi-axis along each axis of the coordinate system,
-     * in the units of that axis. There is one value per dimension.
-     */
-    private final double[] semiAxes;
-
-    /**
-     * @param dimension number of dimensions of the ellipsoid, must be 
positive.
-     */
-    public Ellipsoid(int dimension) {
-        this(Geometries.getUndefinedCRS(dimension));
-    }
-
-    /**
-     * @param crs ellipsoid coordinate system, not null.
-     */
-    public Ellipsoid(CoordinateReferenceSystem crs) {
-        points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(SampleSystem.of(crs), 
DataType.DOUBLE, 1));
-        semiAxes = unitSemiAxes(crs.getCoordinateSystem().getDimension());
-    }
-
-    /**
-     * @param dimension number of dimensions of the ellipsoid, must be 
positive.
-     * @param semiAxes  length of the semi-axis along each axis, one value per 
dimension.
-     *                  Each of them must be positive.
-     */
-    public Ellipsoid(int dimension, double... semiAxes) {
-        this(Geometries.getUndefinedCRS(dimension), semiAxes);
-    }
-
-    /**
-     * @param crs       ellipsoid coordinate system, not null.
-     * @param semiAxes  length of the semi-axis along each axis, one value per 
dimension.
-     *                  Each of them must be positive.
-     */
-    public Ellipsoid(CoordinateReferenceSystem crs, double... semiAxes) {
-        points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(SampleSystem.of(crs), 
DataType.DOUBLE, 1));
-        this.semiAxes = 
checkedSemiAxes(crs.getCoordinateSystem().getDimension(), semiAxes);
-    }
-
-    /**
-     * Creates an ellipsoid centered on the single position of the given 
{@code DataPoints}.
-     * The {@code DataPoints} is taken as-is, so the caller may give the 
ellipsoid the attributes
-     * carried by that {@code DataPoints}.
-     *
-     * @param  points  the center of the ellipsoid.
-     * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly one position.
-     */
-    public Ellipsoid(DataPoints points) {
-        this.points = checkedPoints(points);
-        semiAxes = unitSemiAxes(points.getDimension());
-    }
-
-    /**
-     * Creates an ellipsoid of the given semi-axes centered on the single 
position of the given
-     * {@code DataPoints}.
-     *
-     * @param  points    the center of the ellipsoid.
-     * @param  semiAxes  length of the semi-axis along each axis, one value 
per dimension.
-     *                   Each of them must be positive.
-     * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly one position,
-     *         or if the number of semi-axes is not the number of dimensions.
-     */
-    public Ellipsoid(DataPoints points, double... semiAxes) {
-        this.points = checkedPoints(points);
-        this.semiAxes = checkedSemiAxes(points.getDimension(), semiAxes);
-    }
-
-    /**
-     * Returns the given {@code DataPoints} if it holds the single position an 
ellipsoid is centered on.
-     */
-    private static DataPoints checkedPoints(final DataPoints points) {
-        if (points.size() != 1) {
-            throw new IllegalArgumentException("Ellipsoid sequence must 
contain one point");
-        }
-        return points;
-    }
-
-    /**
-     * Returns the semi-axes of an ellipsoid of the given number of dimensions 
which is a unit sphere.
-     */
-    private static double[] unitSemiAxes(final int dimension) {
-        final var lengths = new double[dimension];
-        Arrays.fill(lengths, 1.0);
-        return lengths;
-    }
-
-    /**
-     * Returns a copy of the given semi-axes after verifying that there is one 
of them per dimension
-     * and that all of them are positive.
-     */
-    private static double[] checkedSemiAxes(final int dimension, final 
double[] semiAxes) {
-        ensureValidSemiAxes(dimension, semiAxes);
-        return semiAxes.clone();
-    }
+public sealed interface Ellipsoid extends ParametricCurveSurface
+        permits DefaultEllipsoid
+{
 
     /**
-     * Verifies that there is one semi-axis per dimension and that all of them 
are positive.
+     * Returns {@link GeometryType#ELLIPSOID}.
      */
-    private static void ensureValidSemiAxes(final int dimension, final 
double[] semiAxes) {
-        if (semiAxes.length != dimension) {
-            throw new IllegalArgumentException("Ellipsoid must have one 
semi-axis per dimension, expected "
-                    + dimension + " but found " + semiAxes.length);
-        }
-        for (int i=0; i<semiAxes.length; i++) {
-            ArgumentChecks.ensurePositive("semiAxes[" + i + ']', semiAxes[i]);
-        }
-    }
-
     @Override
-    public GeometryType getGeometryType() {
+    default GeometryType getGeometryType() {
         return GeometryType.ELLIPSOID;
     }
 
+    /**
+     * Returns {@code false}: an ellipsoid always has a center.
+     */
     @Override
-    public boolean isEmpty() {
+    default boolean isEmpty() {
         return false;
     }
 
-    @Override
-    public Quantity<?> getArea() {
-        throw new UnsupportedOperationException("Not supported.");
-    }
-
-    @Override
-    public void setCoordinateReferenceSystem(CoordinateReferenceSystem cs) 
throws IllegalArgumentException {
-        if (cs.getCoordinateSystem().getDimension() != 
getCoordinateReferenceSystem().getCoordinateSystem().getDimension()) {
-            throw new IllegalArgumentException("New CRS dimension must be the 
same as previous CRS");
-        }
-        points.setCoordinateReferenceSystem(cs);
-    }
-
-    @Override
-    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
-        return points.getCoordinateReferenceSystem();
-    }
-
-    @Override
-    public DataPointsType getDataPointsType() {
-        return points.getType();
-    }
-
     /**
      * Returns {@link SurfaceInterpolation#ELLIPTICAL}: this surface is a 
section of an ellipsoid.
      *
@@ -221,7 +79,7 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
      */
     @UML(identifier="interpolation", specification=ISO_19107)
     @Override
-    public List<SurfaceInterpolation> getInterpolation() {
+    default List<SurfaceInterpolation> getInterpolation() {
         return List.of(SurfaceInterpolation.ELLIPTICAL);
     }
 
@@ -232,51 +90,55 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
      *
      * @return length of the semi-axis along each axis.
      */
-    public double[] getSemiAxes() {
-        return semiAxes.clone();
-    }
+    double[] getSemiAxes();
 
     /**
      * @param  dimension  index of the axis along which to measure the 
semi-axis.
      * @return length of the semi-axis along the given axis.
      */
-    public double getSemiAxis(int dimension) {
-        return semiAxes[dimension];
-    }
+    double getSemiAxis(int dimension);
 
     /**
      * @param semiAxes new semi-axis lengths, one per dimension. Each of them 
must be positive.
      * @throws IllegalArgumentException if the number of semi-axes is not the 
number of dimensions.
      */
-    public void setSemiAxes(double... semiAxes) {
-        ensureValidSemiAxes(this.semiAxes.length, semiAxes);
-        System.arraycopy(semiAxes, 0, this.semiAxes, 0, this.semiAxes.length);
-    }
+    void setSemiAxes(double... semiAxes);
 
     /**
      * @param dimension index of the axis along which to set the semi-axis.
      * @param length    new semi-axis length, must be positive.
      */
-    public void setSemiAxis(int dimension, double length) {
-        ArgumentChecks.ensurePositive("length", length);
-        semiAxes[dimension] = length;
-    }
+    void setSemiAxis(int dimension, double length);
 
     /**
-     * Returns a copy of the ellipsoid center. Writing in the returned tuple 
does not move this
-     * ellipsoid; use {@link #setCenter(Tuple)} for that.
+     * Returns the ellipsoid center. Writing in the returned tuple does not 
move this
+     * ellipsoid; use {@link #setCenter(ReadOnly.Vector)} for that.
      *
      * @return ellipsoid center.
      */
-    public ReadOnly.Vector<?> getCenter() {
-        return points.getPosition(0);
-    }
+    ReadOnly.Vector<?> getCenter();
 
     /**
      * @param position new center of the ellipsoid
      */
-    public void setCenter(ReadOnly.Vector<?> position) {
-        points.setPosition(0, position);
+    void setCenter(ReadOnly.Vector<?> position);
+
+    @Override
+    default CoordinateReferenceSystem getCoordinateReferenceSystem() {
+        return getDataPoints().getCoordinateReferenceSystem();
+    }
+
+    @Override
+    default void setCoordinateReferenceSystem(CoordinateReferenceSystem cs) 
throws IllegalArgumentException {
+        if (cs.getCoordinateSystem().getDimension() != 
getCoordinateReferenceSystem().getCoordinateSystem().getDimension()) {
+            throw new IllegalArgumentException("New CRS dimension must be the 
same as previous CRS");
+        }
+        getDataPoints().setCoordinateReferenceSystem(cs);
+    }
+
+    @Override
+    default DataPointsType getDataPointsType() {
+        return getDataPoints().getType();
     }
 
     /**
@@ -286,13 +148,13 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
      * containing it is the box of that extent around the center.</p>
      */
     @Override
-    public Envelope getEnvelope() {
+    default Envelope getEnvelope() {
         final Vector<?> center = getCenter().copy();
         final BBox env = new BBox(center, center);
         env.setCoordinateReferenceSystem(getCoordinateReferenceSystem());
         for (int i = 0, n = getDimension(); i < n; i++) {
             final double c = center.get(i);
-            final double a = semiAxes[i];
+            final double a = getSemiAxis(i);
             if (a > 0) {
                 env.setRange(i, c-a, c+a);
             }
@@ -300,65 +162,4 @@ public final class Ellipsoid extends AbstractGeometry 
implements ParametricCurve
         return env;
     }
 
-    @Override
-    public Geometry getBoundary() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    // methods from ParametricCurveSurface
-
-    @Override
-    public int getRows() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public int getColumns() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public List<DirectPosition> getControlPoints() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public GeometryType getHorizontalCurveType() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public GeometryType getVerticalCurveType() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public List<double[]> getKnots() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public Curve getHorizontalCurve(double v) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public Curve getVerticalCurve(double u) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public DirectPosition getSurface(double u, double v) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    /**
-     * Returns the center of this ellipsoid, as a {@code DataPoints} of one 
position. An ellipsoid is defined
-     * by that position and by its semi-axes, so the center is the only 
position it holds.
-     */
-    @Override
-    public DataPoints getDataPoints() {
-        return points;
-    }
-
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Sphere.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Sphere.java
index 81ecd4f55c..144d46c9c7 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Sphere.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Sphere.java
@@ -17,29 +17,17 @@
 package org.apache.sis.geometries.surface;
 
 import java.util.List;
-import javax.measure.Quantity;
 import static org.opengis.annotation.Specification.ISO_19107;
 import org.opengis.annotation.UML;
-import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.geometries.BBox;
-import org.apache.sis.geometries.Curve;
-import org.apache.sis.geometries.DataPoints;
-import org.apache.sis.geometries.Geometries;
-import org.apache.sis.geometries.Geometry;
-import org.apache.sis.geometries.GeometryFactory;
+import org.apache.sis.geometries.DataPointsType;
 import org.apache.sis.geometries.GeometryType;
 import org.apache.sis.geometries.SurfaceInterpolation;
-import org.apache.sis.geometries.DataPointsType;
-import org.apache.sis.geometries.internal.shared.AbstractGeometry;
-import org.apache.sis.geometries.surface.ParametricCurveSurface;
-import org.apache.sis.maths.DataType;
-import org.apache.sis.maths.NDArrays;
+import org.apache.sis.geometries.internal.shared.DefaultSphere;
 import org.apache.sis.maths.ReadOnly;
-import org.apache.sis.maths.SampleSystem;
 import org.apache.sis.maths.Vector;
-import org.apache.sis.util.ArgumentChecks;
 
 
 /**
@@ -58,7 +46,7 @@ import org.apache.sis.util.ArgumentChecks;
  * </ul>
  *
  * <p>Difference with ISO 19107, which defines a sphere in a 3-dimensional 
space: even if it is
- * called a Sphere this class can handle 2 to N dimensions.</p>
+ * called a Sphere this interface can handle 2 to N dimensions.</p>
  *
  * <p>Note: ISO 19107 classifies this geometry as a conic surface. It should 
not be confused with
  * the sphere used as a geometric reference surface (ISO 19107:2019 - 
6.2.3.4).</p>
@@ -68,106 +56,26 @@ import org.apache.sis.util.ArgumentChecks;
  * @see ISO 19107:2019 - 8.5.1, 8.5.2
  */
 @UML(identifier="Sphere", specification=ISO_19107)
-public final class Sphere extends AbstractGeometry implements 
ParametricCurveSurface {
+public sealed interface Sphere extends ParametricCurveSurface
+        permits DefaultSphere
+{
 
     /**
-     * Must contain one point: the center of the sphere.
+     * Returns {@link GeometryType#SPHERE}.
      */
-    private final DataPoints points;
-
-    private double radius = 1.0;
-
-    /**
-     * @param dimension number of dimensions of the sphere, must be positive.
-     */
-    public Sphere(int dimension) {
-        this(Geometries.getUndefinedCRS(dimension));
-    }
-
-    /**
-     * @param crs sphere coordinate system, not null.
-     */
-    public Sphere(CoordinateReferenceSystem crs) {
-        points = 
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(SampleSystem.of(crs), 
DataType.DOUBLE, 1));
-    }
-
-    /**
-     * @param dimension number of dimensions of the sphere, must be positive.
-     * @param radius radius new sphere radius, must be positive.
-     */
-    public Sphere(int dimension, double radius) {
-        this(Geometries.getUndefinedCRS(dimension), radius);
-    }
-
-    /**
-     * @param crs sphere coordinate system, not null.
-     * @param radius radius new sphere radius, must be positive.
-     */
-    public Sphere(CoordinateReferenceSystem crs, double radius) {
-        this(crs);
-        this.radius = radius;
-    }
-
-    /**
-     * Creates a sphere centered on the single position of the given {@code 
DataPoints}.
-     * The {@code DataPoints} is taken as-is, so the caller may give the 
sphere the attributes
-     * carried by that {@code DataPoints}.
-     *
-     * @param  points  the center of the sphere.
-     * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly one position.
-     */
-    public Sphere(DataPoints points) {
-        if (points.size() != 1) {
-            throw new IllegalArgumentException("Sphere sequence must contain 
one point");
-        }
-        this.points = points;
-    }
-
-    /**
-     * Creates a sphere of the given radius centered on the single position of 
the given {@code DataPoints}.
-     *
-     * @param  points  the center of the sphere.
-     * @param  radius  new sphere radius, must be positive.
-     * @throws IllegalArgumentException if the given {@code DataPoints} does 
not hold exactly one position.
-     */
-    public Sphere(DataPoints points, double radius) {
-        this(points);
-        this.radius = radius;
-    }
-
     @Override
-    public GeometryType getGeometryType() {
+    default GeometryType getGeometryType() {
         return GeometryType.SPHERE;
     }
 
+    /**
+     * Returns {@code false}: a sphere always has a center.
+     */
     @Override
-    public boolean isEmpty() {
+    default boolean isEmpty() {
         return false;
     }
 
-    @Override
-    public Quantity<?> getArea() {
-        throw new UnsupportedOperationException("Not supported.");
-    }
-
-    @Override
-    public void setCoordinateReferenceSystem(CoordinateReferenceSystem cs) 
throws IllegalArgumentException {
-        if (cs.getCoordinateSystem().getDimension() != 
getCoordinateReferenceSystem().getCoordinateSystem().getDimension()) {
-            throw new IllegalArgumentException("New CRS dimension must be the 
same as previous CRS");
-        }
-        points.setCoordinateReferenceSystem(cs);
-    }
-
-    @Override
-    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
-        return points.getCoordinateReferenceSystem();
-    }
-
-    @Override
-    public DataPointsType getDataPointsType() {
-        return points.getType();
-    }
-
     /**
      * Returns {@link SurfaceInterpolation#SPHERICAL}: this surface is a 
section of a sphere.
      *
@@ -175,48 +83,58 @@ public final class Sphere extends AbstractGeometry 
implements ParametricCurveSur
      */
     @UML(identifier="interpolation", specification=ISO_19107)
     @Override
-    public List<SurfaceInterpolation> getInterpolation() {
+    default List<SurfaceInterpolation> getInterpolation() {
         return List.of(SurfaceInterpolation.SPHERICAL);
     }
 
     /**
      * @return radius of the sphere.
      */
-    public double getRadius() {
-        return radius;
-    }
+    double getRadius();
 
     /**
      * @param radius new sphere radius, must be positive.
      */
-    public void setRadius(double radius) {
-        ArgumentChecks.ensurePositive("radius", radius);
-        this.radius = radius;
-    }
+    void setRadius(double radius);
 
     /**
-     * Returns a copy of the sphere center. Writing in the returned tuple does 
not move this
-     * sphere; use {@link #setCenter(Tuple)} for that.
+     * Returns the sphere center. Writing in the returned tuple does not move 
this
+     * sphere; use {@link #setCenter(ReadOnly.Vector)} for that.
      *
      * @return sphere center.
      */
-    public ReadOnly.Vector<?> getCenter() {
-        return points.getPosition(0);
-    }
+    ReadOnly.Vector<?> getCenter();
 
     /**
      * @param position new center of the sphere
      */
-    public void setCenter(ReadOnly.Vector<?> position) {
-        points.setPosition(0, position);
+    void setCenter(ReadOnly.Vector<?> position);
+
+    @Override
+    default CoordinateReferenceSystem getCoordinateReferenceSystem() {
+        return getDataPoints().getCoordinateReferenceSystem();
+    }
+
+    @Override
+    default void setCoordinateReferenceSystem(CoordinateReferenceSystem cs) 
throws IllegalArgumentException {
+        if (cs.getCoordinateSystem().getDimension() != 
getCoordinateReferenceSystem().getCoordinateSystem().getDimension()) {
+            throw new IllegalArgumentException("New CRS dimension must be the 
same as previous CRS");
+        }
+        getDataPoints().setCoordinateReferenceSystem(cs);
+    }
+
+    @Override
+    default DataPointsType getDataPointsType() {
+        return getDataPoints().getType();
     }
 
     /**
      * {@inheritDoc }
      */
     @Override
-    public Envelope getEnvelope() {
+    default Envelope getEnvelope() {
         final Vector<?> center = getCenter().copy();
+        final double radius = getRadius();
         final BBox env = new BBox(center, center);
         env.setCoordinateReferenceSystem(getCoordinateReferenceSystem());
         if (radius > 0) {
@@ -228,65 +146,4 @@ public final class Sphere extends AbstractGeometry 
implements ParametricCurveSur
         return env;
     }
 
-    @Override
-    public Geometry getBoundary() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    // methods from ParametricCurveSurface
-
-    @Override
-    public int getRows() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public int getColumns() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public List<DirectPosition> getControlPoints() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public GeometryType getHorizontalCurveType() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public GeometryType getVerticalCurveType() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public List<double[]> getKnots() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public Curve getHorizontalCurve(double v) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public Curve getVerticalCurve(double u) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public DirectPosition getSurface(double u, double v) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    /**
-     * Returns the center of this sphere, as a {@code DataPoints} of one 
position. A sphere is defined by
-     * that position and by its radius, so the center is the only position it 
holds.
-     */
-    @Override
-    public DataPoints getDataPoints() {
-        return points;
-    }
-
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/GreatCircleArcTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/GreatCircleArcTest.java
index 497fc6604f..8c6060b351 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/GreatCircleArcTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/GreatCircleArcTest.java
@@ -16,7 +16,7 @@
  */
 package org.apache.sis.geometries.spherical;
 
-import org.apache.sis.geometries.surface.Sphere;
+import org.apache.sis.geometries.internal.shared.DefaultSphere;
 import org.apache.sis.maths.ReadOnly;
 import org.apache.sis.maths.Vector3D;
 
@@ -44,18 +44,18 @@ public class GreatCircleArcTest {
      */
     @Test
     public void getLengthTest() {
-        final GreatCircleArc arc = new GreatCircleArc(new Sphere(3), a, b);
+        final GreatCircleArc arc = new GreatCircleArc(new DefaultSphere(3), a, 
b);
         assertEquals(Math.PI / 2, arc.getLength(), TOLERANCE);
 
-        final GreatCircleArc biggerArc = new GreatCircleArc(new Sphere(3, 
2.0), a, b);
+        final GreatCircleArc biggerArc = new GreatCircleArc(new 
DefaultSphere(3, 2.0), a, b);
         assertEquals(Math.PI, biggerArc.getLength(), TOLERANCE);
 
         // arc between antipodal points spans half a great circle
-        final GreatCircleArc halfArc = new GreatCircleArc(new Sphere(3), a, 
new Vector3D.Double(-1, 0, 0));
+        final GreatCircleArc halfArc = new GreatCircleArc(new 
DefaultSphere(3), a, new Vector3D.Double(-1, 0, 0));
         assertEquals(Math.PI, halfArc.getLength(), TOLERANCE);
 
         // arc between a point and itself has no length
-        final GreatCircleArc emptyArc = new GreatCircleArc(new Sphere(3), a, 
a);
+        final GreatCircleArc emptyArc = new GreatCircleArc(new 
DefaultSphere(3), a, a);
         assertEquals(0, emptyArc.getLength(), TOLERANCE);
     }
 
@@ -65,7 +65,7 @@ public class GreatCircleArcTest {
      */
     @Test
     public void pointAtTest() {
-        final GreatCircleArc arc = new GreatCircleArc(new Sphere(3), a, b);
+        final GreatCircleArc arc = new GreatCircleArc(new DefaultSphere(3), a, 
b);
 
         assertArrayEquals(a.toArrayDouble(), arc.pointAt(0).toArrayDouble(), 
TOLERANCE);
         assertArrayEquals(b.toArrayDouble(), arc.pointAt(1).toArrayDouble(), 
TOLERANCE);
@@ -98,12 +98,12 @@ public class GreatCircleArcTest {
      */
     @Test
     public void pointAtDegenerateTest() {
-        final GreatCircleArc coincident = new GreatCircleArc(new Sphere(3), a, 
a);
+        final GreatCircleArc coincident = new GreatCircleArc(new 
DefaultSphere(3), a, a);
         assertArrayEquals(a.toArrayDouble(), 
coincident.pointAt(0.0).toArrayDouble(), TOLERANCE);
         assertArrayEquals(a.toArrayDouble(), 
coincident.pointAt(0.5).toArrayDouble(), TOLERANCE);
         assertArrayEquals(a.toArrayDouble(), 
coincident.pointAt(1.0).toArrayDouble(), TOLERANCE);
 
-        final GreatCircleArc antipodal = new GreatCircleArc(new Sphere(3), a, 
new Vector3D.Double(-1, 0, 0));
+        final GreatCircleArc antipodal = new GreatCircleArc(new 
DefaultSphere(3), a, new Vector3D.Double(-1, 0, 0));
         //the ends themselves are still well defined
         assertArrayEquals(a.toArrayDouble(), 
antipodal.pointAt(0.0).toArrayDouble(), TOLERANCE);
         assertThrows(IllegalArgumentException.class, () -> 
antipodal.pointAt(0.5));
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/SphericalConvexPolygonTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/SphericalConvexPolygonTest.java
index fd98150822..06a77a4696 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/SphericalConvexPolygonTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/SphericalConvexPolygonTest.java
@@ -17,7 +17,7 @@
 package org.apache.sis.geometries.spherical;
 
 import java.util.List;
-import org.apache.sis.geometries.surface.Sphere;
+import org.apache.sis.geometries.internal.shared.DefaultSphere;
 import org.apache.sis.maths.Array;
 import org.apache.sis.maths.DataType;
 import org.apache.sis.maths.NDArrays;
@@ -45,7 +45,7 @@ public class SphericalConvexPolygonTest {
     private final Vector3D.Double d = new Vector3D.Double(0, -1, 
1).normalize();
 
     private final Array points = NDArrays.of(List.of(a, b, c, d, a), 3, 
DataType.DOUBLE);
-    private final SphericalConvexPolygon polygon = new 
SphericalConvexPolygon(new Sphere(3), points);
+    private final SphericalConvexPolygon polygon = new 
SphericalConvexPolygon(new DefaultSphere(3), points);
 
     /**
      * Corner points lie on the polygon boundary and must be considered 
contained.
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/SphericalTriangleTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/SphericalTriangleTest.java
index 8ed5495a34..291ed55021 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/SphericalTriangleTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/spherical/SphericalTriangleTest.java
@@ -16,7 +16,7 @@
  */
 package org.apache.sis.geometries.spherical;
 
-import org.apache.sis.geometries.surface.Sphere;
+import org.apache.sis.geometries.internal.shared.DefaultSphere;
 import org.apache.sis.maths.ReadOnly;
 import org.apache.sis.maths.Vector3D;
 
@@ -47,7 +47,7 @@ public class SphericalTriangleTest {
      */
     @Test
     public void constructorTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         assertSame(a, triangle.getA());
         assertSame(b, triangle.getB());
         assertSame(c, triangle.getC());
@@ -58,7 +58,7 @@ public class SphericalTriangleTest {
      */
     @Test
     public void getCentroidTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         final ReadOnly.Vector<?> centroid = triangle.getCentroidVector();
         final double v = 1.0 / Math.sqrt(3.0);
         assertArrayEquals(new double[]{v, v, v}, centroid.toArrayDouble(), 
TOLERANCE);
@@ -71,7 +71,7 @@ public class SphericalTriangleTest {
      */
     @Test
     public void getSphericalExcessTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         assertEquals(Math.PI / 2, triangle.getSphericalExcess(), TOLERANCE);
     }
 
@@ -82,10 +82,10 @@ public class SphericalTriangleTest {
      */
     @Test
     public void getAreaTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         assertEquals(Math.PI / 2, triangle.getArea(), TOLERANCE);
 
-        final SphericalTriangle biggerTriangle = new SphericalTriangle(new 
Sphere(3, 2), a, b, c);
+        final SphericalTriangle biggerTriangle = new SphericalTriangle(new 
DefaultSphere(3, 2), a, b, c);
         assertEquals(Math.PI / 2 * 4, biggerTriangle.getArea(), TOLERANCE);
     }
 
@@ -94,7 +94,7 @@ public class SphericalTriangleTest {
      */
     @Test
     public void containsTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         assertTrue(triangle.contains(triangle.getA()));
         assertTrue(triangle.contains(triangle.getB()));
         assertTrue(triangle.contains(triangle.getC()));
@@ -109,7 +109,7 @@ public class SphericalTriangleTest {
      */
     @Test
     public void quadSubdivideTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         final SphericalTriangle[] children = triangle.quadSubdivide();
         assertEquals(4, children.length);
 
@@ -147,7 +147,7 @@ public class SphericalTriangleTest {
      */
     @Test
     public void subdivideByOneTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         final SphericalTriangle[] children = triangle.subdivide(1);
         assertEquals(1, children.length);
         assertSame(triangle, children[0]);
@@ -160,7 +160,7 @@ public class SphericalTriangleTest {
      */
     @Test
     public void subdivideTilesTheParentTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         for (final int n : new int[] {2, 3, 4, 5}) {
             final SphericalTriangle[] children = triangle.subdivide(n);
             assertEquals(n*n, children.length, "Subdivision by " + n);
@@ -188,7 +188,7 @@ public class SphericalTriangleTest {
      */
     @Test
     public void subdivideKeepsParentCornersTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         final SphericalTriangle[] children = triangle.subdivide(3);
         assertEquals(9, children.length);
         /*
@@ -206,7 +206,7 @@ public class SphericalTriangleTest {
      */
     @Test
     public void subdivideInvalidTest() {
-        final SphericalTriangle triangle = new SphericalTriangle(new 
Sphere(3), a, b, c);
+        final SphericalTriangle triangle = new SphericalTriangle(new 
DefaultSphere(3), a, b, c);
         assertThrows(IllegalArgumentException.class, () -> 
triangle.subdivide(0));
         assertThrows(IllegalArgumentException.class, () -> 
triangle.subdivide(-1));
     }
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/surface/ParametricCurveSurfaceTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/surface/ParametricCurveSurfaceTest.java
index 81c8bb7621..7eac9813be 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/surface/ParametricCurveSurfaceTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/surface/ParametricCurveSurfaceTest.java
@@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.apache.sis.geometries.GeometryTest;
+import org.apache.sis.geometries.internal.shared.DefaultSphere;
 
 
 /**
@@ -86,7 +87,7 @@ public class ParametricCurveSurfaceTest extends GeometryTest {
 
     @Test
     public void testGetTopologicDimension() {
-        final ParametricCurveSurface sphere = new Sphere(3);
+        final ParametricCurveSurface sphere = new DefaultSphere(3);
         assertEquals(2, sphere.getTopologicDimension());
     }
 

Reply via email to