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