sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1813747 [6/13] - in /sis/branches/JDK9: ./ application/ application/sis-console/ application/sis-console/src/main/artifact/ application/sis-console/src/main/artifact/lib/ application/sis-console/src/main/artifact/lib/darwin/ application/si...
Date Mon, 30 Oct 2017 10:25:10 GMT
Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -122,10 +122,10 @@ final class CRSPair {
     }
 
     /**
-     * Return a string representation of this key.
+     * Returns a string representation of this key.
      */
     @Override
     public String toString() {
-        return label(sourceCRS) + " → " + label(targetCRS);
+        return label(sourceCRS) + " ⟶ " + label(targetCRS);
     }
 }

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -33,9 +33,10 @@ import org.opengis.metadata.Identifier;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.opengis.parameter.ParameterValueGroup;
 import org.apache.sis.internal.metadata.AxisDirections;
-import org.apache.sis.internal.metadata.VerticalDatumTypes;
-import org.apache.sis.internal.metadata.ReferencingServices;
+import org.apache.sis.internal.referencing.CoordinateOperations;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
+import org.apache.sis.internal.referencing.provider.Affine;
+import org.apache.sis.internal.referencing.provider.DatumShiftMethod;
 import org.apache.sis.internal.referencing.provider.Geographic2Dto3D;
 import org.apache.sis.internal.referencing.provider.Geographic3Dto2D;
 import org.apache.sis.internal.referencing.provider.GeographicToGeocentric;
@@ -116,17 +117,6 @@ import static org.apache.sis.util.Utilit
  */
 public class CoordinateOperationFinder extends CoordinateOperationRegistry {
     /**
-     * The accuracy threshold (in metres) for allowing the use of Molodensky approximation instead than the
-     * Geocentric Translation method. The accuracy of datum shifts with Molodensky approximation is about 5
-     * or 10 metres. However for this constant, we are not interested in absolute accuracy but rather in the
-     * difference between Molodensky and Geocentric Translation methods, which is much lower (less than 1 m).
-     * We nevertheless use a relatively high threshold as a conservative approach.
-     *
-     * @see #desiredAccuracy
-     */
-    private static final double MOLODENSKY_ACCURACY = 5;
-
-    /**
      * Identifiers used as the basis for identifier of CRS used as an intermediate step.
      * The values can be of two kinds:
      *
@@ -442,7 +432,7 @@ public class CoordinateOperationFinder e
          * Actually we do not know if the longitude rotation should be before or after datum shift. But this ambiguity
          * can usually be ignored because Bursa-Wolf parameters are always used with source and target prime meridians
          * set to Greenwich in EPSG dataset 8.9.  For safety, the SIS's DefaultGeodeticDatum class ensures that if the
-         * prime meridian are not the same, then the target meridian must be Greenwich.
+         * prime meridians are not the same, then the target meridian must be Greenwich.
          */
         final DefaultMathTransformFactory.Context context = ReferencingUtilities.createTransformContext(
                 sourceCRS, targetCRS, new MathTransformContext(sourceDatum, targetDatum));
@@ -498,20 +488,34 @@ public class CoordinateOperationFinder e
         final DefaultMathTransformFactory mtFactory = factorySIS.getDefaultMathTransformFactory();
         MathTransform before = null, after = null;
         ParameterValueGroup parameters;
-        if (datumShift != null) {
+        if (identifier == DATUM_SHIFT || identifier == ELLIPSOID_CHANGE) {
             /*
              * If the transform can be represented by a single coordinate operation, returns that operation.
              * Possible operations are:
              *
-             *    - Geocentric translation         (in geocentric, geographic-2D or geographic-3D domains)
              *    - Position Vector transformation (in geocentric, geographic-2D or geographic-3D domains)
+             *    - Geocentric translation         (in geocentric, geographic-2D or geographic-3D domains)
+             *    - [Abridged] Molodensky          (as an approximation of geocentric translation)
+             *    - Identity                       (if the desired accuracy is so large than we can skip datum shift)
              *
-             * Otherwise, maybe we failed to create the operation because the coordinate system type were not the same.
-             * Convert unconditionally to XYZ geocentric coordinates and apply the datum shift in that coordinate space.
+             * TODO: if both CS are ellipsoidal but with different number of dimensions, then we should use
+             * an intermediate 3D geographic CRS in order to enable the use of Molodensky method if desired.
              */
-            parameters = GeocentricAffine.createParameters(sourceCS, targetCS, datumShift, desiredAccuracy >= MOLODENSKY_ACCURACY);
+            final DatumShiftMethod preferredMethod = DatumShiftMethod.forAccuracy(desiredAccuracy);
+            parameters = GeocentricAffine.createParameters(sourceCS, targetCS, datumShift, preferredMethod);
             if (parameters == null) {
-                parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE), datumShift);
+                /*
+                 * Failed to select a coordinate operation. Maybe because the coordinate system types are not the same.
+                 * Convert unconditionally to XYZ geocentric coordinates and apply the datum shift in that CS space.
+                 *
+                 * TODO: operation name should not be "Affine" if 'before' or 'after' transforms are not identity.
+                 *       Reminder: the parameter group name here determines the OperationMethod later in this method.
+                 */
+                if (datumShift != null) {
+                    parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE), datumShift);
+                } else {
+                    parameters = Affine.identity(3);                        // Dimension of geocentric CRS.
+                }
                 final CoordinateSystem normalized = CommonCRS.WGS84.geocentric().getCoordinateSystem();
                 before = mtFactory.createCoordinateSystemChange(sourceCS, normalized, sourceDatum.getEllipsoid());
                 after  = mtFactory.createCoordinateSystemChange(normalized, targetCS, targetDatum.getEllipsoid());
