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 bb9e3560ebb5cfb695219dca73f8762629bd2aa9
Author: jsorel <[email protected]>
AuthorDate: Wed Oct 14 14:46:55 2026 +0200

    feat(Geometry): implements getDimension(coord) on several geometry types
---
 .../main/org/apache/sis/geometries/Geometry.java   | 10 ++++--
 .../apache/sis/geometries/GeometryCollection.java  | 40 ++++++++++++++++++++++
 .../org/apache/sis/geometries/curve/NurbCurve.java | 16 ++++++++-
 .../apache/sis/geometries/curve/ProductCurve.java  | 10 ++++++
 .../internal/shared/DefaultCompoundCurve.java      | 34 ++++++++++++++++--
 .../internal/shared/DefaultGeometryCollection.java |  2 +-
 .../internal/shared/DefaultPolyhedralSurface.java  |  2 +-
 7 files changed, 106 insertions(+), 8 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometry.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometry.java
index 5d5717c340..b90ce9e620 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometry.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometry.java
@@ -215,7 +215,12 @@ public sealed interface Geometry
              */
             return getTopologicDimension();
         }
-        throw new UnsupportedOperationException();
+        /*
+         * A geometry which is not an aggregate is homogeneous: it has the 
same dimension at every
+         * interior position. Aggregates, which may mix components of 
different dimensions,
+         * override this method.
+         */
+        return getTopologicDimension();
     }
 
     /**
@@ -525,8 +530,7 @@ public sealed interface Geometry
      */
     @UML(identifier="isCycle", specification=ISO_19107)
     default boolean isCycle() {
-        //TODO remove this method default when all classes implement it.
-        throw new UnsupportedOperationException();
+        return getBoundary().isEmpty();
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryCollection.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryCollection.java
index 7da33ab2e7..8e3ddf3a56 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryCollection.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryCollection.java
@@ -19,6 +19,7 @@ package org.apache.sis.geometries;
 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.apache.sis.geometries.curve.ProductCurve;
 import org.apache.sis.geometries.internal.shared.ConcatenatedDataPoints;
@@ -143,6 +144,45 @@ public sealed interface GeometryCollection<T extends 
Geometry> extends Geometry
         return getNumGeometries() == 0;
     }
 
+    /**
+     * Returns the largest topological dimension among the elements of this 
collection which contain
+     * the given position, or −1 if none of them contains it. If the given 
position is null, returns
+     * the {@linkplain #getTopologicDimension() largest dimension} among all 
elements.
+     *
+     * <p>Containment is tested with {@link Geometry#intersects(Geometry)}, 
which may be unsupported
+     * for some element types.</p>
+     *
+     * @param  point  position where to evaluate the dimension, or null for 
the whole collection.
+     * @return topological dimension at the given position.
+     *
+     * @see ISO 19107:2019 - 6.4.4.25
+     */
+    @Override
+    default int getDimension(DirectPosition point) {
+        if (point == null) {
+            return getTopologicDimension();
+        }
+        Point position = null;
+        int dimension = -1;
+        for (int i = 0, n = getNumGeometries(); i < n; i++) {
+            final Geometry element = getGeometryN(i);
+            if (element.getTopologicDimension() <= dimension) {
+                continue;       // Cannot increase the result, no need to test 
containment.
+            }
+            if (element instanceof GeometryCollection<?> aggregate) {
+                dimension = Math.max(dimension, aggregate.getDimension(point));
+            } else {
+                if (position == null) {
+                    position = 
GeometryFactory.DEFAULT.createPoint(getCoordinateReferenceSystem(), 
point.getCoordinates());
+                }
+                if (element.intersects(position)) {
+                    dimension = element.getTopologicDimension();
+                }
+            }
+        }
+        return dimension;
+    }
+
     /**
      * Returns the largest topological dimension among the elements of this 
collection,
      * or −1 if this collection is empty.
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/NurbCurve.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/NurbCurve.java
index 076e18414b..45b1017c0e 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/NurbCurve.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/NurbCurve.java
@@ -126,9 +126,23 @@ public sealed interface NurbCurve extends BSplineCurve
         return true;
     }
 
+    /**
+     * Returns the number of non-empty knot spans in the domain of this curve, 
each of them being a
+     * polynomial arc. The domain extends from the knot at index 
<var>degree</var> to the knot at index
+     * <var>n</var>, where <var>n</var> is the number of control points. The 
knots are repeated
+     * according to their multiplicity, so repeated knots delimit empty spans 
which are not counted.
+     */
     @Override
     default Integer getNumArc() {
-        throw new UnsupportedOperationException("Not supported yet.");
+        final double[] knots = getKnots();
+        final int degree = getDegree();
+        int count = 0;
+        for (int i = degree, n = knots.length - degree - 1; i < n; i++) {
+            if (knots[i + 1] > knots[i]) {
+                count++;
+            }
+        }
+        return count;
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/ProductCurve.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/ProductCurve.java
index 66df4564a1..f4d2f43272 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/ProductCurve.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/ProductCurve.java
@@ -20,6 +20,7 @@ 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.apache.sis.geometries.Curve;
 import org.apache.sis.geometries.CurveInterpolation;
 import org.apache.sis.geometries.DataPoints;
@@ -156,5 +157,14 @@ public sealed interface ProductCurve extends Curve, 
GeometryCollection<Curve>
         return Curve.super.isClosed();
     }
 
