sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1812747 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/geometry/ sis-referencing/src/main/java/org/apache/sis/internal/referencing/ sis-referencing/src/main/java/org/apache/sis/referencing/operation/ sis-referen...
Date Fri, 20 Oct 2017 13:39:29 GMT
Author: desruisseaux
Date: Fri Oct 20 13:39:28 2017
New Revision: 1812747

URL: http://svn.apache.org/viewvc?rev=1812747&view=rev
Log:
When a coordinate operation change the longitude axis range from [-180 … +180]° to [0 … 360]°, the Envelopes.transform(…) result should be normalized accordingly.

Added:
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/CoordinateOperationsTest.java   (with props)
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java   (with props)
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java
      - copied, changed from r1812269, sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/package-info.java
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -21,10 +21,10 @@ package org.apache.sis.geometry;
  * support Java2D (e.g. Android),  or applications that do not need it may want to avoid to
  * force installation of the Java2D module (e.g. JavaFX/SWT).
  */
+import java.util.List;
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
-import org.opengis.referencing.cs.RangeMeaning;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -40,6 +40,7 @@ import org.apache.sis.util.ComparisonMod
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
 import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
 import org.apache.sis.internal.referencing.CoordinateOperations;
 import org.apache.sis.internal.referencing.DirectPositionView;
@@ -86,7 +87,7 @@ import static org.apache.sis.util.String
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Johann Sorel (Geomatys)
- * @version 0.5
+ * @version 0.8
  *
  * @see org.apache.sis.metadata.iso.extent.Extents
  * @see CRS
@@ -102,15 +103,6 @@ public final class Envelopes extends Sta
     }
 
     /**
-     * Returns {@code true} if the given axis is of kind "Wrap Around".
-     *
-     * @see AbstractEnvelope#isWrapAround(CoordinateSystemAxis)
-     */
-    static boolean isWrapAround(final CoordinateSystemAxis axis) {
-        return RangeMeaning.WRAPAROUND.equals(axis.getRangeMeaning());
-    }
-
-    /**
      * Invoked when a recoverable exception occurred. Those exceptions must be minor enough
      * that they can be silently ignored in most cases.
      */
@@ -446,6 +438,7 @@ public final class Envelopes extends Sta
         if (envelope == null) {
             return null;
         }
+        boolean isOperationComplete = true;
         final CoordinateReferenceSystem sourceCRS = operation.getSourceCRS();
         if (sourceCRS != null) {
             final CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
@@ -465,6 +458,7 @@ public final class Envelopes extends Sta
                     throw new TransformException(Errors.format(Errors.Keys.CanNotTransformEnvelope), e);
                 }
                 if (!mt.isIdentity()) {
+                    isOperationComplete = false;
                     envelope = transform(mt, envelope);
                 }
             }
@@ -575,7 +569,7 @@ public final class Envelopes extends Sta
         long isWrapAroundAxis = 0;
         long dimensionBitMask = 1;
         final int dimension = targetCS.getDimension();