@@ -522,18 +526,26 @@ public class CoordinateOperationFinder e
             parameters = (isGeographicToGeocentric ? GeographicToGeocentric.PARAMETERS
                                                    : GeocentricToGeographic.PARAMETERS).createValue();
         } else {
+            /*
+             * Coordinate system change (including change in the number of dimensions) without datum shift.
+             */
             final int sourceDim = sourceCS.getDimension();
             final int targetDim = targetCS.getDimension();
-            if ((sourceDim & ~1) == 2 && (sourceDim ^ targetDim) == 1    // sourceDim == 2 or 3 and difference with targetDim is 1.
+            if ((sourceDim & ~1) == 2                           // sourceDim == 2 or 3.
+                    && (sourceDim ^ targetDim) == 1             // abs(sourceDim - targetDim) == 1.
                     && (sourceCS instanceof EllipsoidalCS)
                     && (targetCS instanceof EllipsoidalCS))
             {
                 parameters = (sourceDim == 2 ? Geographic2Dto3D.PARAMETERS
                                              : Geographic3Dto2D.PARAMETERS).createValue();
             } else {
-                parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE));      // Initialized to identity.
-                parameters.parameter(Constants.NUM_COL).setValue(targetDim + 1);
-                parameters.parameter(Constants.NUM_ROW).setValue(targetDim + 1);
+                /*
+                 * TODO: instead than creating parameters for an identity operation, we should create the
+                 *       CoordinateOperation directly from the MathTransform created by mtFactory below.
+                 *       The intend if to get the correct OperationMethod, which should not be "Affine"
+                 *       if there is a CS type change.
+                 */
+                parameters = Affine.identity(targetDim);
                 /*
                  * createCoordinateSystemChange(…) needs the ellipsoid associated to the ellipsoidal coordinate system,
                  * if any. If none or both coordinate systems are ellipsoidal, then the ellipsoid will be ignored (see
@@ -629,7 +641,7 @@ public class CoordinateOperationFinder e
         VerticalCRS heightCRS = targetCRS;      // First candidate, will be replaced if it doesn't fit.
         VerticalCS  heightCS  = heightCRS.getCoordinateSystem();
         if (equalsIgnoreMetadata(heightCS.getAxis(0), expectedAxis)) {
-            isEllipsoidalHeight = VerticalDatumTypes.ELLIPSOIDAL.equals(heightCRS.getDatum().getVerticalDatumType());
+            isEllipsoidalHeight = ReferencingUtilities.isEllipsoidalHeight(heightCRS.getDatum());
         } else {
             heightCRS = CommonCRS.Vertical.ELLIPSOIDAL.crs();
             heightCS  = heightCRS.getCoordinateSystem();
@@ -840,8 +852,8 @@ public class CoordinateOperationFinder e
             } else if (stepComponents.length == 1) {
                 stepTargetCRS = target;                 // Slight optimization of the next block.
             } else {
-                stepTargetCRS = toAuthorityDefinition(CoordinateReferenceSystem.class, ReferencingServices.getInstance()
-                        .createCompoundCRS(factorySIS.getCRSFactory(), factorySIS.getCSFactory(), derivedFrom(target), stepComponents));
+                stepTargetCRS = toAuthorityDefinition(CoordinateReferenceSystem.class,
+                        new CompoundCRSBuilder(factory, factorySIS).createCompoundCRS(derivedFrom(target), stepComponents));
             }
             int delta = source.getCoordinateSystem().getDimension();
             final int startAtDimension = endAtDimension;
@@ -949,10 +961,13 @@ public class CoordinateOperationFinder e
          * trivial operations.
          */
         CoordinateOperation main = null;
-        final boolean isAxisChange1 = (step1.getName() == AXIS_CHANGES);
-        final boolean isAxisChange2 = (step2.getName() == AXIS_CHANGES);
+        final boolean isAxisChange1 = canHide(step1.getName());
+        final boolean isAxisChange2 = canHide(step2.getName());
         if (isAxisChange1 && isAxisChange2 && isAffine(step1) && isAffine(step2)) {
             main = step2;                                           // Arbitrarily take the last step.
+            if (main.getName() == IDENTITY && step1.getName() != IDENTITY) {
+                main = step1;
+            }
         } else {
             if (isAxisChange1 && mt1.getSourceDimensions() == mt1.getTargetDimensions()) main = step2;
             if (isAxisChange2 && mt2.getSourceDimensions() == mt2.getTargetDimensions()) main = step1;
@@ -1005,8 +1020,8 @@ public class CoordinateOperationFinder e
         if (isIdentity(step1)) return concatenate(step2, step3);
         if (isIdentity(step2)) return concatenate(step1, step3);
         if (isIdentity(step3)) return concatenate(step1, step2);
-        if (step1.getName() == AXIS_CHANGES) return concatenate(concatenate(step1, step2), step3);
-        if (step3.getName() == AXIS_CHANGES) return concatenate(step1, concatenate(step2, step3));
+        if (canHide(step1.getName())) return concatenate(concatenate(step1, step2), step3);
+        if (canHide(step3.getName())) return concatenate(step1, concatenate(step2, step3));
         final Map<String,?> properties = defaultName(step1.getSourceCRS(), step3.getTargetCRS());
         return factory.createConcatenatedOperation(properties, step1, step2, step3);
     }
@@ -1030,7 +1045,22 @@ public class CoordinateOperationFinder e
      * are usually datum shift and must be visible.
      */
     private static boolean isIdentity(final CoordinateOperation operation) {
-        return (operation == null) || ((operation instanceof Conversion) && operation.getMathTransform().isIdentity());
+        if (operation == null) {
+            return true;
+        }
+        if ((operation instanceof Conversion) && operation.getMathTransform().isIdentity()) {
+            return CoordinateOperations.wrapAroundChanges(operation).isEmpty();
+        }
+        return false;
+    }
+
+    /**
+     * Returns {@code true} if a coordinate operation of the given name can be hidden
+     * in the list of operations. Note that the {@code MathTransform} will still take
+     * the operation in account however.
+     */
+    private static boolean canHide(final Identifier id) {
+        return (id == AXIS_CHANGES) || (id == IDENTITY);
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -112,7 +112,7 @@ class CoordinateOperationRegistry {
     /**
      * The identifier for an identity operation.
      */
-    private static final Identifier IDENTITY = createIdentifier(Vocabulary.Keys.Identity);
+    static final Identifier IDENTITY = createIdentifier(Vocabulary.Keys.Identity);
 
     /**
      * The identifier for conversion using an affine transform for axis swapping and/or unit conversions.
@@ -384,11 +384,11 @@ class CoordinateOperationRegistry {
                     /*
                      * Above check is necessary because this method may be invoked in some situations where the code
                      * are equal while the CRS are not. Such situation should be illegal, but unfortunately it still
-                     * happen because many softwares are not compliant with EPSG definition of axis order.   In such
-                     * cases we will need to compute a transform from sourceCRS to targetCRS ignoring the source and
-                     * target codes. The CoordinateOperationFinder class can do that, providing that we prevent this
-                     * CoordinateOperationRegistry to (legitimately) claims that the operation from sourceCode to
-                     * targetCode is the identity transform.
+                     * happen because many software products are not compliant with EPSG definition of axis order.
+                     * In such cases we will need to compute a transform from sourceCRS to targetCRS ignoring the
+                     * source and target codes. The CoordinateOperationFinder class can do that, providing that we
+                     * prevent this CoordinateOperationRegistry to (legitimately) claims that the operation from
+                     * sourceCode to targetCode is the identity transform.
                      */
                     return null;
                 }
@@ -1044,10 +1044,7 @@ class CoordinateOperationRegistry {
             }
         }
         return toAuthorityDefinition(CoordinateReferenceSystem.class,
-                ReferencingServices.getInstance().createCompoundCRS(
-                        factorySIS.getCRSFactory(),
-                        factorySIS.getCSFactory(),
-                        derivedFrom(crs), crs, CommonCRS.Vertical.ELLIPSOIDAL.crs()));
+                new CompoundCRSBuilder(factory, factorySIS).createCompoundCRS(derivedFrom(crs), crs, CommonCRS.Vertical.ELLIPSOIDAL.crs()));
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -52,7 +52,7 @@ import static org.apache.sis.util.Utilit
  * reference system associated with the concatenated operation.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.6
  * @module
  */
@@ -113,18 +113,36 @@ final class DefaultConcatenatedOperation
             throw new IllegalArgumentException(Errors.getResources(properties).getString(
                     Errors.Keys.TooFewOccurrences_2, 2, CoordinateOperation.class));
         }
+        initialize(properties, operations, mtFactory);
+        checkDimensions(properties);
+    }
+
+    /**
+     * Initializes the {@link #sourceCRS}, {@link #targetCRS} and {@link #operations} fields.
+     * If the source or target CRS is already non-null (which may happen on JAXB unmarshalling),
+     * leaves that CRS unchanged.
+     *
+     * @param  properties   the properties specified at construction time, or {@code null} if unknown.
+     * @param  operations   the operations to concatenate.
+     * @param  mtFactory    the math transform factory to use, or {@code null} for not performing concatenation.
+     * @throws FactoryException if the factory can not concatenate the math transforms.
+     */
+    private void initialize(final Map<String,?>         properties,
+                            final CoordinateOperation[] operations,
+                            final MathTransformFactory  mtFactory)
+            throws FactoryException
+    {
         final List<CoordinateOperation> flattened = new ArrayList<>(operations.length);
-        initialize(properties, operations, flattened, mtFactory,
-                (coordinateOperationAccuracy == null), (domainOfValidity == null));
+        final CoordinateReferenceSystem crs = initialize(properties, operations, flattened, mtFactory,
+                (sourceCRS == null), (coordinateOperationAccuracy == null), (domainOfValidity == null));
+        if (targetCRS == null) {
+            targetCRS = crs;
+        }
         /*
          * At this point we should have flattened.size() >= 2, except if some operations
          * were omitted because their associated math transform were identity operation.
          */
-        operations      = flattened.toArray(new CoordinateOperation[flattened.size()]);
-        this.operations = UnmodifiableArrayList.wrap(operations);
-        this.sourceCRS  = operations[0].getSourceCRS();
-        this.targetCRS  = operations[operations.length - 1].getTargetCRS();
-        checkDimensions(properties);
+        this.operations = UnmodifiableArrayList.wrap(flattened.toArray(new CoordinateOperation[flattened.size()]));
     }
 
     /**
@@ -162,17 +180,20 @@ final class DefaultConcatenatedOperation
      * @param  operations   the operations to concatenate.
      * @param  flattened    the destination list in which to add the {@code SingleOperation} instances.
      * @param  mtFactory    the math transform factory to use, or {@code null} for not performing concatenation.
+     * @param  setSource    {@code true} for setting the {@link #sourceCRS} on the very first CRS (regardless if null or not).
      * @param  setAccuracy  {@code true} for setting the {@link #coordinateOperationAccuracy} field.
      * @param  setDomain    {@code true} for setting the {@link #domainOfValidity} field.
+     * @return the last target CRS, regardless if null or not.
      * @throws FactoryException if the factory can not concatenate the math transforms.
      */