+    /**
+     * Returns 1 whatever the position: a product curve is a curve, which has 
the same
+     * dimension at every interior position, even if it is also a collection 
of curves.
+     */
+    @Override
+    default int getDimension(DirectPosition point) {
+        return Curve.super.getDimension(point);
+    }
+
 
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCompoundCurve.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCompoundCurve.java
index 6b55998f98..facd052bdb 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCompoundCurve.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCompoundCurve.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.geometries.internal.shared;
 
+import java.util.ArrayList;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.geometries.Curve;
@@ -23,6 +24,8 @@ import org.apache.sis.geometries.DataPointsType;
 import org.apache.sis.geometries.curve.CompoundCurve;
 import org.apache.sis.geometries.Geometries;
 import org.apache.sis.maths.Array;
+import org.apache.sis.maths.DataType;
+import org.apache.sis.maths.NDArrays;
 import org.apache.sis.util.ArgumentChecks;
 
 
@@ -37,7 +40,7 @@ public non-sealed class DefaultCompoundCurve extends 
AbstractGeometry implements
 
     /**
      * The coordinate reference system to report when this curve has no 
component.
-     * Ignored otherwise, in which case the first component is authoritative. 
May be {@code null}.
+     * Ignored otherwise, in which case the first component is authoritative. 
Never null.
      */
     private CoordinateReferenceSystem fallbackCRS;
 
@@ -98,9 +101,36 @@ public non-sealed class DefaultCompoundCurve extends 
AbstractGeometry implements
         return envUnion(curves);
     }
 
+    /**
+     * Returns the control points of the components, in the order of the 
components.
+     * A component without control points, such as a line string, contributes 
none.
+     *
+     * @return control points of the components, or {@code null} if none of 
them has control points.
+     */
     @Override
     public Array getControlPoints() {
-        throw new UnsupportedOperationException("Not supported yet.");
+        final var arrays = new ArrayList<Array>(curves.length);
+        DataType dataType = null;
+        boolean mixed = false;
+        for (final Curve curve : curves) {
+            final Array array = curve.getControlPoints();
+            if (array != null && array.getLength() != 0) {
+                arrays.add(array);
+                if (dataType == null) {
+                    dataType = array.getDataType();
+                } else {
+                    mixed |= (dataType != array.getDataType());
+                }
+            }
+        }
+        switch (arrays.size()) {
+            case 0: return null;
+            case 1: return arrays.get(0);
+        }
+        if (mixed) {
+            arrays.replaceAll((array) -> array.retype(DataType.DOUBLE));
+        }
+        return NDArrays.concatenate(arrays.toArray(Array[]::new));
     }
 
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultGeometryCollection.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultGeometryCollection.java
index 1fbe40671a..79f35bb6bf 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultGeometryCollection.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultGeometryCollection.java
@@ -38,7 +38,7 @@ public non-sealed class DefaultGeometryCollection<T extends 
Geometry> extends Ab
     /**
      * The coordinate reference system to report when this collection is empty.
      * Ignored when at least one geometry is present, in which case the first
-     * geometry is authoritative. May be {@code null}.
+     * geometry is authoritative. Never null.
      */
     private CoordinateReferenceSystem fallbackCRS;
 
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolyhedralSurface.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolyhedralSurface.java
index 2834bae517..f28773790e 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolyhedralSurface.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolyhedralSurface.java
@@ -38,7 +38,7 @@ public non-sealed class DefaultPolyhedralSurface<T extends 
Polygon> extends Abst
 
     /**
      * The coordinate reference system to report when this surface has no 
patch.
-     * Ignored otherwise, in which case the first patch is authoritative. May 
be {@code null}.
+     * Ignored otherwise, in which case the first patch is authoritative. 
Never null.
      */
     private CoordinateReferenceSystem fallbackCRS;
 

Reply via email to