-        for (int i=0; i<dimension; i++, dimensionBitMask <<= 1) {
+poles:  for (int i=0; i<dimension; i++, dimensionBitMask <<= 1) {
             final CoordinateSystemAxis axis = targetCS.getAxis(i);
             if (axis == null) {                 // Should never be null, but check as a paranoiac safety.
                 continue;
@@ -607,9 +601,9 @@ public final class Envelopes extends Sta
                          * lost dimensions. So we don't log any warning in this case.
                          */
                         if (dimension >= mt.getSourceDimensions()) {
-                            recoverableException(Envelopes.class, exception);
+                            warning = exception;
                         }
-                        return transformed;
+                        break poles;
                     }
                     targetPt = new GeneralDirectPosition(mt.getSourceDimensions());
                     for (int j=0; j<dimension; j++) {
@@ -646,7 +640,7 @@ public final class Envelopes extends Sta
              * axis have been included in the envelope  (in which case the next step after this
              * loop doesn't need to be executed for that axis).
              */
-            if ((includedMinValue & includedMaxValue & dimensionBitMask) == 0 && isWrapAround(axis)) {
+            if ((includedMinValue & includedMaxValue & dimensionBitMask) == 0 && CoordinateOperations.isWrapAround(axis)) {
                 isWrapAroundAxis |= dimensionBitMask;
             }
             // Restore 'targetPt' to its initial state, which is equals to 'centerPt'.
@@ -666,7 +660,7 @@ public final class Envelopes extends Sta
             while (isWrapAroundAxis != 0) {
                 final int wrapAroundDimension = Long.numberOfTrailingZeros(isWrapAroundAxis);
                 dimensionBitMask = 1 << wrapAroundDimension;
-                isWrapAroundAxis &= ~dimensionBitMask; // Clear now the bit, for the next iteration.
+                isWrapAroundAxis &= ~dimensionBitMask;              // Clear now the bit, for the next iteration.
                 final CoordinateSystemAxis wrapAroundAxis = targetCS.getAxis(wrapAroundDimension);
                 final double min = wrapAroundAxis.getMinimumValue();
                 final double max = wrapAroundAxis.getMaximumValue();
@@ -689,8 +683,8 @@ public final class Envelopes extends Sta
                     for (int c=0; c<4; c++) {
                         /*
                          * Set the ordinate value along the axis having the singularity point
-                         * (cases c=0 and c=2). If the envelope did not included that point,
-                         * then skip completly this case and the next one, i.e. skip c={0,1}
+                         * (cases c=0 and c=2).  If the envelope did not included that point,
+                         * then skip completely this case and the next one, i.e. skip c={0,1}
                          * or skip c={2,3}.
                          */
                         double value = max;
@@ -722,6 +716,21 @@ public final class Envelopes extends Sta
                 targetPt.setOrdinate(wrapAroundDimension, centerPt[wrapAroundDimension]);
             }
         }
+        /*
+         * At this point we finished envelope transformation. Verify if some ordinates need to be "wrapped around"
+         * as a result of the coordinate operation.  This is usually the longitude axis where the source CRS uses
+         * the [-180 … +180]° range and the target CRS uses the [0 … 360]° range, or the converse. We do not wrap
+         * around if the source and target axes use the same range (e.g. the longitude stay [-180 … +180]°) in order
+         * to reduce the risk of discontinuities. If the user really wants unconditional wrap around, (s)he can call
+         * GeneralEnvelope.normalize().
+         */
+        final List<Integer> wrapAroundChanges;
+        if (isOperationComplete && operation instanceof AbstractCoordinateOperation) {
+            wrapAroundChanges = ((AbstractCoordinateOperation) operation).getWrapAroundChanges();
+        } else {
+            wrapAroundChanges = CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS);
+        }
+        transformed.normalize(wrapAroundChanges);
         if (warning != null) {
             recoverableException(Envelopes.class, warning);
         }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -21,6 +21,7 @@ package org.apache.sis.geometry;
  * support Java2D (e.g. Android),  or applications that do not need it may want to avoid to
  * force installation of the Java2D module (e.g. JavaFX/SWT).
  */
+import java.util.List;
 import java.util.Arrays;
 import java.io.Serializable;
 import java.lang.reflect.Field;
@@ -887,13 +888,23 @@ public class GeneralEnvelope extends Arr
      * @see AbstractDirectPosition#normalize()
      */
     public boolean normalize() {
+        return normalize(null);
+    }
+
+    /**
+     * Normalizes only the dimensions in the given list, or all dimensions if the list is null.
+     * This is used for normalizing the result of a coordinate operation where a wrap around axis
+     * does not necessarily means that the ordinates need to be normalized along that axis.
+     */
+    final boolean normalize(final List<Integer> dimensions) {
         boolean changed = false;
         if (crs != null) {
             final int d = ordinates.length >>> 1;
             final int beginIndex = beginIndex();
-            final int dimension = endIndex() - beginIndex;
+            final int count = (dimensions != null) ? dimensions.size() : endIndex() - beginIndex;
             final CoordinateSystem cs = crs.getCoordinateSystem();
-            for (int i=0; i<dimension; i++) {
+            for (int j=0; j<count; j++) {
+                final int i = (dimensions != null) ? dimensions.get(j) : j;
                 final int iLower = beginIndex + i;
                 final int iUpper = iLower + d;
                 final CoordinateSystemAxis axis = cs.getAxis(i);
@@ -941,8 +952,6 @@ public class GeneralEnvelope extends Arr
         return changed;
     }
 
-    // Note: As of JDK 1.6.0_31, using {@linkplain #getLower(int)} in the first line crash the
-    // Javadoc tools, maybe because getLower/getUpper are defined in a non-public parent class.
     /**
      * Ensures that <var>lower</var> &lt;= <var>upper</var> for every dimensions.
      * If a {@linkplain #getUpper(int) upper ordinate value} is less than a

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.geometry;
 
+import java.util.List;
 import java.awt.geom.Point2D;
 import java.awt.geom.Line2D;
 import java.awt.geom.Ellipse2D;
@@ -32,6 +33,9 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.internal.referencing.j2d.ShapeUtilities;
+import org.apache.sis.internal.referencing.j2d.IntervalRectangle;
+import org.apache.sis.internal.referencing.CoordinateOperations;
+import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
 import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
@@ -351,9 +355,9 @@ public final class Shapes2D extends Stat
             D1=D2;
         }
         if (destination != null) {
-            destination.setRect(xmin, ymin, xmax-xmin, ymax-ymin);
+            destination.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
         } else {
-            destination = new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin);
+            destination = new IntervalRectangle(xmin, ymin, xmax, ymax);
         }
         /*
          * Note: a previous version had an "assert" statement here comparing our calculation
@@ -539,8 +543,8 @@ public final class Shapes2D extends Stat
              * Forget any axes that are not of kind "WRAPAROUND". Then get the final
              * bit pattern indicating which points to test. Iterate over that bits.
              */
-            if ((toTest & 0x33333333) != 0 && !Envelopes.isWrapAround(targetCS.getAxis(0))) toTest &= 0xCCCCCCCC;
-            if ((toTest & 0xCCCCCCCC) != 0 && !Envelopes.isWrapAround(targetCS.getAxis(1))) toTest &= 0x33333333;
+            if ((toTest & 0x33333333) != 0 && !CoordinateOperations.isWrapAround(targetCS.getAxis(0))) toTest &= 0xCCCCCCCC;
+            if ((toTest & 0xCCCCCCCC) != 0 && !CoordinateOperations.isWrapAround(targetCS.getAxis(1))) toTest &= 0x33333333;
             while (toTest != 0) {
                 final int border = Integer.numberOfTrailingZeros(toTest);
                 final int bitMask = 1 << border;
@@ -569,6 +573,42 @@ public final class Shapes2D extends Stat
                 }
             }
         }
+        /*
+         * At this point we finished envelope transformation. Verify if some ordinates need to be "wrapped around"
+         * as a result of the coordinate operation.   This is usually the longitude axis where the source CRS uses
+         * the [-180 … +180]° range and the target CRS uses the [0 … 360]° range, or the converse. In such case we
+         * set the rectangle to the full range (we do not use the mechanism documented in Envelope2D) because most
+         * Rectangle2D implementations do not support spanning the anti-meridian. This results in larger rectangle
+         * than what would be possible with GeneralEnvelope or Envelope2D, but we try to limit the situation where
+         * this expansion is applied.
+         */
+        final List<Integer> wrapAroundChanges;
+        if (operation instanceof AbstractCoordinateOperation) {
+            wrapAroundChanges = ((AbstractCoordinateOperation) operation).getWrapAroundChanges();
+        } else {
+            wrapAroundChanges = CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS);
+        }
+        for (int dim : wrapAroundChanges) {                               // Empty in the vast majority of cases.
+            final CoordinateSystemAxis axis = targetCS.getAxis(dim);
+            final double minimum = axis.getMinimumValue();
+            final double maximum = axis.getMaximumValue();
+            final double o1, o2;
+            if (dim == 0) {
+                o1 = destination.getMinX();
+                o2 = destination.getMaxX();
+            } else {
+                o1 = destination.getMinY();
+                o2 = destination.getMaxY();
+            }
+            if (o1 < minimum || o2 > maximum) {
+                final double span = maximum - minimum;
+                if (dim == 0) {
+                    destination.setRect(minimum, destination.getY(), span, destination.getHeight());
+                } else {
+                    destination.setRect(destination.getX(), minimum, destination.getWidth(), span);
+                }
+            }
+        }
         if (warning != null) {
             Envelopes.recoverableException(Shapes2D.class, warning);
         }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -16,25 +16,54 @@
  */
 package org.apache.sis.internal.referencing;
 
+import java.util.List;
+import java.util.Objects;
+import javax.measure.UnitConverter;
+import javax.measure.IncommensurableException;
+import org.opengis.referencing.cs.RangeMeaning;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.GeneralDerivedCRS;
+import org.apache.sis.internal.metadata.AxisDirections;
 import org.opengis.referencing.operation.CoordinateOperationFactory;
 import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.system.SystemListener;
+import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.internal.jdk9.JDK9;
 
 
 /**
  * The default coordinate operation factory, provided in a separated class for deferring class loading
- * until first needed.
+ * until first needed. Contains also utility methods related to coordinate operations.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
 public final class CoordinateOperations extends SystemListener {
     /**
-     * The factory.
+     * Cached values or {@link #wrapAroundChanges wrapAroundChanges(…)}, created when first needed.
+     * Indices are bit masks computed by {@link #changes changes(…)}. Since the most common "wrap around" axes
+     * are longitude at dimension 0 or 1, and some measurement of time (in climatology) at dimension 2 or 3,
+     * then the most likely values are (binary digits):
+     *
+     * {@preformat text
+     *     0000    0100    1000
+     *     0001    0101    1001
+     *     0010    0110    1010
+     * }
+     *
+     * The last decimal value is 10 (binary {@code 1010}); we don't need to cache more.
+     */
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private static final List<Integer>[] CACHE = new List[11];
+
+    /**
+     * The system-wide default factory.
      */
     private static volatile DefaultCoordinateOperationFactory factory;
 
@@ -73,4 +102,134 @@ public final class CoordinateOperations
         }
         return c;
     }
+
+    /**
+     * Returns {@code true} if the given axis is of kind "Wrap Around".
+     * Defined here because used together with {@link #wrapAroundChanges wrapAroundChanges(…)}.
+     *
+     * @param  axis  the axis to test.
+     * @return {@code true} if the given axis has "wrap around" range meaning.
+     */
+    public static boolean isWrapAround(final CoordinateSystemAxis axis) {
+        return RangeMeaning.WRAPAROUND.equals(axis.getRangeMeaning());
+    }
+
+    /**
+     * Computes indices of target dimensions where "wrap around" may happen as a result of a coordinate operation.
+     * This is usually the longitude axis when the source CRS uses the [-180 … +180]° range and the target CRS uses
+     * the [0 … 360]° range, or the converse.
+     *
+     * @param  source  the source of the coordinate operation.
+     * @param  target  the target of the coordinate operation.
+     * @return list of target dimensions where "wrap around" may happen.
+     */
+    public static List<Integer> wrapAroundChanges(CoordinateReferenceSystem source, final CoordinateSystem target) {
+        long changes = changes(source.getCoordinateSystem(), target);
+        while (source instanceof GeneralDerivedCRS) {
+            source = ((GeneralDerivedCRS) source).getBaseCRS();
+            changes |= changes(source.getCoordinateSystem(), target);
+        }
+        final boolean useCache = (changes >= 0 && changes < CACHE.length);
+        if (useCache) {
+            /*
+             * In most cases we have an existing instance. Since WrapAroundAxes are immutable, if we got a reference,
+             * the object should be okay ("published" in memory model terminology) even if we did not synchronized.
+             * The reference however may not be visible in the array, but we will check later in a synchronized block.
+             */
+            final List<Integer> existing = CACHE[(int) changes];
+            if (existing != null) {
+                return existing;
+            }
+        }
+        /*
+         * No existing instance found. Expand the 'changes' bit mask in an array of integers in order to create an
+         * unmodifiable List<Integer>. The list is for public API; internally, Apache SIS will use toBitMask(…).
+         */
+        long r = changes;
+        final Integer[] indices = new Integer[Long.bitCount(r)];
+        for (int i=0; i<indices.length; i++) {
+            final int dim = Long.numberOfTrailingZeros(r);
+            indices[i] = dim;
+            r &= ~(1L << dim);
+        }
+        final List<Integer> dimensions = JDK9.listOf(indices);
+        if (useCache) {
+            synchronized (CACHE) {
+                final List<Integer> existing = CACHE[(int) changes];
+                if (existing != null) {
+                    return existing;
+                }
+                CACHE[(int) changes] = dimensions;
+            }
+        }
+        return dimensions;
+    }
+
+    /**
+     * Returns the packed indices of target dimensions where ordinate values may need to be wrapped around.
+     * This method matches source and target coordinate system axes having {@link RangeMeaning#WRAPAROUND},
+     * then verifies if the range of values changed (taking unit conversions in account). A target dimension
+     * {@code i} may need to "wrap around" the coordinate values if the {@code 1 << i} bit is set.
+     * If there is no change, then the value is zero.
+     */
+    private static long changes(final CoordinateSystem source, final CoordinateSystem target) {
+        long changes = 0;
+        if (source != target) {                                 // Optimization for a common case.
+            /*
+             * Get the dimensions of all "wrap around" axes in the source coordinate system.
+             * We create this list first because we may iterate more than once on those axes
+             * and we want to clear the axes that we already matched. We use a bitmask for
+             * efficiency, with the bits of "wrap around" dimensions set to 1.
+             */
+            long isWrapAroundAxis = 0;
+            for (int i=Math.min(Long.SIZE, source.getDimension()); --i >= 0;) {
+                if (isWrapAround(source.getAxis(i))) {
+                    isWrapAroundAxis |= (1 << i);
+                }
+            }
+            if (isWrapAroundAxis != 0) {                        // Loop below is useless otherwise.
+                /*
+                 * For each "wrap around" axis in the target CRS, search a matching axis in the source CRS
+                 * which is also "wrap around", is colinear and uses compatible unit of measurement. There
+                 * is usually at most one "wrap around" axis, but this code is nevertheless generic enough
+                 * for an arbitrary amount of axes.
+                 */
+                final int dim = Math.min(Long.SIZE, target.getDimension());
+compare:        for (int i=0; i<dim; i++) {
+                    final CoordinateSystemAxis axis = target.getAxis(i);
+                    if (isWrapAround(axis)) {
+                        long candidates = isWrapAroundAxis;
+                        do {
+                            final long mask = Long.lowestOneBit(candidates);
+                            final CoordinateSystemAxis src = source.getAxis(Long.numberOfTrailingZeros(mask));
+                            if (Objects.equals(AxisDirections.absolute(src .getDirection()),
+                                               AxisDirections.absolute(axis.getDirection())))
+                            {
+                                try {
+                                    final UnitConverter c  = src.getUnit().getConverterToAny(axis.getUnit());
+                                    final double minimum   = axis.getMinimumValue();
+                                    final double maximum   = axis.getMaximumValue();
+                                    final double tolerance = (maximum - minimum) * Numerics.COMPARISON_THRESHOLD;
+                                    if (!Numerics.epsilonEqual(c.convert(src.getMinimumValue()), minimum, tolerance) ||
+                                        !Numerics.epsilonEqual(c.convert(src.getMaximumValue()), maximum, tolerance))
+                                    {
+                                        changes |= (1 << i);
+                                    }
+                                    isWrapAroundAxis &= ~mask;      // We are done with this source axis.
+                                    if (isWrapAroundAxis == 0) {
+                                        break compare;              // Useless to continue if there is no more source axis.
+                                    }
+                                    continue compare;               // Match next pair of wrap around axes.
+                                } catch (IncommensurableException e) {
+                                    // Ignore (should not happen often). We will try to match another pair of axes.
+                                }
+                            }
+                            candidates &= ~mask;        // Unable to use that axis. Check if we can use another one.
+                        } while (candidates != 0);
+                    }
+                }
+            }
+        }
+        return changes;
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -17,9 +17,12 @@
 package org.apache.sis.referencing.operation;
 
 import java.util.Map;
+import java.util.List;
 import java.util.Objects;
 import java.util.Collection;
 import java.util.Collections;
+import java.io.IOException;
+import java.io.ObjectInputStream;
 import javax.xml.bind.Unmarshaller;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlSeeAlso;
@@ -55,6 +58,7 @@ import org.apache.sis.referencing.Abstra
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.PassThroughTransform;
 import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
+import org.apache.sis.internal.referencing.CoordinateOperations;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.referencing.WKTUtilities;
@@ -96,7 +100,7 @@ import static org.apache.sis.util.Utilit
  * synchronization.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.6
  * @module
  */
@@ -207,6 +211,16 @@ public class AbstractCoordinateOperation
     MathTransform transform;
 
     /**
+     * Indices of target dimensions where "wrap around" may happen as a result of this coordinate operation.
+     * This is usually the longitude axis when the source CRS uses the [-180 … +180]° range and the target
+     * CRS uses the [0 … 360]° range, or the converse. If there is no change, then this is an empty list.
+     *
+     * @see #getWrapAroundChanges()
+     * @see #computeTransientFields()
+     */
+    private transient List<Integer> wrapAroundChanges;
+
+    /**
      * Creates a new coordinate operation initialized from the given properties.
      * It is caller's responsibility to:
      *
@@ -369,6 +383,30 @@ check:      for (int isTarget=0; ; isTar
                 }
             }
         }
+        computeTransientFields();
+    }
+
+    /**
+     * Computes the {@link #wrapAroundChanges} field after we verified that the coordinate operation is valid.
+     */
+    final void computeTransientFields() {
+        if (sourceCRS != null && targetCRS != null) {
+            wrapAroundChanges = CoordinateOperations.wrapAroundChanges(sourceCRS, targetCRS.getCoordinateSystem());
+        } else {
+            wrapAroundChanges = Collections.emptyList();
+        }
+    }
+
+    /**
+     * Computes transient fields after deserialization.
+     *
+     * @param  in  the input stream from which to deserialize a coordinate operation.
+     * @throws IOException if an I/O error occurred while reading or if the stream contains invalid data.
+     * @throws ClassNotFoundException if the class serialized on the stream is not on the classpath.
+     */
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        computeTransientFields();
     }
 
     /**
@@ -392,6 +430,11 @@ check:      for (int isTarget=0; ; isTar
         domainOfValidity            = operation.getDomainOfValidity();
         scope                       = operation.getScope();
         transform                   = operation.getMathTransform();
+        if (operation instanceof AbstractCoordinateOperation) {
+            wrapAroundChanges = ((AbstractCoordinateOperation) operation).wrapAroundChanges;
+        } else {
+            computeTransientFields();
+        }
     }
 
     /**
@@ -727,6 +770,26 @@ check:      for (int isTarget=0; ; isTar
     }
 
     /**
+     * Returns the indices of target dimensions where "wrap around" may happen as a result of this coordinate operation.
+     * If such change exists, then this is usually the longitude axis when the source CRS uses the [-180 … +180]° range
+     * and the target CRS uses the [0 … 360]° range, or the converse. If there is no change, then this is an empty list.
+     *
+     * <p>The default implementation infers this list by inspecting the source and target coordinate system axes.
+     * It returns the indices of all target axes having {@link org.opengis.referencing.cs.RangeMeaning#WRAPAROUND}
+     * and for which the following condition holds: a colinear source axis exists with compatible unit of measurement,
+     * that source axis also have "wrap around" range, and the range of those source and target axes are not the same
+     * (taking unit conversions in account).</p>
+     *
+     * @return indices of target dimensions where "wrap around" may happen as a result of this coordinate operation.
+     *
+     * @since 0.8
+     */
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    public List<Integer> getWrapAroundChanges() {
+        return wrapAroundChanges;
+    }
+
+    /**
      * Compares this coordinate operation with the specified object for equality. If the {@code mode} argument
      * is {@link ComparisonMode#STRICT} or {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}, then all available
      * properties are compared including the {@linkplain #getDomainOfValidity() domain of validity} and the

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -317,7 +317,7 @@ public abstract class AbstractMathTransf
      *         double[] ordinates = Arrays.copyOfRange(srcPts, srcOff, srcOff + getSourceDimensions());
      *         derivative = this.derivative(new GeneralDirectPosition(ordinates));
      *     }
-     *     this.transform(srcPts, srcOff, dstPts, dstOff, 1);  // May overwrite srcPts.
+     *     this.transform(srcPts, srcOff, dstPts, dstOff, 1);                   // May overwrite srcPts.
      *     return derivative;
      * }
      *

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -19,13 +19,16 @@ package org.apache.sis.geometry;
 import java.util.Collections;
 import org.opengis.geometry.Envelope;
 import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.MathTransform2D;
 import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.referencing.operation.transform.MathTransformWrapper;
 import org.apache.sis.referencing.crs.DefaultCompoundCRS;
 import org.apache.sis.referencing.crs.HardCodedCRS;
-import org.apache.sis.referencing.operation.transform.MathTransformWrapper;
+import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.referencing.CRS;
 import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
@@ -171,4 +174,22 @@ public final strictfp class EnvelopesTes
         assertEquals( -80, env2D.getMinimum(1), 0);
         assertEquals(  80, env2D.getMaximum(1), 0);
     }
+
+    /**
+     * Tests a transformation where only the range of longitude axis is changed.
+     *
+     * @throws FactoryException if an error occurred while creating the operation.
+     * @throws TransformException if an error occurred while transforming the envelope.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testAxisRangeChange() throws FactoryException, TransformException {
+        final GeographicCRS sourceCRS = HardCodedCRS.WGS84;
+        final GeographicCRS targetCRS = HardCodedCRS.WGS84.forConvention(AxesConvention.POSITIVE_RANGE);
+        final GeneralEnvelope rectangle = createFromExtremums(sourceCRS, -178, -70, 165, 80);
+        final GeneralEnvelope expected  = createFromExtremums(targetCRS,  182, -70, 165, 80);
+        final GeneralEnvelope actual    = transform(CRS.findOperation(sourceCRS, targetCRS, null), rectangle);
+        assertGeometryEquals(expected, actual, STRICT, STRICT);
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -17,11 +17,17 @@
 package org.apache.sis.geometry;
 
 import java.awt.geom.Rectangle2D;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.MathTransform2D;
 import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.test.DependsOn;
+import org.junit.Test;
 
 import static org.apache.sis.test.ReferencingAssert.*;
 
@@ -78,4 +84,22 @@ public final strictfp class Shapes2DTest
     void assertGeometryEquals(Rectangle2D expected, Rectangle2D actual, double tolx, double toly) {
         assertRectangleEquals(expected, actual, tolx, toly);
     }
+
+    /**
+     * Tests a transformation where only the range of longitude axis is changed.
+     *
+     * @throws FactoryException if an error occurred while creating the operation.
+     * @throws TransformException if an error occurred while transforming the envelope.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testAxisRangeChange() throws FactoryException, TransformException {
+        final GeographicCRS sourceCRS = HardCodedCRS.WGS84;
+        final GeographicCRS targetCRS = HardCodedCRS.WGS84.forConvention(AxesConvention.POSITIVE_RANGE);
+        final Rectangle2D rectangle = createFromExtremums(sourceCRS, -178, -70, 165, 80);
+        final Rectangle2D expected  = createFromExtremums(targetCRS,    0, -70, 360, 80);
+        final Rectangle2D actual    = transform(CRS.findOperation(sourceCRS, targetCRS, null), rectangle);
+        assertGeometryEquals(expected, actual, STRICT, STRICT);
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -29,6 +29,7 @@ import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.HardCodedConversions;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
@@ -210,6 +211,29 @@ public abstract strictfp class Transform
         final G actual = transform(conversion, rectangle);
         assertGeometryEquals(expected, actual, ANGULAR_TOLERANCE, ANGULAR_TOLERANCE);
     }
+
+    /**
+     * Tests transform of an envelope over the ±180° limit. The Mercator projection used in this test
+     * is not expected to wrap the longitude around Earth when using only the {@code MathTransform}.
+     * However when the target CRS is known, then "wrap around" should be applied.
+     *
+     * @throws TransformException if an error occurred while transforming the envelope.
+     *
+     * @since 0.8
+     */
+    @Test
+    @DependsOnMethod("testTransform")
+    public void testTransformOverAntiMeridian() throws TransformException {
+        final ProjectedCRS  sourceCRS  = HardCodedConversions.mercator();
+        final GeographicCRS targetCRS  = sourceCRS.getBaseCRS();
+        final Conversion    conversion = inverse(sourceCRS.getConversionFromBase());
+        final G expected  = createFromExtremums(targetCRS, 179, 40, 181, 50);
+        final G rectangle = createFromExtremums(sourceCRS,
+                19926188.852, 4838471.398,                      // Computed by SIS (not validated by external authority).
+                20148827.834, 6413524.594);
+        final G actual = transform(conversion, rectangle);
+        assertGeometryEquals(expected, actual, ANGULAR_TOLERANCE, ANGULAR_TOLERANCE);
+    }
 
     /**
      * Returns the inverse of the given conversion. This method is not strictly correct

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/CoordinateOperationsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/CoordinateOperationsTest.java?rev=1812747&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/CoordinateOperationsTest.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/CoordinateOperationsTest.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -0,0 +1,77 @@
+/*
+ * 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.internal.referencing;
+
+import org.opengis.referencing.crs.GeographicCRS;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.referencing.cs.HardCodedAxes;
+import org.apache.sis.referencing.cs.HardCodedCS;
+import org.apache.sis.referencing.crs.HardCodedCRS;
+import org.apache.sis.referencing.operation.HardCodedConversions;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests {@link CoordinateOperations}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ * @since   0.8
+ * @module
+ */
+public final strictfp class CoordinateOperationsTest extends TestCase {
+    /**
+     * Tests {@link CoordinateOperations#isWrapAround(CoordinateSystemAxis)}.
+     */
+    @Test
+    public void testIsWrapAround() {
+        assertFalse("GEODETIC_LATITUDE",  CoordinateOperations.isWrapAround(HardCodedAxes.GEODETIC_LATITUDE));
+        assertTrue ("GEODETIC_LONGITUDE", CoordinateOperations.isWrapAround(HardCodedAxes.GEODETIC_LONGITUDE));
+        assertFalse("ELLIPSOIDAL_HEIGHT", CoordinateOperations.isWrapAround(HardCodedAxes.ELLIPSOIDAL_HEIGHT));
+    }
+
+    /**
+     * Tests {@link CoordinateOperations#wrapAroundChanges(CoordinateReferenceSystem, CoordinateSystem)}.
+     */
+    @Test
+    @DependsOnMethod("testIsWrapAround")
+    public void testWrapAroundChanges() {
+        CoordinateReferenceSystem sourceCRS = HardCodedCRS.WGS84_3D;
+        CoordinateSystem          targetCS = HardCodedCS.GEODETIC_2D;
+        assertTrue("(λ,φ,h) → (λ,φ)", CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS).isEmpty());
+
+        sourceCRS = HardCodedCRS.WGS84_3D.forConvention(AxesConvention.POSITIVE_RANGE);
+        assertArrayEquals("(λ′,φ,h) → (λ,φ)", new Integer[] {0},
+                CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS).toArray());
+
+        targetCS = HardCodedCS.GEODETIC_φλ;
+        assertArrayEquals("(λ′,φ,h) → (φ,λ)", new Integer[] {1},
+                CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS).toArray());
+
+        sourceCRS = HardCodedConversions.mercator((GeographicCRS) sourceCRS);
+        assertArrayEquals("(λ′,φ,h) → (φ,λ)", new Integer[] {1},
+                CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS).toArray());
+
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/CoordinateOperationsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/CoordinateOperationsTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -19,7 +19,6 @@ package org.apache.sis.referencing;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Arrays;
-import java.util.Collections;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -39,7 +38,6 @@ import org.apache.sis.util.Utilities;
 // Test imports
 import org.apache.sis.referencing.operation.HardCodedConversions;
 import org.apache.sis.referencing.crs.HardCodedCRS;