-    private void initialize(final Map<String,?>             properties,
-                            final CoordinateOperation[]     operations,
-                            final List<CoordinateOperation> flattened,
-                            final MathTransformFactory      mtFactory,
-                            boolean                         setAccuracy,
-                            boolean                         setDomain)
-            throws FactoryException
+    private CoordinateReferenceSystem initialize(
+            final Map<String,?>             properties,
+            final CoordinateOperation[]     operations,
+            final List<CoordinateOperation> flattened,
+            final MathTransformFactory      mtFactory,
+            boolean                         setSource,
+            boolean                         setAccuracy,
+            boolean                         setDomain) throws FactoryException
     {
         CoordinateReferenceSystem previous = null;
         for (int i=0; i<operations.length; i++) {
@@ -182,17 +203,19 @@ final class DefaultConcatenatedOperation
              * Verify consistency of user argument: for each coordinate operation, the number of dimensions of the
              * source CRS shall be equals to the number of dimensions of the target CRS in the previous operation.
              */
-            if (previous != null) {
-                final CoordinateReferenceSystem next = op.getSourceCRS();
-                if (next != null) {
-                    final int dim1 = previous.getCoordinateSystem().getDimension();
-                    final int dim2 = next.getCoordinateSystem().getDimension();
-                    if (dim1 != dim2) {
-                        throw new IllegalArgumentException(Errors.getResources(properties).getString(
-                                Errors.Keys.MismatchedDimension_3, "operations[" + i + "].sourceCRS", dim1, dim2));
-                    }
+            final CoordinateReferenceSystem next = op.getSourceCRS();
+            if (previous != null && next != null) {
+                final int dim1 = previous.getCoordinateSystem().getDimension();
+                final int dim2 = next.getCoordinateSystem().getDimension();
+                if (dim1 != dim2) {
+                    throw new IllegalArgumentException(Errors.getResources(properties).getString(
+                            Errors.Keys.MismatchedDimension_3, "operations[" + i + "].sourceCRS", dim1, dim2));
                 }
             }
+            if (setSource) {
+                setSource = false;
+                sourceCRS = next;                                               // Take even if null.
+            }
             previous = op.getTargetCRS();                                       // For next iteration cycle.
             /*
              * Now that we have verified the CRS dimensions, we should be able to concatenate the transforms.
@@ -207,11 +230,11 @@ final class DefaultConcatenatedOperation
                 final List<? extends CoordinateOperation> children = ((ConcatenatedOperation) op).getOperations();
                 @SuppressWarnings("SuspiciousToArrayCall")
                 final CoordinateOperation[] asArray = children.toArray(new CoordinateOperation[children.size()]);
-                initialize(properties, asArray, flattened, (step == null) ? mtFactory : null, setAccuracy, setDomain);
+                initialize(properties, asArray, flattened, (step == null) ? mtFactory : null, false, setAccuracy, setDomain);
             } else if (!step.isIdentity()) {
                 flattened.add(op);
             }
-            if (mtFactory != null) {
+            if (mtFactory != null && step != null) {
                 transform = (transform != null) ? mtFactory.createConcatenatedTransform(transform, step) : step;
             }
             /*
@@ -248,6 +271,7 @@ final class DefaultConcatenatedOperation
                 }
             }
         }
+        return previous;
     }
 
     /**
@@ -388,7 +412,6 @@ final class DefaultConcatenatedOperation
      * Returns the operations to marshal. We use this private methods instead than annotating
      * {@link #getOperations()} in order to force JAXB to invoke the setter method on unmarshalling.
      */
-    @SuppressWarnings("SuspiciousToArrayCall")
     @XmlElement(name = "coordOperation", required = true)
     private CoordinateOperation[] getSteps() {
         final List<? extends CoordinateOperation> operations = getOperations();
@@ -399,9 +422,6 @@ final class DefaultConcatenatedOperation
      * Invoked by JAXB for setting the operations.
      */
     private void setSteps(final CoordinateOperation[] steps) throws FactoryException {
-        final List<CoordinateOperation> flattened = new ArrayList<>(steps.length);
-        initialize(null, steps, flattened, DefaultFactories.forBuildin(MathTransformFactory.class),
-                (coordinateOperationAccuracy == null), (domainOfValidity == null));
-        operations = UnmodifiableArrayList.wrap(flattened.toArray(new CoordinateOperation[flattened.size()]));
+        initialize(null, steps, DefaultFactories.forBuildin(MathTransformFactory.class));
     }
 }

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -20,6 +20,7 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.ServiceLoader;
 import org.opengis.util.FactoryException;
 import org.opengis.util.NoSuchIdentifierException;
@@ -42,8 +43,10 @@ import org.apache.sis.internal.metadata.
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.util.Constants;
-import org.apache.sis.internal.util.LazySet;
+import org.apache.sis.internal.referencing.LazySet;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
 import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
 import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
@@ -676,6 +679,13 @@ next:   for (int i=components.size(); --
     public CoordinateOperation createConcatenatedOperation(final Map<String,?> properties,
             final CoordinateOperation... operations) throws FactoryException
     {
+        /*
+         * If the user specified a single operation, there is no need to create a ConcatenatedOperation;
+         * the operation to return will be the specified one. The metadata given in argument are ignored
+         * on the assumption that the single operation has more complete metadata (in particular an EPSG
+         * code, in which case we do not want to modify any other metadata in order to stay compliant
+         * with EPSG definition).
+         */
         if (operations != null && operations.length == 1) {
             return operations[0];
         }
@@ -688,14 +698,39 @@ next:   for (int i=components.size(); --
         /*
          * Verifies again the number of single operations.  We may have a singleton if some operations
          * were omitted because their associated math transform were identity. This happen for example
-         * if a "Geographic 3D to 2D conversion" as been redimensioned to a "3D to 3D" operation.
+         * if a "Geographic 3D to 2D conversion" has been redimensioned to a "3D to 3D" operation.
          */
         final List<? extends CoordinateOperation> co = op.getOperations();
-        if (co.size() == 1) {
-            assert op.getMathTransform().equals(co.get(0).getMathTransform()) : op;
-            return co.get(0);
+        if (co.size() != 1) {
+            return pool.unique(op);
         }
-        return pool.unique(op);
+        final CoordinateOperation single = co.get(0);
+        assert op.getMathTransform().equals(single.getMathTransform()) : op;
+        if (!Objects.equals(single.getSourceCRS(), op.getSourceCRS()) ||
+            !Objects.equals(single.getTargetCRS(), op.getTargetCRS()))
+        {
+            /*
+             * The CRS of the single operation may be different than the CRS of the concatenated operation
+             * if the first or the last operation was an identity operation. It happens for example if the
+             * sole purpose of an operation step was to change the longitude range from [-180 … +180]° to
+             * [0 … 360]°: the MathTransform is identity (because Apache SIS does not handle those changes
+             * in MathTransform; we handle that elsewhere, for example in the Envelopes utility class),
+             * but omitting the transform should not cause the lost of the CRS with desired longitude range.
+             */
+            if (single instanceof SingleOperation) {
+                final Map<String,Object> merge = new HashMap<>(
+                        IdentifiedObjects.getProperties(single, CoordinateOperation.IDENTIFIERS_KEY));
+                merge.put(ReferencingServices.PARAMETERS_KEY, ((SingleOperation) single).getParameterValues());
+                if (single instanceof AbstractIdentifiedObject) {
+                    merge.put(ReferencingServices.OPERATION_TYPE_KEY, ((AbstractIdentifiedObject) single).getInterface());
+                }
+                merge.putAll(properties);
+                return createSingleOperation(merge, op.getSourceCRS(), op.getTargetCRS(),
+                        AbstractCoordinateOperation.getInterpolationCRS(op),
+                        ((SingleOperation) single).getMethod(), single.getMathTransform());
+            }
+        }
+        return single;
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/MathTransformContext.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/MathTransformContext.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/MathTransformContext.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/MathTransformContext.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -18,6 +18,8 @@ package org.apache.sis.referencing.opera
 
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.cs.CartesianCS;
+import org.opengis.referencing.cs.SphericalCS;
+import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.datum.GeodeticDatum;
 import org.opengis.referencing.operation.Matrix;
@@ -102,7 +104,7 @@ final class MathTransformContext extends
                 } else {
                     matrix = cm.multiply(rot);                  // Apply the rotation before normalization.
                 }
-            } else {
+            } else if (cs == null || cs instanceof EllipsoidalCS || cs instanceof SphericalCS) {
                 final Double value = rotation;
                 if (inverse) {
                     cm.convertBefore(0, null, value);           // Longitude is the first axis in normalized CS.
@@ -110,6 +112,8 @@ final class MathTransformContext extends
                     cm.convertAfter(0, null, value);
                 }
                 matrix = cm;
+            } else {
+                throw new FactoryException(Errors.format(Errors.Keys.UnsupportedCoordinateSystem_1, cs.getName()));
             }
         }
         return matrix;

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -41,7 +41,7 @@
  *
  * <div class="section">Apache SIS specific behavior</div>
  * The following operations have a behavior in Apache SIS which may be different
- * than the behavior found in other softwares. Those particularities apply only when the math transform is
+ * than the behavior found in other software products. Those particularities apply only when the math transform is
  * {@linkplain org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory#createParameterizedTransform
  * created directly}. Users do not need to care about them when the coordinate operation is
  * {@linkplain org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory#createOperation

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -63,11 +63,11 @@ public class AlbersEqualArea extends Equ
      *
      * <p>In Apache SIS implementation, we use modified formulas in which the (1 - ℯ²) factor is omitted in
      * {@link #qm(double)} calculation. Consequently what we get is a modified value <var>nm</var> which is
-     * related to Synder's <var>n</var> value by {@literal n = nm / (1 - ℯ²)}.  The omitted (1 - ℯ²) factor
+     * related to Snyder's <var>n</var> value by {@literal n = nm / (1 - ℯ²)}.  The omitted (1 - ℯ²) factor
      * is either taken in account by the (de)normalization matrix, or cancels with other (1 - ℯ²) factors
      * when we develop the formulas.</p>
      *
-     * <p>Note that in the spherical case, <var>nm</var> = Synder's <var>n</var>.</p>
+     * <p>Note that in the spherical case, <var>nm</var> = Snyder's <var>n</var>.</p>
      */
     final double nm;
 
@@ -136,7 +136,7 @@ public class AlbersEqualArea extends Equ
         }
         C = m1*m1 + nm*α1;                  // Omitted (1-ℯ²) term in nm cancels with omitted (1-ℯ²) term in α₁.
         /*
-         * Compute rn = (1-ℯ²)/nm, which is the reciprocal of the "real" n used in Synder and EPSG guidance note.
+         * Compute rn = (1-ℯ²)/nm, which is the reciprocal of the "real" n used in Snyder and EPSG guidance note.
          * We opportunistically use double-double arithmetic since the MatrixSIS operations use them anyway, but
          * we do not really have that accuracy because of the limited precision of 'nm'. The intend is rather to
          * increase the chances term cancellations happen during concatenation of coordinate operations.
@@ -260,14 +260,14 @@ public class AlbersEqualArea extends Equ
         final double x = srcPts[srcOff  ];
         final double y = srcPts[srcOff+1];
         /*
-         * Note: Synder suggests to reverse the sign of x, y and ρ₀ if n is negative. It should not done in Apache SIS
+         * Note: Snyder suggests to reverse the sign of x, y and ρ₀ if n is negative. It should not done in Apache SIS
          * implementation because (x,y) are premultiplied by n (by the normalization affine transform) before to enter
          * in this method, so if n was negative those values have already their sign reverted.
          */
         dstPts[dstOff  ] = atan2(x, y);
         dstPts[dstOff+1] = φ((C - (x*x + y*y)) / nm);
         /*
-         * Note: Synder 14-19 gives  q = (C - ρ²n²/a²)/n  where  ρ = √(x² + (ρ₀ - y)²).
+         * Note: Snyder 14-19 gives  q = (C - ρ²n²/a²)/n  where  ρ = √(x² + (ρ₀ - y)²).
          * But in Apache SIS implementation, ρ₀ has already been subtracted by the matrix before we reach this point.
          * So we can simplify by ρ² = x² + y². Furthermore the matrix also divided x and y by a (the semi-major axis
          * length) before this method, and multiplied by n. so what we have is actually (ρ⋅n/a)² = x² + y².
@@ -318,10 +318,10 @@ public class AlbersEqualArea extends Equ
             final double cosθ = cos(θ);
             final double sinθ = sin(θ);
             final double sinφ = sin(φ);
-            final double ρ = sqrt(C - 2*nm*sinφ);           // Synder 14-3 with radius and division by n omitted.
+            final double ρ = sqrt(C - 2*nm*sinφ);           // Snyder 14-3 with radius and division by n omitted.
             if (dstPts != null) {
-                dstPts[dstOff  ] = ρ * sinθ;                // Synder 14-1
-                dstPts[dstOff+1] = ρ * cosθ;                // Synder 14-2
+                dstPts[dstOff  ] = ρ * sinθ;                // Snyder 14-1
+                dstPts[dstOff+1] = ρ * cosθ;                // Snyder 14-2
             }
             if (!derivate) {
                 return null;
@@ -341,8 +341,8 @@ public class AlbersEqualArea extends Equ
         {
             final double x = srcPts[srcOff];
             final double y = srcPts[srcOff + 1];
-            dstPts[dstOff  ] = atan2(x, y);                         // Part of Synder 14-11
-            dstPts[dstOff+1] = asin((C - (x*x + y*y)) / (nm*2));    // Synder 14-8 modified
+            dstPts[dstOff  ] = atan2(x, y);                         // Part of Snyder 14-11
+            dstPts[dstOff+1] = asin((C - (x*x + y*y)) / (nm*2));    // Snyder 14-8 modified
         }
     }
 }

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -248,7 +248,7 @@ public class CylindricalEqualArea extend
             dstOff--;
             while (--numPts >= 0) {
                 final double φ = dstPts[dstOff += DIMENSION];           // Same as srcPts[srcOff + 1].
-                dstPts[dstOff] = qm_ellipsoid(sin(φ));                  // Part of Synder equation (10-15)
+                dstPts[dstOff] = qm_ellipsoid(sin(φ));                  // Part of Snyder equation (10-15)
             }
         }
     }
@@ -268,7 +268,7 @@ public class CylindricalEqualArea extend
         dstPts[dstOff  ] = srcPts[srcOff  ];            // Must be before writing y.
         dstPts[dstOff+1] = φ(y);
         /*
-         * Equation 10-26 of Synder gives β = asin(2y⋅k₀/(a⋅qPolar)).
+         * Equation 10-26 of Snyder gives β = asin(2y⋅k₀/(a⋅qPolar)).
          * In our case it simplifies to sinβ = (y/qmPolar) because:
          *
          *   - y is already multiplied by 2k₀/a because of the denormalization matrix

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -39,7 +39,7 @@ abstract class EqualAreaProjection exten
     private static final long serialVersionUID = -6175270149094989517L;
 
     /**
-     * {@code false} for using the original formulas as published by Synder, or {@code true} for using formulas
+     * {@code false} for using the original formulas as published by Snyder, or {@code true} for using formulas
      * modified using trigonometric identities. The use of trigonometric identities is for reducing the amount
      * of calls to the {@link Math#sin(double)} and similar methods. Some identities used are:
      *
@@ -193,7 +193,7 @@ abstract class EqualAreaProjection exten
     }
 
     /**
-     * Computes the latitude using equation 3-18 from Synder, followed by iterative resolution of Synder 3-16.
+     * Computes the latitude using equation 3-18 from Snyder, followed by iterative resolution of Snyder 3-16.
      * In theory, the series expansion given by equation 3-18 (φ ≈ c₂⋅sin(2β) + c₄⋅sin(4β) + c₈⋅sin(8β)) should
      * be used in replacement of the iterative method. However in practice the series expansion seems to not
      * have a sufficient amount of terms for achieving the centimetric precision, so we "finish" it by the
@@ -210,7 +210,7 @@ abstract class EqualAreaProjection exten
             φ = ci8 * sin(8*β)
               + ci4 * sin(4*β)
               + ci2 * sin(2*β)
-              + β;                                                                  // Synder 3-18
+              + β;                                                                  // Snyder 3-18
         } else {
             /*
              * Same formula than above, but rewriten using trigonometric identities in order to avoid
@@ -233,11 +233,11 @@ abstract class EqualAreaProjection exten
          * Use the iterative method for reaching the last part of missing accuracy. Usually this loop
          * will perform exactly one iteration, no more, because φ is already quite close to the result.
          *
-         * Mathematical note: Synder 3-16 gives q/(1-ℯ²) instead of y in the calculation of Δφ below.
-         * For Cylindrical Equal Area projection, Synder 10-17 gives  q = (qPolar⋅sinβ), which simplifies
+         * Mathematical note: Snyder 3-16 gives q/(1-ℯ²) instead of y in the calculation of Δφ below.
+         * For Cylindrical Equal Area projection, Snyder 10-17 gives  q = (qPolar⋅sinβ), which simplifies
          * as y.
          *
-         * For Albers Equal Area projection, Synder 14-19 gives  q = (C - ρ²n²/a²)/n,  which we rewrite
+         * For Albers Equal Area projection, Snyder 14-19 gives  q = (C - ρ²n²/a²)/n,  which we rewrite
          * as  q = (C - ρ²)/n  (see comment in AlbersEqualArea.inverseTransform(…) for the mathematic).
          * The y value given to this method is y = (C - ρ²) / (n⋅(1-ℯ²)) = q/(1-ℯ²), the desired value.
          */
@@ -253,7 +253,7 @@ abstract class EqualAreaProjection exten
             }
         }
         /*
-         * In the Albers Equal Area discussion, Synder said that above algorithm does not converge if
+         * In the Albers Equal Area discussion, Snyder said that above algorithm does not converge if
          *
          *   q = ±(1 - (1-ℯ²)/(2ℯ) ⋅ ln((1-ℯ)/(1+ℯ)))
          *
@@ -265,9 +265,9 @@ abstract class EqualAreaProjection exten
          *
          *   y  =  ±(1/(1-ℯ²) + atanh(ℯ)/ℯ)  =  ±qmPolar
          *
-         * which implies  sinβ = ±1. This is consistent with Synder discussion of Cylndrical Equal Area
+         * which implies  sinβ = ±1. This is consistent with Snyder discussion of Cylndrical Equal Area
          * projection, where he said exactly that about the same formula (that it does not converge for
-         * β = ±90°). In both case, Synder said that the result is φ = β, with the same sign.
+         * β = ±90°). In both case, Snyder said that the result is φ = β, with the same sign.
          */
         final double as = abs(sinβ);
         if (abs(as - 1) < ANGULAR_TOLERANCE) {

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -410,7 +410,7 @@ public class ObliqueStereographic extend
              * Formulas below are the same than the elliptical formulas after the geodetic coordinates
              * have been converted to conformal coordinates.  In this spherical case we do not need to
              * perform such conversion. Instead we have directly   χ = φ  and  Λ = λ.   The simplified
-             * EPSG formulas then become the same than Synder formulas for the spherical case.
+             * EPSG formulas then become the same than Snyder formulas for the spherical case.
              */
             final double sinφ      = sin(φ);
             final double cosφ      = cos(φ);
@@ -419,10 +419,10 @@ public class ObliqueStereographic extend
             final double sinφsinφ0 = sinφ * sinχ0;
             final double cosφcosφ0 = cosφ * cosχ0;
             final double cosφsinλ  = cosφ * sinλ;
-            final double B = 1 + sinφsinφ0 + cosφcosφ0*cosλ;                    // Synder 21-4
+            final double B = 1 + sinφsinφ0 + cosφcosφ0*cosλ;                    // Snyder 21-4
             if (dstPts != null) {
-                dstPts[dstOff  ] = cosφsinλ / B;                                // Synder 21-2
-                dstPts[dstOff+1] = (sinφ*cosχ0 - cosφ*sinχ0*cosλ) / B;          // Synder 21-3
+                dstPts[dstOff  ] = cosφsinλ / B;                                // Snyder 21-2
+                dstPts[dstOff+1] = (sinφ*cosχ0 - cosφ*sinχ0*cosλ) / B;          // Snyder 21-3
             }
             if (!derivate) {
                 return null;

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -197,7 +197,7 @@ public class PolarStereographic extends
         if (abs(φ1 + PI/2) < ANGULAR_TOLERANCE) {
             /*
              * Polar Stereographic (variant A)
-             * True scale at pole (part of Synder 21-33). From EPSG guide (April 2015) §1.3.7.2:
+             * True scale at pole (part of Snyder 21-33). From EPSG guide (April 2015) §1.3.7.2:
              *
              *    ρ = 2⋅a⋅k₀⋅t / √[(1+ℯ)^(1+ℯ) ⋅ (1–ℯ)^(1–ℯ)]
              *
@@ -212,7 +212,7 @@ public class PolarStereographic extends
         } else {
             /*
              * Polar Stereographic (variant B or C)
-             * Derived from Synder 21-32 and 21-33. From EPSG guide (April 2015) §1.3.7.2:
+             * Derived from Snyder 21-32 and 21-33. From EPSG guide (April 2015) §1.3.7.2:
              *
              *   tF = tan(π/4 + φ1/2) / {[(1 + ℯ⋅sinφ1) / (1 – ℯ⋅sinφ1)]^(ℯ/2)}
              *   mF = cosφ1 / √[1 – ℯ²⋅sin²φ1]
@@ -225,7 +225,7 @@ public class PolarStereographic extends
              *   ρ  = mF / tF
              *   k₀ = ρ⋅√[…]/2  but we do not need that value.
              *
-             * In the spherical case, should give ρ = 1 + sinφ1   (Synder 21-7 and 21-11).
+             * In the spherical case, should give ρ = 1 + sinφ1   (Snyder 21-7 and 21-11).
              */
             final double sinφ1 = sin(φ1);
             final double mF = initializer.scaleAtφ(sinφ1, cos(φ1));
@@ -378,8 +378,8 @@ public class PolarStereographic extends
             final double sinθ = sin(θ);
             final double cosθ = cos(θ);
             final double t    = tan(PI/4 + 0.5*φ);
-            final double x    = t * sinθ;               // Synder 21-5
-            final double y    = t * cosθ;               // Synder 21-6
+            final double x    = t * sinθ;               // Snyder 21-5
+            final double y    = t * cosθ;               // Snyder 21-6
             if (dstPts != null) {
                 dstPts[dstOff  ] = x;
                 dstPts[dstOff+1] = y;

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -32,7 +32,7 @@ import org.opengis.referencing.operation
  *   <li>If the expected mathematical value is infinite (for example the Mercator projection at ±90° of latitude),
  *       then the map projection should return a {@link Double#POSITIVE_INFINITY} or {@link Double#NEGATIVE_INFINITY},
  *       depending on the sign of the correct mathematical answer.</li>
- *   <li>If no real number is expected to exist for the input coordinate (for example the root of a negative value),
+ *   <li>If no real number is expected to exist for the input coordinate (for example at a latitude greater than 90°),
  *       then the map projection should return {@link Double#NaN}.</li>
  *   <li>If a real number is expected to exist but the map projection fails to compute it (for example because an
  *       iterative algorithm does not converge), then the projection should throw {@code ProjectionException}.</li>

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] Mon Oct 30 10:25:08 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/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -53,7 +53,7 @@ import org.opengis.util.FactoryException
 import org.opengis.util.NoSuchIdentifierException;
 
 import org.apache.sis.io.wkt.Parser;
-import org.apache.sis.internal.util.LazySet;
+import org.apache.sis.internal.referencing.LazySet;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.metadata.ReferencingServices;
@@ -128,7 +128,7 @@ import org.apache.sis.util.resources.Err
  * </ul>
  *
  * <p>The main purpose of those dynamic parameters is to support some less commonly used conventions
- * without duplicating the most commonly used conventions. The alternative ways are used in NetCDF
+ * without duplicating the most commonly used conventions. The alternative ways are used in netCDF
  * files for example, which often use spherical models instead than ellipsoidal ones.</p>
  *
  *

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -153,7 +153,11 @@ public class MolodenskyTransform extends
         super(source, isSource3D, target, isTarget3D, tX, tY, tZ, null, isAbridged,
                 isAbridged ? AbridgedMolodensky.PARAMETERS : Molodensky.PARAMETERS);
         if (!isSource3D && !isTarget3D) {
-            inverse = new MolodenskyTransform2D(this, source, target);
+            if (isAbridged && tX == 0 && tY == 0 && tZ == 0) {
+                inverse = new AbridgedMolodenskyTransform2D(this, source, target);
+            } else {
+                inverse = new MolodenskyTransform2D(this, source, target);
+            }
         } else {
             inverse = new MolodenskyTransform(this, source, target);
         }
@@ -226,7 +230,11 @@ public class MolodenskyTransform extends
     {
         final MolodenskyTransform tr;
         if (!isSource3D && !isTarget3D) {
-            tr = new MolodenskyTransform2D(source, target, tX, tY, tZ, isAbridged);
+            if (isAbridged && tX == 0 && tY == 0 && tZ == 0) {
+                tr = new AbridgedMolodenskyTransform2D(source, target);
+            } else {
+                tr = new MolodenskyTransform2D(source, target, tX, tY, tZ, isAbridged);
+            }
         } else {
             tr = new MolodenskyTransform(source, isSource3D, target, isTarget3D, tX, tY, tZ, isAbridged);
         }

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -33,7 +33,7 @@ import org.opengis.referencing.operation
  * @since   0.7
  * @module
  */
-final class MolodenskyTransform2D extends MolodenskyTransform implements MathTransform2D {
+class MolodenskyTransform2D extends MolodenskyTransform implements MathTransform2D {
     /**
      * Serial number for compatibility with different versions.
      */
@@ -52,9 +52,9 @@ final class MolodenskyTransform2D extend
     /**
      * Constructs the inverse of a 2D transform.
      *
-     * @param inverse The transform for which to create the inverse.
-     * @param source  The source ellipsoid of the given {@code inverse} transform.
-     * @param target  The target ellipsoid of the given {@code inverse} transform.
+     * @param inverse  the transform for which to create the inverse.
+     * @param source   the source ellipsoid of the given {@code inverse} transform.
+     * @param target   the target ellipsoid of the given {@code inverse} transform.
      */
     MolodenskyTransform2D(final MolodenskyTransform inverse, final Ellipsoid source, final Ellipsoid target) {
         super(inverse, source, target);

Modified: sis/branches/JDK9/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.crs.CRSAuthorityFactory
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.crs.CRSAuthorityFactory?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.crs.CRSAuthorityFactory [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.crs.CRSAuthorityFactory [UTF-8] Mon Oct 30 10:25:08 2017
@@ -1,2 +1,3 @@
 # Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.
+org.apache.sis.internal.referencing.EPSGFactoryProxy
 org.apache.sis.referencing.factory.CommonAuthorityFactory

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractEnvelopeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractEnvelopeTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractEnvelopeTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractEnvelopeTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -19,8 +19,8 @@ package org.apache.sis.geometry;
 import java.awt.geom.Rectangle2D;
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
-import org.opengis.referencing.crs.GeographicCRS;
-import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.referencing.crs.DefaultGeographicCRS;
+import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
@@ -36,7 +36,7 @@ import static org.apache.sis.test.Refere
  * Various implementations are used for each test.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.4
+ * @version 0.8
  * @since   0.3
  * @module
  */
@@ -51,7 +51,7 @@ public final strictfp class AbstractEnve
     /**
      * The coordinate reference system used for the tests.
      */
-    static final GeographicCRS WGS84 = CommonCRS.WGS84.normalizedGeographic();
+    static final DefaultGeographicCRS WGS84 = HardCodedCRS.WGS84;
 
     /**
      * Creates an envelope of the given type. The type shall be one of the

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/CoordinateFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/CoordinateFormatTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/CoordinateFormatTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/CoordinateFormatTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -21,6 +21,7 @@ import java.util.Locale;
 import java.util.TimeZone;
 import java.text.ParsePosition;
 import java.text.ParseException;
+import java.io.IOException;
 import org.opengis.geometry.DirectPosition;
 import org.apache.sis.measure.Angle;
 import org.apache.sis.referencing.crs.HardCodedCRS;
@@ -36,6 +37,8 @@ import static org.junit.Assert.*;
  * Tests the {@link CoordinateFormat} class.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @author  Michael Hausegger
+ *
  * @version 0.8
  *
  * @see org.apache.sis.measure.AngleFormatTest
@@ -63,6 +66,7 @@ public final strictfp class CoordinateFo
          * Try again with a different separator.
          */
         format.setSeparator("; ");
+        assertEquals("; ", format.getSeparator());
         assertEquals("4.64; 10.25; -3.12", format.format(position));
     }
 
@@ -75,30 +79,31 @@ public final strictfp class CoordinateFo
     @Test
     public void testParseUnknownCRS() throws ParseException {
         final CoordinateFormat format = new CoordinateFormat(null, null);
-        final ParsePosition index = new ParsePosition(0);
-        DirectPosition position = format.parse("23.78 -12.74 127.9 3.25", index);
+        final ParsePosition charPos = new ParsePosition(0);
+        DirectPosition position = format.parse("23.78 -12.74 127.9 3.25", charPos);
         assertArrayEquals(new double[] {23.78, -12.74, 127.9, 3.25}, position.getCoordinate(), STRICT);
-        assertEquals("ParsePosition.getErrorIndex()", -1, index.getErrorIndex());
-        assertEquals("ParsePosition.getIndex()",      23, index.getIndex());
+        assertEquals("ParsePosition.getErrorIndex()", -1, charPos.getErrorIndex());
+        assertEquals("ParsePosition.getIndex()",      23, charPos.getIndex());
         /*
          * Try another point having a different number of position
          * for verifying that no cached values are causing problem.
          */
-        index.setIndex(0);
-        position = format.parse("4.64 10.25 -3.12", index);
+        charPos.setIndex(0);
+        position = format.parse("4.64 10.25 -3.12", charPos);
         assertArrayEquals(new double[] {4.64, 10.25, -3.12}, position.getCoordinate(), STRICT);
-        assertEquals("ParsePosition.getErrorIndex()", -1, index.getErrorIndex());
-        assertEquals("ParsePosition.getIndex()",      16, index.getIndex());
+        assertEquals("ParsePosition.getErrorIndex()", -1, charPos.getErrorIndex());
+        assertEquals("ParsePosition.getIndex()",      16, charPos.getIndex());
         /*
          * Try again with a different separator. Also put or remove some spaces
          * around the separator for testing UnitFormat capabilities to ignore them.
          */
         format.setSeparator("; ");
-        index.setIndex(0);
-        position = format.parse("4.64;10.25 ;  -3.12", index);
+        assertEquals("; ", format.getSeparator());
+        charPos.setIndex(0);
+        position = format.parse("4.64;10.25 ;  -3.12", charPos);
         assertArrayEquals(new double[] {4.64, 10.25, -3.12}, position.getCoordinate(), STRICT);
-        assertEquals("ParsePosition.getErrorIndex()", -1, index.getErrorIndex());
-        assertEquals("ParsePosition.getIndex()",      19, index.getIndex());
+        assertEquals("ParsePosition.getErrorIndex()", -1, charPos.getErrorIndex());
+        assertEquals("ParsePosition.getIndex()",      19, charPos.getIndex());
     }
 
     /**
@@ -150,6 +155,7 @@ public final strictfp class CoordinateFo
          * Try again with the original CRS, but different separator.
          */
         format.setSeparator("; ");
+        assertEquals("; ", format.getSeparator());
         position.setCoordinateReferenceSystem(HardCodedCRS.GEOID_4D);
         assertEquals("getPattern(Angle)", anglePattern, format.getPattern(Angle.class));
         assertEquals("getPattern(Date)",   datePattern, format.getPattern(Date .class));
@@ -168,24 +174,97 @@ public final strictfp class CoordinateFo
         final CoordinateFormat format = new CoordinateFormat(Locale.FRANCE, TimeZone.getTimeZone("GMT+01:00"));
         format.applyPattern(Date.class, "dd-MM-yyyy HH:mm");
         format.setDefaultCRS(HardCodedCRS.GEOID_4D);
-        final ParsePosition index = new ParsePosition(11);
-        final DirectPosition pos = format.parse("(to skip); 23°46,8′E 12°44,4′S 127,9 m 22-09-2006 07:00 (ignore)", index);
+        final ParsePosition charPos = new ParsePosition(11);
+        final DirectPosition pos = format.parse("(to skip); 23°46,8′E 12°44,4′S 127,9 m 22-09-2006 07:00 (ignore)", charPos);
         assertArrayEquals(new double[] {23.78, -12.74, 127.90, 54000.25}, pos.getCoordinate(), STRICT);
-        assertEquals("ParsePosition.getErrorIndex()", -1, index.getErrorIndex());
-        assertEquals("ParsePosition.getIndex()",      55, index.getIndex());
+        assertEquals("ParsePosition.getErrorIndex()", -1, charPos.getErrorIndex());
+        assertEquals("ParsePosition.getIndex()",      55, charPos.getIndex());
         /*
          * Tests error message when parsing the same string but with unknown units of measurement.
          */
-        index.setIndex(11);
+        charPos.setIndex(11);
         try {
-            format.parse("(to skip); 23°46,8′E 12°44,4′S 127,9 Foo 22-09-2006 07:00", index);
+            format.parse("(to skip); 23°46,8′E 12°44,4′S 127,9 Foo 22-09-2006 07:00", charPos);
             fail("Should not have parsed a coordinate with unknown units.");
         } catch (ParseException e) {
-            assertEquals("ParsePosition.getIndex()",        11, index.getIndex());
-            assertEquals("ParsePosition.getErrorIndex()",   37, index.getErrorIndex());
+            assertEquals("ParsePosition.getIndex()",        11, charPos.getIndex());
+            assertEquals("ParsePosition.getErrorIndex()",   37, charPos.getErrorIndex());
             assertEquals("ParseException.getErrorOffset()", 37, e.getErrorOffset());
             assertEquals("Les caractères « Foo » après « 23°46,8′E 12°44,4′S 127,9 » sont inattendus.",
                          e.getLocalizedMessage());  // In the language specified at CoordinateFormat construction time.
         }
     }
+
+    /**
+     * Tests formatting a coordinate in default locale, then parsing the result. This test verifies that the
+     * parsing is consistent with formatting in whatever locale used by the platform. This test does not verify
+     * if the formatted string is equal to any expected value since it is locale-dependent.
+     *
+     * @throws IOException    should never happen since we format into a {@link StringBuffer}.
+     * @throws ParseException if {@code CoordinateFormat} fails to parse the value that it formatted.
+     */
+    @Test
+    public void testParseInDefaultLocale() throws IOException, ParseException {
+        CoordinateFormat format = new CoordinateFormat();
+        StringBuffer     buffer = new StringBuffer();
+        format.format(new DirectPosition2D(-3, 4), buffer);
+
+        ParsePosition  charPos  = new ParsePosition(0);
+        DirectPosition position = format.parse(buffer, charPos);
+        assertEquals("Should have parsed the whole text.", buffer.length(), charPos.getIndex());
+        assertEquals("DirectPosition.getDimension()", 2, position.getDimension());
+        assertArrayEquals(new double[] {-3, 4}, position.getCoordinate(), STRICT);
+    }
+
+    /**
+     * Tests parsing from a position different then the beginning of the string.
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    public void testParseFromOffset() throws ParseException {
+        CoordinateFormat coordinateFormat = new CoordinateFormat(Locale.CANADA, null);
+        coordinateFormat.setDefaultCRS(VerticalCRSMock.BAROMETRIC_HEIGHT);
+        ParsePosition  charPos  = new ParsePosition(7);
+        DirectPosition position = coordinateFormat.parse("[skip] 12", charPos);
+        assertEquals("Should have parsed the whole text.", 9, charPos.getIndex());
+        assertEquals("DirectPosition.getDimension()", 1, position.getDimension());
+        assertArrayEquals(new double[] {12}, position.getCoordinate(), STRICT);
+    }
+
+    /**
+     * Verifies the pattern returned by {@link CoordinateFormat#getPattern(Class)}. This includes verifying that
+     * the method returns {@code null} when invoked for an unknown type, or a type that does not support pattern.
+     */
+    @Test
+    public void testGetPattern() {
+        CoordinateFormat coordinateFormat = new CoordinateFormat(Locale.UK, null);
+        assertEquals("#,##0.###", coordinateFormat.getPattern(Byte.class));
+        assertNull(coordinateFormat.getPattern(Object.class));
+        assertNull(coordinateFormat.getPattern(Class.class));
+    }
+
+    /**
+     * Verifies that {@link CoordinateFormat#applyPattern(Class, String)} when
+     * invoked for an unknown type, or for a type that does not support patterns.
+     */
+    @Test
+    public void testApplyPattern() {
+        CoordinateFormat format = new CoordinateFormat();
+        assertFalse(format.applyPattern(Object.class, "A dummy pattern"));
+        assertFalse(format.applyPattern(Class.class,  "A dummy pattern"));
+    }
+
+    /**
+     * Tests {@link CoordinateFormat#clone()}, then verifies that the clone has the same configuration
+     * than the original object.
+     */
+    @Test
+    public void testClone() {
+        CoordinateFormat format = new CoordinateFormat(Locale.CANADA, null);
+        CoordinateFormat clone  = format.clone();
+        assertNotSame("clone()", clone, format);
+        assertEquals("getSeparator()",  format.getSeparator(),  clone.getSeparator());
+        assertEquals("getDefaultCRS()", format.getDefaultCRS(), clone.getDefaultCRS());
+    }
 }

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -19,14 +19,18 @@ 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.apache.sis.test.DependsOnMethod;
 import org.junit.Test;
 
 import static org.apache.sis.test.ReferencingAssert.*;
@@ -171,4 +175,56 @@ public final strictfp class EnvelopesTes
         assertEquals( -80, env2D.getMinimum(1), 0);
         assertEquals(  80, env2D.getMaximum(1), 0);
     }
+
+    /**
+     * Tests a transformation from a three-dimensional CRS where the range of longitude axis is changed.
+     * This is the same test than {@link #testAxisRangeChange()} but using a compound CRS as the source.
+     * Internally, this results in the concatenation of transforms. We want to ensure that information
+     * about axis range changes are not lost in this process.
+     *
+     * @throws FactoryException if an error occurred while creating the operation.
+     * @throws TransformException if an error occurred while transforming the envelope.
+     *
+     * @since 0.8
+     */
+    @Test
+    @DependsOnMethod("testAxisRangeChange")
+    public void testAxisRangeChange3D() throws FactoryException, TransformException {
+        testAxisRangeChange3D(HardCodedCRS.WGS84);
+    }
+
+    /**
+     * Tests a change of longitude axis range together with change of ellipsoid. This is the same test
+     * than {@link #testAxisRangeChange3D()} with an additional complexity: a change of ellipsoid.
+     * This causes the execution of different code branches in {@code ConcatenatedOperation} creation.
+     *
+     * @throws FactoryException if an error occurred while creating the operation.
+     * @throws TransformException if an error occurred while transforming the envelope.
+     *
+     * @since 0.8
+     */
+    @Test
+    @DependsOnMethod("testAxisRangeChange3D")
+    public void testAxisRangeChangeWithDatumShift() throws FactoryException, TransformException {
+        testAxisRangeChange3D(HardCodedCRS.SPHERE);
+    }
+
+    /**
+     * Implementation of {@link #testAxisRangeChange3D()} and {@link #testAxisRangeChangeWithDatumShift()}.
+     */
+    private void testAxisRangeChange3D(final GeographicCRS targetCRS) throws FactoryException, TransformException {
+        final GeneralEnvelope envelope  = new GeneralEnvelope(new double[] { -0.5, -90, 1000},
+                                                              new double[] {354.5, +90, 1002});
+        envelope.setCoordinateReferenceSystem(CRS.compound(
+                HardCodedCRS.WGS84.forConvention(AxesConvention.POSITIVE_RANGE), HardCodedCRS.TIME));
+        final GeneralEnvelope expected = createFromExtremums(targetCRS, -0.5, -90, -5.5, 90);
+        assertEnvelopeEquals(expected, Envelopes.transform(envelope, targetCRS), STRICT, STRICT);
+        /*
+         * When the envelope to transform span the full longitude range,
+         * target envelope should unconditionally be [-180 … +180]°.
+         */
+        envelope.setRange(0, -0.5, 359.5);
+        expected.setRange(0, -180, 180);
+        assertEnvelopeEquals(expected, Envelopes.transform(envelope, targetCRS), STRICT, STRICT);
+    }
 }

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -39,11 +39,11 @@ import static org.apache.sis.geometry.Ab
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Johann Sorel (Geomatys)
- * @version 0.5
+ * @version 0.8
  * @since   0.3
  * @module
  */
-@DependsOn(AbstractEnvelopeTest.class)
+@DependsOn(ArrayEnvelopeTest.class)
 public strictfp class GeneralEnvelopeTest extends TestCase {
     /**
      * Tolerance threshold for floating point comparisons.
@@ -97,7 +97,7 @@ public strictfp class GeneralEnvelopeTes
             final double xLower, final double ymin, final double xUpper, final double ymax)
     {
         final double xmin, xmax;
-        if (MathFunctions.isNegative(xUpper - xLower)) { // Check for anti-meridian spanning.
+        if (MathFunctions.isNegative(xUpper - xLower)) {                // Check for anti-meridian spanning.
             xmin = -180;
             xmax = +180;
         } else {
@@ -131,25 +131,24 @@ public strictfp class GeneralEnvelopeTes
     private static void assertIntersectEquals(final GeneralEnvelope e1, final GeneralEnvelope e2,
             final double xmin, final double ymin, final double xmax, final double ymax)
     {
-        final boolean isEmpty = !(((xmax - xmin) * (ymax - ymin)) != 0);        // Use ! for catching NaN.
         final Envelope2D r1 = new Envelope2D(e1);
         final Envelope2D r2 = new Envelope2D(e2);
         final Envelope2D ri = r1.createIntersection(r2);
-        assertEquals("isEmpty", isEmpty, r1.isEmpty());
+        assertFalse("isEmpty", r1.isEmpty());
         assertEnvelopeEquals(ri, xmin, ymin, xmax, ymax);
         assertEquals("Interchanged arguments.", ri, r2.createIntersection(r1));
 
         // Compares with GeneralEnvelope.
         final GeneralEnvelope ei = new GeneralEnvelope(e1);
         ei.intersect(e2);
-        assertEquals("isEmpty", isEmpty, e1.isEmpty());
+        assertFalse("isEmpty", e1.isEmpty());
         assertEnvelopeEquals(ei, xmin, ymin, xmax, ymax);
         assertTrue("Using GeneralEnvelope.", ei.equals(ri, STRICT, false));
 
         // Interchanges arguments.
         ei.setEnvelope(e2);
         ei.intersect(e1);
-        assertEquals("isEmpty", isEmpty, e1.isEmpty());
+        assertFalse("isEmpty", e1.isEmpty());
         assertEnvelopeEquals(ei, xmin, ymin, xmax, ymax);
         assertTrue("Using GeneralEnvelope.", ei.equals(ri, STRICT, false));
     }
@@ -233,7 +232,7 @@ public strictfp class GeneralEnvelopeTes
         //  └──────────┘
         e1.setEnvelope(20, -20,  80, 12);
         e2.setEnvelope(40, -10, 100, 30);
-        final double ymin=-10, ymax=12; // Will not change anymore
+        final double ymin=-10, ymax=12;                         // Will not change anymore
         assertIntersectEquals(e1, e2, 40, ymin, 80, ymax);
         //  ────┐  ┌────
         //  ──┐ │  │ ┌──
@@ -272,6 +271,20 @@ public strictfp class GeneralEnvelopeTes
         //  ─────┘     └─────
         e2.setRange(0, 10, 90);
         assertIntersectEquals(e1, e2, NaN, ymin, NaN, ymax);
+        //  ────────┬────────
+        //        ┌─┼────┐
+        //        └─┼────┘
+        //  ────────┴────────
+        e1.setRange(0, 0.0, -0.0);
+        e2.setRange(0, -10,   30);
+        assertIntersectEquals(e1, e2, -10, ymin, 30, ymax);
+        //  ┌───────────────┐
+        //  │               │
+        //  │               │
+        //  └───────────────┘
+        e1.setRange(0, 0.0, -0.0);
+        e2.setRange(0, 0.0, -0.0);
+        assertIntersectEquals(e1, e2, 0.0, ymin, -0.0, ymax);
 
         // Post-test verification, mostly for SubEnvelope.
         verifyInvariants(e1);

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/ImmutableEnvelopeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/ImmutableEnvelopeTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/ImmutableEnvelopeTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/ImmutableEnvelopeTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -35,7 +35,7 @@ import static org.apache.sis.geometry.Ab
  * @since   0.3
  * @module
  */
-@DependsOn(AbstractEnvelopeTest.class)
+@DependsOn(ArrayEnvelopeTest.class)
 public final strictfp class ImmutableEnvelopeTest extends TestCase {
     /**
      * Tests {@code ImmutableEnvelope} serialization.

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -17,6 +17,7 @@
 package org.apache.sis.geometry;
 
 import java.awt.geom.Rectangle2D;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.MathTransform2D;
@@ -42,6 +43,12 @@ public final strictfp class Shapes2DTest
      */
     @Override
     Rectangle2D createFromExtremums(CoordinateReferenceSystem crs, double xmin, double ymin, double xmax, double ymax) {
+        if (xmin > xmax) {
+            // This implementation does not support spanning anti-meridian.
+            final CoordinateSystemAxis axis = crs.getCoordinateSystem().getAxis(0);
+            xmin = axis.getMinimumValue();
+            xmax = axis.getMaximumValue();
+        }
         return new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin);
     }
 

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -28,7 +28,10 @@ import org.opengis.util.FactoryException
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.cs.AxesConvention;
 import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.HardCodedConversions;
+import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
@@ -82,6 +85,12 @@ public abstract strictfp class Transform
     abstract void assertGeometryEquals(G expected, G actual, double tolx, double toly);
 
     /**
+     * Allows sub-classing in same package only.
+     */
+    TransformTestCase() {
+    }
+
+    /**
      * Tests the transformation of an envelope or rectangle. This is a relatively simple test case
      * working in the two-dimensional space only, with a coordinate operation of type "conversion"
      * (not a "transformation") and with no need to adjust for poles.
@@ -212,6 +221,29 @@ public abstract strictfp class Transform
     }
 
     /**
+     * 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 final 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
      * since we reuse the properties (name, aliases, etc.) from the given conversion.
      * However those properties are not significant for the purpose of this test.
@@ -220,4 +252,22 @@ public abstract strictfp class Transform
         return new DefaultConversion(IdentifiedObjects.getProperties(conversion), conversion.getTargetCRS(),
                 conversion.getSourceCRS(), null, conversion.getMethod(), conversion.getMathTransform().inverse());
     }
+
+    /**
+     * 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 final void testAxisRangeChange() throws FactoryException, TransformException {
+        final GeographicCRS sourceCRS = HardCodedCRS.WGS84;
+        final GeographicCRS targetCRS = HardCodedCRS.WGS84.forConvention(AxesConvention.POSITIVE_RANGE);
+        final G rectangle = createFromExtremums(sourceCRS, -178, -70, 165, 80);
+        final G expected  = createFromExtremums(targetCRS,  182, -70, 165, 80);
+        final G actual    = transform(CRS.findOperation(sourceCRS, targetCRS, null), rectangle);
+        assertGeometryEquals(expected, actual, STRICT, STRICT);
+    }
 }



Mime
View raw message