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.

Reply via email to