-import org.apache.sis.referencing.cs.HardCodedCS;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
@@ -288,9 +286,7 @@ public final strictfp class CRSTest exte
      */
     @Test
     public void testComponentsOfProjectedCRS() {
-        final ProjectedCRS volumetric = new DefaultProjectedCRS(Collections.singletonMap(ProjectedCRS.NAME_KEY, "3D"),
-                HardCodedCRS.WGS84_3D, HardCodedConversions.MERCATOR, HardCodedCS.PROJECTED_3D);
-
+        final ProjectedCRS volumetric = HardCodedConversions.mercator3D();
         assertFalse("isHorizontalCRS", CRS.isHorizontalCRS(volumetric));
         assertNull("getTemporalComponent", CRS.getTemporalComponent(volumetric));
         assertNull("getVerticalComponent", CRS.getVerticalComponent(volumetric, false));

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -16,9 +16,15 @@
  */
 package org.apache.sis.referencing.operation;
 
+import java.util.Map;
 import java.util.Collections;
+import org.opengis.referencing.crs.GeographicCRS;
+import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.operation.OperationMethod;
 import org.apache.sis.internal.referencing.provider.Mercator1SP;
+import org.apache.sis.referencing.crs.DefaultProjectedCRS;
+import org.apache.sis.referencing.crs.HardCodedCRS;
+import org.apache.sis.referencing.cs.HardCodedCS;
 
 
 /**
@@ -46,4 +52,52 @@ public final strictfp class HardCodedCon
      */
     private HardCodedConversions() {
     }
+
+    /**
+     * A two-dimensional Mercator projection using the WGS84 datum.
+     * This CRS uses (<var>easting</var>, <var>northing</var>) ordinates in metres.
+     * The base CRS uses (<var>longitude</var>, <var>latitude</var>) axes
+     * and the prime meridian is Greenwich.
+     *
+     * <p>This CRS is equivalent to {@code EPSG:3395} except for base CRS axis order,
+     * since EPSG puts latitude before longitude.</p>
+     *
+     * @return two-dimensional Mercator projection.
+     */
+    public static DefaultProjectedCRS mercator() {
+        return new DefaultProjectedCRS(name("Mercator"),
+                HardCodedCRS.WGS84, HardCodedConversions.MERCATOR, HardCodedCS.PROJECTED);
+    }
+
+    /**
+     * A three-dimensional Mercator projection using the WGS84 datum.
+     * This CRS uses (<var>easting</var>, <var>northing</var>, <var>height</var>) ordinates in metres.
+     * The base CRS uses (<var>longitude</var>, <var>latitude</var>, <var>height</var>) axes
+     * and the prime meridian is Greenwich.
+     *
+     * @return three-dimensional Mercator projection.
+     */
+    public static DefaultProjectedCRS mercator3D() {
+        return new DefaultProjectedCRS(name("Mercator 3D"),
+                HardCodedCRS.WGS84_3D, HardCodedConversions.MERCATOR, HardCodedCS.PROJECTED_3D);
+    }
+
+    /**
+     * A two- or three-dimensional Mercator projection using the given base CRS.
+     * This CRS uses (<var>easting</var>, <var>northing</var>) ordinates in metres.
+     *
+     * @param  baseCRS  the two- or three-dimensional base CRS.
+     * @return two- or three-dimensional Mercator projection.
+     */
+    public static DefaultProjectedCRS mercator(final GeographicCRS baseCRS) {
+        return new DefaultProjectedCRS(name("Mercator (other)"), baseCRS, HardCodedConversions.MERCATOR,
+                baseCRS.getCoordinateSystem().getDimension() == 3 ? HardCodedCS.PROJECTED_3D : HardCodedCS.PROJECTED);
+    }
+
+    /**
+     * Puts the given name in a property map CRS constructors.
+     */
+    private static Map<String,?> name(final String name) {
+        return Collections.singletonMap(ProjectedCRS.NAME_KEY, name);
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1812747&r1=1812746&r2=1812747&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -221,6 +221,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.cs.CodesTest.class,
     org.apache.sis.referencing.CRSTest.class,
     org.apache.sis.internal.referencing.DefinitionVerifierTest.class,
+    org.apache.sis.internal.referencing.CoordinateOperationsTest.class,
 
     // Coordinate operation finders are last, since they need everything else.
     org.apache.sis.referencing.operation.CoordinateOperationRegistryTest.class,

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java?rev=1812747&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -0,0 +1,55 @@
+/*
+ * 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.internal.jdk9;
+
+import java.util.List;
+import java.util.Collections;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
+
+
+/**
+ * Place holder for some functionalities defined only in JDK9.
+ * This file will be deleted on the SIS JDK9 branch.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+public final class JDK9 {
+    /**
+     * Do not allow instantiation of this class.
+     */
+    private JDK9() {
+    }
+
+    /**
+     * Placeholder for {@code List.of(...)}.
+     *
+     * @param  <E>       type of elements.
+     * @param  elements  the elements to put in an unmodifiable list.
+     * @return an unmodifiable list of the given elements.
+     */
+    @SafeVarargs
+    public static <E> List<E> listOf(final E... elements) {
+        switch (elements.length) {
+            case 0:  return Collections.emptyList();
+            case 1:  return Collections.singletonList(elements[0]);
+            default: return UnmodifiableArrayList.wrap(elements);
+        }
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Copied: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java (from r1812269, sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/package-info.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java?p2=sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java&p1=sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/package-info.java&r1=1812269&r2=1812747&rev=1812747&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/package-info.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java [UTF-8] Fri Oct 20 13:39:28 2017
@@ -16,7 +16,7 @@
  */
 
 /**
- * Place-holder for JDK8 classes that do not exist in JDK7.
+ * Place-holder for JDK9 classes that do not exist in JDK8.
  *
  * <STRONG>Do not use!</STRONG>
  *
@@ -24,8 +24,8 @@
  * may change in incompatible ways in any future version without notice.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @since   0.3
- * @version 0.7
+ * @since   0.8
+ * @version 0.8
  * @module
  */
-package org.apache.sis.internal.jdk8;
+package org.apache.sis.internal.jdk9;



Mime
View raw message