You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by js...@apache.org on 2026/10/14 15:00:07 UTC
(sis) branch geoapi-4.0 updated (cfbd7caf6b -> 7b6223be12)
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
from cfbd7caf6b feat(Geometry): finish test cases for DataPoints
new 297c91c8d5 feat(Geometry): separate Ray as a Curve subtype, implement several small methods
new 33275b4dd4 feat(Geometry): separate Capsule,Cylinder,Sphere,Ellipsoid as interface and default implementation
new ef13105bb1 feat(Geometry): implement Surface methods on nurbSuface, implement surface interpolation methods
new 48ec4dd862 feat(Geometry): implement closed surfaces boundary method
new d301c93958 feat(Geometry): implement surface perimeter and area on several classes
new 7fd587c143 feat(Geometry): implement getEnvelope on spline type geometries and getBoundary on BBox and OBBox
new 42152ec382 feat(Geometry): implement default polygon area computation
new e2171bdbd8 feat(Geometry): enfore Geometry always have a CRS, compute polyhedron volume
new bb9e3560eb feat(Geometry): implements getDimension(coord) on several geometry types
new c596827e59 fix(Geometry): fix EarClipping holes not correctly handled
new 03f05372fa feat(Geometry): earclipping now support polygons not on the XY plan
new 7b6223be12 feat(Geometry): implement toPrimitive for PolyhedralSurface
The 12 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../main/org/apache/sis/geometries/BBox.java | 103 ++++++-
.../main/org/apache/sis/geometries/Curve.java | 5 +-
.../main/org/apache/sis/geometries/Geometries.java | 32 ++-
.../main/org/apache/sis/geometries/Geometry.java | 16 +-
.../apache/sis/geometries/GeometryCollection.java | 40 +++
.../org/apache/sis/geometries/GeometryFactory.java | 73 ++++-
.../org/apache/sis/geometries/GeometryType.java | 1 +
.../main/org/apache/sis/geometries/HyperPlane.java | 7 +-
.../main/org/apache/sis/geometries/OBBox.java | 14 +-
.../main/org/apache/sis/geometries/Orientable.java | 11 +-
.../main/org/apache/sis/geometries/Plane.java | 7 +-
.../main/org/apache/sis/geometries/Point.java | 40 ++-
.../main/org/apache/sis/geometries/Primitive.java | 6 +-
.../main/org/apache/sis/geometries/Solid.java | 12 +-
.../main/org/apache/sis/geometries/Surface.java | 55 +++-
.../org/apache/sis/geometries/adapter/Srid.java | 5 +-
.../sis/geometries/adapter/WellKnownBinary.java | 9 +-
.../sis/geometries/adapter/WellKnownText.java | 8 +-
.../org/apache/sis/geometries/curve/NurbCurve.java | 16 +-
.../apache/sis/geometries/curve/ProductCurve.java | 10 +
.../main/org/apache/sis/geometries/curve/Ray.java | 197 ++++++++++++++
.../internal/shared/AbstractGeometry.java | 56 ++++
.../sis/geometries/internal/shared/Curves.java | 69 ++++-
.../internal/shared/DefaultBSplineCurve.java | 6 +-
.../internal/shared/DefaultBSplineSolid.java | 18 +-
.../internal/shared/DefaultBSplineSurface.java | 25 +-
.../internal/shared/DefaultBilinearGrid.java | 25 +-
.../shared/DefaultCapsule.java} | 141 ++--------
.../internal/shared/DefaultCompoundCurve.java | 39 ++-
.../internal/shared/DefaultCurvePolygon.java | 11 +-
.../internal/shared/DefaultCylinder.java | 158 +++++++++++
.../shared/DefaultEllipsoid.java} | 166 ++----------
.../geometries/internal/shared/DefaultEmpty.java | 5 +-
.../internal/shared/DefaultGeometryCollection.java | 7 +-
.../internal/shared/DefaultNurbCurve.java | 1 +
.../internal/shared/DefaultNurbSurface.java | 71 +++--
.../internal/shared/DefaultOffsetCurve.java | 33 ++-
.../geometries/internal/shared/DefaultPolygon.java | 15 +-
.../internal/shared/DefaultPolyhedralSurface.java | 10 +-
.../internal/shared/DefaultPolyhedron.java | 24 +-
.../geometries/internal/shared/DefaultPrism.java | 48 +++-
.../{Ray.java => internal/shared/DefaultRay.java} | 76 +-----
.../internal/shared/DefaultReversedSurface.java | 32 ++-
.../geometries/internal/shared/DefaultSolid.java | 18 +-
.../shared/DefaultSphere.java} | 156 +----------
.../sis/geometries/mesh/MultiMeshPrimitive.java | 1 +
.../geometries/operation/GeometryProcessor.java | 4 +
.../sis/geometries/operation/ProcessorUtils.java | 2 -
.../sis/geometries/operation/ToPrimitive.java | 35 +++
.../operation/simplify/greedyinsert/WTriangle.java | 9 +-
.../operation/triangulate/EarClipping.java | 147 ++++++++--
.../triangulate/delaunay/OrientedEdge.java | 3 +-
.../triangulate/delaunay/OrientedTriangle.java | 9 +-
.../org/apache/sis/geometries/solid/Frustrum.java | 22 +-
.../sis/geometries/solid/ParametricCurveSolid.java | 8 +-
.../apache/sis/geometries/solid/Polyhedron.java | 88 ++++++
.../solid/polyhedron/AbstractPolyhedron.java | 68 ++++-
.../org/apache/sis/geometries/surface/Capsule.java | 191 ++++---------
.../apache/sis/geometries/surface/Cylinder.java | 301 +++++++++++++--------
.../apache/sis/geometries/surface/Ellipsoid.java | 301 ++++-----------------
.../geometries/surface/ParametricCurveSurface.java | 21 +-
.../org/apache/sis/geometries/surface/Sphere.java | 253 ++++++-----------
.../apache/sis/geometries/surface/Triangle.java | 28 +-
.../main/org/apache/sis/maths/ArrayFactory.java | 2 +-
.../test/org/apache/sis/geometries/EmptyTest.java | 6 +-
.../sis/geometries/operation/ToPrimitiveTest.java | 187 ++++++++++++-
.../geometries/spherical/GreatCircleArcTest.java | 16 +-
.../spherical/SphericalConvexPolygonTest.java | 4 +-
.../spherical/SphericalTriangleTest.java | 24 +-
.../surface/ParametricCurveSurfaceTest.java | 3 +-
70 files changed, 2170 insertions(+), 1440 deletions(-)
create mode 100644 incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/Ray.java
copy incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/{surface/Capsule.java => internal/shared/DefaultCapsule.java} (50%)
create mode 100644 incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCylinder.java
copy incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/{surface/Ellipsoid.java => internal/shared/DefaultEllipsoid.java} (58%)
rename incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/{Ray.java => internal/shared/DefaultRay.java} (65%)
copy incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/{surface/Sphere.java => internal/shared/DefaultSphere.java} (50%)
(sis) 04/12: feat(Geometry): implement closed surfaces boundary method
Posted by js...@apache.org.
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 48ec4dd86222c7a0c03b0ef2eab9507904310b86
Author: jsorel <jo...@geomatys.com>
AuthorDate: Wed Oct 14 11:00:22 2026 +0200
feat(Geometry): implement closed surfaces boundary method
---
.../main/org/apache/sis/geometries/Geometry.java | 6 +-
.../main/org/apache/sis/geometries/HyperPlane.java | 5 +-
.../main/org/apache/sis/geometries/Plane.java | 5 +-
.../main/org/apache/sis/geometries/Primitive.java | 6 +-
.../geometries/internal/shared/DefaultCapsule.java | 6 --
.../internal/shared/DefaultCylinder.java | 19 +++-
.../internal/shared/DefaultEllipsoid.java | 6 --
.../geometries/internal/shared/DefaultSphere.java | 6 --
.../org/apache/sis/geometries/surface/Capsule.java | 10 ++
.../apache/sis/geometries/surface/Cylinder.java | 112 ++++++++++++++++++++-
.../apache/sis/geometries/surface/Ellipsoid.java | 10 ++
.../org/apache/sis/geometries/surface/Sphere.java | 10 ++
12 files changed, 171 insertions(+), 30 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 150c5239b5..5d5717c340 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
@@ -276,14 +276,16 @@ public sealed interface Geometry
* boundary, and in the interior those lying on more than one.</li>
* </ul>
*
+ * <p>The default implementation returns {@link BoundaryType#MOD_2}, the rule of OGC Simple Feature
+ * Access. The three rules agree on geometries which are not aggregates.</p>
+ *
* @return rule used to compute the boundary of this geometry.
*
* @see ISO 19107:2019 - 6.4.3, 10.8.3
*/
@UML(identifier="boundaryType", specification=ISO_19107)
default BoundaryType getBoundaryType() {
- //TODO remove this method default when all classes implement it.
- throw new UnsupportedOperationException();
+ return BoundaryType.MOD_2;
}
/**
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/HyperPlane.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/HyperPlane.java
index 74627f4640..ecff5d2b03 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/HyperPlane.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/HyperPlane.java
@@ -149,9 +149,12 @@ public final class HyperPlane extends AbstractGeometry{
return points.getCoordinateReferenceSystem();
}
+ /**
+ * Returns the empty geometry: a hyperplane extends to infinity in all its directions, so it has no edge.
+ */
@Override
public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return GeometryFactory.DEFAULT.createEmpty(getCoordinateReferenceSystem());
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Plane.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Plane.java
index 62cdc24f30..3bd323154a 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Plane.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Plane.java
@@ -210,9 +210,12 @@ public final class Plane extends AbstractGeometry{
return points.getCoordinateReferenceSystem();
}
+ /**
+ * Returns the empty geometry: a plane extends to infinity in all its directions, so it has no edge.
+ */
@Override
public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return GeometryFactory.DEFAULT.createEmpty(getCoordinateReferenceSystem());
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Primitive.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Primitive.java
index b4bac83149..8b1d49a277 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Primitive.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Primitive.java
@@ -95,12 +95,14 @@ public sealed interface Primitive extends Geometry
* <li>For a surface or a solid, the order may be ignored.</li>
* </ul>
*
+ * <p>The default implementation returns an empty list, for a primitive which is not
+ * decomposed in smaller ones.</p>
+ *
* @return segments of this primitive, possibly empty.
*
* @see ISO 19107:2019 - 6.4.11.2
*/
default List<Primitive> getSegments() {
- //TODO
- throw new UnsupportedOperationException();
+ return List.of();
}
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCapsule.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCapsule.java
index 1ffdcb304e..d33f5c06e2 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCapsule.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCapsule.java
@@ -19,7 +19,6 @@ 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.Capsule;
import org.apache.sis.maths.NDArrays;
@@ -121,11 +120,6 @@ public non-sealed class DefaultCapsule extends AbstractGeometry implements Capsu
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) {
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
index b7405ea923..cffd3f385f 100644
--- 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
@@ -19,7 +19,6 @@ 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;
@@ -43,6 +42,11 @@ public non-sealed class DefaultCylinder extends AbstractGeometry implements Cyli
private double radiusTop = 1.0;
private double radiusBottom = 1.0;
+ /**
+ * Whether the ends are closed by disks.
+ */
+ private boolean closed = true;
+
/**
* Creates a cylinder along the axis joining the two given positions.
* The reference system of the cylinder is the one of those positions.
@@ -122,8 +126,13 @@ public non-sealed class DefaultCylinder extends AbstractGeometry implements Cyli
}
@Override
- public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
+ public boolean isClosed() {
+ return closed;
+ }
+
+ @Override
+ public void setClosed(boolean closed) {
+ this.closed = closed;
}
@Override
@@ -139,7 +148,8 @@ public non-sealed class DefaultCylinder extends AbstractGeometry implements Cyli
return false;
}
return Double.doubleToLongBits(this.radiusTop) == Double.doubleToLongBits(other.radiusTop)
- && Double.doubleToLongBits(this.radiusBottom) == Double.doubleToLongBits(other.radiusBottom);
+ && Double.doubleToLongBits(this.radiusBottom) == Double.doubleToLongBits(other.radiusBottom)
+ && this.closed == other.closed;
}
@Override
@@ -148,6 +158,7 @@ public non-sealed class DefaultCylinder extends AbstractGeometry implements Cyli
hash = 89 * hash + Objects.hashCode(this.points);
hash = 89 * hash + Double.hashCode(this.radiusTop);
hash = 89 * hash + Double.hashCode(this.radiusBottom);
+ hash = 89 * hash + Boolean.hashCode(this.closed);
return hash;
}
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
index 9c15c172b5..40b491e35b 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
@@ -24,7 +24,6 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
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.surface.Ellipsoid;
import org.apache.sis.maths.DataType;
@@ -193,11 +192,6 @@ public non-sealed class DefaultEllipsoid extends AbstractGeometry implements Ell
points.setPosition(0, position);
}
- @Override
- public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
// methods from ParametricCurveSurface
@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 70223e83d6..3d298d699d 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
@@ -23,7 +23,6 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
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.surface.Sphere;
import org.apache.sis.maths.DataType;
@@ -131,11 +130,6 @@ public non-sealed class DefaultSphere extends AbstractGeometry implements Sphere
points.setPosition(0, position);
}
- @Override
- public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
// methods from ParametricCurveSurface
@Override
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 d7ddb0f81b..edc2769956 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
@@ -22,6 +22,8 @@ import org.opengis.annotation.UML;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.BBox;
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.Surface;
import org.apache.sis.geometries.SurfaceInterpolation;
@@ -155,6 +157,14 @@ public sealed interface Capsule extends Surface
return bbox;
}
+ /**
+ * Returns the empty geometry: a capsule is a closed surface, so it has no edge.
+ */
+ @Override
+ default Geometry getBoundary() {
+ return GeometryFactory.DEFAULT.createEmpty(getCoordinateReferenceSystem());
+ }
+
@Override
default CoordinateReferenceSystem getCoordinateReferenceSystem() {
return getDataPoints().getCoordinateReferenceSystem();
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 c36befb43b..73b4b4d232 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,17 +16,22 @@
*/
package org.apache.sis.geometries.surface;
+import java.util.ArrayList;
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.DataPointsType;
+import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Surface;
import org.apache.sis.geometries.SurfaceInterpolation;
+import org.apache.sis.geometries.curve.Circle;
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;
@@ -41,6 +46,10 @@ import org.apache.sis.maths.Vector;
*
* ISO 19107 : classified as a conic surface
*
+ * <p>A cylinder is either {@linkplain #isClosed() closed} by two disks at its ends, which makes it
+ * the boundary of a volume as in the KHR_implicit_shapes extension, or open, in which case it is
+ * only the lateral surface that ISO 19107 defines.</p>
+ *
* <p>Constraints:</p>
* <ul>
* <li>The {@linkplain #getDataPoints() data points} hold exactly two positions:
@@ -66,15 +75,33 @@ public sealed interface Cylinder extends Surface
}
/**
- * Returns {@link SurfaceInterpolation#CONIC}: ISO 19107 classifies cylinders and cones as conic surfaces.
+ * Returns {@link SurfaceInterpolation#CONIC}, since ISO 19107 classifies cylinders and cones
+ * as conic surfaces, followed by {@link SurfaceInterpolation#PLANAR} for the end disks if this
+ * cylinder is {@linkplain #isClosed() closed}.
*
* @see ISO 19107:2019 - 8.5.3, 8.5.4
*/
@Override
default List<SurfaceInterpolation> getInterpolation() {
- return List.of(SurfaceInterpolation.CONIC);
+ return isClosed() ? List.of(SurfaceInterpolation.CONIC, SurfaceInterpolation.PLANAR)
+ : List.of(SurfaceInterpolation.CONIC);
}
+ /**
+ * Returns whether this cylinder is closed by a disk at each of its ends.
+ * An end whose radius is zero, the apex of a cone, needs no disk.
+ *
+ * @return {@code true} if the ends are closed by disks, or {@code false} for the lateral surface only.
+ */
+ boolean isClosed();
+
+ /**
+ * Sets whether this cylinder is closed by a disk at each of its ends.
+ *
+ * @param closed {@code true} to close the ends by disks, or {@code false} for the lateral surface only.
+ */
+ void setClosed(boolean closed);
+
/**
* Returns {@code false}: a cylinder always has its two circle centers.
*/
@@ -191,6 +218,87 @@ public sealed interface Cylinder extends Surface
return bbox;
}
+ /**
+ * Returns the empty geometry if this cylinder is {@linkplain #isClosed() closed},
+ * or the circles at its ends otherwise.
+ *
+ * <p>The circles of an open cylinder are oriented so that the lateral surface is on their left
+ * when seen from its outward normal: the bottom circle turns counterclockwise around the axis
+ * going from the bottom to the top, and the top circle clockwise. An end whose radius is zero,
+ * the apex of a cone, is a single point which bounds nothing, so it has no circle.</p>
+ *
+ * @return the empty geometry, or a collection of the end circles.
+ * @throws UnsupportedOperationException if this cylinder is open and not 3-dimensional.
+ * @throws IllegalStateException if this cylinder is open and its two positions are the same.
+ */
+ @Override
+ default Geometry getBoundary() {
+ final CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
+ if (isClosed()) {
+ return GeometryFactory.DEFAULT.createEmpty(crs);
+ }
+ if (getDataPoints().getDimension() != 3) {
+ throw new UnsupportedOperationException("The boundary of an open cylinder is computed in 3 dimensions only.");
+ }
+ final Vector<?> axis = getAxis();
+ final double height = axis.length();
+ if (!(height > 0)) {
+ throw new IllegalStateException("The cylinder has no axis, its two positions are the same.");
+ }
+ axis.scale(1 / height);
+ /*
+ * Builds (u, w, axis) as a right-handed orthonormal basis. The vector `u` is derived from the
+ * coordinate axis which is the most perpendicular to the cylinder axis, for numerical stability.
+ */
+ int k = 0;
+ for (int i = 1; i < 3; i++) {
+ if (Math.abs(axis.get(i)) < Math.abs(axis.get(k))) k = i;
+ }
+ final Vector<?> u = Vectors.create(axis.getSampleSystem(), DataType.DOUBLE);
+ u.set(k, 1);
+ u.subtract(axis.copy().scale(axis.get(k)));
+ u.normalize();
+ final Vector<?> w = axis.cross(u);
+ final var circles = new ArrayList<Circle>(2);
+ final double radiusBottom = getRadiusBottom();
+ final double radiusTop = getRadiusTop();
+ if (radiusBottom > 0) {
+ circles.add(circle(getBottom(), radiusBottom, u, w));
+ }
+ if (radiusTop > 0) {
+ circles.add(circle(getTop(), radiusTop, u, w.copy().scale(-1)));
+ }
+ return GeometryFactory.DEFAULT.createMultiCurve(crs, circles.toArray(Circle[]::new));
+ }
+
+ /**
+ * Returns the circle of the given center and radius in the plane of the two given unit vectors,
+ * turning from {@code u} toward {@code w}. The circle is made of four quarter arcs, since a
+ * single arc of a circle shall stay below a half turn.
+ */
+ private static Circle circle(final ReadOnly.Vector<?> center, final double radius,
+ final ReadOnly.Vector<?> u, final ReadOnly.Vector<?> w)
+ {
+ final var points = new ArrayList<Vector<?>>(5);
+ final var centers = new ArrayList<Vector<?>>(4);
+ final ReadOnly.Vector<?>[] directions = {u, w, u.copy().scale(-1), w.copy().scale(-1), u};
+ for (final ReadOnly.Vector<?> direction : directions) {
+ final Vector<?> p = Vectors.create(center.getSampleSystem(), DataType.DOUBLE);
+ p.set(center);
+ p.add(direction.copy().scale(radius));
+ points.add(p);
+ }
+ for (int i = 0; i < 4; i++) {
+ final Vector<?> c = Vectors.create(center.getSampleSystem(), DataType.DOUBLE);
+ c.set(center);
+ centers.add(c);
+ }
+ final GeometryFactory factory = GeometryFactory.DEFAULT;
+ return factory.createCircle(
+ factory.createDataPoints(NDArrays.of(points, center.getSampleSystem(), DataType.DOUBLE)),
+ NDArrays.of(centers, center.getSampleSystem(), DataType.DOUBLE), null);
+ }
+
@Override
default CoordinateReferenceSystem getCoordinateReferenceSystem() {
return getDataPoints().getCoordinateReferenceSystem();
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 254686b012..1c8639c6f7 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
@@ -23,6 +23,8 @@ 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.Geometry;
+import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.SurfaceInterpolation;
import org.apache.sis.geometries.internal.shared.DefaultEllipsoid;
@@ -139,6 +141,14 @@ public sealed interface Ellipsoid extends ParametricCurveSurface
*/
void setCenter(ReadOnly.Vector<?> position);
+ /**
+ * Returns the empty geometry: an ellipsoid is a closed surface, so it has no edge.
+ */
+ @Override
+ default Geometry getBoundary() {
+ return GeometryFactory.DEFAULT.createEmpty(getCoordinateReferenceSystem());
+ }
+
@Override
default CoordinateReferenceSystem getCoordinateReferenceSystem() {
return getDataPoints().getCoordinateReferenceSystem();
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 8e8218958d..f8c5ac2d05 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
@@ -23,6 +23,8 @@ 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.Geometry;
+import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.SurfaceInterpolation;
import org.apache.sis.geometries.internal.shared.DefaultSphere;
@@ -130,6 +132,14 @@ public sealed interface Sphere extends ParametricCurveSurface
*/
void setCenter(ReadOnly.Vector<?> position);
+ /**
+ * Returns the empty geometry: a sphere is a closed surface, so it has no edge.
+ */
+ @Override
+ default Geometry getBoundary() {
+ return GeometryFactory.DEFAULT.createEmpty(getCoordinateReferenceSystem());
+ }
+
@Override
default CoordinateReferenceSystem getCoordinateReferenceSystem() {
return getDataPoints().getCoordinateReferenceSystem();
(sis) 01/12: feat(Geometry): separate Ray as a Curve subtype, implement several small methods
Posted by js...@apache.org.
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 297c91c8d54cbcc81567bc5bcb22a8185a1289cd
Author: jsorel <jo...@geomatys.com>
AuthorDate: Wed Oct 14 10:03:03 2026 +0200
feat(Geometry): separate Ray as a Curve subtype, implement several small methods
---
.../main/org/apache/sis/geometries/Curve.java | 2 +
.../org/apache/sis/geometries/GeometryFactory.java | 6 +-
.../org/apache/sis/geometries/GeometryType.java | 1 +
.../main/org/apache/sis/geometries/curve/Ray.java | 197 +++++++++++++++++++++
.../internal/shared/DefaultBSplineSurface.java | 8 -
.../internal/shared/DefaultBilinearGrid.java | 8 -
.../internal/shared/DefaultNurbSurface.java | 8 -
.../{Ray.java => internal/shared/DefaultRay.java} | 76 ++------
.../org/apache/sis/geometries/solid/Frustrum.java | 5 +-
.../solid/polyhedron/AbstractPolyhedron.java | 19 +-
.../apache/sis/geometries/surface/Ellipsoid.java | 8 -
.../geometries/surface/ParametricCurveSurface.java | 14 ++
.../org/apache/sis/geometries/surface/Sphere.java | 8 -
13 files changed, 252 insertions(+), 108 deletions(-)
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Curve.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Curve.java
index 59693f4cc5..526b71a5fc 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Curve.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Curve.java
@@ -32,6 +32,7 @@ import org.apache.sis.geometries.curve.Line;
import org.apache.sis.geometries.curve.LineString;
import org.apache.sis.geometries.curve.OffsetCurve;
import org.apache.sis.geometries.curve.ProductCurve;
+import org.apache.sis.geometries.curve.Ray;
import org.apache.sis.geometries.curve.Rhumb;
import org.apache.sis.geometries.curve.Spiral;
import org.apache.sis.geometries.internal.shared.DefaultReversedCurve;
@@ -79,6 +80,7 @@ public sealed interface Curve extends Orientable
LineString,
OffsetCurve,
ProductCurve,
+ Ray,
Rhumb,
Spiral,
DefaultReversedCurve
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 d92129f139..3b7cca3a4c 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
@@ -48,6 +48,7 @@ import org.apache.sis.geometries.curve.OffsetCurve;
import org.apache.sis.geometries.curve.PolynomialSpline;
import org.apache.sis.geometries.curve.ProductCurve;
import org.apache.sis.geometries.curve.RealFunction;
+import org.apache.sis.geometries.curve.Ray;
import org.apache.sis.geometries.curve.Rhumb;
import org.apache.sis.geometries.curve.Spiral;
import org.apache.sis.geometries.curve.SplineCurveForm;
@@ -84,6 +85,7 @@ import org.apache.sis.geometries.internal.shared.DefaultPolyhedron;
import org.apache.sis.geometries.internal.shared.DefaultPolynomialSpline;
import org.apache.sis.geometries.internal.shared.DefaultPrism;
import org.apache.sis.geometries.internal.shared.DefaultProductCurve;
+import org.apache.sis.geometries.internal.shared.DefaultRay;
import org.apache.sis.geometries.internal.shared.DefaultReversedCurve;
import org.apache.sis.geometries.internal.shared.DefaultReversedSurface;
import org.apache.sis.geometries.internal.shared.DefaultRhumb;
@@ -773,7 +775,7 @@ public final class GeometryFactory {
* @return the ray starting from the given position.
*/
public Ray createRay(Vector<?> position, Vector<?> direction) {
- return new Ray(position, direction);
+ return new DefaultRay(position, direction);
}
/**
@@ -786,7 +788,7 @@ public final class GeometryFactory {
* @throws IllegalArgumentException if the given {@code DataPoints} does not hold exactly one position.
*/
public Ray createRay(DataPoints points, Vector<?> direction) {
- return new Ray(points, direction);
+ return new DefaultRay(points, direction);
}
/**
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryType.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryType.java
index 36601c3aa3..952396a479 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryType.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryType.java
@@ -37,6 +37,7 @@ import org.apache.sis.geometries.curve.NurbCurve;
import org.apache.sis.geometries.curve.OffsetCurve;
import org.apache.sis.geometries.curve.PolynomialSpline;
import org.apache.sis.geometries.curve.ProductCurve;
+import org.apache.sis.geometries.curve.Ray;
import org.apache.sis.geometries.curve.Rhumb;
import org.apache.sis.geometries.curve.Spiral;
import org.apache.sis.geometries.curve.SplineCurve;
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/Ray.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/Ray.java
new file mode 100644
index 0000000000..7412d9ff32
--- /dev/null
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/Ray.java
@@ -0,0 +1,197 @@
+/*
+ * 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.curve;
+
+import javax.measure.Quantity;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.geometries.BBox;
+import org.apache.sis.geometries.Curve;
+import org.apache.sis.geometries.CurveInterpolation;
+import org.apache.sis.geometries.DataPointsType;
+import org.apache.sis.geometries.Geometries;
+import org.apache.sis.geometries.GeometryType;
+import org.apache.sis.geometries.Point;
+import org.apache.sis.geometries.internal.shared.DefaultRay;
+import org.apache.sis.maths.Array;
+import org.apache.sis.maths.ReadOnly;
+import org.apache.sis.measure.Quantities;
+
+
+/**
+ * A ray is a one direction infinite line.
+ * It is build from a position and a direction.
+ *
+ * <p>Constraints:</p>
+ * <ul>
+ * <li>The {@linkplain #getDataPoints() data points} hold exactly one position,
+ * the {@linkplain #getStartPoint() start point} of the ray.</li>
+ * <li>The ray has no end point and its length is infinite.</li>
+ * </ul>
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public sealed interface Ray extends Curve
+ permits DefaultRay
+{
+
+ /**
+ * Returns {@link GeometryType#RAY}.
+ */
+ @Override
+ default GeometryType getGeometryType() {
+ return GeometryType.RAY;
+ }
+
+ /**
+ * Returns the position the ray starts from.
+ *
+ * @return the start position of this ray.
+ */
+ ReadOnly.Vector<?> getPosition();
+
+ /**
+ * Sets the position the ray starts from.
+ *
+ * @param position the new start position, not null.
+ */
+ void setPosition(ReadOnly.Vector<?> position);
+
+ /**
+ * Returns the direction the ray extends toward.
+ *
+ * @return the direction of this ray.
+ */
+ ReadOnly.Vector<?> getDirection();
+
+ /**
+ * Sets the direction the ray extends toward.
+ *
+ * @param direction the new direction, not null.
+ */
+ void setDirection(ReadOnly.Vector<?> direction);
+
+ /**
+ * Returns {@code false}: a ray always has a start position.
+ */
+ @Override
+ default boolean isEmpty() {
+ return false;
+ }
+
+ /**
+ * Returns {@link CurveInterpolation#LINEAR}.
+ */
+ @Override
+ default CurveInterpolation getInterpolation() {
+ return CurveInterpolation.LINEAR;
+ }
+
+ /**
+ * Returns null, a ray has no control points.
+ */
+ @Override
+ default Array getControlPoints() {
+ return null;
+ }
+
+ /**
+ * Returns an infinite length, in the linear unit of the reference system of this ray.
+ */
+ @Override
+ default Quantity<?> getLength() {
+ return Quantities.create(Double.POSITIVE_INFINITY, Geometries.getLinearUnit(this));
+ }
+
+ /**
+ * Returns {@code null}: the ray extends to infinity, it has no end point.
+ */
+ @Override
+ default Point getEndPoint() {
+ return null;
+ }
+
+ /**
+ * Returns the {@linkplain #getStartPoint() start point}, which lies on the ray.
+ */
+ @Override
+ default Point getRepresentativePoint() {
+ return getStartPoint();
+ }
+
+ /**
+ * Returns {@code false}: the ray extends to infinity and never comes back to its start point.
+ */
+ @Override
+ default boolean isClosed() {
+ return false;
+ }
+
+ /**
+ * Returns {@code true}: a straight half-line never crosses itself.
+ */
+ @Override
+ default boolean isSimple() {
+ return true;
+ }
+
+ /**
+ * Returns the position the ray starts from: the ray extends to infinity
+ * in its direction, so its origin is its only end.
+ */
+ @Override
+ default Point getBoundary() {
+ return getStartPoint();
+ }
+
+ /**
+ * Returns the envelope of this ray, infinite on each axis where the direction is not zero.
+ */
+ @Override
+ default BBox getEnvelope() {
+ final ReadOnly.Vector<?> position = getPosition();
+ final ReadOnly.Vector<?> direction = getDirection();
+ final int dim = direction.getDimension();
+ final BBox bbox = new BBox(dim);
+ for (int i=0;i<dim;i++){
+ double d = direction.get(i);
+ if (d<0){
+ bbox.setRange(i, Double.NEGATIVE_INFINITY, position.get(i));
+ } else if (d>0){
+ bbox.setRange(i, position.get(i), Double.POSITIVE_INFINITY);
+ } else {
+ bbox.setRange(i, position.get(i), position.get(i));
+ }
+ }
+ return bbox;
+ }
+
+ @Override
+ default CoordinateReferenceSystem getCoordinateReferenceSystem() {
+ return getDataPoints().getCoordinateReferenceSystem();
+ }
+
+ @Override
+ default void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) throws IllegalArgumentException {
+ getDataPoints().setCoordinateReferenceSystem(crs);
+ }
+
+ @Override
+ default DataPointsType getDataPointsType() {
+ return getDataPoints().getType();
+ }
+
+}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSurface.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSurface.java
index 1d3cf43978..943d4c8207 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSurface.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSurface.java
@@ -28,9 +28,6 @@ import org.apache.sis.geometries.curve.KnotType;
import org.apache.sis.geometries.surface.BSplineSurface;
import org.apache.sis.geometries.surface.BSplineSurfaceForm;
-// Specific to the geoapi-4.0 branch:
-import org.opengis.metadata.Identifier;
-
/**
* A rational or polynomial parametric surface represented by control points, b-spline basis
@@ -195,9 +192,4 @@ public non-sealed class DefaultBSplineSurface extends AbstractGeometry implement
throw new UnsupportedOperationException("Not supported yet.");
}
- @Override
- public Identifier getName() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBilinearGrid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBilinearGrid.java
index d94593c97d..92dedded85 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBilinearGrid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBilinearGrid.java
@@ -29,9 +29,6 @@ import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.surface.BilinearGrid;
-// Specific to the geoapi-4.0 branch:
-import org.opengis.metadata.Identifier;
-
/**
* A parametric curve surface using polylines as both horizontal and vertical curves, each cell of
@@ -179,9 +176,4 @@ public non-sealed class DefaultBilinearGrid extends AbstractGeometry implements
throw new UnsupportedOperationException("Not supported yet.");
}
- @Override
- public Identifier getName() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
}
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 28f5d14b89..eeb5736e90 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
@@ -32,9 +32,6 @@ import org.apache.sis.maths.NDArrays;
import org.apache.sis.maths.Vector;
import org.apache.sis.maths.Vectors;
-// Specific to the geoapi-4.0 branch:
-import org.opengis.metadata.Identifier;
-
/**
* NURBS surface: tensor product of two directions (u, v), each one with its own degree and its own knot vector.
@@ -385,9 +382,4 @@ public non-sealed class DefaultNurbSurface extends AbstractGeometry implements N
throw new UnsupportedOperationException("Not supported yet.");
}
- @Override
- public Identifier getName() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Ray.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultRay.java
similarity index 65%
rename from incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Ray.java
rename to incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultRay.java
index 129ba74266..ea76a5c8b9 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Ray.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultRay.java
@@ -14,23 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sis.geometries;
+package org.apache.sis.geometries.internal.shared;
import java.util.Objects;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.apache.sis.geometries.internal.shared.AbstractGeometry;
+import org.apache.sis.geometries.DataPoints;
+import org.apache.sis.geometries.GeometryFactory;
+import org.apache.sis.geometries.curve.Ray;
import org.apache.sis.maths.NDArrays;
import org.apache.sis.maths.ReadOnly;
import org.apache.sis.maths.Vector;
/**
- * A ray is a one direction infinite line.
- * It is build from a position and a direction.
+ * Default implementation of a ray, storing its start position in a {@code DataPoints}.
*
- * @author Johann Sorel
+ * @author Johann Sorel (Geomatys)
*/
-public final class Ray extends AbstractGeometry{
+public non-sealed class DefaultRay extends AbstractGeometry implements Ray {
/**
* Must contain a single point.
@@ -46,7 +46,7 @@ public final class Ray extends AbstractGeometry{
* @param position the position the ray starts from, not null.
* @param direction the direction the ray extends toward, not null.
*/
- public Ray(Vector<?> position, Vector<?> direction) {
+ public DefaultRay(Vector<?> position, Vector<?> direction) {
points = GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(position.getSampleSystem(), position.getDataType(), 1));
points.setPosition(0, position);
this.direction = direction;
@@ -61,7 +61,7 @@ public final class Ray extends AbstractGeometry{
* @param direction the direction the ray extends toward, not null.
* @throws IllegalArgumentException if the given {@code DataPoints} does not hold exactly one position.
*/
- public Ray(DataPoints points, Vector<?> direction) {
+ public DefaultRay(DataPoints points, Vector<?> direction) {
if (points.size() != 1) {
throw new IllegalArgumentException("Ray sequence must contain one point");
}
@@ -70,78 +70,30 @@ public final class Ray extends AbstractGeometry{
}
@Override
- public GeometryType getGeometryType() {
- return GeometryType.RAY;
- }
-
- @Override
- public int getTopologicDimension() {
- return 1;
- }
-
public DataPoints getDataPoints() {
return points;
}
+ @Override
public ReadOnly.Vector<?> getPosition() {
return points.getPosition(0);
}
+ @Override
public void setPosition(ReadOnly.Vector<?> position) {
points.setPosition(0, position);
}
+ @Override
public ReadOnly.Vector<?> getDirection() {
return direction;
}
+ @Override
public void setDirection(ReadOnly.Vector<?> direction) {
this.direction = direction.copy();
}
- @Override
- public BBox getEnvelope() {
- final ReadOnly.Vector<?> position = getPosition();
- final int dim = direction.getDimension();
- final BBox bbox = new BBox(dim);
- for (int i=0;i<dim;i++){
- double d = direction.get(i);
- if (d<0){
- bbox.setRange(i, Double.NEGATIVE_INFINITY, position.get(i));
- } else if (d>0){
- bbox.setRange(i, position.get(i), Double.POSITIVE_INFINITY);
- } else {
- bbox.setRange(i, position.get(i), position.get(i));
- }
- }
- return bbox;
- }
-
- @Override
- public boolean isEmpty() {
- return false;
- }
-
- @Override
- public CoordinateReferenceSystem getCoordinateReferenceSystem() {
- return points.getCoordinateReferenceSystem();
- }
-
- @Override
- public Geometry getBoundary() {
- 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) {
@@ -150,7 +102,7 @@ public final class Ray extends AbstractGeometry{
if (getClass() != obj.getClass()) {
return false;
}
- final Ray other = (Ray) obj;
+ final DefaultRay other = (DefaultRay) obj;
if (!Objects.equals(this.points, other.points)) {
return false;
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Frustrum.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Frustrum.java
index 3e3c59861b..da5c29511c 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Frustrum.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Frustrum.java
@@ -89,9 +89,12 @@ public final class Frustrum extends AbstractGeometry {
return ConcatenatedDataPoints.of(planes);
}
+ /**
+ * Returns 3: a frustum is the volume enclosed by its planes.
+ */
@Override
public int getTopologicDimension() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return 3;
}
}
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 39fde90bd4..5f87692628 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
@@ -103,19 +103,32 @@ public abstract sealed class AbstractPolyhedron extends AbstractGeometry impleme
throw new UnsupportedOperationException("Not supported yet.");
}
+ /**
+ * Returns {@code false}: a regular polyhedron always has faces.
+ */
@Override
public boolean isEmpty() {
- throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
+ return false;
}
+ /**
+ * Returns the faces of this polyhedron, in the order of their indices.
+ */
@Override
public GeometryCollection<Polygon> getExteriorShell() {
- throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
+ final Polygon[] faces = new Polygon[getFaceCount()];
+ for (int i = 0; i < faces.length; i++) {
+ faces[i] = getFace(i);
+ }
+ return GeometryFactory.DEFAULT.createMultiPolygon(faces);
}
+ /**
+ * Returns an empty list: a regular polyhedron has no void.
+ */
@Override
public List<GeometryCollection<Polygon>> getInteriorShells() {
- throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
+ return List.of();
}
/**
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 c7b6f91670..2583f181c5 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
@@ -42,9 +42,6 @@ import org.apache.sis.maths.SampleSystem;
import org.apache.sis.maths.Vector;
import org.apache.sis.util.ArgumentChecks;
-// Specific to the geoapi-4.0 branch:
-import org.opengis.metadata.Identifier;
-
/**
* An ellipsoid geometry defined by a center and one semi-axis length per axis of the coordinate
@@ -355,11 +352,6 @@ public final class Ellipsoid extends AbstractGeometry implements ParametricCurve
throw new UnsupportedOperationException("Not supported yet.");
}
- @Override
- public Identifier getName() {
- 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.
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/ParametricCurveSurface.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/ParametricCurveSurface.java
index 5f7489dd08..6e61608751 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/ParametricCurveSurface.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/ParametricCurveSurface.java
@@ -21,12 +21,16 @@ import static org.opengis.annotation.Specification.ISO_19107;
import org.opengis.annotation.UML;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.ReferenceSystem;
+import org.apache.sis.referencing.NamedIdentifier;
import org.apache.sis.geometries.Curve;
import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Surface;
import org.apache.sis.geometries.SurfaceInterpolation;
+// Specific to the geoapi-4.0 branch:
+import org.opengis.metadata.Identifier;
+
/**
* A surface given by a family of continuous curves,
@@ -229,5 +233,15 @@ public sealed interface ParametricCurveSurface extends Surface, ReferenceSystem
@UML(identifier="surface", specification=ISO_19107)
DirectPosition getSurface(double u, double v);
+ /**
+ * Returns the name of this surface used as a reference system for its parameter space.
+ * The default implementation returns the name of the {@linkplain #getGeometryType() geometry type}.
+ *
+ * @return name of the parameter space of this surface.
+ */
+ @Override
+ default Identifier getName() {
+ return new NamedIdentifier(null, getGeometryType().name());
+ }
}
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 3497d9f022..81ecd4f55c 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
@@ -41,9 +41,6 @@ import org.apache.sis.maths.SampleSystem;
import org.apache.sis.maths.Vector;
import org.apache.sis.util.ArgumentChecks;
-// Specific to the geoapi-4.0 branch:
-import org.opengis.metadata.Identifier;
-
/**
* A sphere geometry defined by a center and a radius.
@@ -283,11 +280,6 @@ public final class Sphere extends AbstractGeometry implements ParametricCurveSur
throw new UnsupportedOperationException("Not supported yet.");
}
- @Override
- public Identifier getName() {
- 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.
(sis) 03/12: feat(Geometry): implement Surface methods on nurbSuface, implement surface interpolation methods
Posted by js...@apache.org.
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 ef13105bb13c4014fc51c8fedde774aa516dc639
Author: jsorel <jo...@geomatys.com>
AuthorDate: Wed Oct 14 10:42:32 2026 +0200
feat(Geometry): implement Surface methods on nurbSuface, implement surface interpolation methods
---
.../main/org/apache/sis/geometries/Curve.java | 3 +-
.../main/org/apache/sis/geometries/Orientable.java | 11 ++----
.../main/org/apache/sis/geometries/Solid.java | 6 +--
.../main/org/apache/sis/geometries/Surface.java | 30 ++++++++++-----
.../internal/shared/DefaultCurvePolygon.java | 11 +++++-
.../internal/shared/DefaultEllipsoid.java | 11 ------
.../internal/shared/DefaultNurbSurface.java | 44 ++++++++++++++++------
.../internal/shared/DefaultPolyhedron.java | 10 -----
.../internal/shared/DefaultReversedSurface.java | 32 +++++++++++++++-
.../geometries/internal/shared/DefaultSolid.java | 10 -----
.../geometries/internal/shared/DefaultSphere.java | 11 ------
.../solid/polyhedron/AbstractPolyhedron.java | 6 ++-
.../org/apache/sis/geometries/surface/Capsule.java | 11 ++++++
.../apache/sis/geometries/surface/Ellipsoid.java | 16 ++++++++
.../org/apache/sis/geometries/surface/Sphere.java | 20 ++++++++++
15 files changed, 152 insertions(+), 80 deletions(-)
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Curve.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Curve.java
index 526b71a5fc..4303695aa9 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Curve.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Curve.java
@@ -297,8 +297,7 @@ public sealed interface Curve extends Orientable
*/
@UML(identifier="interpolation", specification=ISO_19107)
default CurveInterpolation getInterpolation() {
- //TODO
- throw new UnsupportedOperationException();
+ return CurveInterpolation.LINEAR;
}
/**
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Orientable.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Orientable.java
index 31a2915255..03e8959602 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Orientable.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Orientable.java
@@ -84,8 +84,7 @@ public sealed interface Orientable extends Primitive
*/
@UML(identifier="proxy", specification=ISO_19107)
default Orientable getProxy(){
- //TODO
- throw new UnsupportedOperationException();
+ return getReverse();
}
/**
@@ -97,8 +96,7 @@ public sealed interface Orientable extends Primitive
*/
@UML(identifier="primitive", specification=ISO_19107)
default Primitive getPrimitive(){
- //TODO
- throw new UnsupportedOperationException();
+ return this;
}
/**
@@ -112,9 +110,6 @@ public sealed interface Orientable extends Primitive
*/
// called opposite on figure 16
@UML(identifier="reverse", specification=ISO_19107)
- default Orientable getReverse(){
- //TODO
- throw new UnsupportedOperationException();
- }
+ Orientable getReverse();
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Solid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Solid.java
index 90ee93519a..11f965cc22 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Solid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Solid.java
@@ -167,7 +167,7 @@ public sealed interface Solid extends Primitive
*/
@UML(identifier="controlPoint", specification=ISO_19107)
default List<DirectPosition> getControlPoints() {
- throw new UnsupportedOperationException();
+ return List.of();
}
/**
@@ -180,7 +180,7 @@ public sealed interface Solid extends Primitive
*/
@UML(identifier="interpolation", specification=ISO_19107)
default SolidInterpolation getInterpolation() {
- throw new UnsupportedOperationException();
+ return SolidInterpolation.NONE;
}
/**
@@ -200,7 +200,7 @@ public sealed interface Solid extends Primitive
*/
@UML(identifier="knot", specification=ISO_19107)
default List<double[]> getKnots() {
- throw new UnsupportedOperationException();
+ return List.of();
}
}
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 c9e94c5880..50cbba7829 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
@@ -87,6 +87,21 @@ public sealed interface Surface extends Orientable
return 2;
}
+ /**
+ * Returns this surface with the opposite orientation, i.e. with its up-normal flipped.
+ *
+ * @see ISO 19107:2019 - 6.4.15.3
+ */
+ @Override
+ default Surface getReverse() {
+ /*
+ * A view of this surface carrying the opposite orientation sign, for the same reason as
+ * `Curve.getReverse()`: reversing changes no coordinate. `DefaultReversedSurface` returns
+ * this surface back from its own `getReverse()`, so wrapping never nests.
+ */
+ return GeometryFactory.DEFAULT.createReversed(this);
+ }
+
/**
* The area of this Surface, as measured in the spatial reference system of this Surface.
*
@@ -176,8 +191,7 @@ public sealed interface Surface extends Orientable
*/
@UML(identifier="interpolation", specification=ISO_19107)
default List<SurfaceInterpolation> getInterpolation() {
- //TODO
- throw new UnsupportedOperationException();
+ return List.of(SurfaceInterpolation.PLANAR);
}
/**
@@ -192,8 +206,7 @@ public sealed interface Surface extends Orientable
*/
@UML(identifier="numDerivativesBoundary", specification=ISO_19107)
default Integer getNumDerivativesBoundary() {
- //TODO
- throw new UnsupportedOperationException();
+ return 0;
}
/**
@@ -206,8 +219,7 @@ public sealed interface Surface extends Orientable
*/
@UML(identifier="numDerivativeInterior", specification=ISO_19107)
default Integer getNumDerivativeInterior() {
- //TODO
- throw new UnsupportedOperationException();
+ return 0;
}
/**
@@ -250,8 +262,7 @@ public sealed interface Surface extends Orientable
*/
@UML(identifier="controlPoint", specification=ISO_19107)
default List<DirectPosition> getControlPoints() {
- //TODO
- throw new UnsupportedOperationException();
+ return List.of();
}
/**
@@ -272,8 +283,7 @@ public sealed interface Surface extends Orientable
*/
@UML(identifier="knot", specification=ISO_19107)
default List<double[]> getKnots() {
- //TODO
- throw new UnsupportedOperationException();
+ return List.of();
}
/**
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCurvePolygon.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCurvePolygon.java
index 9d7fbc4c52..80c9b74ae3 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCurvePolygon.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultCurvePolygon.java
@@ -24,6 +24,7 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.Curve;
import org.apache.sis.geometries.Geometries;
import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.surface.CurvePolygon;
@@ -83,9 +84,17 @@ public non-sealed class DefaultCurvePolygon extends AbstractGeometry implements
return exterior.getEnvelope();
}
+ /**
+ * Returns the exterior ring followed by the interior rings.
+ */
@Override
public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
+ final var rings = new Curve[interiors.size() + 1];
+ rings[0] = exterior;
+ for (int i = 1; i < rings.length; i++) {
+ rings[i] = interiors.get(i - 1);
+ }
+ return GeometryFactory.DEFAULT.createMultiCurve(getCoordinateReferenceSystem(), rings);
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
index 8a427c31e2..9c15c172b5 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
@@ -26,7 +26,6 @@ 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.surface.Ellipsoid;
import org.apache.sis.maths.DataType;
import org.apache.sis.maths.NDArrays;
@@ -216,16 +215,6 @@ public non-sealed class DefaultEllipsoid extends AbstractGeometry implements Ell
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.");
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 eeb5736e90..85a86dd6ad 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
@@ -16,6 +16,7 @@
*/
package org.apache.sis.geometries.internal.shared;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.measure.Quantity;
@@ -322,24 +323,30 @@ public non-sealed class DefaultNurbSurface extends AbstractGeometry implements N
return degree;
}
+ /**
+ * Returns a copy of the knot vectors, the one of the <var>u</var> parameter first.
+ */
@Override
public List<double[]> getKnots() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return List.of(knotsU.clone(), knotsV.clone());
}
+ /**
+ * Returns {@link KnotType#NON_UNIFORM}, the general case which makes no assumption
+ * on the spacing and multiplicity of the knots.
+ */
@Override
public KnotType getKnotSpec() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return KnotType.NON_UNIFORM;
}
+ /**
+ * Returns {@link BSplineSurfaceForm#UNSPECIFIED}: this surface is not known to approximate
+ * any particular surface.
+ */
@Override
public BSplineSurfaceForm getSurfaceForm() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- @Override
- public boolean isPolynomial() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return BSplineSurfaceForm.UNSPECIFIED;
}
@Override
@@ -347,19 +354,34 @@ public non-sealed class DefaultNurbSurface extends AbstractGeometry implements N
throw new UnsupportedOperationException("Not supported yet.");
}
+ /**
+ * Returns the number of control points along the <var>u</var> direction.
+ */
@Override
public int getRows() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return controlPoints.length;
}
+ /**
+ * Returns the number of control points along the <var>v</var> direction.
+ */
@Override
public int getColumns() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return controlPoints[0].length;
}
+ /**
+ * Returns views of the control points in row-major order, without their weights.
+ */
@Override
public List<DirectPosition> getControlPoints() {
- throw new UnsupportedOperationException("Not supported yet.");
+ final var positions = new ArrayList<DirectPosition>(getRows() * getColumns());
+ for (final Vector<?>[] row : controlPoints) {
+ for (final Vector<?> p : row) {
+ positions.add(Vectors.asDirectPostion(p));
+ }
+ }
+ return positions;
}
@Override
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 04c2772b47..dfc2e9da58 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
@@ -19,7 +19,6 @@ package org.apache.sis.geometries.internal.shared;
import java.util.List;
import java.util.Objects;
import javax.measure.Quantity;
-import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.GeometryCollection;
@@ -99,13 +98,4 @@ public non-sealed class DefaultPolyhedron extends AbstractGeometry implements Po
throw new UnsupportedOperationException("Not supported yet.");
}
- @Override
- public List<DirectPosition> getControlPoints() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- @Override
- public List<double[]> getKnots() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultReversedSurface.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultReversedSurface.java
index 5114c5c4a0..6ed92ccf10 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultReversedSurface.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultReversedSurface.java
@@ -16,16 +16,20 @@
*/
package org.apache.sis.geometries.internal.shared;
+import java.util.List;
import java.util.Objects;
import javax.measure.Quantity;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryCollection;
+import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Orientable;
import org.apache.sis.geometries.Primitive;
import org.apache.sis.geometries.Surface;
+import org.apache.sis.geometries.SurfaceInterpolation;
import org.apache.sis.geometries.DataPointsType;
@@ -58,10 +62,18 @@ public non-sealed class DefaultReversedSurface extends AbstractGeometry implemen
}
@Override
- public Orientable getReverse() {
+ public Surface getReverse() {
return base;
}
+ /**
+ * Returns the interpolations of the base surface: reversing a surface does not change how it is built.
+ */
+ @Override
+ public List<SurfaceInterpolation> getInterpolation() {
+ return base.getInterpolation();
+ }
+
/**
* Returns the geometry type of the base surface: reversing a surface does not change what kind
* of surface it is.
@@ -104,9 +116,25 @@ public non-sealed class DefaultReversedSurface extends AbstractGeometry implemen
return base.getEnvelope();
}
+ /**
+ * Returns the boundary of the base surface with each curve reversed, so that this surface,
+ * whose up-normal is flipped, stays on the left of its boundary.
+ */
@Override
public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
+ final Geometry boundary = base.getBoundary();
+ if (boundary instanceof Orientable o) {
+ return o.getReverse();
+ }
+ if (boundary instanceof GeometryCollection<?> c) {
+ final var reversed = new Geometry[c.getNumGeometries()];
+ for (int i = 0; i < reversed.length; i++) {
+ final Geometry g = c.getGeometryN(i);
+ reversed[i] = (g instanceof Orientable o) ? o.getReverse() : g;
+ }
+ return GeometryFactory.DEFAULT.createGeometryCollection(c.getElementType(), c.getCoordinateReferenceSystem(), reversed);
+ }
+ return boundary;
}
@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 4cc582d057..7c6c4acebc 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
@@ -21,7 +21,6 @@ import javax.measure.Quantity;
import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.Geometry;
-import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.GeometryCollection;
@@ -120,13 +119,4 @@ public non-sealed class DefaultSolid extends AbstractGeometry implements Solid {
throw new UnsupportedOperationException("Not supported yet.");
}
- @Override
- public List<DirectPosition> getControlPoints() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- @Override
- public List<double[]> getKnots() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
}
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 ef1e4d6984..70223e83d6 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
@@ -25,7 +25,6 @@ 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.surface.Sphere;
import org.apache.sis.maths.DataType;
import org.apache.sis.maths.NDArrays;
@@ -154,16 +153,6 @@ public non-sealed class DefaultSphere extends AbstractGeometry implements Sphere
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.");
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 0f2a763c2c..3d24496ada 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
@@ -99,9 +99,13 @@ public abstract sealed class AbstractPolyhedron extends AbstractGeometry impleme
throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
}
+ /**
+ * Returns the {@linkplain #getExteriorShell() exterior shell}: a regular polyhedron has no void,
+ * so its faces are its whole boundary.
+ */
@Override
public GeometryCollection<? extends Surface> getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return getExteriorShell();
}
/**
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 5ea62d8613..d7ddb0f81b 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,6 +16,7 @@
*/
package org.apache.sis.geometries.surface;
+import java.util.List;
import static org.opengis.annotation.Specification.ISO_12113;
import org.opengis.annotation.UML;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -23,6 +24,7 @@ import org.apache.sis.geometries.BBox;
import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Surface;
+import org.apache.sis.geometries.SurfaceInterpolation;
import org.apache.sis.geometries.internal.shared.DefaultCapsule;
import org.apache.sis.maths.DataType;
import org.apache.sis.maths.ReadOnly;
@@ -64,6 +66,15 @@ public sealed interface Capsule extends Surface
return false;
}
+ /**
+ * Returns {@link SurfaceInterpolation#SPHERICAL} for the two hemispherical ends
+ * and {@link SurfaceInterpolation#CONIC} for the band joining them.
+ */
+ @Override
+ default List<SurfaceInterpolation> getInterpolation() {
+ return List.of(SurfaceInterpolation.SPHERICAL, SurfaceInterpolation.CONIC);
+ }
+
/**
* @return the center of the bottom sphere
*/
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 653f04c508..254686b012 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
@@ -83,6 +83,22 @@ public sealed interface Ellipsoid extends ParametricCurveSurface
return List.of(SurfaceInterpolation.ELLIPTICAL);
}
+ /**
+ * Returns {@link GeometryType#ELLIPTICARC}: the horizontal curves are the ellipses of constant latitude.
+ */
+ @Override
+ default GeometryType getHorizontalCurveType() {
+ return GeometryType.ELLIPTICARC;
+ }
+
+ /**
+ * Returns {@link GeometryType#ELLIPTICARC}: the vertical curves are the ellipses of constant longitude.
+ */
+ @Override
+ default GeometryType getVerticalCurveType() {
+ return GeometryType.ELLIPTICARC;
+ }
+
/**
* 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;
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 144d46c9c7..8e8218958d 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
@@ -87,6 +87,26 @@ public sealed interface Sphere extends ParametricCurveSurface
return List.of(SurfaceInterpolation.SPHERICAL);
}
+ /**
+ * Returns {@link GeometryType#CIRCLE}: the horizontal curves are the circles of constant latitude.
+ *
+ * @see ISO 19107:2019 - 8.5.2
+ */
+ @Override
+ default GeometryType getHorizontalCurveType() {
+ return GeometryType.CIRCLE;
+ }
+
+ /**
+ * Returns {@link GeometryType#CIRCLE}: the vertical curves are the great circles of constant longitude.
+ *
+ * @see ISO 19107:2019 - 8.5.2
+ */
+ @Override
+ default GeometryType getVerticalCurveType() {
+ return GeometryType.CIRCLE;
+ }
+
/**
* @return radius of the sphere.
*/
(sis) 05/12: feat(Geometry): implement surface perimeter and area on several classes
Posted by js...@apache.org.
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 <jo...@geomatys.com>
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;
(sis) 07/12: feat(Geometry): implement default polygon area computation
Posted by js...@apache.org.
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 42152ec38249a43068359f3df73250c16ef67f8a
Author: jsorel <jo...@geomatys.com>
AuthorDate: Wed Oct 14 12:31:43 2026 +0200
feat(Geometry): implement default polygon area computation
---
.../sis/geometries/internal/shared/Curves.java | 69 +++++++++++++++++++++-
.../geometries/internal/shared/DefaultPolygon.java | 15 ++++-
.../apache/sis/geometries/surface/Triangle.java | 18 +++---
3 files changed, 88 insertions(+), 14 deletions(-)
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/Curves.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/Curves.java
index cda7562b28..47e6c59ea8 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/Curves.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/Curves.java
@@ -21,11 +21,13 @@ import javax.measure.Quantity;
import javax.measure.Unit;
import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.Point;
+import org.apache.sis.maths.Vector;
import org.apache.sis.measure.Units;
/**
- * Helper methods for locating positions at a distance along a curve.
+ * Helper methods for computations on curves, such as locating positions at a distance along a curve
+ * or measuring the area enclosed by a ring.
*
* @author Johann Sorel (Geomatys)
*/
@@ -34,6 +36,71 @@ public final class Curves {
private Curves() {
}
+ /**
+ * Returns the area enclosed by the given ring of positions.
+ *
+ * <ul>
+ * <li>In 2 dimensions, the area is computed with the shoelace formula.</li>
+ * <li>In 3 dimensions or more, the area is computed on the first three coordinates with Newell's
+ * method: half the norm of the sum of the cross products of consecutive positions. This is
+ * exact for a ring lying in a plane, in any orientation of that plane, and gives the area
+ * projected on the best-fitting plane otherwise.</li>
+ * </ul>
+ *
+ * <p>The ring may repeat its first position at the end or not. The positions are taken relative
+ * to the first one, which avoids losing precision with large coordinate values.</p>
+ *
+ * @param ring positions of the ring, in order.
+ * @return the area enclosed by the ring, in the square of the unit of the coordinates.
+ */
+ public static double ringArea(final DataPoints ring) {
+ final int n = ring.size();
+ final int dim = ring.getDimension();
+ if (n < 3 || dim < 2) {
+ return 0;
+ }
+ final Vector<?> origin = ring.getPosition(0);
+ if (dim == 2) {
+ final double ox = origin.get(0);
+ final double oy = origin.get(1);
+ Vector<?> p = ring.getPosition(n - 1);
+ double ax = p.get(0) - ox;
+ double ay = p.get(1) - oy;
+ double sum = 0;
+ for (int i = 0; i < n; i++) {
+ p = ring.getPosition(i);
+ final double bx = p.get(0) - ox;
+ final double by = p.get(1) - oy;
+ sum += ax*by - ay*bx;
+ ax = bx;
+ ay = by;
+ }
+ return Math.abs(sum) / 2;
+ } else {
+ final double ox = origin.get(0);
+ final double oy = origin.get(1);
+ final double oz = origin.get(2);
+ Vector<?> p = ring.getPosition(n - 1);
+ double ax = p.get(0) - ox;
+ double ay = p.get(1) - oy;
+ double az = p.get(2) - oz;
+ double nx = 0, ny = 0, nz = 0;
+ for (int i = 0; i < n; i++) {
+ p = ring.getPosition(i);
+ final double bx = p.get(0) - ox;
+ final double by = p.get(1) - oy;
+ final double bz = p.get(2) - oz;
+ nx += ay*bz - az*by;
+ ny += az*bx - ax*bz;
+ nz += ax*by - ay*bx;
+ ax = bx;
+ ay = by;
+ az = bz;
+ }
+ return Math.sqrt(nx*nx + ny*ny + nz*nz) / 2;
+ }
+ }
+
/**
* Returns whether the given distance is expressed in percent of the curve length.
*
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolygon.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolygon.java
index 42ab75bc80..5df2377c5c 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolygon.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPolygon.java
@@ -22,6 +22,7 @@ import javax.measure.Quantity;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
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;
@@ -87,10 +88,20 @@ public non-sealed class DefaultPolygon extends AbstractGeometry implements Polyg
return getExteriorRing().getEnvelope();
}
+ /**
+ * Returns the area enclosed by the exterior ring minus the areas enclosed by the interior rings.
+ * The area is measured in the plane of the polygon, which may have any orientation in a
+ * 3-dimensional space.
+ *
+ * @see Curves#ringArea(DataPoints)
+ */
@Override
public Quantity<?> getArea() {
- //TODO : fallback on JTS until implemented
- return Quantities.create(asJTS().getArea(), Geometries.getAreaUnit(this));
+ double area = Curves.ringArea(exterior.getDataPoints());
+ for (final LinearRing interior : interiors) {
+ area -= Curves.ringArea(interior.getDataPoints());
+ }
+ return Quantities.create(area, Geometries.getAreaUnit(this));
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Triangle.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Triangle.java
index 8fb960cc00..30baca299e 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Triangle.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Triangle.java
@@ -25,10 +25,12 @@ import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.BBox;
import org.apache.sis.geometries.DataPoints;
+import org.apache.sis.geometries.Geometries;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Point;
import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.curve.LinearRing;
+import org.apache.sis.geometries.internal.shared.Curves;
import org.apache.sis.geometries.internal.shared.DefaultTriangle;
import org.apache.sis.geometries.operation.simplify.greedyinsert.WTriangle;
import org.apache.sis.geometries.operation.triangulate.delaunay.OrientedTriangle;
@@ -37,7 +39,6 @@ import org.apache.sis.maths.Vector;
import org.apache.sis.maths.Vector2D;
import org.apache.sis.maths.Vector3D;
import org.apache.sis.measure.Quantities;
-import org.apache.sis.measure.Units;
/**
@@ -124,18 +125,13 @@ public sealed interface Triangle extends Polygon
return env;
}
+ /**
+ * Returns the area of this triangle, measured in its plane, which may have any orientation
+ * in a 3-dimensional space.
+ */
@Override
default Quantity<?> getArea() {
- final DataPoints points = getExteriorRing().getDataPoints();
- final Vector<?> a = points.getPosition(0);
- final Vector<?> b = points.getPosition(1);
- final Vector<?> c = points.getPosition(2);
- final double area = (
- a.get(0) * (b.get(1) - c.get(1))
- + b.get(0) * (c.get(1) - a.get(1))
- + c.get(0) * (a.get(1) - b.get(1))
- ) / 2.0;
- return Quantities.create(Math.abs(area), Units.SQUARE_METRE);
+ return Quantities.create(Curves.ringArea(getExteriorRing().getDataPoints()), Geometries.getAreaUnit(this));
}
/**
(sis) 08/12: feat(Geometry): enfore Geometry always have a CRS, compute polyhedron volume
Posted by js...@apache.org.
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 e2171bdbd82ba897ad4005e1276b9759767815ff
Author: jsorel <jo...@geomatys.com>
AuthorDate: Wed Oct 14 14:32:43 2026 +0200
feat(Geometry): enfore Geometry always have a CRS, compute polyhedron volume
---
.../main/org/apache/sis/geometries/BBox.java | 30 ++++++--
.../main/org/apache/sis/geometries/Geometries.java | 32 +++++---
.../main/org/apache/sis/geometries/HyperPlane.java | 2 +-
.../main/org/apache/sis/geometries/Plane.java | 2 +-
.../main/org/apache/sis/geometries/Point.java | 40 +++++++++-
.../org/apache/sis/geometries/adapter/Srid.java | 5 +-
.../sis/geometries/adapter/WellKnownBinary.java | 9 +--
.../sis/geometries/adapter/WellKnownText.java | 8 +-
.../main/org/apache/sis/geometries/curve/Ray.java | 2 +-
.../internal/shared/DefaultCompoundCurve.java | 5 +-
.../geometries/internal/shared/DefaultEmpty.java | 5 +-
.../internal/shared/DefaultGeometryCollection.java | 5 +-
.../internal/shared/DefaultNurbCurve.java | 1 +
.../internal/shared/DefaultNurbSurface.java | 12 +++
.../internal/shared/DefaultOffsetCurve.java | 4 +-
.../internal/shared/DefaultPolyhedralSurface.java | 8 +-
.../internal/shared/DefaultPolyhedron.java | 4 -
.../geometries/internal/shared/DefaultPrism.java | 4 +-
.../geometries/internal/shared/DefaultSolid.java | 3 +-
.../sis/geometries/mesh/MultiMeshPrimitive.java | 1 +
.../sis/geometries/operation/ProcessorUtils.java | 2 -
.../operation/simplify/greedyinsert/WTriangle.java | 9 +--
.../triangulate/delaunay/OrientedEdge.java | 3 +-
.../triangulate/delaunay/OrientedTriangle.java | 9 +--
.../apache/sis/geometries/solid/Polyhedron.java | 88 ++++++++++++++++++++++
.../org/apache/sis/geometries/surface/Capsule.java | 2 +-
.../apache/sis/geometries/surface/Cylinder.java | 2 +-
.../apache/sis/geometries/surface/Triangle.java | 10 +++
28 files changed, 232 insertions(+), 75 deletions(-)
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/BBox.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/BBox.java
index 50a71fd685..873d348f15 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/BBox.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/BBox.java
@@ -47,7 +47,7 @@ public final class BBox extends GeneralEnvelope implements Geometry {
* @param dimension number of dimensions of the bbox, must be positive.
*/
public BBox(int dimension) {
- super(dimension);
+ super(Geometries.getUndefinedCRS(dimension));
}
/**
@@ -55,7 +55,7 @@ public final class BBox extends GeneralEnvelope implements Geometry {
* @param corners lower corner and higher corner, in crs axis order
*/
public BBox(int dimension, double ... corners) {
- super(dimension);
+ super(Geometries.getUndefinedCRS(dimension));
setEnvelope(corners);
}
@@ -65,6 +65,7 @@ public final class BBox extends GeneralEnvelope implements Geometry {
*/
public BBox(Vector lower, Vector upper) {
super(Vectors.asDirectPostion(lower), Vectors.asDirectPostion(upper));
+ ensureCRS();
}
/**
@@ -88,6 +89,26 @@ public final class BBox extends GeneralEnvelope implements Geometry {
*/
public BBox(Envelope env) {
super(env);
+ ensureCRS();
+ }
+
+ /**
+ * Assigns an undefined reference system if this box has none,
+ * since the reference system of a geometry is never null.
+ */
+ private void ensureCRS() {
+ if (super.getCoordinateReferenceSystem() == null) {
+ super.setCoordinateReferenceSystem(Geometries.getUndefinedCRS(getDimension()));
+ }
+ }
+
+ /**
+ * Sets the reference system of this box, or an undefined one if the given one is null.
+ * The coordinates are not transformed.
+ */
+ @Override
+ public void setCoordinateReferenceSystem(final CoordinateReferenceSystem crs) {
+ super.setCoordinateReferenceSystem(Geometries.orUndefinedCRS(crs, getDimension()));
}
@Override
@@ -140,10 +161,7 @@ public final class BBox extends GeneralEnvelope implements Geometry {
@Override
public Geometry getBoundary() {
final int dim = getDimension();
- CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
- if (crs == null) {
- crs = Geometries.getUndefinedCRS(dim);
- }
+ final CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
final GeometryFactory factory = GeometryFactory.DEFAULT;
if (isEmpty()) {
return factory.createEmpty(crs);
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
index 73922149d2..9eb963ebc8 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
@@ -129,6 +129,19 @@ public final class Geometries {
*/
public static final CoordinateReferenceSystem PSEUDOGEO_3D = createCartesianCRS3D(X_RIGHT, Y_FORWARD, Z_UP);
+ /**
+ * Returns the given reference system, or the undefined reference system of the given dimension
+ * if the given one is null. The reference system of a geometry is never null, so this method is
+ * used by geometries built on coordinates which may carry no reference system.
+ *
+ * @param crs the reference system, or {@code null}.
+ * @param dimension number of dimensions of the undefined reference system to use if {@code crs} is null.
+ * @return the given reference system, or an undefined one if {@code null}.
+ */
+ public static CoordinateReferenceSystem orUndefinedCRS(final CoordinateReferenceSystem crs, final int dimension) {
+ return (crs != null) ? crs : getUndefinedCRS(dimension);
+ }
+
/**
* Get an undefined CRS whic cannot be converted to any other CRS.
* @param nbDim CRS dimension
@@ -703,8 +716,7 @@ public final class Geometries {
* {@linkplain Surface#getPerimeter() perimeter}, are expressed.
*
* @param geom the geometry from which to get the coordinate unit.
- * @return unit of the first coordinate system axis, or {@link Units#UNITY} if the geometry
- * has no coordinate reference system.
+ * @return unit of the first coordinate system axis.
*/
@SuppressWarnings("rawtypes")
public static Unit getLinearUnit(final Geometry geom) {
@@ -716,8 +728,8 @@ public final class Geometries {
* geometry is expressed, as the product of the units of the two first coordinate system axes.
*
* @param geom the geometry from which to get the area unit.
- * @return product of the units of the two first axes, or {@link Units#UNITY} if the geometry
- * has no coordinate reference system or if that system is not at least 2-dimensional.
+ * @return product of the units of the two first axes, or {@link Units#UNITY} if the
+ * coordinate reference system is not at least 2-dimensional.
*/
@SuppressWarnings("rawtypes")
public static Unit getAreaUnit(final Geometry geom) {
@@ -729,8 +741,8 @@ public final class Geometries {
* geometry is expressed, as the product of the units of the three first coordinate system axes.
*
* @param geom the geometry from which to get the volume unit.
- * @return product of the units of the three first axes, or {@link Units#UNITY} if the geometry
- * has no coordinate reference system or if that system is not at least 3-dimensional.
+ * @return product of the units of the three first axes, or {@link Units#UNITY} if the
+ * coordinate reference system is not at least 3-dimensional.
*/
@SuppressWarnings("rawtypes")
public static Unit getVolumeUnit(final Geometry geom) {
@@ -745,15 +757,11 @@ public final class Geometries {
* @param geom the geometry from which to get the units.
* @param n number of leading axes to multiply.
* @return product of the units of the {@code n} first axes, or {@link Units#UNITY} if the
- * geometry has no coordinate reference system or has less than {@code n} dimensions.
+ * geometry has less than {@code n} dimensions.
*/
@SuppressWarnings("rawtypes")
private static Unit productOfAxisUnits(final Geometry geom, final int n) {
- final CoordinateReferenceSystem crs = geom.getCoordinateReferenceSystem();
- if (crs == null) {
- return Units.UNITY;
- }
- final CoordinateSystem cs = crs.getCoordinateSystem();
+ final CoordinateSystem cs = geom.getCoordinateReferenceSystem().getCoordinateSystem();
if (cs.getDimension() < n) {
return Units.UNITY;
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/HyperPlane.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/HyperPlane.java
index ecff5d2b03..72068f6ad6 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/HyperPlane.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/HyperPlane.java
@@ -114,7 +114,7 @@ public final class HyperPlane extends AbstractGeometry{
public BBox getEnvelope() {
final ReadOnly.Vector<?> position = getPosition();
final int dim = normal.getDimension();
- final BBox bbox = new BBox(dim);
+ final BBox bbox = new BBox(getCoordinateReferenceSystem());
/*
* Search the axis the normal is aligned with. There is one only if every other
* dimension of the normal is null, in which case the hyperplane is flat on it.
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Plane.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Plane.java
index 3bd323154a..da8d2ee557 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Plane.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Plane.java
@@ -175,7 +175,7 @@ public final class Plane extends AbstractGeometry{
public BBox getEnvelope() {
final ReadOnly.Vector<?> position = getPosition();
final int dim = normal.getDimension();
- final BBox bbox = new BBox(dim);
+ final BBox bbox = new BBox(getCoordinateReferenceSystem());
/*
* Search the axis the normal is aligned with. There is one only if every other
* dimension of the normal is null, in which case the plane is flat on it.
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Point.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Point.java
index 59100d9cb9..dcb9d731b5 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Point.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Point.java
@@ -19,9 +19,14 @@ package org.apache.sis.geometries;
import java.util.List;
import static org.opengis.annotation.Specification.ISO_19107;
import org.opengis.annotation.UML;
+import org.opengis.coordinate.MismatchedDimensionException;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.SingleCRS;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.cs.EllipsoidalCS;
+import org.opengis.referencing.cs.SphericalCS;
import org.apache.sis.geometries.cs.Bearing;
import org.apache.sis.geometries.internal.shared.ArrayDataPoints;
import org.apache.sis.geometries.internal.shared.DefaultPoint;
@@ -29,6 +34,9 @@ import org.apache.sis.geometries.internal.shared.IndexedPoint;
import org.apache.sis.geometries.mesh.MeshPrimitive;
import org.apache.sis.maths.ReadOnly;
import org.apache.sis.maths.Vector;
+import org.apache.sis.maths.Vectors;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.util.Utilities;
/**
@@ -260,15 +268,43 @@ public sealed interface Point extends Primitive
* Returns the vector, in the tangent space at this point, whose direction determines the
* geodesic curve reaching the given position and whose length is the distance to it.
*
+ * <p>In a reference system without ellipsoidal or spherical axes, the geodesic curve is the straight
+ * line, so the returned vector is the difference between the coordinates of the given position and
+ * the coordinates of this point.</p>
+ *
* @param toPoint position to reach from this point.
* @return vector from this point to the given position.
+ * @throws MismatchedDimensionException if the given position does not have the dimension of this point.
+ * @throws IllegalArgumentException if the given position is in another reference system.
+ * @throws UnsupportedOperationException if the reference system of this point has ellipsoidal or
+ * spherical axes, for which the geodesic curves are not yet supported.
*
* @see ISO 19107:2019 - 6.4.13.4
*/
@UML(identifier="vectorToPoint", specification=ISO_19107)
default Vector<?> vectorToPoint(DirectPosition toPoint) {
- //TODO
- throw new UnsupportedOperationException();
+ final CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
+ for (final SingleCRS component : CRS.getSingleComponents(crs)) {
+ final CoordinateSystem cs = component.getCoordinateSystem();
+ if (cs instanceof EllipsoidalCS || cs instanceof SphericalCS) {
+ throw new UnsupportedOperationException("Geodesic curves in " + component.getName() + " are not supported yet.");
+ }
+ }
+ final CoordinateReferenceSystem other = toPoint.getCoordinateReferenceSystem();
+ if (other != null && !Utilities.equalsIgnoreMetadata(crs, other)) {
+ throw new IllegalArgumentException("The position is not in the reference system of this point.");
+ }
+ final Vector<?> position = getPosition();
+ final int dimension = position.getDimension();
+ if (toPoint.getDimension() != dimension) {
+ throw new MismatchedDimensionException("Expected a position of dimension " + dimension
+ + " but got " + toPoint.getDimension() + '.');
+ }
+ final Vector<?> vector = Vectors.createDouble(dimension);
+ for (int i = 0; i < dimension; i++) {
+ vector.set(i, toPoint.getCoordinate(i) - position.get(i));
+ }
+ return vector;
}
/**
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/Srid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/Srid.java
index c84585611f..1e9fb91359 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/Srid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/Srid.java
@@ -85,13 +85,10 @@ final class Srid {
* to a two-dimensional one keeps no identifier — neither the combined system nor the
* horizontal component extracted back from it carries the code the height was added to.</p>
*
- * @param crs the system whose identifier to return, or {@code null}.
+ * @param crs the system whose identifier to return.
* @return the spatial reference identifier, or {@link #UNDEFINED} if none.
*/
static int of(final CoordinateReferenceSystem crs) {
- if (crs == null) {
- return UNDEFINED;
- }
int srid = declaredCode(crs);
if (srid == UNDEFINED) {
final SingleCRS horizontal = CRS.getHorizontalComponent(crs);
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinary.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinary.java
index 33ac4ffc3b..de6100c825 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinary.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownBinary.java
@@ -523,14 +523,7 @@ public final class WellKnownBinary {
* ordinate following the position ones.
*/
private boolean writeHeader(final Output out, final int code, final Geometry geometry) {
- final CoordinateReferenceSystem crs = geometry.getCoordinateReferenceSystem();
- if (crs == null) {
- throw new IllegalArgumentException("Cannot write a " + geometry.getGeometryType()
- + " in Well-Known Binary:"
- + " it has no coordinate reference system, therefore no known number of dimensions."
- + " An empty collection takes one from the factory method which creates it.");
- }
- final int dimension = crs.getCoordinateSystem().getDimension();
+ final int dimension = geometry.getCoordinateReferenceSystem().getCoordinateSystem().getDimension();
final boolean hasM = hasMeasure(geometry);
final boolean hasZ;
switch (dimension) {
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownText.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownText.java
index b79fdc1376..d574e8fc29 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownText.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/WellKnownText.java
@@ -551,13 +551,7 @@ public final class WellKnownText {
*/
private static boolean appendHeader(final StringBuilder sb, final String keyword, final Geometry geometry) {
sb.append(keyword);
- final CoordinateReferenceSystem crs = geometry.getCoordinateReferenceSystem();
- if (crs == null) {
- throw new IllegalArgumentException("Cannot write a " + keyword + " in Well-Known Text:"
- + " it has no coordinate reference system, therefore no known number of dimensions."
- + " An empty collection takes one from the factory method which creates it.");
- }
- final int dimension = crs.getCoordinateSystem().getDimension();
+ final int dimension = geometry.getCoordinateReferenceSystem().getCoordinateSystem().getDimension();
final boolean hasM = hasMeasure(geometry);
switch (dimension) {
case 2: sb.append(hasM ? " M " : " "); break;
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/Ray.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/Ray.java
index 7412d9ff32..acf844e8a7 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/Ray.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/curve/Ray.java
@@ -165,7 +165,7 @@ public sealed interface Ray extends Curve
final ReadOnly.Vector<?> position = getPosition();
final ReadOnly.Vector<?> direction = getDirection();
final int dim = direction.getDimension();
- final BBox bbox = new BBox(dim);
+ final BBox bbox = new BBox(getCoordinateReferenceSystem());
for (int i=0;i<dim;i++){
double d = direction.get(i);
if (d<0){
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 5bab80f024..6b55998f98 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
@@ -21,7 +21,9 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.Curve;
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.util.ArgumentChecks;
/**
@@ -49,7 +51,7 @@ public non-sealed class DefaultCompoundCurve extends AbstractGeometry implements
*/
public DefaultCompoundCurve(CoordinateReferenceSystem fallbackCRS, Curve... curves) {
this.curves = curves;
- this.fallbackCRS = fallbackCRS;
+ this.fallbackCRS = (fallbackCRS != null) ? fallbackCRS : Geometries.getUndefinedCRS(2);
}
@Override
@@ -79,6 +81,7 @@ public non-sealed class DefaultCompoundCurve extends AbstractGeometry implements
@Override
public void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) throws IllegalArgumentException {
+ ArgumentChecks.ensureNonNull("crs", crs);
this.fallbackCRS = crs;
for (final Curve c : curves) {
c.setCoordinateReferenceSystem(crs);
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEmpty.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEmpty.java
index 96c9df9f0b..3beed3926b 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEmpty.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEmpty.java
@@ -21,7 +21,9 @@ import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.Empty;
import org.apache.sis.geometries.DataPointsType;
+import org.apache.sis.geometries.Geometries;
import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.maths.SampleSystem;
/**
@@ -38,7 +40,8 @@ public non-sealed class DefaultEmpty extends AbstractGeometry implements Empty {
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
- return attType.getAttributeSystem(DataPointsType.ATT_POSITION).getCoordinateReferenceSystem();
+ final SampleSystem system = attType.getAttributeSystem(DataPointsType.ATT_POSITION);
+ return Geometries.orUndefinedCRS(system.getCoordinateReferenceSystem(), system.getSize());
}
@Override
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 9ab8087dd1..1fbe40671a 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
@@ -22,6 +22,8 @@ import org.apache.sis.geometries.GeometryCollection;
import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Point;
+import org.apache.sis.geometries.Geometries;
+import org.apache.sis.util.ArgumentChecks;
/**
@@ -51,7 +53,7 @@ public non-sealed class DefaultGeometryCollection<T extends Geometry> extends Ab
public DefaultGeometryCollection(GeometryType elementType, CoordinateReferenceSystem fallbackCRS, T[] geometries) {
this.elementType = elementType;
this.geometries = geometries;
- this.fallbackCRS = fallbackCRS;
+ this.fallbackCRS = (fallbackCRS != null) ? fallbackCRS : Geometries.getUndefinedCRS(2);
}
@Override
@@ -66,6 +68,7 @@ public non-sealed class DefaultGeometryCollection<T extends Geometry> extends Ab
@Override
public void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) throws IllegalArgumentException {
+ ArgumentChecks.ensureNonNull("crs", crs);
fallbackCRS = crs;
for (T c : geometries) {
c.setCoordinateReferenceSystem(crs);
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbCurve.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbCurve.java
index c235e5f91a..90de4c52ed 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbCurve.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbCurve.java
@@ -298,6 +298,7 @@ public non-sealed class DefaultNurbCurve extends AbstractGeometry implements Nur
for (final Vector<?> p : controlPoints) {
bbox.add(p);
}
+ bbox.setCoordinateReferenceSystem(getCoordinateReferenceSystem());
return bbox;
}
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 6c57b652fb..3d66ba5d88 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
@@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.List;
import javax.measure.Quantity;
import org.opengis.geometry.DirectPosition;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.BBox;
import org.apache.sis.geometries.Curve;
import org.apache.sis.geometries.DataPoints;
@@ -29,6 +30,7 @@ import org.apache.sis.geometries.curve.KnotType;
import org.apache.sis.geometries.operation.DeBoorAlgorithm;
import org.apache.sis.geometries.surface.BSplineSurfaceForm;
import org.apache.sis.geometries.surface.NurbSurface;
+import org.apache.sis.geometries.Geometries;
import org.apache.sis.maths.Array;
import org.apache.sis.maths.NDArrays;
import org.apache.sis.maths.Vector;
@@ -299,6 +301,15 @@ public non-sealed class DefaultNurbSurface extends AbstractGeometry implements N
}
+ /**
+ * Returns the reference system of the control points, or an undefined one if they have none.
+ */
+ @Override
+ public CoordinateReferenceSystem getCoordinateReferenceSystem() {
+ final Vector<?> first = controlPoints[0][0];
+ return Geometries.orUndefinedCRS(first.getCoordinateReferenceSystem(), first.getDimension());
+ }
+
/**
* Bounding box of the control points, which contains the surface (convex hull property).
*
@@ -311,6 +322,7 @@ public non-sealed class DefaultNurbSurface extends AbstractGeometry implements N
bbox.add(p);
}
}
+ bbox.setCoordinateReferenceSystem(getCoordinateReferenceSystem());
return bbox;
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultOffsetCurve.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultOffsetCurve.java
index c92ef3f4da..38b2d9ae4f 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultOffsetCurve.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultOffsetCurve.java
@@ -132,11 +132,11 @@ public non-sealed class DefaultOffsetCurve extends AbstractGeometry implements O
return null;
}
final var envelope = new GeneralEnvelope(baseEnvelope);
- final CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
+ final CoordinateReferenceSystem crs = baseCurve.getCoordinateReferenceSystem();
final double value = Math.abs(distance.getValue().doubleValue());
for (int i = 0, n = envelope.getDimension(); i < n; i++) {
double d = value;
- final Unit<?> axisUnit = (crs != null) ? crs.getCoordinateSystem().getAxis(i).getUnit() : null;
+ final Unit<?> axisUnit = crs.getCoordinateSystem().getAxis(i).getUnit();
if (axisUnit != null && !Units.UNITY.equals(distance.getUnit())) {
try {
d = distance.getUnit().getConverterToAny(axisUnit).convert(value);
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 dde10dad89..2834bae517 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
@@ -22,6 +22,8 @@ import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.surface.Polygon;
import org.apache.sis.geometries.surface.PolyhedralSurface;
+import org.apache.sis.geometries.Geometries;
+import org.apache.sis.util.ArgumentChecks;
/**
@@ -38,7 +40,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}.
*/
- private final CoordinateReferenceSystem fallbackCRS;
+ private CoordinateReferenceSystem fallbackCRS;
@SafeVarargs
public DefaultPolyhedralSurface(T... patches) {
@@ -51,7 +53,7 @@ public non-sealed class DefaultPolyhedralSurface<T extends Polygon> extends Abst
*/
public DefaultPolyhedralSurface(CoordinateReferenceSystem fallbackCRS, T[] patches) {
this.patches = patches;
- this.fallbackCRS = fallbackCRS;
+ this.fallbackCRS = (fallbackCRS != null) ? fallbackCRS : Geometries.getUndefinedCRS(2);
}
@Override
@@ -81,6 +83,8 @@ public non-sealed class DefaultPolyhedralSurface<T extends Polygon> extends Abst
@Override
public void setCoordinateReferenceSystem(CoordinateReferenceSystem cs) throws IllegalArgumentException {
+ ArgumentChecks.ensureNonNull("crs", cs);
+ fallbackCRS = cs;
for (final T patch : patches) {
patch.setCoordinateReferenceSystem(cs);
}
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 e850cfe4b9..0a8258f40b 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
@@ -101,9 +101,5 @@ public non-sealed class DefaultPolyhedron extends AbstractGeometry implements Po
return area;
}
- @Override
- public Quantity<?> getVolume() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPrism.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPrism.java
index ea00a68ebc..f035562b4a 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPrism.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPrism.java
@@ -108,7 +108,7 @@ public non-sealed class DefaultPrism extends AbstractGeometry implements Prism {
final double min = extrusionRange.getMinDouble();
final double max = extrusionRange.getMaxDouble();
final CoordinateReferenceSystem baseCRS = base.getCoordinateReferenceSystem();
- if (baseCRS != null && extrusionCrs != null) {
+ if (extrusionCrs != null) {
int offset = 0;
for (final SingleCRS component : CRS.getSingleComponents(baseCRS)) {
final int dimension = component.getCoordinateSystem().getDimension();
@@ -125,7 +125,7 @@ public non-sealed class DefaultPrism extends AbstractGeometry implements Prism {
}
final int n = baseEnvelope.getDimension();
final GeneralEnvelope envelope;
- if (baseCRS != null && extrusionCrs != null) {
+ if (extrusionCrs != null) {
try {
envelope = new GeneralEnvelope(CRS.compound(baseCRS, extrusionCrs));
} catch (FactoryException e) {
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 4c8b444488..f0c3709098 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
@@ -28,6 +28,7 @@ import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Solid;
import org.apache.sis.geometries.Surface;
+import org.apache.sis.geometries.Geometries;
import org.apache.sis.geometry.GeneralEnvelope;
@@ -56,7 +57,7 @@ public non-sealed class DefaultSolid extends AbstractGeometry implements Solid {
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
- return boundaries.get(0).getCoordinateReferenceSystem();
+ return boundaries.isEmpty() ? Geometries.getUndefinedCRS(3) : boundaries.get(0).getCoordinateReferenceSystem();
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MultiMeshPrimitive.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MultiMeshPrimitive.java
index 2f2a1af339..176ae005d5 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MultiMeshPrimitive.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MultiMeshPrimitive.java
@@ -88,6 +88,7 @@ public final class MultiMeshPrimitive<T extends MeshPrimitive> extends AbstractG
@Override
public void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) {
+ ArgumentChecks.ensureNonNull("crs", crs);
for (MeshPrimitive p : primitives) {
p.setCoordinateReferenceSystem(crs);
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ProcessorUtils.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ProcessorUtils.java
index 22098cd059..efa2df09a0 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ProcessorUtils.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ProcessorUtils.java
@@ -86,8 +86,6 @@ public final class ProcessorUtils {
* @see <a href="https://issues.apache.org/jira/browse/SIS-162">SIS-162</a>
*/
private static CoordinateReferenceSystem getOrCreateSubCRS(CoordinateReferenceSystem crs, int lower, int upper) {
- if (crs == null) return crs;
-
int dimension = crs.getCoordinateSystem().getDimension();
if (lower < 0 || lower > upper || upper > dimension) {
throw new IndexOutOfBoundsException("["+lower+".."+upper+"] range is not possible with crs dimension "+ dimension);
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/simplify/greedyinsert/WTriangle.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/simplify/greedyinsert/WTriangle.java
index 7d34e79203..afabf3ac3d 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/simplify/greedyinsert/WTriangle.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/simplify/greedyinsert/WTriangle.java
@@ -21,7 +21,6 @@ import java.util.List;
import java.util.function.BiFunction;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.DataPoints;
-import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.Point;
import org.apache.sis.geometries.DataPointsType;
@@ -30,6 +29,7 @@ import org.apache.sis.geometries.internal.shared.AbstractGeometry;
import org.apache.sis.geometries.internal.shared.IndexedPoint;
import org.apache.sis.geometries.operation.OperationException;
import org.apache.sis.geometries.surface.Triangle;
+import org.apache.sis.geometries.Geometries;
import org.apache.sis.maths.Maths;
import org.apache.sis.maths.ReadOnly;
import org.apache.sis.maths.Vector;
@@ -45,7 +45,7 @@ public final class WTriangle extends AbstractGeometry implements Triangle {
private final LinearRing ring = GeometryFactory.DEFAULT.createLinearRing(new DataPoints() {
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
- return p0.getCoordinateReferenceSystem();
+ return Geometries.orUndefinedCRS(p0.getCoordinateReferenceSystem(), p0.getDimension());
}
@Override
@@ -119,11 +119,6 @@ public final class WTriangle extends AbstractGeometry implements Triangle {
return ring;
}
- @Override
- public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
private static class Candidate implements Comparable<Candidate>{
private final Vector tuple;
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/delaunay/OrientedEdge.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/delaunay/OrientedEdge.java
index f9ec9d9b7d..1840eed752 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/delaunay/OrientedEdge.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/delaunay/OrientedEdge.java
@@ -25,6 +25,7 @@ import org.apache.sis.geometries.curve.LineString;
import org.apache.sis.geometries.internal.shared.AbstractGeometry;
import org.apache.sis.geometries.mesh.MeshPrimitive;
import org.apache.sis.geometries.mesh.MeshPrimitive.Vertex;
+import org.apache.sis.geometries.Geometries;
import org.apache.sis.maths.Maths;
import org.apache.sis.maths.ReadOnly;
import org.apache.sis.maths.Vector;
@@ -203,7 +204,7 @@ public final class OrientedEdge extends AbstractGeometry implements LineString,
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
- return structure.start.getCoordinateReferenceSystem();
+ return Geometries.orUndefinedCRS(structure.start.getCoordinateReferenceSystem(), structure.start.getDimension());
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/delaunay/OrientedTriangle.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/delaunay/OrientedTriangle.java
index 3bb630ae25..e20ab8dc93 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/delaunay/OrientedTriangle.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/delaunay/OrientedTriangle.java
@@ -20,7 +20,6 @@ import java.awt.geom.Point2D;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.DataPoints;
-import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.Point;
import org.apache.sis.geometries.DataPointsType;
@@ -30,6 +29,7 @@ import org.apache.sis.geometries.mesh.MeshPrimitive;
import org.apache.sis.geometries.mesh.MeshPrimitive.Vertex;
import org.apache.sis.geometries.operation.OperationException;
import org.apache.sis.geometries.surface.Triangle;
+import org.apache.sis.geometries.Geometries;
import org.apache.sis.maths.ReadOnly;
import org.apache.sis.maths.Vector;
import org.apache.sis.referencing.internal.shared.ShapeUtilities;
@@ -232,7 +232,7 @@ public final class OrientedTriangle extends AbstractGeometry implements Triangle
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
- return a.getCoordinateReferenceSystem();
+ return Geometries.orUndefinedCRS(a.getCoordinateReferenceSystem(), a.getDimension());
}
@Override
@@ -255,11 +255,6 @@ public final class OrientedTriangle extends AbstractGeometry implements Triangle
return DataPoints.super.getEnvelope();
}
- @Override
- public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
@Override
public DataPointsType getType() {
return DataPointsType.EMPTY;
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Polyhedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Polyhedron.java
index ff9686abb9..62219843b1 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Polyhedron.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Polyhedron.java
@@ -17,16 +17,21 @@
package org.apache.sis.geometries.solid;
import java.util.List;
+import javax.measure.Quantity;
import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.DataPointsType;
+import org.apache.sis.geometries.Geometries;
import org.apache.sis.geometries.GeometryCollection;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Solid;
import org.apache.sis.geometries.SolidInterpolation;
+import org.apache.sis.geometries.curve.LinearRing;
import org.apache.sis.geometries.internal.shared.ConcatenatedDataPoints;
import org.apache.sis.geometries.internal.shared.DefaultPolyhedron;
import org.apache.sis.geometries.solid.polyhedron.AbstractPolyhedron;
import org.apache.sis.geometries.surface.Polygon;
+import org.apache.sis.maths.Vector;
+import org.apache.sis.measure.Quantities;
/**
@@ -80,6 +85,89 @@ public sealed interface Polyhedron extends Solid
return SolidInterpolation.NONE;
}
+ /**
+ * Returns the volume enclosed by the exterior shell minus the volumes enclosed by the interior shells.
+ *
+ * <p>The volume enclosed by a shell is computed with the divergence theorem: each face is split in
+ * triangles, and the signed volumes of the tetrahedra joining those triangles to a common origin are
+ * summed. The faces of a shell shall have consistent orientations, either all outward or all inward.
+ * The holes of a face are subtracted whatever their orientation.</p>
+ *
+ * @throws UnsupportedOperationException if this polyhedron is not 3-dimensional.
+ *
+ * @see ISO 19107:2019 - 6.4.28.4
+ */
+ @Override
+ default Quantity<?> getVolume() {
+ if (getDimension() != 3) {
+ throw new UnsupportedOperationException("The volume of a polyhedron is computed in 3 dimensions only.");
+ }
+ final GeometryCollection<Polygon> exterior = getExteriorShell();
+ double volume = 0;
+ if (exterior.getNumGeometries() != 0) {
+ /*
+ * The volume enclosed by a closed shell does not depend on the origin of the tetrahedra.
+ * Taking a position of the shell avoids losing precision with large coordinate values.
+ */
+ final double[] origin = exterior.getGeometryN(0).getExteriorRing().getDataPoints().getPosition(0).toArrayDouble();
+ volume = Math.abs(signedVolume(exterior, origin));
+ for (final GeometryCollection<Polygon> interior : getInteriorShells()) {
+ volume -= Math.abs(signedVolume(interior, origin));
+ }
+ }
+ return Quantities.create(volume, Geometries.getVolumeUnit(this));
+ }
+
+ /**
+ * Returns the signed volume enclosed by the given shell, positive if the faces are oriented outward.
+ */
+ private static double signedVolume(final GeometryCollection<Polygon> shell, final double[] origin) {
+ double volume = 0;
+ for (int i = 0, n = shell.getNumGeometries(); i < n; i++) {
+ final Polygon face = shell.getGeometryN(i);
+ double v = signedVolume(face.getExteriorRing().getDataPoints(), origin);
+ for (final LinearRing hole : face.getInteriorRings()) {
+ // A hole lies in the plane of its face, so its contribution has the sign of the face if both have the same orientation.
+ final double h = signedVolume(hole.getDataPoints(), origin);
+ v += (Math.signum(h) == Math.signum(v)) ? -h : h;
+ }
+ volume += v;
+ }
+ return volume;
+ }
+
+ /**
+ * Returns the signed volume of the cone joining the given origin to the given planar ring.
+ * The ring is split in a fan of triangles from its first position, and the volumes of the
+ * tetrahedra joining the origin to those triangles are summed.
+ */
+ private static double signedVolume(final DataPoints ring, final double[] origin) {
+ final int n = ring.size();
+ if (n < 3) {
+ return 0;
+ }
+ Vector<?> p = ring.getPosition(0);
+ final double ax = p.get(0) - origin[0];
+ final double ay = p.get(1) - origin[1];
+ final double az = p.get(2) - origin[2];
+ p = ring.getPosition(1);
+ double bx = p.get(0) - origin[0];
+ double by = p.get(1) - origin[1];
+ double bz = p.get(2) - origin[2];
+ double sum = 0;
+ for (int i = 2; i < n; i++) {
+ p = ring.getPosition(i);
+ final double cx = p.get(0) - origin[0];
+ final double cy = p.get(1) - origin[1];
+ final double cz = p.get(2) - origin[2];
+ sum += ax * (by*cz - bz*cy) + ay * (bz*cx - bx*cz) + az * (bx*cy - by*cx);
+ bx = cx;
+ by = cy;
+ bz = cz;
+ }
+ return sum / 6;
+ }
+
/**
* Returns the exterior shell of this polyhedron.
*
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 edc2769956..31407662c5 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
@@ -147,7 +147,7 @@ public sealed interface Capsule extends Surface
final double radiusBottom = getRadiusBottom();
final double radiusTop = getRadiusTop();
final int dim = getDataPoints().getDimension();
- final BBox bbox = new BBox(dim);
+ final BBox bbox = new BBox(getCoordinateReferenceSystem());
for (int i=0;i<dim;i++){
final double b = bottom.get(i);
final double t = top.get(i);
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 08f6f758d2..15ade1d337 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
@@ -204,7 +204,7 @@ public sealed interface Cylinder extends Surface
final Vector<?> axis = getAxis();
final double height = axis.length();
final int dim = getDataPoints().getDimension();
- final BBox bbox = new BBox(dim);
+ final BBox bbox = new BBox(getCoordinateReferenceSystem());
for (int i=0;i<dim;i++){
final double b = bottom.get(i);
final double t = top.get(i);
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Triangle.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Triangle.java
index 30baca299e..e11c6b75a9 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Triangle.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/Triangle.java
@@ -26,6 +26,7 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.BBox;
import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.Geometries;
+import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Point;
import org.apache.sis.geometries.DataPointsType;
@@ -114,6 +115,15 @@ public sealed interface Triangle extends Polygon
getExteriorRing().setCoordinateReferenceSystem(cs);
}
+ /**
+ * Returns the {@linkplain #getExteriorRing() exterior ring}: a triangle has no hole,
+ * so the ring through its three corners is its whole boundary.
+ */
+ @Override
+ default Geometry getBoundary() {
+ return getExteriorRing();
+ }
+
@Override
default Envelope getEnvelope() {
final DataPoints exterior = getExteriorRing().getDataPoints();
(sis) 02/12: feat(Geometry): separate Capsule,Cylinder,Sphere,Ellipsoid as interface and default implementation
Posted by js...@apache.org.
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 <jo...@geomatys.com>
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());
}
(sis) 06/12: feat(Geometry): implement getEnvelope on spline type geometries and getBoundary on BBox and OBBox
Posted by js...@apache.org.
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 7fd587c143600a00a0beee6734464ca69d53dae5
Author: jsorel <jo...@geomatys.com>
AuthorDate: Wed Oct 14 12:14:16 2026 +0200
feat(Geometry): implement getEnvelope on spline type geometries and getBoundary on BBox and OBBox
---
.../main/org/apache/sis/geometries/BBox.java | 81 +++++++++++++++++++++-
.../org/apache/sis/geometries/GeometryFactory.java | 7 +-
.../main/org/apache/sis/geometries/OBBox.java | 14 +++-
.../main/org/apache/sis/geometries/Solid.java | 8 +--
.../main/org/apache/sis/geometries/Surface.java | 7 +-
.../internal/shared/AbstractGeometry.java | 56 +++++++++++++++
.../internal/shared/DefaultBSplineCurve.java | 6 +-
.../internal/shared/DefaultBSplineSolid.java | 18 +++--
.../internal/shared/DefaultBSplineSurface.java | 17 +++--
.../internal/shared/DefaultBilinearGrid.java | 17 +++--
.../internal/shared/DefaultCylinder.java | 6 --
.../internal/shared/DefaultEllipsoid.java | 3 +-
.../internal/shared/DefaultNurbSurface.java | 14 ++--
.../internal/shared/DefaultOffsetCurve.java | 33 ++++++++-
.../geometries/internal/shared/DefaultPrism.java | 48 ++++++++++++-
.../geometries/internal/shared/DefaultSphere.java | 3 +-
.../org/apache/sis/geometries/solid/Frustrum.java | 17 ++++-
.../sis/geometries/solid/ParametricCurveSolid.java | 8 +--
.../apache/sis/geometries/surface/Cylinder.java | 25 +++++++
.../geometries/surface/ParametricCurveSurface.java | 7 +-
20 files changed, 335 insertions(+), 60 deletions(-)
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/BBox.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/BBox.java
index f188a6c71a..50a71fd685 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/BBox.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/BBox.java
@@ -16,14 +16,17 @@
*/
package org.apache.sis.geometries;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.opengis.coordinate.MismatchedDimensionException;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.internal.shared.IndexedPoint;
+import org.apache.sis.geometries.surface.Polygon;
import org.apache.sis.geometry.GeneralEnvelope;
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.Vectors;
@@ -127,9 +130,85 @@ public final class BBox extends GeneralEnvelope implements Geometry {
return this.clone();
}
+ /**
+ * Returns the boundary of this box: its two end points in 1 dimension, a counterclockwise ring
+ * in 2 dimensions, or its six faces oriented outward in 3 dimensions. An empty box has an empty
+ * boundary.
+ *
+ * @throws UnsupportedOperationException if this box has more than 3 dimensions.
+ */
@Override
public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
+ final int dim = getDimension();
+ CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
+ if (crs == null) {
+ crs = Geometries.getUndefinedCRS(dim);
+ }
+ final GeometryFactory factory = GeometryFactory.DEFAULT;
+ if (isEmpty()) {
+ return factory.createEmpty(crs);
+ }
+ switch (dim) {
+ case 1: {
+ return factory.createMultiPoint(crs, factory.createPoint(crs, getMinimum(0)),
+ factory.createPoint(crs, getMaximum(0)));
+ }
+ case 2: {
+ final double xmin = getMinimum(0), xmax = getMaximum(0);
+ final double ymin = getMinimum(1), ymax = getMaximum(1);
+ return factory.createLinearRing(factory.createDataPoints(NDArrays.of(SampleSystem.of(crs),
+ xmin, ymin, xmax, ymin, xmax, ymax, xmin, ymax, xmin, ymin)));
+ }
+ case 3: {
+ final var corners = new Vector<?>[8];
+ for (int i = 0; i < corners.length; i++) {
+ final Vector<?> corner = Vectors.create(crs, DataType.DOUBLE);
+ for (int k = 0; k < 3; k++) {
+ corner.set(k, ((i & (1 << k)) != 0) ? getMaximum(k) : getMinimum(k));
+ }
+ corners[i] = corner;
+ }
+ return boxFaces(crs, corners, false);
+ }
+ default: {
+ throw new UnsupportedOperationException("The boundary of a box is computed in 3 dimensions at most.");
+ }
+ }
+ }
+
+ /**
+ * Returns the six faces of a 3-dimensional box, each one a quadrilateral whose vertices are in
+ * counterclockwise order when seen from outside the box, so that its up-normal points outward.
+ *
+ * @param crs reference system of the corners.
+ * @param corners the 8 corners of the box. The corner at index <var>i</var> is on the upper side
+ * of the box axis <var>k</var> if the bit <var>k</var> of <var>i</var> is set.
+ * @param reverse whether the box axes are a left-handed system, which reverses the orientation.
+ * @return the six faces of the box.
+ */
+ static GeometryCollection<Polygon> boxFaces(final CoordinateReferenceSystem crs,
+ final ReadOnly.Vector<?>[] corners, final boolean reverse)
+ {
+ final int[][] faces = {
+ {0, 4, 6, 2}, // Lower side of axis 0.
+ {1, 3, 7, 5}, // Upper side of axis 0.
+ {0, 1, 5, 4}, // Lower side of axis 1.
+ {2, 6, 7, 3}, // Upper side of axis 1.
+ {0, 2, 3, 1}, // Lower side of axis 2.
+ {4, 5, 7, 6} // Upper side of axis 2.
+ };
+ final GeometryFactory factory = GeometryFactory.DEFAULT;
+ final var polygons = new Polygon[faces.length];
+ for (int f = 0; f < faces.length; f++) {
+ final var ring = new ArrayList<ReadOnly.Vector<?>>(5);
+ for (int k = 0; k < 4; k++) {
+ ring.add(corners[faces[f][reverse ? 3 - k : k]]);
+ }
+ ring.add(ring.get(0));
+ polygons[f] = factory.createPolygon(factory.createLinearRing(factory.createDataPoints(
+ NDArrays.of(ring, SampleSystem.of(crs), DataType.DOUBLE))), null);
+ }
+ return factory.createMultiPolygon(crs, polygons);
}
@Override
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 cbb8e18d00..3ab912a8e3 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
@@ -22,7 +22,6 @@ import java.util.List;
import java.util.Map;
import javax.measure.Quantity;
import javax.measure.Unit;
-import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.SingleCRS;
import org.apache.sis.geometries.cs.Bearing;
@@ -573,7 +572,7 @@ public final class GeometryFactory {
* @param columns number of columns in the parameter grid.
* @param knots knot values, one sequence per surface parameter, or {@code null} if none.
*/
- public BilinearGrid createBilinearGrid(DataPoints points, List<DirectPosition> controlPoints,
+ public BilinearGrid createBilinearGrid(DataPoints points, Array controlPoints,
int rows, int columns, List<double[]> knots)
{
return new DefaultBilinearGrid(points, controlPoints, rows, columns, knots);
@@ -597,7 +596,7 @@ public final class GeometryFactory {
* @param polynomial {@code true} if the surface is polynomial, {@code false} if the control
* points are expressed in homogeneous coordinates, making it rational.
*/
- public BSplineSurface createBSplineSurface(DataPoints points, List<DirectPosition> controlPoints,
+ public BSplineSurface createBSplineSurface(DataPoints points, Array controlPoints,
int rows, int columns, List<double[]> knots, int degree, KnotType knotSpec,
BSplineSurfaceForm surfaceForm, boolean polynomial)
{
@@ -732,7 +731,7 @@ public final class GeometryFactory {
* @param columns number of vertical columns in the parameter grid.
* @param files number of depth files in the parameter grid.
*/
- public BSplineSolid createBSolidSpline(DataPoints points, List<DirectPosition> controlPoints,
+ public BSplineSolid createBSolidSpline(DataPoints points, Array controlPoints,
int rows, int columns, int files)
{
return new DefaultBSplineSolid(points, controlPoints, rows, columns, files);
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/OBBox.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/OBBox.java
index 5ebb9b4567..5af435198a 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/OBBox.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/OBBox.java
@@ -141,9 +141,21 @@ public final class OBBox extends AbstractGeometry {
return env;
}
+ /**
+ * Returns the six faces of this box, oriented outward.
+ */
@Override
public Geometry getBoundary() {
- throw new UnsupportedOperationException("Not supported yet.");
+ final var corners = new Vector<?>[8];
+ for (int i = 0; i < corners.length; i++) {
+ final Vector<?> corner = center.copy();
+ corner.add(((i & 1) != 0) ? xAxis : xAxis.copy().scale(-1));
+ corner.add(((i & 2) != 0) ? yAxis : yAxis.copy().scale(-1));
+ corner.add(((i & 4) != 0) ? zAxis : zAxis.copy().scale(-1));
+ corners[i] = corner;
+ }
+ final boolean leftHanded = xAxis.dot(yAxis.cross(zAxis)) < 0;
+ return BBox.boxFaces(getCoordinateReferenceSystem(), corners, leftHanded);
}
/**
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Solid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Solid.java
index 11f965cc22..4c9bf77492 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Solid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Solid.java
@@ -21,9 +21,9 @@ import javax.measure.Quantity;
import org.apache.sis.geometries.internal.shared.DefaultSolid;
import static org.opengis.annotation.Specification.ISO_19107;
import org.opengis.annotation.UML;
-import org.opengis.geometry.DirectPosition;
import org.apache.sis.geometries.solid.ParametricCurveSolid;
import org.apache.sis.geometries.solid.Polyhedron;
+import org.apache.sis.maths.Array;
/**
@@ -161,13 +161,13 @@ public sealed interface Solid extends Primitive
* {@linkplain #getBoundary() boundary} of this solid.</li>
* </ul>
*
- * @return solid control points, possibly empty.
+ * @return solid control points, or {@code null} if none.
*
* @see ISO 19107:2019 - 6.4.28.6
*/
@UML(identifier="controlPoint", specification=ISO_19107)
- default List<DirectPosition> getControlPoints() {
- return List.of();
+ default Array getControlPoints() {
+ return null;
}
/**
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 00f3850044..7e10f416be 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
@@ -28,6 +28,7 @@ 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;
+import org.apache.sis.maths.Array;
import org.apache.sis.maths.Vector;
import org.apache.sis.measure.Quantities;
@@ -266,13 +267,13 @@ public sealed interface Surface extends Orientable
* Positions used to build the geometry of this surface, the way they are used depending on the interpolation.
* Control points do not necessarily lie on the surface.
*
- * @return surface control points, possibly empty.
+ * @return surface control points, or {@code null} if none.
*
* @see ISO 19107:2019 - 6.4.25.9
*/
@UML(identifier="controlPoint", specification=ISO_19107)
- default List<DirectPosition> getControlPoints() {
- return List.of();
+ default Array getControlPoints() {
+ return null;
}
/**
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/AbstractGeometry.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/AbstractGeometry.java
index 88d7c74fac..af03496c60 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/AbstractGeometry.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/AbstractGeometry.java
@@ -16,12 +16,18 @@
*/
package org.apache.sis.geometries.internal.shared;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.opengis.geometry.Envelope;
+import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.Geometries;
import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometry.GeneralDirectPosition;
import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.maths.Array;
/**
@@ -91,6 +97,56 @@ public abstract non-sealed class AbstractGeometry implements Geometry {
return union;
}
+ /**
+ * Returns the envelope of the given data points and control points, or {@code null} if both are empty.
+ * A spline whose basis functions are a partition of unity, such as a b-spline, stays inside the convex
+ * hull of its control points, so the returned envelope contains it.
+ *
+ * @param points data points of the spline, or {@code null} if none.
+ * @param controlPoints coordinates of the control points, possibly empty.
+ * @param homogeneous whether the control points have one more coordinate than the reference system,
+ * the weight by which the other coordinates are divided. Weights are assumed positive.
+ * @return envelope of the data points and of the control points, or {@code null} if there is none.
+ */
+ protected static Envelope controlHullEnvelope(final DataPoints points, final List<double[]> controlPoints,
+ final boolean homogeneous)
+ {
+ GeneralEnvelope envelope = (points == null || points.isEmpty()) ? null : new GeneralEnvelope(points.getEnvelope());
+ for (double[] coordinates : controlPoints) {
+ if (homogeneous) {
+ final double weight = coordinates[coordinates.length - 1];
+ coordinates = Arrays.copyOf(coordinates, coordinates.length - 1);
+ for (int i = 0; i < coordinates.length; i++) {
+ coordinates[i] /= weight;
+ }
+ }
+ final var position = new GeneralDirectPosition(coordinates);
+ if (envelope == null) {
+ envelope = new GeneralEnvelope(position, position);
+ if (points != null) {
+ envelope.setCoordinateReferenceSystem(points.getCoordinateReferenceSystem());
+ }
+ } else {
+ envelope.add(position);
+ }
+ }
+ return envelope;
+ }
+
+ /**
+ * Returns the coordinates of the given control points, or an empty list if {@code null}.
+ */
+ protected static List<double[]> coordinates(final Array controlPoints) {
+ if (controlPoints == null) {
+ return List.of();
+ }
+ final var coordinates = new ArrayList<double[]>();
+ for (long i = 0, n = controlPoints.getLength(); i < n; i++) {
+ coordinates.add(controlPoints.get(i).toArrayDouble());
+ }
+ return coordinates;
+ }
+
/**
* Adds the envelope of the given geometry to the given union, creating it if needed.
* Geometries with no envelope at all (an empty one, typically) are skipped.
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineCurve.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineCurve.java
index 661ab93fe1..97c27f2e9d 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineCurve.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineCurve.java
@@ -173,9 +173,13 @@ public non-sealed class DefaultBSplineCurve extends AbstractGeometry implements
return points.isEmpty();
}
+ /**
+ * Returns the envelope of the data points and of the control points, which contains this curve
+ * since a b-spline stays inside the convex hull of its control points.
+ */
@Override
public Envelope getEnvelope() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return controlHullEnvelope(points, coordinates(controlPoints), rational);
}
/**
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSolid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSolid.java
index 0cdd9b695d..95a6520574 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSolid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSolid.java
@@ -16,7 +16,6 @@
*/
package org.apache.sis.geometries.internal.shared;
-import java.util.List;
import java.util.Objects;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
@@ -29,6 +28,7 @@ import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.GeometryCollection;
import org.apache.sis.geometries.Surface;
import org.apache.sis.geometries.solid.BSplineSolid;
+import org.apache.sis.maths.Array;
/**
@@ -44,9 +44,9 @@ public non-sealed class DefaultBSplineSolid extends AbstractGeometry implements
protected final DataPoints points;
/**
- * Control points of the section curves, in row-major order, or an empty list if none.
+ * Control points in row-major order, or {@code null} if none.
*/
- protected final List<DirectPosition> controlPoints;
+ protected final Array controlPoints;
/**
* Number of horizontal rows in the parameter grid.
@@ -73,11 +73,11 @@ public non-sealed class DefaultBSplineSolid extends AbstractGeometry implements
* @param columns number of vertical columns in the parameter grid.
* @param files number of depth files in the parameter grid.
*/
- public DefaultBSplineSolid(final DataPoints points, final List<DirectPosition> controlPoints,
+ public DefaultBSplineSolid(final DataPoints points, final Array controlPoints,
final int rows, final int columns, final int files)
{
this.points = Objects.requireNonNull(points);
- this.controlPoints = (controlPoints == null) ? List.of() : List.copyOf(controlPoints);
+ this.controlPoints = controlPoints;
this.rows = rows;
this.columns = columns;
this.files = files;
@@ -104,7 +104,7 @@ public non-sealed class DefaultBSplineSolid extends AbstractGeometry implements
}
@Override
- public List<DirectPosition> getControlPoints() {
+ public Array getControlPoints() {
return controlPoints;
}
@@ -172,9 +172,13 @@ public non-sealed class DefaultBSplineSolid extends AbstractGeometry implements
return points.isEmpty();
}
+ /**
+ * Returns the envelope of the data points and of the control points, which contains this solid
+ * since a b-spline stays inside the convex hull of its control points.
+ */
@Override
public Envelope getEnvelope() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return controlHullEnvelope(points, coordinates(controlPoints), false);
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSurface.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSurface.java
index 943d4c8207..2db202201e 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSurface.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBSplineSurface.java
@@ -27,6 +27,7 @@ import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.curve.KnotType;
import org.apache.sis.geometries.surface.BSplineSurface;
import org.apache.sis.geometries.surface.BSplineSurfaceForm;
+import org.apache.sis.maths.Array;
/**
@@ -46,9 +47,9 @@ public non-sealed class DefaultBSplineSurface extends AbstractGeometry implement
protected final DataPoints points;
/**
- * Control points of the section curves, in row-major order, or an empty list if none.
+ * Control points in row-major order, or {@code null} if none.
*/
- protected final List<DirectPosition> controlPoints;
+ protected final Array controlPoints;
/**
* Number of rows in the parameter grid.
@@ -102,12 +103,12 @@ public non-sealed class DefaultBSplineSurface extends AbstractGeometry implement
* control points are expressed in homogeneous coordinates, which makes
* it rational.
*/
- public DefaultBSplineSurface(final DataPoints points, final List<DirectPosition> controlPoints,
+ public DefaultBSplineSurface(final DataPoints points, final Array controlPoints,
final int rows, final int columns, final List<double[]> knots, final int degree,
final KnotType knotSpec, final BSplineSurfaceForm surfaceForm, final boolean polynomial)
{
this.points = Objects.requireNonNull(points);
- this.controlPoints = (controlPoints == null) ? List.of() : List.copyOf(controlPoints);
+ this.controlPoints = controlPoints;
this.rows = rows;
this.columns = columns;
this.knots = (knots == null) ? List.of() : List.copyOf(knots);
@@ -158,7 +159,7 @@ public non-sealed class DefaultBSplineSurface extends AbstractGeometry implement
}
@Override
- public List<DirectPosition> getControlPoints() {
+ public Array getControlPoints() {
return controlPoints;
}
@@ -182,9 +183,13 @@ public non-sealed class DefaultBSplineSurface extends AbstractGeometry implement
throw new UnsupportedOperationException("Not supported yet.");
}
+ /**
+ * Returns the envelope of the data points and of the control points, which contains this surface
+ * since a b-spline stays inside the convex hull of its control points.
+ */
@Override
public Envelope getEnvelope() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return controlHullEnvelope(points, coordinates(controlPoints), !polynomial);
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBilinearGrid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBilinearGrid.java
index 92dedded85..f744d08321 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBilinearGrid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultBilinearGrid.java
@@ -28,6 +28,7 @@ import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.surface.BilinearGrid;
+import org.apache.sis.maths.Array;
/**
@@ -44,9 +45,9 @@ public non-sealed class DefaultBilinearGrid extends AbstractGeometry implements
protected final DataPoints points;
/**
- * Control points of the section curves, in row-major order, or an empty list if none.
+ * Control points of the section curves, in row-major order, or {@code null} if none.
*/
- protected final List<DirectPosition> controlPoints;
+ protected final Array controlPoints;
/**
* Number of rows in the parameter grid.
@@ -75,11 +76,11 @@ public non-sealed class DefaultBilinearGrid extends AbstractGeometry implements
* @param columns number of columns in the parameter grid.
* @param knots knot values, one sequence per surface parameter, or {@code null} if none.
*/
- public DefaultBilinearGrid(final DataPoints points, final List<DirectPosition> controlPoints,
+ public DefaultBilinearGrid(final DataPoints points, final Array controlPoints,
final int rows, final int columns, final List<double[]> knots)
{
this.points = Objects.requireNonNull(points);
- this.controlPoints = (controlPoints == null) ? List.of() : List.copyOf(controlPoints);
+ this.controlPoints = controlPoints;
this.rows = rows;
this.columns = columns;
this.knots = (knots == null) ? List.of() : List.copyOf(knots);
@@ -101,7 +102,7 @@ public non-sealed class DefaultBilinearGrid extends AbstractGeometry implements
}
@Override
- public List<DirectPosition> getControlPoints() {
+ public Array getControlPoints() {
return controlPoints;
}
@@ -166,9 +167,13 @@ public non-sealed class DefaultBilinearGrid extends AbstractGeometry implements
throw new UnsupportedOperationException("Not supported yet.");
}
+ /**
+ * Returns the envelope of the grid points. Each cell is a bilinear interpolation of its four
+ * corners with positive weights, so it stays inside the box of those corners.
+ */
@Override
public Envelope getEnvelope() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return points.isEmpty() ? null : points.getEnvelope();
}
@Override
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
index cffd3f385f..46077c3f8f 100644
--- 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
@@ -17,7 +17,6 @@
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.GeometryFactory;
import org.apache.sis.geometries.surface.Cylinder;
@@ -120,11 +119,6 @@ public non-sealed class DefaultCylinder extends AbstractGeometry implements Cyli
this.radiusBottom = radius;
}
- @Override
- public Quantity<?> getArea() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
@Override
public boolean isClosed() {
return closed;
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
index 40b491e35b..c4bb81d492 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultEllipsoid.java
@@ -26,6 +26,7 @@ import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.Geometries;
import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.surface.Ellipsoid;
+import org.apache.sis.maths.Array;
import org.apache.sis.maths.DataType;
import org.apache.sis.maths.NDArrays;
import org.apache.sis.maths.ReadOnly;
@@ -205,7 +206,7 @@ public non-sealed class DefaultEllipsoid extends AbstractGeometry implements Ell
}
@Override
- public List<DirectPosition> getControlPoints() {
+ public Array getControlPoints() {
throw new UnsupportedOperationException("Not supported yet.");
}
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 e541aad4bb..6c57b652fb 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
@@ -29,6 +29,7 @@ import org.apache.sis.geometries.curve.KnotType;
import org.apache.sis.geometries.operation.DeBoorAlgorithm;
import org.apache.sis.geometries.surface.BSplineSurfaceForm;
import org.apache.sis.geometries.surface.NurbSurface;
+import org.apache.sis.maths.Array;
import org.apache.sis.maths.NDArrays;
import org.apache.sis.maths.Vector;
import org.apache.sis.maths.Vectors;
@@ -371,17 +372,16 @@ public non-sealed class DefaultNurbSurface extends AbstractGeometry implements N
}
/**
- * Returns views of the control points in row-major order, without their weights.
+ * Returns a copy of the control points in row-major order, without their weights.
*/
@Override
- public List<DirectPosition> getControlPoints() {
- final var positions = new ArrayList<DirectPosition>(getRows() * getColumns());
+ public Array getControlPoints() {
+ final var positions = new ArrayList<Vector<?>>(getRows() * getColumns());
for (final Vector<?>[] row : controlPoints) {
- for (final Vector<?> p : row) {
- positions.add(Vectors.asDirectPostion(p));
- }
+ positions.addAll(Arrays.asList(row));
}
- return positions;
+ final Vector<?> first = controlPoints[0][0];
+ return NDArrays.of(positions, first.getSampleSystem(), first.getDataType());
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultOffsetCurve.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultOffsetCurve.java
index ccb47a4c91..c92ef3f4da 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultOffsetCurve.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultOffsetCurve.java
@@ -17,7 +17,9 @@
package org.apache.sis.geometries.internal.shared;
import java.util.Objects;
+import javax.measure.IncommensurableException;
import javax.measure.Quantity;
+import javax.measure.Unit;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.Curve;
@@ -25,7 +27,9 @@ import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.cs.Bearing;
import org.apache.sis.geometries.curve.OffsetCurve;
+import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.maths.Array;
+import org.apache.sis.measure.Units;
/**
@@ -114,9 +118,36 @@ public non-sealed class DefaultOffsetCurve extends AbstractGeometry implements O
return baseCurve.isEmpty();
}
+ /**
+ * Returns the envelope of the base curve expanded by the offset distance on every axis.
+ * This envelope contains this curve, but may be larger than needed since the curve is
+ * offset on one side only and not along every axis.
+ *
+ * @throws UnsupportedOperationException if the distance unit cannot be converted to the unit of an axis.
+ */
@Override
public Envelope getEnvelope() {
- throw new UnsupportedOperationException("Not supported yet.");
+ final Envelope baseEnvelope = baseCurve.getEnvelope();
+ if (baseEnvelope == null) {
+ return null;
+ }
+ final var envelope = new GeneralEnvelope(baseEnvelope);
+ final CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
+ final double value = Math.abs(distance.getValue().doubleValue());
+ for (int i = 0, n = envelope.getDimension(); i < n; i++) {
+ double d = value;
+ final Unit<?> axisUnit = (crs != null) ? crs.getCoordinateSystem().getAxis(i).getUnit() : null;
+ if (axisUnit != null && !Units.UNITY.equals(distance.getUnit())) {
+ try {
+ d = distance.getUnit().getConverterToAny(axisUnit).convert(value);
+ } catch (IncommensurableException e) {
+ throw new UnsupportedOperationException("The offset distance in " + distance.getUnit()
+ + " cannot be converted to the unit of axis " + i + " (" + axisUnit + ").", e);
+ }
+ }
+ envelope.setRange(i, envelope.getMinimum(i) - d, envelope.getMaximum(i) + d);
+ }
+ return envelope;
}
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPrism.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPrism.java
index 90aeba25b4..ea00a68ebc 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPrism.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultPrism.java
@@ -20,9 +20,13 @@ import java.util.Objects;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.SingleCRS;
+import org.opengis.util.FactoryException;
import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.Prism;
+import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.measure.NumberRange;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.util.Utilities;
/**
@@ -90,9 +94,51 @@ public non-sealed class DefaultPrism extends AbstractGeometry implements Prism {
return base.isEmpty();
}
+ /**
+ * Returns the envelope of the base extended by the extrusion range. If the reference system of the base
+ * contains the extrusion reference system, the range extends the corresponding axis. Otherwise the
+ * extrusion adds a dimension, and the envelope is in the compound of the two reference systems.
+ */
@Override
public Envelope getEnvelope() {
- throw new UnsupportedOperationException("Not supported yet.");
+ final Envelope baseEnvelope = base.getEnvelope();
+ if (baseEnvelope == null) {
+ return null;
+ }
+ final double min = extrusionRange.getMinDouble();
+ final double max = extrusionRange.getMaxDouble();
+ final CoordinateReferenceSystem baseCRS = base.getCoordinateReferenceSystem();
+ if (baseCRS != null && extrusionCrs != null) {
+ int offset = 0;
+ for (final SingleCRS component : CRS.getSingleComponents(baseCRS)) {
+ final int dimension = component.getCoordinateSystem().getDimension();
+ if (Utilities.equalsIgnoreMetadata(component, extrusionCrs)) {
+ final var envelope = new GeneralEnvelope(baseEnvelope);
+ for (int i = offset; i < offset + dimension; i++) {
+ envelope.setRange(i, Math.min(envelope.getMinimum(i), min),
+ Math.max(envelope.getMaximum(i), max));
+ }
+ return envelope;
+ }
+ offset += dimension;
+ }
+ }
+ final int n = baseEnvelope.getDimension();
+ final GeneralEnvelope envelope;
+ if (baseCRS != null && extrusionCrs != null) {
+ try {
+ envelope = new GeneralEnvelope(CRS.compound(baseCRS, extrusionCrs));
+ } catch (FactoryException e) {
+ throw new IllegalStateException("Cannot combine the base and extrusion reference systems.", e);
+ }
+ } else {
+ envelope = new GeneralEnvelope(n + 1);
+ }
+ for (int i = 0; i < n; i++) {
+ envelope.setRange(i, baseEnvelope.getMinimum(i), baseEnvelope.getMaximum(i));
+ }
+ envelope.setRange(n, min, max);
+ return envelope;
}
@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 90a9c7d109..a25f66c120 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
@@ -24,6 +24,7 @@ import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.Geometries;
import org.apache.sis.geometries.GeometryFactory;
import org.apache.sis.geometries.surface.Sphere;
+import org.apache.sis.maths.Array;
import org.apache.sis.maths.DataType;
import org.apache.sis.maths.NDArrays;
import org.apache.sis.maths.ReadOnly;
@@ -137,7 +138,7 @@ public non-sealed class DefaultSphere extends AbstractGeometry implements Sphere
}
@Override
- public List<DirectPosition> getControlPoints() {
+ public Array getControlPoints() {
throw new UnsupportedOperationException("Not supported yet.");
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Frustrum.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Frustrum.java
index da5c29511c..4cc00bc52b 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Frustrum.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/Frustrum.java
@@ -55,19 +55,30 @@ public final class Frustrum extends AbstractGeometry {
return planes;
}
+ /**
+ * Returns the reference system of the planes, taken from the first one.
+ */
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return planes[0].getCoordinateReferenceSystem();
}
+ /**
+ * Sets the reference system of all the planes.
+ */
@Override
public void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) throws IllegalArgumentException {
- throw new UnsupportedOperationException("Not supported yet.");
+ for (final Plane plane : planes) {
+ plane.setCoordinateReferenceSystem(crs);
+ }
}
+ /**
+ * Returns the type of the data points of the planes, taken from the first one.
+ */
@Override
public DataPointsType getDataPointsType() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return planes[0].getDataPointsType();
}
@Override
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/ParametricCurveSolid.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/ParametricCurveSolid.java
index 15aa333019..52e2d5582a 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/ParametricCurveSolid.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/solid/ParametricCurveSolid.java
@@ -16,7 +16,6 @@
*/
package org.apache.sis.geometries.solid;
-import java.util.List;
import static org.opengis.annotation.Specification.ISO_19107;
import org.opengis.annotation.UML;
import org.opengis.geometry.DirectPosition;
@@ -25,6 +24,7 @@ import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Solid;
import org.apache.sis.geometries.SolidInterpolation;
+import org.apache.sis.maths.Array;
/**
@@ -163,18 +163,18 @@ public sealed interface ParametricCurveSolid extends Solid
*
* <p>Constraints:</p>
* <ul>
- * <li>The list holds {@link #getRows()} × {@link #getColumns()} × {@link #getFiles()}
+ * <li>The array holds {@link #getRows()} × {@link #getColumns()} × {@link #getFiles()}
* positions, stored in row-major order.</li>
* <li>Control points do not necessarily lie inside this solid.</li>
* </ul>
*
- * @return solid control points, possibly empty.
+ * @return solid control points, or {@code null} if none.
*
* @see ISO 19107:2019 - 9.3.1.3
*/
@UML(identifier="controlPoints", specification=ISO_19107)
@Override
- List<DirectPosition> getControlPoints();
+ Array getControlPoints();
/**
* Returns the curve traversing this solid horizontally at the given vertical and depth parameters.
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 73b4b4d232..08f6f758d2 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
@@ -18,11 +18,13 @@ package org.apache.sis.geometries.surface;
import java.util.ArrayList;
import java.util.List;
+import javax.measure.Quantity;
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.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;
@@ -35,6 +37,7 @@ import org.apache.sis.maths.NDArrays;
import org.apache.sis.maths.ReadOnly;
import org.apache.sis.maths.Vectors;
import org.apache.sis.maths.Vector;
+import org.apache.sis.measure.Quantities;
/**
@@ -218,6 +221,28 @@ public sealed interface Cylinder extends Surface
return bbox;
}
+ /**
+ * Returns the area of the lateral surface, π(r₁ + r₂)⋅√(h² + (r₁ − r₂)²) where <var>h</var> is the
+ * {@linkplain #getHeight() height}, plus the areas π<var>r</var>² of the two end disks if this
+ * cylinder is {@linkplain #isClosed() closed}.
+ *
+ * @throws UnsupportedOperationException if this cylinder is not 3-dimensional.
+ */
+ @Override
+ default Quantity<?> getArea() {
+ if (getDataPoints().getDimension() != 3) {
+ throw new UnsupportedOperationException("The area of a cylinder is computed in 3 dimensions only.");
+ }
+ final double r1 = getRadiusBottom();
+ final double r2 = getRadiusTop();
+ final double h = getHeight();
+ double area = Math.PI * (r1 + r2) * Math.hypot(h, r1 - r2);
+ if (isClosed()) {
+ area += Math.PI * (r1*r1 + r2*r2);
+ }
+ return Quantities.create(area, Geometries.getAreaUnit(this));
+ }
+
/**
* Returns the empty geometry if this cylinder is {@linkplain #isClosed() closed},
* or the circles at its ends otherwise.
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/ParametricCurveSurface.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/ParametricCurveSurface.java
index 6e61608751..0d0bd0e803 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/ParametricCurveSurface.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/surface/ParametricCurveSurface.java
@@ -27,6 +27,7 @@ import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.GeometryType;
import org.apache.sis.geometries.Surface;
import org.apache.sis.geometries.SurfaceInterpolation;
+import org.apache.sis.maths.Array;
// Specific to the geoapi-4.0 branch:
import org.opengis.metadata.Identifier;
@@ -110,18 +111,18 @@ public sealed interface ParametricCurveSurface extends Surface, ReferenceSystem
*
* <p>Constraints:</p>
* <ul>
- * <li>The list holds {@link #getRows()} × {@link #getColumns()} positions,
+ * <li>The array holds {@link #getRows()} × {@link #getColumns()} positions,
* stored in row-major order.</li>
* <li>Control points do not necessarily lie on this surface.</li>
* </ul>
*
- * @return surface control points, possibly empty.
+ * @return surface control points, or {@code null} if none.
*
* @see ISO 19107:2019 - 8.3.2.6
*/
@UML(identifier="controlPoints", specification=ISO_19107)
@Override
- List<DirectPosition> getControlPoints();
+ Array getControlPoints();
/**
* Positions of this surface at the knots of the parameter grid, so that the data point at
(sis) 10/12: fix(Geometry): fix EarClipping holes not correctly handled
Posted by js...@apache.org.
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 c596827e598825d02b203338999b8fab0ea50ae9
Author: jsorel <jo...@geomatys.com>
AuthorDate: Wed Oct 14 16:27:00 2026 +0200
fix(Geometry): fix EarClipping holes not correctly handled
---
.../operation/triangulate/EarClipping.java | 24 +++++--
.../test/org/apache/sis/geometries/EmptyTest.java | 6 +-
.../sis/geometries/operation/ToPrimitiveTest.java | 75 ++++++++++++++++++++--
3 files changed, 92 insertions(+), 13 deletions(-)
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/EarClipping.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/EarClipping.java
index 2a73af2664..b3f4660abc 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/EarClipping.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/EarClipping.java
@@ -17,9 +17,12 @@
package org.apache.sis.geometries.operation.triangulate;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.stream.Collectors;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.curve.LineString;
@@ -130,7 +133,9 @@ public class EarClipping {
public MeshPrimitive.Triangles toMesh(Polygon polygon) {
final List<Vector[]> list = triangulate(polygon);
- final Array positions = NDArrays.of(polygon.getCoordinateReferenceSystem(), new double[list.size()*3*2]);
+ final CoordinateReferenceSystem crs = polygon.getCoordinateReferenceSystem();
+ final int dimension = crs.getCoordinateSystem().getDimension();
+ final Array positions = NDArrays.of(crs, new double[list.size()*3*dimension]);
for (int i = 0, k = 0, n = list.size(); i < n; i++, k+=3) {
final Vector[] t = list.get(i);
positions.set(k+0, t[0]);
@@ -147,19 +152,23 @@ public class EarClipping {
private void run(SimplePolygon part){
//build a single geometry linking inner holes.
+ //the array stream reuses a single vector for all positions, so each one is copied.
final List<Vector<?>> borderCoords = new ArrayList<>();
- part.outter.getDataPoints().getAttributeArray(DataPointsType.ATT_POSITION).stream(false).forEach(borderCoords::add);
+ part.outter.getDataPoints().getAttributeArray(DataPointsType.ATT_POSITION).stream(false)
+ .map(Vector::copy).forEach(borderCoords::add);
//sort inner holes by minimum x value
orderHoles(part);
//attach holes to the main geometry
for(int i=0,n=part.inners.size();i<n;i++){
//we must find the minimum x coordinate in the inner loop
- final List<Vector<?>> loop = part.inners.get(i).getDataPoints().getAttributeArray(DataPointsType.ATT_POSITION).stream(false).toList();
+ //copied for the same reason as the outer loop, in a mutable list since its last point is removed below.
+ final List<Vector<?>> loop = part.inners.get(i).getDataPoints().getAttributeArray(DataPointsType.ATT_POSITION).stream(false)
+ .map(Vector::copy).collect(Collectors.toCollection(ArrayList::new));
int index = 0;
Vector min = loop.get(index);
for(int k=1,p=loop.size();k<p;k++){
- Vector candidate = (Vector) loop.get(1);
+ Vector candidate = (Vector) loop.get(k);
if (candidate.get(0) < min.get(0)) {
min = candidate;
index = k;
@@ -304,8 +313,13 @@ public class EarClipping {
if (!isConvex(i)){
//check it's not one of the current segment
if(i!=index && i!=indexNext && i!=indexPrevious){
+ final double[] p = new double[]{coords[i].get(0),coords[i].get(1)};
+ //a hole bridge duplicates its end points, a copy located on a triangle vertex does not block the ear
+ if (Arrays.equals(p, a) || Arrays.equals(p, b) || Arrays.equals(p, c)) {
+ continue;
+ }
//check if it's in the segment triangle
- if(Maths.inTriangle(a, b, c, new double[]{coords[i].get(0),coords[i].get(1)})){
+ if(Maths.inTriangle(a, b, c, p)){
return false;
}
}
diff --git a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/EmptyTest.java b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/EmptyTest.java
index b324a85ba9..57e21329b7 100644
--- a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/EmptyTest.java
+++ b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/EmptyTest.java
@@ -163,11 +163,11 @@ public abstract class EmptyTest extends GeometryTest {
final Empty empty = createEmpty(CRS_2D);
assertEquals(-1, empty.getDimension(null), "Dimension of the whole geometry.");
/*
- * TODO: the empty set has no interior position, therefore no position where the dimension
- * is unambiguous. Replace this check if a value is defined for that case.
+ * The empty set contains no position, so no position has a dimension in it:
+ * −1, as for a position outside the elements of a collection.
*/
final DirectPosition position = new GeneralDirectPosition(CRS_2D);
- assertThrows(UnsupportedOperationException.class, () -> empty.getDimension(position));
+ assertEquals(-1, empty.getDimension(position), "Dimension at a position.");
}
@Test
diff --git a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
index bed4931ace..a71bccc12c 100644
--- a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
+++ b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
@@ -67,14 +67,79 @@ public class ToPrimitiveTest {
@Test
public void testPolygon() {
- final Array positions = NDArrays.of(CRS2D, new double[]{0,1,2,3});
- final DataPoints points = GeometryFactory.DEFAULT.createDataPoints(positions);
- final LinearRing exterior = GeometryFactory.DEFAULT.createLinearRing(points);
- final Polygon point = GeometryFactory.DEFAULT.createPolygon(exterior, null);
- final Geometry result = new GeometryProcessor().toPrimitive(point);
+ final LinearRing exterior = ring(0,0, 2,0, 2,2, 0,2, 0,0);
+ final Polygon polygon = GeometryFactory.DEFAULT.createPolygon(exterior, null);
+ final Geometry result = new GeometryProcessor().toPrimitive(polygon);
assertTrue(result instanceof MeshPrimitive);
final MeshPrimitive primitive = (MeshPrimitive) result;
assertEquals(MeshPrimitive.Type.TRIANGLES, primitive.getType());
+ assertEquals(6, primitive.getPositions().getLength(), "Expected 2 triangles.");
+ assertEquals(4, trianglesArea(primitive), 1E-12);
+ }
+
+ /**
+ * Tests the triangulation of a polygon with a hole, which requires to cut the exterior
+ * ring toward the hole. The triangles shall cover the polygon area without the hole.
+ */
+ @Test
+ public void testPolygonWithHole() {
+ final LinearRing exterior = ring(0,0, 10,0, 10,10, 0,10, 0,0);
+ final LinearRing hole = ring(2,2, 2,4, 4,4, 4,2, 2,2);
+ final Polygon polygon = GeometryFactory.DEFAULT.createPolygon(exterior, java.util.List.of(hole));
+ final MeshPrimitive primitive = (MeshPrimitive) new GeometryProcessor().toPrimitive(polygon);
+ assertEquals(MeshPrimitive.Type.TRIANGLES, primitive.getType());
+ assertEquals(96, trianglesArea(primitive), 1E-9);
+ }
+
+ /**
+ * Same test as {@link #testPolygonWithHole()}, with the hole ring starting at a point which is
+ * not its leftmost one. The cut toward the hole shall still start from the leftmost point.
+ */
+ @Test
+ public void testPolygonWithHoleStartingAnywhere() {
+ final LinearRing exterior = ring(0,0, 10,0, 10,10, 0,10, 0,0);
+ final LinearRing hole = ring(4,4, 4,2, 2,2, 2,4, 4,4);
+ final Polygon polygon = GeometryFactory.DEFAULT.createPolygon(exterior, java.util.List.of(hole));
+ final MeshPrimitive primitive = (MeshPrimitive) new GeometryProcessor().toPrimitive(polygon);
+ assertEquals(96, trianglesArea(primitive), 1E-9);
+ }
+
+ /**
+ * Tests the triangulation of a three-dimensional polygon, which shall keep the z coordinates.
+ */
+ @Test
+ public void testPolygon3D() {
+ final LinearRing exterior = GeometryFactory.DEFAULT.createLinearRing(GeometryFactory.DEFAULT.createDataPoints(
+ NDArrays.of(CommonCRS.WGS84.geographic3D(), 0,0,5, 2,0,5, 2,2,7, 0,2,7, 0,0,5)));
+ final Polygon polygon = GeometryFactory.DEFAULT.createPolygon(exterior, null);
+ final MeshPrimitive primitive = (MeshPrimitive) new GeometryProcessor().toPrimitive(polygon);
+ assertEquals(MeshPrimitive.Type.TRIANGLES, primitive.getType());
+ assertEquals(3, primitive.getPositions().getDimension());
+ assertEquals(6, primitive.getPositions().getLength(), "Expected 2 triangles.");
+ final double[] c = primitive.getPositions().toArrayDouble();
+ for (int i = 0; i < c.length; i += 3) {
+ assertEquals(c[i+1] == 0 ? 5 : 7, c[i+2], "z shall follow the polygon plane.");
+ }
+ }
+
+ /**
+ * Creates a ring in the two-dimensional test CRS.
+ */
+ private static LinearRing ring(final double... coordinates) {
+ return GeometryFactory.DEFAULT.createLinearRing(
+ GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(CRS2D, coordinates)));
+ }
+
+ /**
+ * Returns the sum of the areas of the triangles of the given non-indexed mesh.
+ */
+ private static double trianglesArea(final MeshPrimitive primitive) {
+ final double[] c = primitive.getPositions().toArrayDouble();
+ double area = 0;
+ for (int i = 0; i + 5 < c.length; i += 6) {
+ area += Math.abs((c[i+2] - c[i]) * (c[i+5] - c[i+1]) - (c[i+4] - c[i]) * (c[i+3] - c[i+1])) / 2;
+ }
+ return area;
}
@Test
(sis) 12/12: feat(Geometry): implement toPrimitive for PolyhedralSurface
Posted by js...@apache.org.
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 7b6223be12fabde62b3a807ee61484f0a9faf2a5
Author: jsorel <jo...@geomatys.com>
AuthorDate: Wed Oct 14 16:58:39 2026 +0200
feat(Geometry): implement toPrimitive for PolyhedralSurface
---
.../geometries/operation/GeometryProcessor.java | 4 +++
.../sis/geometries/operation/ToPrimitive.java | 35 +++++++++++++++++++
.../sis/geometries/operation/ToPrimitiveTest.java | 39 ++++++++++++++++++++++
3 files changed, 78 insertions(+)
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
index 2d4c54560b..6a1d27d284 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
@@ -38,6 +38,7 @@ import org.apache.sis.geometries.mesh.MeshPrimitive;
import org.apache.sis.geometries.mesh.MeshPrimitiveVisitor;
import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
import org.apache.sis.geometries.surface.Polygon;
+import org.apache.sis.geometries.surface.PolyhedralSurface;
import org.apache.sis.geometries.surface.Triangle;
import org.apache.sis.maths.Array;
import org.apache.sis.maths.DataType;
@@ -482,6 +483,9 @@ public final class GeometryProcessor {
return ToPrimitive.toPrimitive(cdt);
} else if (geom instanceof Polygon cdt) {
return ToPrimitive.toPrimitive(cdt);
+ } else if (geom instanceof PolyhedralSurface<?> cdt) {
+ //also handles TIN, which is a polyhedral surface of triangles.
+ return ToPrimitive.toPrimitive(cdt);
} else if (geom instanceof GeometryCollection<?> cdt) {
final GeometryType elementType = cdt.getElementType();
switch (elementType) {
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
index 8ac878939c..0d7e8877e2 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
@@ -16,9 +16,12 @@
*/
package org.apache.sis.geometries.operation;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometries.DataPoints;
import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.GeometryCollection;
@@ -30,8 +33,10 @@ import org.apache.sis.geometries.mesh.MeshPrimitive;
import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
import org.apache.sis.geometries.operation.triangulate.EarClipping;
import org.apache.sis.geometries.surface.Polygon;
+import org.apache.sis.geometries.surface.PolyhedralSurface;
import org.apache.sis.maths.Array;
import org.apache.sis.maths.NDArrays;
+import org.apache.sis.maths.Vector;
/**
@@ -84,6 +89,36 @@ public final class ToPrimitive {
return new EarClipping().toMesh(geometry);
}
+ /**
+ * Transform PolyhedralSurface, including TIN, to Primitive.
+ * Each patch is triangulated and all triangles are merged in a single non-indexed mesh.
+ */
+ public static MeshPrimitive.Triangles toPrimitive(PolyhedralSurface<?> geometry) throws OperationException {
+ final CoordinateReferenceSystem crs = geometry.getCoordinateReferenceSystem();
+ final int dimension = crs.getCoordinateSystem().getDimension();
+ final EarClipping triangulator = new EarClipping();
+ final List<double[]> triangles = new ArrayList<>();
+ for (int i = 0, n = geometry.getNumPatches(); i < n; i++) {
+ //the returned list is reused by the next call, so the coordinates are copied now.
+ for (final Vector<?>[] triangle : triangulator.triangulate(geometry.getPatchN(i))) {
+ final double[] coordinates = new double[3 * dimension];
+ for (int v = 0; v < 3; v++) {
+ for (int d = 0; d < dimension; d++) {
+ coordinates[v * dimension + d] = triangle[v].get(d);
+ }
+ }
+ triangles.add(coordinates);
+ }
+ }
+ final double[] positions = new double[triangles.size() * 3 * dimension];
+ for (int i = 0; i < triangles.size(); i++) {
+ System.arraycopy(triangles.get(i), 0, positions, i * 3 * dimension, 3 * dimension);
+ }
+ final MeshPrimitive.Triangles primitive = new MeshPrimitive.Triangles();
+ primitive.setPositions(NDArrays.of(crs, positions));
+ return primitive;
+ }
+
/**
* Transform MultiPoint to Primitive.
*/
diff --git a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
index 5c3c8a360f..bb0e23be50 100644
--- a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
+++ b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
@@ -175,6 +175,45 @@ public class ToPrimitiveTest {
}
}
+ /**
+ * Tests the conversion of a TIN, which shall give a single mesh of all its triangles.
+ */
+ @Test
+ public void testTIN() {
+ final CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic3D();
+ final GeometryFactory f = GeometryFactory.DEFAULT;
+ final org.apache.sis.geometries.surface.TIN tin = f.createTIN(crs, new org.apache.sis.geometries.surface.Triangle[] {
+ f.createTriangle(ring3D(crs, 0,0,0, 2,0,0, 0,2,1, 0,0,0)),
+ f.createTriangle(ring3D(crs, 2,0,0, 2,2,1, 0,2,1, 2,0,0))});
+ final Geometry result = new GeometryProcessor().toPrimitive(tin);
+ assertTrue(result instanceof MeshPrimitive.Triangles);
+ final MeshPrimitive primitive = (MeshPrimitive) result;
+ assertEquals(3, primitive.getPositions().getDimension());
+ assertEquals(6, primitive.getPositions().getLength(), "Expected 2 triangles.");
+ assertEquals(2 * Math.sqrt(5), trianglesArea3D(primitive), 1E-9);
+ }
+
+ /**
+ * Tests the conversion of a polyhedral surface made of the five faces of an open box,
+ * including vertical ones. All faces shall be triangulated in a single mesh.
+ */
+ @Test
+ public void testPolyhedralSurface() {
+ final CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic3D();
+ final GeometryFactory f = GeometryFactory.DEFAULT;
+ final Polygon[] faces = {
+ f.createPolygon(ring3D(crs, 0,0,0, 0,0,2, 0,2,2, 0,2,0, 0,0,0), null),
+ f.createPolygon(ring3D(crs, 2,0,0, 2,2,0, 2,2,2, 2,0,2, 2,0,0), null),
+ f.createPolygon(ring3D(crs, 0,0,0, 2,0,0, 2,0,2, 0,0,2, 0,0,0), null),
+ f.createPolygon(ring3D(crs, 0,2,0, 0,2,2, 2,2,2, 2,2,0, 0,2,0), null),
+ f.createPolygon(ring3D(crs, 0,0,0, 0,2,0, 2,2,0, 2,0,0, 0,0,0), null)};
+ final Geometry result = new GeometryProcessor().toPrimitive(f.createPolyhedralSurface(crs, faces));
+ assertTrue(result instanceof MeshPrimitive.Triangles);
+ final MeshPrimitive primitive = (MeshPrimitive) result;
+ assertEquals(30, primitive.getPositions().getLength(), "Expected 2 triangles per face.");
+ assertEquals(20, trianglesArea3D(primitive), 1E-9);
+ }
+
private static LinearRing ring3D(final CoordinateReferenceSystem crs, final double... coordinates) {
return GeometryFactory.DEFAULT.createLinearRing(
GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(crs, coordinates)));
(sis) 11/12: feat(Geometry): earclipping now support polygons not on the XY plan
Posted by js...@apache.org.
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 03f05372fad6b36e7e6efec7bc19b4175b9fe407
Author: jsorel <jo...@geomatys.com>
AuthorDate: Wed Oct 14 16:42:45 2026 +0200
feat(Geometry): earclipping now support polygons not on the XY plan
---
.../operation/triangulate/EarClipping.java | 131 ++++++++++++++++-----
.../sis/geometries/operation/ToPrimitiveTest.java | 73 ++++++++++++
2 files changed, 176 insertions(+), 28 deletions(-)
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/EarClipping.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/EarClipping.java
index b3f4660abc..70023f1917 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/EarClipping.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/triangulate/EarClipping.java
@@ -18,12 +18,10 @@ package org.apache.sis.geometries.operation.triangulate;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.apache.sis.geometries.Geometry;
import org.apache.sis.geometries.DataPointsType;
import org.apache.sis.geometries.curve.LineString;
import org.apache.sis.geometries.mesh.MeshPrimitive;
@@ -31,7 +29,6 @@ import org.apache.sis.geometries.surface.Polygon;
import org.apache.sis.maths.Array;
import org.apache.sis.maths.Maths;
import org.apache.sis.maths.NDArrays;
-import org.apache.sis.maths.Vector2D;
import org.apache.sis.maths.Vector;
import static org.apache.sis.maths.Vectors.*;
import org.apache.sis.util.ArraysExt;
@@ -47,14 +44,6 @@ import org.apache.sis.util.ArraysExt;
*/
public class EarClipping {
- private static final Comparator<Geometry> X_SORTER = new Comparator<Geometry>() {
- public int compare(Geometry first, Geometry second) {
- final double m1 = first.getEnvelope().getMinimum(0);
- final double m2 = second.getEnvelope().getMinimum(0);
- double d = m2-m1;
- return (d<0) ? -1 : (d>0) ? +1 : 0;
- }
- };
private static final class SimplePolygon{
private LineString outter;
private final List<LineString> inners = new ArrayList();
@@ -85,6 +74,14 @@ public class EarClipping {
*/
private int[] coordType;
+ /**
+ * Order of the coordinates used by the triangulation, as indices of the coordinates of the
+ * polygon: the triangulation works on the two first ones. The polygon is projected on the
+ * coordinate plane where it has its largest area, so that a vertical polygon is not degenerate.
+ * {@code null} if the polygon is triangulated in its (x,y) plane, without reordering.
+ */
+ private int[] axes;
+
//current segment analyzed
private int indexPrevious;
private int index;
@@ -122,11 +119,81 @@ public class EarClipping {
part.inners.add(inner);
}
+ axes = workingAxes(geometry.getExteriorRing().getDataPoints().getAttributeArray(DataPointsType.ATT_POSITION));
run(part);
+ if (axes != null) {
+ //triangles share their vertices, so each one is restored in a new vector.
+ for (final Vector[] triangle : triangles) {
+ for (int i = 0; i < triangle.length; i++) {
+ triangle[i] = fromWorking(triangle[i]);
+ }
+ }
+ }
return triangles;
}
+ /**
+ * Returns the order of the coordinates to use for triangulating a polygon with the given positions,
+ * or {@code null} if the polygon can be triangulated in its (x,y) plane. The axis along which the
+ * normal of the polygon is the largest, computed with Newell's method, is moved after the two
+ * axes of the projection plane.
+ */
+ private static int[] workingAxes(final Array positions) {
+ final int dim = positions.getDimension();
+ if (dim < 3) {
+ return null;
+ }
+ final double[] c = positions.toArrayDouble();
+ final int n = c.length / dim;
+ double nx = 0, ny = 0, nz = 0;
+ for (int i = 0; i < n; i++) {
+ final int a = i * dim, b = ((i + 1) % n) * dim;
+ nx += (c[a+1] - c[b+1]) * (c[a+2] + c[b+2]);
+ ny += (c[a+2] - c[b+2]) * (c[a] + c[b]);
+ nz += (c[a] - c[b]) * (c[a+1] + c[b+1]);
+ }
+ nx = Math.abs(nx); ny = Math.abs(ny); nz = Math.abs(nz);
+ if (nz >= nx && nz >= ny) {
+ return null;
+ }
+ final int drop = (nx >= ny) ? 0 : 1;
+ final int[] axes = new int[dim];
+ int k = 0;
+ for (int i = 0; i < 3; i++) {
+ if (i != drop) axes[k++] = i;
+ }
+ axes[k++] = drop;
+ for (int i = 3; i < dim; i++) {
+ axes[k++] = i;
+ }
+ return axes;
+ }
+
+ /**
+ * Returns a copy of the given position with its coordinates in the order used by the triangulation.
+ */
+ private Vector<?> toWorking(final Vector<?> position) {
+ final Vector<?> copy = position.copy();
+ if (axes != null) {
+ for (int i = 0; i < axes.length; i++) {
+ copy.set(i, position.get(axes[i]));
+ }
+ }
+ return copy;
+ }
+
+ /**
+ * Returns a copy of the given position with its coordinates restored in the order of the polygon.
+ */
+ private Vector<?> fromWorking(final Vector<?> position) {
+ final Vector<?> copy = position.copy();
+ for (int i = 0; i < axes.length; i++) {
+ copy.set(axes[i], position.get(i));
+ }
+ return copy;
+ }
+
/**
* TODO not efficient, improve performances.
*/
@@ -155,16 +222,21 @@ public class EarClipping {
//the array stream reuses a single vector for all positions, so each one is copied.
final List<Vector<?>> borderCoords = new ArrayList<>();
part.outter.getDataPoints().getAttributeArray(DataPointsType.ATT_POSITION).stream(false)
- .map(Vector::copy).forEach(borderCoords::add);
- //sort inner holes by minimum x value
- orderHoles(part);
+ .map(this::toWorking).forEach(borderCoords::add);
+
+ //copy the holes in the same way, in mutable lists since their last point is removed below,
+ //and sort them by decreasing minimum x value.
+ final List<List<Vector<?>>> holes = new ArrayList<>();
+ for (final LineString inner : part.inners) {
+ holes.add(inner.getDataPoints().getAttributeArray(DataPointsType.ATT_POSITION).stream(false)
+ .map(this::toWorking).collect(Collectors.toCollection(ArrayList::new)));
+ }
+ holes.sort(Comparator.comparingDouble((List<Vector<?>> h) ->
+ h.stream().mapToDouble((v) -> v.get(0)).min().orElse(Double.NaN)).reversed());
//attach holes to the main geometry
- for(int i=0,n=part.inners.size();i<n;i++){
+ for (final List<Vector<?>> loop : holes) {
//we must find the minimum x coordinate in the inner loop
- //copied for the same reason as the outer loop, in a mutable list since its last point is removed below.
- final List<Vector<?>> loop = part.inners.get(i).getDataPoints().getAttributeArray(DataPointsType.ATT_POSITION).stream(false)
- .map(Vector::copy).collect(Collectors.toCollection(ArrayList::new));
int index = 0;
Vector min = loop.get(index);
for(int k=1,p=loop.size();k<p;k++){
@@ -184,9 +256,20 @@ public class EarClipping {
final int[] offset = new int[2];
nearest(borderCoords, buffer1, line2, buffer2, ratio, offset, 0.000000001);
+ //the cut starts on the outter loop segment nearest to the hole. The point found above has only
+ //the two working coordinates, all coordinates are interpolated along that segment.
+ final Vector<?> start = borderCoords.get(offset[0]);
+ final Vector<?> end = borderCoords.get(offset[0] + 1);
+ final double length = Math.hypot(end.get(0) - start.get(0), end.get(1) - start.get(1));
+ final double ratioAlong = (length > 0) ? Math.hypot(buffer1[0] - start.get(0), buffer1[1] - start.get(1)) / length : 0;
+ final Vector<?> bridge = start.copy();
+ for (int d = 0; d < bridge.getDimension(); d++) {
+ bridge.set(d, start.get(d) + ratioAlong * (end.get(d) - start.get(d)));
+ }
+
//make a cut from outter loop to inner loop
int insertIndex = offset[0]+1;
- borderCoords.add(insertIndex, new Vector2D.Double(buffer1[0],buffer1[1]));
+ borderCoords.add(insertIndex, bridge);
insertIndex++;
//remove the duplicateion inner loop end point
loop.remove(loop.size()-1);
@@ -196,7 +279,7 @@ public class EarClipping {
for(int k=0;k<=index;k++,insertIndex++){
borderCoords.add(insertIndex,loop.get(k));
}
- borderCoords.add(insertIndex, new Vector2D.Double(buffer1[0],buffer1[1]));
+ borderCoords.add(insertIndex, bridge.copy());
}
@@ -262,14 +345,6 @@ public class EarClipping {
}
}
- /**
- * Sort inner holes by minimum x value.
- */
- private void orderHoles(SimplePolygon part){
- Collections.sort(part.inners, X_SORTER);
- }
-
-
/**
* @return true if currentsegment is convex
*/
diff --git a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
index a71bccc12c..5c3c8a360f 100644
--- a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
+++ b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
@@ -122,6 +122,79 @@ public class ToPrimitiveTest {
}
}
+ /**
+ * Tests the triangulation of a vertical polygon with a hole: a wall with a window.
+ * The polygon is degenerate in the (x,y) plane, so it shall be triangulated in its own plane.
+ */
+ @Test
+ public void testVerticalPolygonWithHole() {
+ final CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic3D();
+ final LinearRing exterior = ring3D(crs, 0,0,0, 6,0,0, 6,0,4, 0,0,4, 0,0,0);
+ final LinearRing window = ring3D(crs, 2,0,1, 2,0,3, 4,0,3, 4,0,1, 2,0,1);
+ final Polygon polygon = GeometryFactory.DEFAULT.createPolygon(exterior, java.util.List.of(window));
+ final MeshPrimitive primitive = (MeshPrimitive) new GeometryProcessor().toPrimitive(polygon);
+ assertEquals(20, trianglesArea3D(primitive), 1E-9);
+ final double[] c = primitive.getPositions().toArrayDouble();
+ for (int i = 1; i < c.length; i += 3) {
+ assertEquals(0, c[i], "y shall stay in the wall plane.");
+ }
+ }
+
+ /**
+ * Tests the triangulation of a vertical polygon in the (y,z) plane, which uses another order of
+ * coordinates than {@link #testVerticalPolygonWithHole()}. The x coordinate shall be restored.
+ */
+ @Test
+ public void testVerticalPolygonInYZPlane() {
+ final CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic3D();
+ final LinearRing exterior = ring3D(crs, 3,0,0, 3,5,0, 3,5,2, 3,0,2, 3,0,0);
+ final Polygon polygon = GeometryFactory.DEFAULT.createPolygon(exterior, null);
+ final MeshPrimitive primitive = (MeshPrimitive) new GeometryProcessor().toPrimitive(polygon);
+ assertEquals(10, trianglesArea3D(primitive), 1E-9);
+ final double[] c = primitive.getPositions().toArrayDouble();
+ for (int i = 0; i < c.length; i += 3) {
+ assertEquals(3, c[i], "x shall stay in the wall plane.");
+ }
+ }
+
+ /**
+ * Tests the triangulation of a horizontal three-dimensional polygon with a hole.
+ * The points added to cut the exterior ring toward the hole shall keep their z coordinate.
+ */
+ @Test
+ public void testPolygon3DWithHole() {
+ final CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic3D();
+ final LinearRing exterior = ring3D(crs, 0,0,5, 10,0,5, 10,10,5, 0,10,5, 0,0,5);
+ final LinearRing hole = ring3D(crs, 2,2,5, 2,4,5, 4,4,5, 4,2,5, 2,2,5);
+ final Polygon polygon = GeometryFactory.DEFAULT.createPolygon(exterior, java.util.List.of(hole));
+ final MeshPrimitive primitive = (MeshPrimitive) new GeometryProcessor().toPrimitive(polygon);
+ assertEquals(96, trianglesArea3D(primitive), 1E-9);
+ final double[] c = primitive.getPositions().toArrayDouble();
+ for (int i = 2; i < c.length; i += 3) {
+ assertEquals(5, c[i], "z shall be preserved.");
+ }
+ }
+
+ private static LinearRing ring3D(final CoordinateReferenceSystem crs, final double... coordinates) {
+ return GeometryFactory.DEFAULT.createLinearRing(
+ GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(crs, coordinates)));
+ }
+
+ /**
+ * Returns the sum of the areas of the triangles of the given non-indexed three-dimensional mesh.
+ */
+ private static double trianglesArea3D(final MeshPrimitive primitive) {
+ final double[] c = primitive.getPositions().toArrayDouble();
+ double area = 0;
+ for (int i = 0; i + 8 < c.length; i += 9) {
+ final double ux = c[i+3] - c[i], uy = c[i+4] - c[i+1], uz = c[i+5] - c[i+2];
+ final double vx = c[i+6] - c[i], vy = c[i+7] - c[i+1], vz = c[i+8] - c[i+2];
+ final double nx = uy*vz - uz*vy, ny = uz*vx - ux*vz, nz = ux*vy - uy*vx;
+ area += Math.sqrt(nx*nx + ny*ny + nz*nz) / 2;
+ }
+ return area;
+ }
+
/**
* Creates a ring in the two-dimensional test CRS.
*/
(sis) 09/12: feat(Geometry): implements getDimension(coord) on several geometry types
Posted by js...@apache.org.
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 <jo...@geomatys.com>
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;