This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit b5cccfe9ce5e6d135276dbb837cec2e8b8eada40 Merge: e6d4c167a5 f5a308bb58 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sun Aug 21 16:26:11 2022 +0200 Merge remote-tracking branch 'origin/fix/world-envelope-to-polygon' into geoapi-4.0. The fix, which adds intermediate points during the "envelope to JTS" conversion, is modified in two ways: - Instead of adding mid-points, add points on the edges of axis ranges (e.g. 180°E or 180°W). - Disabled for now, because generally not needed after SIS-547 and SIS-486 new fixes. We need to revisit what could be a public API for enabling on demand. .../apache/sis/internal/feature/Geometries.java | 128 +++++++++++++++++---- .../internal/feature/SpatialOperationContext.java | 23 +++- .../apache/sis/internal/feature/package-info.java | 2 +- .../sis/internal/filter/GeometryConverter.java | 13 ++- .../apache/sis/internal/filter/package-info.java | 2 +- .../sis/filter/BinarySpatialFilterTestCase.java | 34 +++++- .../filter/BinarySpatialFilterUsingESRI_Test.java | 11 +- .../BinarySpatialFilterUsingJava2D_Test.java | 11 +- .../sis/internal/feature/GeometriesTestCase.java | 21 +++- 9 files changed, 212 insertions(+), 33 deletions(-) diff --cc core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java index 4a2a9a2b91,9a14b019db..1040107663 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java @@@ -21,10 -21,11 +21,12 @@@ import java.io.Serializable import java.nio.ByteBuffer; import java.util.Optional; import java.util.Iterator; -import org.apache.sis.internal.referencing.WraparoundAxesFinder; --import org.opengis.geometry.DirectPosition; import org.opengis.geometry.Envelope; --import org.opengis.referencing.crs.CoordinateReferenceSystem; ++import org.opengis.geometry.DirectPosition; ++import org.opengis.geometry.MismatchedDimensionException; import org.opengis.referencing.cs.CoordinateSystem; ++import org.opengis.referencing.cs.CoordinateSystemAxis; ++import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.apache.sis.geometry.AbstractEnvelope; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.geometry.WraparoundMethod; @@@ -54,7 -55,7 +56,7 @@@ import org.apache.sis.util.Classes * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) * @author Alexis Manin (Geomatys) -- * @version 1.1 ++ * @version 1.3 * @since 0.7 * @module */ @@@ -273,6 -274,6 +275,27 @@@ public abstract class Geometries<G> imp */ public abstract GeometryWrapper<G> parseWKB(ByteBuffer data) throws Exception; ++ /** ++ * Creates and wraps a point from the given position. ++ * ++ * @param point the point to convert to a geometry. ++ * @return the given point converted to a geometry. ++ */ ++ public final GeometryWrapper<G> createPoint(final DirectPosition point) { ++ final Object geometry; ++ final int n = point.getDimension(); ++ switch (n) { ++ case 2: geometry = createPoint(point.getOrdinate(0), point.getOrdinate(1)); break; ++ case 3: geometry = createPoint(point.getOrdinate(0), point.getOrdinate(1), point.getOrdinate(2)); break; ++ default: throw new MismatchedDimensionException(Errors.format(Errors.Keys.MismatchedDimension_3, "point", (n <= 2) ? 2 : 3, n)); ++ } ++ final GeometryWrapper<G> wrapper = castOrWrap(geometry); ++ if (point.getCoordinateReferenceSystem() != null) { ++ wrapper.setCoordinateReferenceSystem(point.getCoordinateReferenceSystem()); ++ } ++ return wrapper; ++ } ++ /** * Returns whether this library can produce geometry backed by the {@code float} primitive type * instead of the {@code double} primitive type. If single-precision mode is supported, using @@@ -387,21 -388,41 +410,78 @@@ /** * Creates a polyline made of points describing a rectangle whose start point is the lower left corner. * The sequence of points describes each corner, going in clockwise direction and repeating the starting -- * point to properly close the ring. - * In case a wrap-around ambiguity resides, control points are also added in the middle of the rectangle edges. -- * - * @param xd dimension of first axis. - * @param yd dimension of second axis. - * @param xd dimension of first axis. - * @param yd dimension of second axis. - * @param xPeriod Maximum span on <em>first</em> axis before triggering a wrap-around. - * If no wrap-around is possible, please set it to {@link Double#POSITIVE_INFINITY}. - * @param yPeriod Maximum span on <em>second</em> axis before triggering a wrap-around. - * If no wrap-around is possible, please set it to {@link Double#POSITIVE_INFINITY}. -- * @return a polyline made of a sequence of 5 points describing the given rectangle. ++ * point to properly close the ring. If wraparound may happen on at least one axis, then this method may ++ * add intermediate points on the axes where the envelope crosses the axis limit. ++ * ++ * @param xd dimension of first axis. ++ * @param yd dimension of second axis. ++ * @param expand whether to expand the envelope to full axis range if there is a wraparound. ++ * @param addPts whether to allow insertion of intermediate points on edges of axis domains. ++ * @return a polyline made of a sequence of at least 5 points describing the given rectangle. */ - private GeometryWrapper<G> createGeometry2D(final Envelope envelope, final int xd, final int yd) { - private GeometryWrapper<G> createGeometry2D(final Envelope envelope, final int xd, final int yd, double xPeriod, double yPeriod) { -- final DirectPosition lc = envelope.getLowerCorner(); -- final DirectPosition uc = envelope.getUpperCorner(); -- final double xmin = lc.getOrdinate(xd); -- final double ymin = lc.getOrdinate(yd); -- final double xmax = uc.getOrdinate(xd); -- final double ymax = uc.getOrdinate(yd); - return createWrapper(createPolyline(true, BIDIMENSIONAL, Vector.create(new double[] { - final boolean applyXWrapAround = xPeriod / 2 < xmax - xmin; - final boolean applyYWrapAround = yPeriod / 2 < ymax - ymin; - if (applyXWrapAround && applyYWrapAround) { - final double xmid = (xmin + xmax) / 2; - final double ymid = (ymin + ymax) / 2; - return createWrapper(createPolyline(true, BIDIMENSIONAL, Vector.create(new double[] { - xmin, ymin, xmin, ymid, xmin, ymax, xmid, ymax, xmax, ymid, xmax, ymax, xmax, ymid, xmax, ymin, xmid, ymin, xmin, ymin}))); - } else if (applyXWrapAround) { - final double xmid = (xmin + xmax) / 2; - return createWrapper(createPolyline(true, BIDIMENSIONAL, Vector.create(new double[] { - xmin, ymin, xmin, ymax, xmid, ymax, xmax, ymax, xmax, ymin, xmid, ymin, xmin, ymin}))); - } else if (applyYWrapAround) { - final double ymid = (ymin + ymax) / 2; - return createWrapper(createPolyline(true, BIDIMENSIONAL, Vector.create(new double[] { - xmin, ymin, xmin, ymid, xmin, ymax, xmax, ymax, xmax, ymid, xmax, ymin, xmin, ymin}))); - } else return createWrapper(createPolyline(true, BIDIMENSIONAL, Vector.create(new double[] { -- xmin, ymin, xmin, ymax, xmax, ymax, xmax, ymin, xmin, ymin}))); ++ private GeometryWrapper<G> createGeometry2D(final Envelope envelope, final int xd, final int yd, ++ final boolean expand, final boolean addPts) ++ { ++ final double xmin, ymin, xmax, ymax; ++ if (expand) { ++ xmin = envelope.getMinimum(xd); ++ ymin = envelope.getMinimum(yd); ++ xmax = envelope.getMaximum(xd); ++ ymax = envelope.getMaximum(yd); ++ } else { ++ final DirectPosition lc = envelope.getLowerCorner(); ++ final DirectPosition uc = envelope.getUpperCorner(); ++ xmin = lc.getOrdinate(xd); ++ ymin = lc.getOrdinate(yd); ++ xmax = uc.getOrdinate(xd); ++ ymax = uc.getOrdinate(yd); ++ } ++ final double[] coordinates; ++ /* ++ * Find if some intermediate points need to be added. We add points only at the edges of axis domain, ++ * for example at 180°E or 180°W. Furthermore we add points only on axes having increasing values, ++ * i.e. we do not add points on axes using the "end point < start point" convention. ++ */ ++ final CoordinateReferenceSystem crs; ++ if (addPts && (crs = envelope.getCoordinateReferenceSystem()) != null) { ++ final double xminIn, yminIn, xmaxIn, ymaxIn; // Intermediate min/max. ++ final boolean addXmin, addYmin, addXmax, addYmax; // Whether to add intermediate min/max. ++ int n = 5*BIDIMENSIONAL; // Number of coordinate values. ++ ++ final CoordinateSystem cs = crs.getCoordinateSystem(); ++ CoordinateSystemAxis axis = cs.getAxis(xd); ++ xminIn = axis.getMinimumValue(); ++ xmaxIn = axis.getMaximumValue(); ++ axis = cs.getAxis(yd); ++ yminIn = axis.getMinimumValue(); ++ ymaxIn = axis.getMaximumValue(); ++ final boolean addX = xmin <= xmax; // Whether we can add intermediates X/Y. ++ final boolean addY = ymin <= ymax; ++ if (addXmin = (addX && xminIn > xmin)) n += 2*BIDIMENSIONAL; ++ if (addYmin = (addY && yminIn > ymin)) n += 2*BIDIMENSIONAL; ++ if (addXmax = (addX && xmaxIn < xmax)) n += 2*BIDIMENSIONAL; ++ if (addYmax = (addY && ymaxIn < ymax)) n += 2*BIDIMENSIONAL; ++ ++ int i = 0; ++ coordinates = new double[n]; ++ /*Envelope*/ {coordinates[i++] = xmin; coordinates[i++] = ymin;} ++ if (addYmin) {coordinates[i++] = xmin; coordinates[i++] = yminIn;} ++ if (addYmax) {coordinates[i++] = xmin; coordinates[i++] = ymaxIn;} ++ /*Envelope*/ {coordinates[i++] = xmin; coordinates[i++] = ymax;} ++ if (addXmin) {coordinates[i++] = xminIn; coordinates[i++] = ymax;} ++ if (addXmax) {coordinates[i++] = xmaxIn; coordinates[i++] = ymax;} ++ /*Envelope*/ {coordinates[i++] = xmax; coordinates[i++] = ymax;} ++ if (addYmax) {coordinates[i++] = xmax; coordinates[i++] = ymaxIn;} ++ if (addYmin) {coordinates[i++] = xmax; coordinates[i++] = yminIn;} ++ /*Envelope*/ {coordinates[i++] = xmax; coordinates[i++] = ymin;} ++ if (addXmax) {coordinates[i++] = xmaxIn; coordinates[i++] = ymin;} ++ if (addXmin) {coordinates[i++] = xminIn; coordinates[i++] = ymin;} ++ /*Envelope*/ {coordinates[i++] = xmin; coordinates[i++] = ymin;} ++ assert i == n : i; ++ } else { ++ coordinates = new double[] {xmin, ymin, xmin, ymax, xmax, ymax, xmax, ymin, xmin, ymin}; ++ } ++ return createWrapper(createPolyline(true, BIDIMENSIONAL, Vector.create(coordinates))); } /** @@@ -446,27 -472,27 +526,31 @@@ case NORMALIZE: { throw new IllegalArgumentException(); } ++ /* ++ * TODO: `addPts` is `false` in all cases. We have not yet determined ++ * what could be a public API for enabling this option. ++ */ case NONE: { - result = createGeometry2D(envelope, xd, yd); - result = createGeometry2D(envelope, xd, yd, xPeriod, yPeriod); ++ result = createGeometry2D(envelope, xd, yd, false, false); break; } default: { final GeneralEnvelope ge = new GeneralEnvelope(envelope); ge.normalize(); ge.wraparound(strategy); - result = createGeometry2D(ge, xd, yd); - result = createGeometry2D(ge, xd, yd, xPeriod, yPeriod); ++ result = createGeometry2D(ge, xd, yd, true, false); break; } case SPLIT: { final Envelope[] parts = AbstractEnvelope.castOrCopy(envelope).toSimpleEnvelopes(); if (parts.length == 1) { - result = createGeometry2D(parts[0], xd, yd); - result = createGeometry2D(parts[0], xd, yd, xPeriod, yPeriod); ++ result = createGeometry2D(parts[0], xd, yd, true, false); break; } @SuppressWarnings({"unchecked", "rawtypes"}) final GeometryWrapper<G>[] polygons = new GeometryWrapper[parts.length]; for (int i=0; i<parts.length; i++) { - polygons[i] = createGeometry2D(parts[i], xd, yd); - polygons[i] = createGeometry2D(parts[i], xd, yd, xPeriod, yPeriod); ++ polygons[i] = createGeometry2D(parts[i], xd, yd, true, false); polygons[i].setCoordinateReferenceSystem(crs); } result = createMultiPolygon(polygons); diff --cc core/sis-feature/src/main/java/org/apache/sis/internal/feature/SpatialOperationContext.java index 82d090097a,82d090097a..5c08b642ef --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/SpatialOperationContext.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/SpatialOperationContext.java @@@ -61,7 -61,7 +61,7 @@@ import org.opengis.filter.DistanceOpera * <p>The serialization form is not a committed API and may change in any future version.</p> * * @author Martin Desruisseaux (Geomatys) -- * @version 1.1 ++ * @version 1.3 * @since 1.1 * @module */ @@@ -79,11 -79,11 +79,13 @@@ public final class SpatialOperationCont /** * Approximate geographic area of geometries, or {@code null} if unspecified. */ ++ @SuppressWarnings("serial") // Not statically typed as Serializable. private final GeographicBoundingBox areaOfInterest; /** * The target CRS in which to transform geometries, or {@code null} for inferring automatically. */ ++ @SuppressWarnings("serial") // Not statically typed as Serializable. private final CoordinateReferenceSystem computationCRS; /** @@@ -92,6 -92,6 +94,7 @@@ * Note that it does not mean that the units of measurement must be meters; only that they must * be compatible with meters. */ ++ @SuppressWarnings("serial") // Not statically typed as Serializable. private final Unit<?> systemUnit; /** @@@ -103,6 -103,6 +106,7 @@@ /** * The common CRS found by {@link #transform(GeometryWrapper[])}. May be null. */ ++ @SuppressWarnings("serial") // Not statically typed as Serializable. CoordinateReferenceSystem commonCRS; /** @@@ -335,6 -335,6 +339,17 @@@ select: if (commonCRS == null) * This is defined in a separated class for lazy static field initialization. */ private static final class Projector { ++ /** ++ * Whether the operation {@linkplain #method} used by this projector can handle longitude wraparounds ++ * as a continuous mathematical function. It is the case of projections using longitude value only in ++ * trigonometric functions such as {@link Math#sin(double)}. It is <strong>not</strong> the case of ++ * Mercator projection, where wraparounds cause a sudden jump from big positive values to big negative ++ * values (or conversely). ++ * ++ * @see org.apache.sis.referencing.operation.projection.LongitudeWraparound ++ */ ++ private static final boolean CONTINUOUS_WRAPAROUND = false; ++ /** A singleton map containing the name to assign to the CRS. */ private final Map<String,?> name; @@@ -401,10 -401,10 +416,14 @@@ } /* * Create a projected coordinate reference system for the geometry center. ++ * The central meridian should be set only if it does not cause insertion ++ * of `LongitudeWraparound` transform. */ final ParameterValueGroup p = method.getParameters().createValue(); p.parameter(Constants.STANDARD_PARALLEL_1).setValue(latitude); -- p.parameter(Constants.CENTRAL_MERIDIAN).setValue(longitude); ++ if (CONTINUOUS_WRAPAROUND) { ++ p.parameter(Constants.CENTRAL_MERIDIAN).setValue(longitude); ++ } final DefaultConversion conversion = new DefaultConversion(name, method, null, p); return new DefaultProjectedCRS(name, baseCRS, conversion, cartCS); } diff --cc core/sis-feature/src/main/java/org/apache/sis/internal/feature/package-info.java index 4e460ef936,4e460ef936..2bb87081ab --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/package-info.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/package-info.java @@@ -28,7 -28,7 +28,7 @@@ * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) * @author Alexis Manin (Geomatys) -- * @version 1.2 ++ * @version 1.3 * @since 0.7 * @module */ diff --cc core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java index 2d362d29b5,1fdfc0ca4c..435dce5cbf --- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java @@@ -19,8 -19,9 +19,10 @@@ package org.apache.sis.internal.filter import java.util.List; import java.util.Collection; import java.util.Collections; -import org.opengis.geometry.DirectPosition; import org.opengis.util.ScopedName; import org.opengis.geometry.Envelope; ++import org.opengis.geometry.DirectPosition; ++import org.opengis.geometry.MismatchedDimensionException; import org.opengis.metadata.extent.GeographicBoundingBox; import org.apache.sis.util.Classes; import org.apache.sis.util.ArgumentChecks; @@@ -42,7 -43,7 +44,7 @@@ import org.opengis.filter.InvalidFilter * * @author Martin Desruisseaux (Geomatys) * @author Alexis Manin (Geomatys) -- * @version 1.1 ++ * @version 1.3 * * @param <R> the type of resources (e.g. {@link org.opengis.feature.Feature}) used as inputs. * @param <G> the geometry implementation type. @@@ -73,6 -74,6 +75,7 @@@ final class GeometryConverter<R,G> exte * * @see #getParameters() */ ++ @SuppressWarnings("serial") // Not statically typed as Serializable. final Expression<? super R, ?> expression; /** @@@ -149,9 -150,21 +152,13 @@@ envelope = new ImmutableEnvelope((GeographicBoundingBox) value); } else if (value instanceof Envelope) { envelope = (Envelope) value; - } else if (value instanceof DirectPosition) { - final DirectPosition pt = (DirectPosition) value; - final Object geometry; - if (pt.getDimension() == 2) { - geometry = library.createPoint(pt.getOrdinate(0), pt.getOrdinate(1)); - } else if (pt.getDimension() == 3) { - geometry = library.createPoint(pt.getOrdinate(0), pt.getOrdinate(1), pt.getOrdinate(2)); - } else throw new InvalidFilterValueException(Errors.format( - Errors.Keys.IllegalClass_2, library.rootClass, Classes.getClass(value))); - final GeometryWrapper<G> wrapper = library.castOrWrap(geometry); - if (pt.getCoordinateReferenceSystem() != null) wrapper.setCoordinateReferenceSystem(pt.getCoordinateReferenceSystem()); - return wrapper; } else try { -- return library.castOrWrap(value); -- } catch (ClassCastException e) { ++ if (value instanceof DirectPosition) { ++ return library.createPoint((DirectPosition) value); ++ } else { ++ return library.castOrWrap(value); ++ } ++ } catch (ClassCastException | MismatchedDimensionException e) { throw new InvalidFilterValueException(Errors.format( Errors.Keys.IllegalClass_2, library.rootClass, Classes.getClass(value)), e); } diff --cc core/sis-feature/src/main/java/org/apache/sis/internal/filter/package-info.java index 915385a223,915385a223..bf70a5ca86 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/package-info.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/package-info.java @@@ -20,7 -20,7 +20,7 @@@ * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) -- * @version 1.1 ++ * @version 1.3 * @since 1.1 * @module */ diff --cc core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterTestCase.java index 5879bc6d82,be5f44f78e..0526fc1c7a --- a/core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterTestCase.java +++ b/core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterTestCase.java @@@ -18,9 -18,14 +18,14 @@@ package org.apache.sis.filter import javax.measure.Quantity; import javax.measure.quantity.Length; -import org.apache.sis.geometry.DirectPosition2D; -import org.apache.sis.geometry.GeneralEnvelope; -import org.apache.sis.setup.GeometryLibrary; -import org.apache.sis.test.Assume; -import org.opengis.filter.DistanceOperatorName; import org.opengis.geometry.Envelope; ++import org.opengis.util.FactoryException; ++import org.opengis.referencing.operation.TransformException; import org.opengis.referencing.crs.CoordinateReferenceSystem; ++import org.opengis.referencing.crs.GeographicCRS; import org.apache.sis.geometry.Envelope2D; ++import org.apache.sis.geometry.GeneralEnvelope; ++import org.apache.sis.geometry.DirectPosition2D; import org.apache.sis.geometry.WraparoundMethod; import org.apache.sis.internal.feature.Geometries; import org.apache.sis.measure.Quantities; @@@ -41,6 -46,6 +46,7 @@@ import org.opengis.filter.Expression import org.opengis.filter.Literal; import org.opengis.filter.FilterFactory; import org.opengis.filter.DistanceOperator; ++import org.opengis.filter.DistanceOperatorName; import org.opengis.filter.BinarySpatialOperator; @@@ -51,7 -56,7 +57,7 @@@ * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) * @author Alexis Manin (Geomatys) -- * @version 1.1 ++ * @version 1.3 * * @param <G> root class of geometry implementation. * @@@ -347,4 -352,22 +353,30 @@@ public abstract strictfp class BinarySp BinarySpatialOperator<Feature> overlaps = factory.overlaps(literal(Polygon.CONTAINS), right); assertSerializedEquals(overlaps); } + + /** + * Ensures that a world geographic envelope, once converted to a polygon and reprojected, remain coherent. - * This is a regression test. In the past, the operation pipeline [envelope -> polygon -> reprojected polygon] ++ * This is a regression test. In the past, the operation pipeline [envelope → polygon → reprojected polygon] + * caused the result to degenerate to single line following the anti-meridian. ++ * ++ * @throws FactoryException if an error occurred while fetching a CRS. ++ * @throws TransformException if a coordinate conversion was required but failed. + */ + @Test - public void testSpatialContextDoesNotDegenerateEnvelope() throws Exception { - Assume.assumeTrue("Require reprojection. Only supported for JTS for now", library.library == GeometryLibrary.JTS); - final Envelope e1 = new Envelope2D(HardCodedCRS.WGS84, -180, -90, 360, 180); ++ public void testSpatialContextDoesNotDegenerateEnvelope() throws FactoryException, TransformException { ++ final GeographicCRS sourceCRS = HardCodedCRS.WGS84; ++ final Envelope e1 = new Envelope2D(sourceCRS, -180, -90, 360, 180); + final DistanceFilter<?, G> within = new DistanceFilter<>(DistanceOperatorName.WITHIN, + library, factory.literal(e1), - factory.literal(new DirectPosition2D(HardCodedCRS.WGS84, 44, 2)), ++ factory.literal(new DirectPosition2D(sourceCRS, 44, 2)), + Quantities.create(1.0, Units.METRE)); + + final GeneralEnvelope envInCtx = within.context.transform(within.expression1.apply(null)).getEnvelope(); - assertNotEquals(envInCtx.getMinimum(0), envInCtx.getMaximum(0), 1000); ++ final double xmin = envInCtx.getMinimum(0); ++ final double xmax = envInCtx.getMaximum(0); ++ assertNotEquals("Degenerated envelope.", xmin, xmax, 1000); ++ ++ final double expected = sourceCRS.getDatum().getEllipsoid().getSemiMajorAxis() * (2*Math.PI); ++ assertEquals(expected, xmax - xmin, expected / 1000); + } } diff --cc core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterUsingESRI_Test.java index ed3ae6ae4f,ed3ae6ae4f..bb7332439e --- a/core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterUsingESRI_Test.java +++ b/core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterUsingESRI_Test.java @@@ -26,7 -26,7 +26,7 @@@ import org.junit.Test * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) -- * @version 1.1 ++ * @version 1.3 * @since 1.1 * @module */ @@@ -64,4 -64,4 +64,13 @@@ public final strictfp class BinarySpati @Ignore("Not yet mapped to an ESRI operation.") public void testWithReprojection() { } ++ ++ /** ++ * Test ignored for now (not yet mapped to an ESRI operation). ++ */ ++ @Test ++ @Override ++ @Ignore("Requires geometry reprojection.") ++ public void testSpatialContextDoesNotDegenerateEnvelope() { ++ } } diff --cc core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterUsingJava2D_Test.java index 5fccd95640,5fccd95640..0dd298f1bc --- a/core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterUsingJava2D_Test.java +++ b/core/sis-feature/src/test/java/org/apache/sis/filter/BinarySpatialFilterUsingJava2D_Test.java @@@ -25,7 -25,7 +25,7 @@@ import org.junit.Test * Tests {@link BinarySpatialFilter} implementations using ESRI library. * * @author Martin Desruisseaux (Geomatys) -- * @version 1.1 ++ * @version 1.3 * @since 1.1 * @module */ @@@ -91,6 -91,6 +91,15 @@@ public final strictfp class BinarySpati public void testWithReprojection() { } ++ /** ++ * Test ignored for now (not yet mapped to a Java2D operation). ++ */ ++ @Test ++ @Override ++ @Ignore("Requires geometry reprojection.") ++ public void testSpatialContextDoesNotDegenerateEnvelope() { ++ } ++ /** * Test ignored for now because {@link java.awt.geom.Path2D} does not implement {@code equals(Object)}. */ diff --cc core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java index 73a9d0c714,a0633569e2..59acd7822e --- a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java +++ b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java @@@ -17,9 -17,10 +17,11 @@@ package org.apache.sis.internal.feature; import java.util.Arrays; ++import java.util.EnumSet; import java.util.Iterator; -import org.apache.sis.geometry.Envelope2D; import org.opengis.geometry.Envelope; import org.apache.sis.setup.GeometryLibrary; ++import org.apache.sis.geometry.Envelope2D; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.geometry.WraparoundMethod; import org.apache.sis.referencing.crs.HardCodedCRS; @@@ -37,7 -38,7 +39,7 @@@ import static org.junit.Assert.* * * @author Martin Desruisseaux (Geomatys) * @author Alexis Manin (Geomatys) -- * @version 1.1 ++ * @version 1.3 * @since 1.0 * @module */ @@@ -218,13 -223,30 +220,30 @@@ public abstract strictfp class Geometri e.setRange(1, 1000, 1007); // Time e.setRange(2, 2, 3); // Latitude e.setRange(3, 89, 19); // Longitude (span anti-meridian). - - assertToGeometryEquals(e, WraparoundMethod.NONE, 2, 89, 2, 19, 3, 19, 3, 89, 2, 89); - - assertToGeometryEquals(e, WraparoundMethod.CONTIGUOUS, 2, -271, 2, -126, 2, 19, 3, 19, 3, -126, 3, -271, 2, -271); - - assertToGeometryEquals(e, WraparoundMethod.EXPAND, 2, -180, 2, 0, 2, 180, 3, 180, 3, 0, 3, -180, 2, -180); - - assertToGeometryEquals(e, WraparoundMethod.SPLIT, 2, 89, 2, 180, 3, 180, 3, 89, 2, 89, - 2, -180, 2, -80.5, 2, 19, 3, 19, 3, -80.5, 3, -180, 2, -180); + assertToGeometryEquals(e, WraparoundMethod.NONE, 2, 89, 2, 19, 3, 19, 3, 89, 2, 89); + assertToGeometryEquals(e, WraparoundMethod.CONTIGUOUS, 2, -271, 2, 19, 3, 19, 3, -271, 2, -271); + assertToGeometryEquals(e, WraparoundMethod.EXPAND, 2, -180, 2, 180, 3, 180, 3, -180, 2, -180); + assertToGeometryEquals(e, WraparoundMethod.SPLIT, 2, 89, 2, 180, 3, 180, 3, 89, 2, 89, + 2, -180, 2, 19, 3, 19, 3, -180, 2, -180); } ++ /** ++ * Tests conversion of a world-wide envelope using all supported wraparound methods. ++ */ + @Test - public void testWorldWGS84ToGeometry2D() { ++ public void testWorldToGeometry2D() { ++ final EnumSet<WraparoundMethod> methods = EnumSet.allOf(WraparoundMethod.class); ++ assertTrue(methods.remove(WraparoundMethod.NORMALIZE)); + Envelope2D env2d = new Envelope2D(HardCodedCRS.WGS84, -180, -90, 360, 180); - for (WraparoundMethod method : new WraparoundMethod[] { WraparoundMethod.NONE, WraparoundMethod.CONTIGUOUS, WraparoundMethod.EXPAND, WraparoundMethod.SPLIT}) { - assertToGeometryEquals(env2d, method, -180, -90, -180, 90, 0, 90, 180, 90, 180, -90, 0, -90, -180, -90); ++ for (WraparoundMethod method : methods) { ++ assertToGeometryEquals(env2d, method, -180, -90, -180, 90, 180, 90, 180, -90, -180, -90); + } - + env2d = new Envelope2D(HardCodedCRS.WGS84_LATITUDE_FIRST, -90, -180, 180, 360); - for (WraparoundMethod method : new WraparoundMethod[] { WraparoundMethod.NONE, WraparoundMethod.CONTIGUOUS, WraparoundMethod.EXPAND, WraparoundMethod.SPLIT}) { - assertToGeometryEquals(env2d, method, -90, -180, -90, 0, -90, 180, 90, 180, 90, 0, 90, -180, -90, -180); ++ for (WraparoundMethod method : methods) { ++ assertToGeometryEquals(env2d, method, -90, -180, -90, 180, 90, 180, 90, -180, -90, -180); + } + } + /** * Verifies that call to {@link Geometries#toGeometry2D(Envelope, WraparoundMethod)} * with the given argument values produces a geometry will all expected coordinates.