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 d301c9395865cab5cdc8d000c905c6a9128850e0 Author: jsorel <[email protected]> AuthorDate: Wed Oct 14 11:36:19 2026 +0200 feat(Geometry): implement surface perimeter and area on several classes --- .../main/org/apache/sis/geometries/Surface.java | 14 ++++++-- .../internal/shared/DefaultNurbSurface.java | 5 ++- .../internal/shared/DefaultPolyhedron.java | 10 +++++- .../geometries/internal/shared/DefaultSolid.java | 5 ++- .../geometries/internal/shared/DefaultSphere.java | 6 ---- .../solid/polyhedron/AbstractPolyhedron.java | 40 ++++++++++++++++++---- .../org/apache/sis/geometries/surface/Sphere.java | 26 ++++++++++++++ .../main/org/apache/sis/maths/ArrayFactory.java | 2 +- 8 files changed, 90 insertions(+), 18 deletions(-) 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 50cbba7829..00f3850044 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 @@ -29,6 +29,7 @@ import org.apache.sis.geometries.surface.ParametricCurveSurface; import org.apache.sis.geometries.surface.Polygon; import org.apache.sis.geometries.surface.PolyhedralSurface; import org.apache.sis.maths.Vector; +import org.apache.sis.measure.Quantities; /** @@ -234,8 +235,17 @@ public sealed interface Surface extends Orientable */ @UML(identifier="perimeter", specification=ISO_19107) default Quantity<?> getPerimeter() { - //TODO - throw new UnsupportedOperationException(); + final Geometry boundary = getBoundary(); + if (boundary instanceof Curve c) { + return c.getLength(); + } + if (boundary instanceof GeometryCollection<?> c && c.getNumGeometries() != 0) { + return c.getLength(); + } + if (boundary.isEmpty()) { + return Quantities.create(0, Geometries.getLinearUnit(this)); + } + throw new UnsupportedOperationException("Unsupported boundary type: " + boundary.getGeometryType()); } /** diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbSurface.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbSurface.java index 85a86dd6ad..e541aad4bb 100644 --- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbSurface.java +++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbSurface.java @@ -399,9 +399,12 @@ public non-sealed class DefaultNurbSurface extends AbstractGeometry implements N throw new UnsupportedOperationException("Not supported yet."); } + /** + * Returns the position of this surface at the given parameters, computed by {@link #evaluate(double, double)}. + */ @Override public DirectPosition getSurface(double u, double v) { - throw new UnsupportedOperationException("Not supported yet."); + return Vectors.asDirectPostion(evaluate(u, v)); } } diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolyhedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolyhedron.java index dfc2e9da58..e850cfe4b9 100644 --- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolyhedron.java +++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolyhedron.java @@ -21,6 +21,7 @@ import java.util.Objects; import javax.measure.Quantity; import org.opengis.geometry.Envelope; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.apache.sis.geometries.Geometries; import org.apache.sis.geometries.GeometryCollection; import org.apache.sis.geometries.Surface; import org.apache.sis.geometries.solid.Polyhedron; @@ -88,9 +89,16 @@ public non-sealed class DefaultPolyhedron extends AbstractGeometry implements Po throw new UnsupportedOperationException("Not supported yet."); } + /** + * Returns the sum of the areas of the polygons of the exterior and interior shells. + */ @Override public Quantity<?> getArea() { - throw new UnsupportedOperationException("Not supported yet."); + Quantity<?> area = exterior.getArea(); + for (final GeometryCollection<Polygon> interior : interiors) { + area = Geometries.add(area, interior.getArea()); + } + return area; } @Override diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSolid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSolid.java index 7c6c4acebc..4c8b444488 100644 --- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSolid.java +++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSolid.java @@ -109,9 +109,12 @@ public non-sealed class DefaultSolid extends AbstractGeometry implements Solid { return GeometryFactory.DEFAULT.createGeometryCollection(GeometryType.SURFACE, getCoordinateReferenceSystem(), boundaries.toArray(Surface[]::new)); } + /** + * Returns the sum of the areas of the boundary surfaces. + */ @Override public Quantity<?> getArea() { - throw new UnsupportedOperationException("Not supported yet."); + return getBoundary().getArea(); } @Override diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSphere.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSphere.java index 3d298d699d..90a9c7d109 100644 --- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSphere.java +++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultSphere.java @@ -17,7 +17,6 @@ package org.apache.sis.geometries.internal.shared; import java.util.List; -import javax.measure.Quantity; import org.opengis.geometry.DirectPosition; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.apache.sis.geometries.Curve; @@ -104,11 +103,6 @@ public non-sealed class DefaultSphere extends AbstractGeometry implements Sphere this.radius = radius; } - @Override - public Quantity<?> getArea() { - throw new UnsupportedOperationException("Not supported."); - } - @Override public double getRadius() { return radius; 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 3d24496ada..c4830db71d 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 @@ -18,6 +18,7 @@ package org.apache.sis.geometries.solid.polyhedron; import java.util.List; import org.apache.sis.geometries.DataPoints; +import org.apache.sis.geometries.Geometries; import org.apache.sis.geometries.GeometryCollection; import org.apache.sis.geometries.GeometryFactory; import org.apache.sis.geometries.Surface; @@ -33,6 +34,7 @@ import org.apache.sis.maths.NDArrays; import org.apache.sis.maths.ReadOnly; import org.apache.sis.maths.SampleSystem; import org.apache.sis.maths.Vector3D; +import org.apache.sis.util.ArgumentChecks; import org.opengis.geometry.Envelope; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -84,19 +86,39 @@ public abstract sealed class AbstractPolyhedron extends AbstractGeometry impleme */ protected static final double ATAN_INV_PHI2 = Math.PI/2 - ATAN_PHI2; + /** + * Reference system of the faces of this polyhedron. The vertices are constants shared by every + * instance, expressed in an undefined 3-dimensional Cartesian system; this system gives them a + * meaning for this instance only. + */ + private CoordinateReferenceSystem crs = Geometries.getUndefinedCRS(3); + @Override public CoordinateReferenceSystem getCoordinateReferenceSystem() { - throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + return crs; } + /** + * Sets the reference system in which the vertices of this polyhedron are expressed. + * The coordinates are not transformed. + * + * @throws IllegalArgumentException if the given reference system is not 3-dimensional. + */ @Override public void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) throws IllegalArgumentException { - throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + ArgumentChecks.ensureNonNull("crs", crs); + if (crs.getCoordinateSystem().getDimension() != 3) { + throw new IllegalArgumentException("New CRS must be 3-dimensional"); + } + this.crs = crs; } + /** + * Returns the union of the envelopes of the faces. + */ @Override public Envelope getEnvelope() { - throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + return getExteriorShell().getEnvelope(); } /** @@ -209,14 +231,20 @@ public abstract sealed class AbstractPolyhedron extends AbstractGeometry impleme } /** - * Build the polygon of one face. + * Build the polygon of one face, in the {@linkplain #getCoordinateReferenceSystem() reference system} + * of this polyhedron. * * @param vertices polyhedron vertices * @param face face vertex indices into {@code vertices}, in CCW order viewed from outside * @return face polygon */ - protected static Polygon toPolygon(Array vertices, int[] face) { - final Array positions = NDArrays.subset(vertices, face); + protected Polygon toPolygon(Array vertices, int[] face) { + Array positions = NDArrays.subset(vertices, face); + if (!crs.equals(positions.getCoordinateReferenceSystem())) { + // The subset is a view, and the vertices are shared by every instance: change a copy. + positions = positions.copy(); + positions.setSampleSystem(SampleSystem.of(crs)); + } final LinearRing ring = GeometryFactory.DEFAULT.createLinearRing(GeometryFactory.DEFAULT.createDataPoints(positions)); return GeometryFactory.DEFAULT.createPolygon(ring, null); } 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 f8c5ac2d05..7fcc4743c8 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,12 +17,14 @@ 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.Envelope; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.apache.sis.geometries.BBox; import org.apache.sis.geometries.DataPointsType; +import org.apache.sis.geometries.Geometries; import org.apache.sis.geometries.Geometry; import org.apache.sis.geometries.GeometryFactory; import org.apache.sis.geometries.GeometryType; @@ -30,6 +32,7 @@ import org.apache.sis.geometries.SurfaceInterpolation; import org.apache.sis.geometries.internal.shared.DefaultSphere; import org.apache.sis.maths.ReadOnly; import org.apache.sis.maths.Vector; +import org.apache.sis.measure.Quantities; /** @@ -109,6 +112,29 @@ public sealed interface Sphere extends ParametricCurveSurface return GeometryType.CIRCLE; } + /** + * Returns the area of this sphere, which is 4πr² in a 3-dimensional space. + * + * <p>In an <var>n</var>-dimensional space, this is the measure of the (<var>n</var>−1)-sphere, + * 2π<sup><var>n</var>/2</sup>∕Γ(<var>n</var>/2)⋅r<sup><var>n</var>−1</sup>, expressed in the + * linear unit raised to the power <var>n</var>−1. In a 2-dimensional space, it is therefore + * the perimeter 2πr of the circle.</p> + */ + @Override + default Quantity<?> getArea() { + final int n = getDimension(); + /* + * Measure of the unit (n−1)-sphere, from S(1) = 2 and S(2) = 2π with the recurrence + * S(n+2) = 2π⋅S(n)/n, which avoids computing the Gamma function. + */ + int k = (n % 2 == 0) ? 2 : 1; + double measure = (k == 2) ? 2*Math.PI : 2; + for (; k + 2 <= n; k += 2) { + measure *= 2*Math.PI / k; + } + return Quantities.create(measure * Math.pow(getRadius(), n - 1), Geometries.getLinearUnit(this).pow(n - 1)); + } + /** * @return radius of the sphere. */ diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/maths/ArrayFactory.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/maths/ArrayFactory.java index b49df5dfa1..7a63fbfc36 100644 --- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/maths/ArrayFactory.java +++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/maths/ArrayFactory.java @@ -257,7 +257,7 @@ public interface ArrayFactory { final Cursor cursor = target.cursor(); while (cursor.next()) { final Vector tuple = cursor.samples(); - tuple.set((ReadOnly.Vector) java.lang.reflect.Array.get(values, idx)); + tuple.set((ReadOnly.Vector) java.lang.reflect.Array.get(values, idx++)); } } else { int idx = 0;
