sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1803070 [10/20] - in /sis/branches/JDK9: ./ application/sis-console/src/main/java/org/apache/sis/console/ core/sis-build-helper/ core/sis-build-helper/src/main/java/org/apache/sis/internal/book/ core/sis-build-helper/src/main/java/org/apac...
Date Wed, 26 Jul 2017 16:14:14 GMT
Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -81,8 +81,11 @@
  * @author  Jody Garnett (Refractions)
  * @author  Didier Richard (IGN)
  * @author  John Grange
- * @version 0.7
- * @since   0.7
+ * @version 0.8
+ *
+ * @see org.apache.sis.metadata.sql
+ *
+ * @since 0.7
  * @module
  */
 package org.apache.sis.referencing.factory.sql;

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=1803070&r1=1803069&r2=1803070&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] Wed Jul 26 16:14:09 2017
@@ -41,6 +41,7 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.referencing.provider.GeographicToGeocentric;
 import org.apache.sis.internal.referencing.provider.GeocentricToGeographic;
 import org.apache.sis.internal.referencing.provider.GeocentricAffine;
+import org.apache.sis.internal.referencing.SpecializedOperationFactory;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.measure.Units;
@@ -118,8 +119,8 @@ public class CoordinateOperationFinder e
      * 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. We nevertheless
-     * use a relatively high threshold as a conservative approach.
+     * 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
      */
@@ -221,6 +222,17 @@ public class CoordinateOperationFinder e
             throw new FactoryException(Resources.format(Resources.Keys.RecursiveCreateCallForCode_2, CoordinateOperation.class, key));
         }
         /*
+         * Verify if some extension module handles this pair of CRS in a special way. For example it may
+         * be the "sis-gdal" module checking if the given CRS are wrappers around Proj.4 data structure.
+         */
+        for (final SpecializedOperationFactory sp : factorySIS.getSpecializedFactories()) {
+            for (final CoordinateOperation op : sp.findOperations(sourceCRS, targetCRS)) {
+                if (filter(op)) {
+                    return op;
+                }
+            }
+        }
+        /*
          * If the user did not specified an area of interest, use the domain of validity of the CRS.
          * Then verify in the EPSG dataset if the operation is explicitely defined by an authority.
          */
@@ -501,8 +513,8 @@ public class CoordinateOperationFinder e
             if (parameters == null) {
                 parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE), datumShift);
                 final CoordinateSystem normalized = CommonCRS.WGS84.geocentric().getCoordinateSystem();
-                before = mtFactory.createCoordinateSystemChange(sourceCS, normalized);
-                after  = mtFactory.createCoordinateSystemChange(normalized, targetCS);
+                before = mtFactory.createCoordinateSystemChange(sourceCS, normalized, sourceDatum.getEllipsoid());
+                after  = mtFactory.createCoordinateSystemChange(normalized, targetCS, targetDatum.getEllipsoid());
                 context.setSource(normalized);
                 context.setTarget(normalized);
             }
@@ -522,7 +534,13 @@ public class CoordinateOperationFinder e
                 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);
-                before = mtFactory.createCoordinateSystemChange(sourceCS, targetCS);
+                /*
+                 * 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
+                 * createCoordinateSystemChange(…) javadoc for the rational) so it does not matter which one we pick.
+                 */
+                before = mtFactory.createCoordinateSystemChange(sourceCS, targetCS,
+                        (sourceCS instanceof EllipsoidalCS ? sourceDatum : targetDatum).getEllipsoid());
                 context.setSource(targetCS);
             }
         }

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=1803070&r1=1803069&r2=1803070&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] Wed Jul 26 16:14:09 2017
@@ -18,10 +18,10 @@ package org.apache.sis.referencing.opera
 
 import java.util.Map;
 import java.util.HashMap;
-import java.util.Set;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.Objects;
 import java.util.logging.Level;
@@ -59,11 +59,13 @@ import org.apache.sis.referencing.factor
 import org.apache.sis.referencing.factory.NoSuchAuthorityFactoryException;
 import org.apache.sis.metadata.iso.extent.Extents;
 import org.apache.sis.internal.referencing.CoordinateOperations;
+import org.apache.sis.internal.referencing.DeferredCoordinateOperation;
 import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.referencing.provider.Affine;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.metadata.ReferencingServices;
+import org.apache.sis.internal.system.Semaphores;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.util.Citations;
 import org.apache.sis.util.ArgumentChecks;
@@ -385,97 +387,90 @@ class CoordinateOperationRegistry {
             return null;
         }
         final boolean inverse;
-        Set<CoordinateOperation> operations;
+        Collection<CoordinateOperation> operations;
+        boolean mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);    // See comment for the same call inside the loop.
         try {
-            operations = registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
-            inverse = Containers.isNullOrEmpty(operations);
-            if (inverse) {
-                /*
-                 * No operation from 'source' to 'target' available. But maybe there is an inverse operation.
-                 * This is typically the case when the user wants to convert from a projected to a geographic CRS.
-                 * The EPSG database usually contains transformation paths for geographic to projected CRS only.
-                 */
-                operations = registry.createFromCoordinateReferenceSystemCodes(targetID, sourceID);
-                if (Containers.isNullOrEmpty(operations)) {
-                    return null;
+            try {
+                operations = registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
+                inverse = Containers.isNullOrEmpty(operations);
+                if (inverse) {
+                    /*
+                     * No operation from 'source' to 'target' available. But maybe there is an inverse operation.
+                     * This is typically the case when the user wants to convert from a projected to a geographic CRS.
+                     * The EPSG database usually contains transformation paths for geographic to projected CRS only.
+                     */
+                    operations = registry.createFromCoordinateReferenceSystemCodes(targetID, sourceID);
+                    if (Containers.isNullOrEmpty(operations)) {
+                        return null;
+                    }
+                }
+            } finally {
+                if (!mdOnly) {
+                    Semaphores.clear(Semaphores.METADATA_ONLY);
                 }
             }
-        } catch (NoSuchAuthorityCodeException | MissingFactoryResourceException exception) {
+        } catch (NoSuchAuthorityCodeException | MissingFactoryResourceException e) {
             /*
              * sourceCode or targetCode is unknown to the underlying authority factory.
              * Ignores the exception and fallback on the generic algorithm provided by
              * CoordinateOperationFinder.
              */
-            log(exception);
+            log(null, e);
             return null;
         }
         /*
-         * We will loop over all coordinate operations and select the one having the largest intersection
-         * with the area of interest. Note that if the user did not specified an area of interest himself,
-         * then we need to get one from the CRS. This is necessary for preventing the transformation from
-         * NAD27 to NAD83 in Idaho to select the transform for Alaska (since the later has a larger area).
+         * This outer loop is executed exactly once in most case. It may be executed more than once if an
+         * ignoreable error occurred while creating the CoordinateOperation, in which case we will fallback
+         * on the next best choice until we succeed.
          */
-        double largestArea = 0;
-        double finestAccuracy = Double.POSITIVE_INFINITY;
-        CoordinateOperation bestChoice = null;
-        boolean stopAtFirstDeprecated = false;
-        for (final Iterator<CoordinateOperation> it=operations.iterator(); it.hasNext();) {
-            CoordinateOperation candidate;
-            try {
-                candidate = it.next();
-            } catch (BackingStoreException exception) {
-                FactoryException cause = exception.unwrapOrRethrow(FactoryException.class);
-                if (cause instanceof MissingFactoryResourceException) {
-                    log(cause);
-                    continue;
-                }
-                throw cause;
-            }
-            if (candidate != null) {
+        CoordinateOperation bestChoice;
+        while (true) {
+            /*
+             * We will loop over all coordinate operations and select the one having the largest intersection
+             * with the area of interest. Note that if the user did not specified an area of interest himself,
+             * then we need to get one from the CRS. This is necessary for preventing the transformation from
+             * NAD27 to NAD83 in Idaho to select the transform for Alaska (since the later has a larger area).
+             */
+            bestChoice = null;
+            double largestArea = 0;
+            double finestAccuracy = Double.POSITIVE_INFINITY;
+            boolean stopAtFirstDeprecated = false;
+            for (final Iterator<CoordinateOperation> it=operations.iterator();;) {
+                CoordinateOperation candidate;
                 /*
-                 * If we found at least one non-deprecated operation, we will stop the search at
-                 * the first deprecated one (assuming that deprecated operations are sorted last).
+                 * Some pair of CRS have a lot of coordinate operations backed by datum shift grids.
+                 * We do not want to load all of them until we found the right coordinate operation.
+                 * The non-public Semaphores.METADATA_ONLY mechanism instructs EPSGDataAccess to
+                 * instantiate DeferredCoordinateOperation instead of full coordinate operations.
                  */
-                final boolean isDeprecated = (candidate instanceof Deprecable) && ((Deprecable) candidate).isDeprecated();
-                if (isDeprecated && stopAtFirstDeprecated) {
-                    break;
-                }
-                final double area = Extents.area(Extents.intersection(
-                        Extents.getGeographicBoundingBox(areaOfInterest),
-                        Extents.getGeographicBoundingBox(candidate.getDomainOfValidity())));
-                if (bestChoice == null || area >= largestArea) {
-                    final double accuracy = CRS.getLinearAccuracy(candidate);
-                    if (bestChoice == null || area != largestArea || accuracy < finestAccuracy) {
-                        /*
-                         * Inverse the operation only after we verified the metadata (domain of validity,
-                         * accuracy, etc.) since the creation of inverse operation is not guaranteed to
-                         * preserve all metadata.
-                         */
-                        if (inverse) try {
-                            candidate = inverse(candidate);
-                        } catch (NoninvertibleTransformException exception) {
-                            // It may be a normal failure - the operation is not required to be invertible.
-                            Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION),
-                                    CoordinateOperationRegistry.class, "createOperation", exception);
-                            continue;
-                        } catch (MissingFactoryResourceException e) {
-                            log(e);
-                            continue;
+                mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
+                try {
+                    try {
+                        if (!it.hasNext()) break;
+                        candidate = it.next();
+                    } finally {
+                        if (!mdOnly) {
+                            Semaphores.clear(Semaphores.METADATA_ONLY);
                         }
-                        /*
-                         * It is possible that the CRS given to this method were not quite right.  For example the user
-                         * may have created his CRS from a WKT using a different axis order than the order specified by
-                         * the authority and still (wrongly) call those CRS "EPSG:xxxx".  So we check if the source and
-                         * target CRS for the operation we just created are equivalent to the CRS specified by the user.
-                         *
-                         * NOTE: FactoryException may be thrown if we failed to create a transform from the user-provided
-                         * CRS to the authority-provided CRS. That transform should have been only an identity transform,
-                         * or a simple affine transform if the user specified wrong CRS as explained in above paragraph.
-                         * If we failed here, we are likely to fail for all other transforms. So we are better to let
-                         * the FactoryException propagate.
-                         */
-                        candidate = complete(candidate, sourceCRS, targetCRS);
-                        if (filter == null || filter.test(candidate)) {
+                    }
+                } catch (BackingStoreException exception) {
+                    throw exception.unwrapOrRethrow(FactoryException.class);
+                }
+                if (candidate != null) {
+                    /*
+                     * If we found at least one non-deprecated operation, we will stop the search at
+                     * the first deprecated one (assuming that deprecated operations are sorted last).
+                     */
+                    final boolean isDeprecated = (candidate instanceof Deprecable) && ((Deprecable) candidate).isDeprecated();
+                    if (isDeprecated && stopAtFirstDeprecated) {
+                        break;
+                    }
+                    final double area = Extents.area(Extents.intersection(
+                            Extents.getGeographicBoundingBox(areaOfInterest),
+                            Extents.getGeographicBoundingBox(candidate.getDomainOfValidity())));
+                    if (bestChoice == null || area >= largestArea) {
+                        final double accuracy = CRS.getLinearAccuracy(candidate);
+                        if (bestChoice == null || area != largestArea || accuracy < finestAccuracy) {
                             bestChoice = candidate;
                             if (!Double.isNaN(area)) {
                                 largestArea = area;
@@ -486,11 +481,79 @@ class CoordinateOperationRegistry {
                     }
                 }
             }
+            /*
+             * At this point we filtered a CoordinateOperation by looking only at its metadata.
+             * Code following this point will need the full coordinate operation, including its
+             * MathTransform. So if we got a deferred operation, we need to resolve it now.
+             * Conversely, we should not use metadata below this point because the call to
+             * inverse(CoordinateOperation) is not guaranteed to preserve all metadata.
+             */
+            if (bestChoice == null) break;
+            final CoordinateOperation deferred = bestChoice;
+            try {
+                if (bestChoice instanceof DeferredCoordinateOperation) {
+                    bestChoice = ((DeferredCoordinateOperation) bestChoice).create();
+                }
+                if (bestChoice instanceof SingleOperation && bestChoice.getMathTransform() == null) {
+                    bestChoice = fromDefiningConversion((SingleOperation) bestChoice,
+                                                        inverse ? targetCRS : sourceCRS,
+                                                        inverse ? sourceCRS : targetCRS);
+                    if (bestChoice == null) {
+                        return null;
+                    }
+                }
+                if (inverse) {
+                    bestChoice = inverse(bestChoice);
+                }
+            } catch (NoninvertibleTransformException | MissingFactoryResourceException e) {
+                /*
+                 * If we failed to get the real CoordinateOperation instance, remove it from the collection
+                 * and try again in order to get the next best choice. The Apache SIS implementation allows
+                 * to remove directly from the Set<CoordinateOperation>, but that removal may fail with non-SIS
+                 * implementations, in which case we copy the CoordinateOperation in a temporary list before
+                 * removal. We do not perform that copy unconditionally in order to avoid lazy initialization
+                 * of unneeded CoordinateOperations like the deprecated ones.
+                 */
+                boolean removed;
+                try {
+                    removed = operations.remove(deferred);
+                } catch (UnsupportedOperationException ignored) {
+                    operations = new ArrayList<>(operations);
+                    removed = operations.remove(deferred);
+                }
+                if (removed) {
+                    log(null, e);
+                    continue;                                   // Try again with the next best case.
+                }
+                // Should never happen, but if happen anyway we should fail for avoiding never-ending loop.
+                throw (e instanceof FactoryException) ? (FactoryException) e : new FactoryException(e);
+            }
+            /*
+             * It is possible that the CRS given to this method were not quite right.  For example the user
+             * may have created his CRS from a WKT using a different axis order than the order specified by
+             * the authority and still (wrongly) call those CRS "EPSG:xxxx".  So we check if the source and
+             * target CRS for the operation we just created are equivalent to the CRS specified by the user.
+             *
+             * NOTE: FactoryException may be thrown if we fail to create a transform from the user-provided
+             * CRS to the authority-provided CRS. That transform should have been only an identity transform,
+             * or a simple affine transform if the user specified wrong CRS as explained in above paragraph.
+             * If we fail here, we are likely to fail for all other transforms. So we are better to let the
+             * FactoryException propagate.
+             */
+            bestChoice = complete(bestChoice, sourceCRS, targetCRS);
+            if (filter(bestChoice)) break;
         }
         return bestChoice;
     }
 
     /**
+     * Returns {@code true} if the given operation can be accepted.
+     */
+    final boolean filter(final CoordinateOperation op) {
+        return filter == null || filter.test(op);
+    }
+
+    /**
      * Creates the inverse of the given single operation.
      * If this operation succeed, then the returned coordinate operations has the following properties:
      *
@@ -610,6 +673,12 @@ class CoordinateOperationRegistry {
                                                   final MathTransformFactory      mtFactory)
             throws IllegalArgumentException, IncommensurableException, FactoryException
     {
+        /*
+         * Assertion: source and target CRS must be equals, ignoring change in axis order or units.
+         * The first line is for disabling this check if the number of dimensions are not the same
+         * (e.g. as in the "geographic 3D to geographic 2D" conversion) because ALLOW_VARIANT mode
+         * still requires a matching number of dimensions.
+         */
         assert ReferencingUtilities.getDimension(sourceCRS) != ReferencingUtilities.getDimension(targetCRS)
                 || Utilities.deepEquals(sourceCRS, targetCRS, ComparisonMode.ALLOW_VARIANT);
         final Matrix m = CoordinateSystems.swapAndScaleAxes(sourceCRS.getCoordinateSystem(), targetCRS.getCoordinateSystem());
@@ -752,6 +821,42 @@ class CoordinateOperationRegistry {
     }
 
     /**
+     * Creates a complete coordinate operation from a defining conversion. Defining conversions usually have
+     * null source and target CRS, but this method nevertheless checks that, in order to reuse the operation
+     * CRS if it happens to have some.
+     *
+     * @param  operation  the operation specified by the authority.
+     * @param  sourceCRS  the source CRS specified by the user.
+     * @param  targetCRS  the target CRS specified by the user
+     * @return a new operation from the given source CRS to target CRS.
+     * @throws FactoryException if an error occurred while creating the new operation.
+     */
+    private CoordinateOperation fromDefiningConversion(final SingleOperation     operation,
+                                                       CoordinateReferenceSystem sourceCRS,
+                                                       CoordinateReferenceSystem targetCRS)
+            throws FactoryException
+    {
+        final ParameterValueGroup parameters = operation.getParameterValues();
+        if (parameters != null) {
+            CoordinateReferenceSystem crs;
+            if (Utilities.equalsApproximatively(sourceCRS, crs = operation.getSourceCRS())) sourceCRS = crs;
+            if (Utilities.equalsApproximatively(targetCRS, crs = operation.getTargetCRS())) targetCRS = crs;
+            final MathTransformFactory mtFactory = factorySIS.getMathTransformFactory();
+            if (mtFactory instanceof DefaultMathTransformFactory) {
+                MathTransform mt = ((DefaultMathTransformFactory) mtFactory).createParameterizedTransform(
+                        parameters, ReferencingUtilities.createTransformContext(sourceCRS, targetCRS, null));
+                return factorySIS.createSingleOperation(IdentifiedObjects.getProperties(operation),
+                        sourceCRS, targetCRS, null, operation.getMethod(), mt);
+            }
+        } else {
+            // Should never happen because parameters are mandatory, but let be safe.
+            log(Resources.forLocale(null).getLogRecord(Level.WARNING, Resources.Keys.MissingParameterValues_1,
+                    IdentifiedObjects.getIdentifierOrName(operation)), null);
+        }
+        return null;
+    }
+
+    /**
      * Returns a new coordinate operation with the ellipsoidal height added either in the source coordinates,
      * in the target coordinates or both. If there is an ellipsoidal transform, then this method updates the
      * transforms in order to use the ellipsoidal height (it has an impact on the transformed values).
@@ -861,7 +966,7 @@ class CoordinateOperationRegistry {
                                     ((SingleOperation) op).getParameterValues(),
                                     ReferencingUtilities.createTransformContext(sourceCRS, targetCRS, null));
                         } catch (InvalidGeodeticParameterException e) {
-                            log(e);
+                            log(null, e);
                             break;
                         }
                         operations.set(recreate(op, sourceCRS, targetCRS, mt, mtFactory.getLastMethodUsed()));
@@ -1077,11 +1182,23 @@ class CoordinateOperationRegistry {
      * come from {@link CoordinateOperationFinder} since this is the public API which
      * use this {@code CoordinateOperationRegistry} class.
      *
-     * @param exception  the exception which occurred.
+     * @param record     the record to log, or {@code null} for creating from the exception.
+     * @param exception  the exception which occurred, or {@code null} if a {@code record} is specified instead.
      */
-    private static void log(final FactoryException exception) {
-        final LogRecord record = new LogRecord(Level.WARNING, exception.getLocalizedMessage());
+    private static void log(LogRecord record, final Exception exception) {
+        if (record == null) {
+            record = new LogRecord(Level.WARNING, exception.getLocalizedMessage());
+        }
         record.setLoggerName(Loggers.COORDINATE_OPERATION);
+        /*
+         * We usually do not log the stack trace since this method should be invoked only for exceptions
+         * like NoSuchAuthorityCodeException or MissingFactoryResourceException, for which the message
+         * is descriptive enough. But we make a special case for NoninvertibleTransformException since
+         * its cause may have deeper root.
+         */
+        if (exception instanceof NoninvertibleTransformException) {
+            record.setThrown(exception);
+        }
         Logging.log(CoordinateOperationFinder.class, "createOperation", record);
     }
 }

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=1803070&r1=1803069&r2=1803070&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] Wed Jul 26 16:14:09 2017
@@ -104,19 +104,22 @@ final class DefaultConcatenatedOperation
      * @param  mtFactory   the math transform factory to use for math transforms concatenation.
      * @throws FactoryException if the factory can not concatenate the math transforms.
      */
-    @SuppressWarnings("PublicConstructorInNonPublicClass")
     public DefaultConcatenatedOperation(final Map<String,?> properties, CoordinateOperation[] operations,
             final MathTransformFactory mtFactory) throws FactoryException
     {
         super(properties);
         ArgumentChecks.ensureNonNull("operations", operations);
-        final List<CoordinateOperation> flattened = new ArrayList<>(operations.length);
-        initialize(properties, operations, flattened, mtFactory,
-                (coordinateOperationAccuracy == null), (domainOfValidity == null));
-        if (flattened.size() < 2) {
+        if (operations.length < 2) {
             throw new IllegalArgumentException(Errors.getResources(properties).getString(
                     Errors.Keys.TooFewOccurrences_2, 2, CoordinateOperation.class));
         }
+        final List<CoordinateOperation> flattened = new ArrayList<>(operations.length);
+        initialize(properties, operations, flattened, mtFactory,
+                (coordinateOperationAccuracy == null), (domainOfValidity == null));
+        /*
+         * 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();
@@ -190,7 +193,7 @@ final class DefaultConcatenatedOperation
                     }
                 }
             }
-            previous = op.getTargetCRS();   // For next iteration cycle.
+            previous = op.getTargetCRS();                                       // For next iteration cycle.
             /*
              * Now that we have verified the CRS dimensions, we should be able to concatenate the transforms.
              * If an operation is a nested ConcatenatedOperation (not allowed by ISO 19111, but we try to be
@@ -205,7 +208,7 @@ final class DefaultConcatenatedOperation
                 @SuppressWarnings("SuspiciousToArrayCall")
                 final CoordinateOperation[] asArray = children.toArray(new CoordinateOperation[children.size()]);
                 initialize(properties, asArray, flattened, (step == null) ? mtFactory : null, setAccuracy, setDomain);
-            } else {
+            } else if (!step.isIdentity()) {
                 flattened.add(op);
             }
             if (mtFactory != null) {

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=1803070&r1=1803069&r2=1803070&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] Wed Jul 26 16:14:09 2017
@@ -20,6 +20,7 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.Collections;
 import java.util.List;
+import java.util.ServiceLoader;
 import org.opengis.util.FactoryException;
 import org.opengis.util.NoSuchIdentifierException;
 import org.opengis.parameter.ParameterValueGroup;
@@ -36,10 +37,12 @@ import org.opengis.referencing.cs.CSFact
 import org.opengis.referencing.datum.Datum;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.referencing.MergedProperties;
+import org.apache.sis.internal.referencing.SpecializedOperationFactory;
 import org.apache.sis.internal.metadata.ReferencingServices;
 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.referencing.CRS;
 import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
 import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
@@ -122,6 +125,17 @@ public class DefaultCoordinateOperationF
     private volatile MathTransformFactory mtFactory;
 
     /**
+     * Factories specialized to some particular pair of CRS. For example a module doing the bindings between
+     * Apache SIS and another map projection library may create wrappers around the transformation method of
+     * that other library when {@code SpecializedOperationFactory.tryCreateOperation(…)} recognizes the given
+     * CRS as wrappers around their data structures.
+     *
+     * <p>This array is created when first needed. After creation, the array shall not be modified anymore.</p>
+     */
+    @SuppressWarnings("VolatileArrayField")
+    private volatile SpecializedOperationFactory[] specializedFactories;
+
+    /**
      * Weak references to existing objects.
      * This set is used in order to return a pre-existing object instead of creating a new one.
      * This applies to objects created explicitly, not to coordinate operations inferred by a
@@ -147,8 +161,8 @@ public class DefaultCoordinateOperationF
 
     /**
      * Constructs a factory with the given default properties.
-     * {@code DefaultCoordinateOperationFactory} will fallback on the map given to this constructor
-     * for any property not present in the map provided to a {@code createFoo(Map<String,?>, …)} method.
+     * The new factory will fallback on the map given to this constructor
+     * for any property not present in the map given to a {@code createFoo(Map<String,?>, …)} method.
      *
      * @param properties  the default properties, or {@code null} if none.
      * @param factory     the factory to use for creating {@linkplain AbstractMathTransform math transforms},
@@ -174,6 +188,7 @@ public class DefaultCoordinateOperationF
                 throw new IllegalArgumentException(Errors.getResources(properties)
                         .getString(Errors.Keys.IllegalPropertyValueClass_2, key, Classes.getClass(value)));
             }
+            properties.remove(ReferencingServices.DATUM_FACTORY);
             properties = CollectionsExt.compact(properties);
         }
         defaultProperties = properties;
@@ -254,6 +269,20 @@ public class DefaultCoordinateOperationF
     }
 
     /**
+     * Returns all known factories specialized in the creation of coordinate operations between some particular
+     * pairs of CRS.
+     */
+    final SpecializedOperationFactory[] getSpecializedFactories() {
+        SpecializedOperationFactory[] factories = specializedFactories;
+        if (factories == null) {
+            final LazySet<SpecializedOperationFactory> set =
+                    new LazySet<>(ServiceLoader.load(SpecializedOperationFactory.class).iterator());
+            specializedFactories = factories = set.toArray(new SpecializedOperationFactory[set.size()]);
+        }
+        return factories;
+    }
+
+    /**
      * Returns the operation method of the given name. The given argument shall be either a method
      * {@linkplain DefaultOperationMethod#getName() name} (e.g. <cite>"Transverse Mercator"</cite>)
      * or one of its {@linkplain DefaultOperationMethod#getIdentifiers() identifiers} (e.g. {@code "EPSG:9807"}).
@@ -513,7 +542,7 @@ next:   for (int i=components.size(); --
             if (parameters == null) {
                 throw new NullArgumentException(Errors.format(Errors.Keys.NullArgument_1, "transform"));
             }
-            transform = mtFactory.createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem());
+            transform = getMathTransformFactory().createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem());
         }
         /*
          * The "operationType" property is currently undocumented. The intend is to help this factory method in
@@ -650,12 +679,22 @@ next:   for (int i=components.size(); --
         if (operations != null && operations.length == 1) {
             return operations[0];
         }
-        final CoordinateOperation op;
+        final ConcatenatedOperation op;
         try {
             op = new DefaultConcatenatedOperation(properties, operations, getMathTransformFactory());
         } catch (IllegalArgumentException exception) {
             throw new InvalidGeodeticParameterException(exception.getLocalizedMessage(), exception);
         }
+        /*
+         * 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.
+         */
+        final List<? extends CoordinateOperation> co = op.getOperations();
+        if (co.size() == 1) {
+            assert op.getMathTransform().equals(co.get(0).getMathTransform()) : op;
+            return co.get(0);
+        }
         return pool.unique(op);
     }
 

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -18,18 +18,24 @@ package org.apache.sis.referencing.opera
 
 import org.opengis.util.FactoryException;
 import org.opengis.geometry.MismatchedDimensionException;
+import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.apache.sis.referencing.operation.transform.InterpolatedTransform;
 import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.datum.DatumShiftGrid;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.measure.NumberRange;
 import org.apache.sis.math.MathFunctions;
+import org.apache.sis.math.Vector;
 
 
 /**
@@ -63,6 +69,11 @@ import org.apache.sis.math.MathFunctions
  */
 public class LocalizationGridBuilder extends TransformBuilder {
     /**
+     * Tolerance threshold for comparing pixel coordinates relative to integer values.
+     */
+    private static final double EPS = Numerics.COMPARISON_THRESHOLD;
+
+    /**
      * The transform for the linear part.
      */
     private final LinearTransformBuilder linear;
@@ -71,7 +82,7 @@ public class LocalizationGridBuilder ext
      * A temporary array for two-dimensional source coordinates.
      * Used for reducing object allocations.
      */
-    private final int[] tmp;
+    private final int[] tmp = new int[2];
 
     /**
      * Conversions from source real-world coordinates to grid indices before interpolation.
@@ -90,18 +101,86 @@ public class LocalizationGridBuilder ext
     static final double DEFAULT_PRECISION = 1E-7;
 
     /**
-     * Creates a new, initially empty, builder.
+     * Creates a new, initially empty, builder for a localization grid of the given size.
      *
      * @param width   the number of columns in the grid of target positions.
      * @param height  the number of rows in the grid of target positions.
      */
     public LocalizationGridBuilder(final int width, final int height) {
         linear       = new LinearTransformBuilder(width, height);
-        tmp          = new int[2];
         sourceToGrid = MathTransforms.identity(2);
     }
 
     /**
+     * Creates a new, initially empty, builder for a localization grid of a size inferred from the given points.
+     * This constructor uses the given vectors for computing a grid size and the following initial conversion:
+     *
+     * <blockquote>({@code sourceX}, {@code sourceY}) → ({@code gridX}, {@code gridY})</blockquote>
+     *
+     * Above conversion can be obtained by {@link #getSourceToGrid()}.
+     *
+     * <p>Values in the given vectors should be integers, but this constructor is tolerant to non-integer values
+     * if they have a constant offset (typically 0.5) relative to integer values. The two vectors do not need to
+     * have the same length (i.e. {@code sourceX[i]} are not necessarily related to {@code sourceY[i]}).</p>
+     *
+     * @param  sourceX  all possible <var>x</var> inputs before conversion to grid coordinates.
+     * @param  sourceY  all possible <var>y</var> inputs before conversion to grid coordinates.
+     * @throws ArithmeticException if this constructor can not infer a reasonable grid size from the given vectors.
+     */
+    public LocalizationGridBuilder(final Vector sourceX, final Vector sourceY) {
+        final Matrix fromGrid = Matrices.createDiagonal(3,3);
+        linear = new LinearTransformBuilder(infer(sourceX, fromGrid, 0), infer(sourceY, fromGrid, 1));
+        try {
+            sourceToGrid = MathTransforms.linear(fromGrid).inverse();
+        } catch (NoninvertibleTransformException e) {
+            // Should not happen because infer(…) verified that the coefficients are okay.
+            throw (ArithmeticException) new ArithmeticException(e.getLocalizedMessage()).initCause(e);
+        }
+    }
+
+    /**
+     * Infers a grid size by searching for the greatest common divisor (GCD) for values in the given vector.
+     * The vector values should be integers, but this method is tolerant to constant offsets (typically 0.5).
+     * The GCD is taken as a "grid to source" scale factor and the minimal value as the translation term.
+     * Those two values are stored in the {@code dim} row of the given matrix.
+     *
+     * @param  source    the vector of values for which to get the GCD and minimum value.
+     * @param  fromGrid  matrix where to store the minimum value and the GCD.
+     * @param  dim       index of the matrix row to update.
+     * @return grid size.
+     */
+    private static int infer(final Vector source, final Matrix fromGrid, final int dim) {
+        final NumberRange<?> range = source.range();
+        final double min  = range.getMinDouble(true);
+        final double span = range.getMaxDouble(true) - min;
+        final Number increment = source.increment(EPS * span);
+        double inc;
+        if (increment != null) {
+            inc = increment.doubleValue();
+        } else {
+            inc = span;
+            final int size = source.size();
+            for (int i=0; i<size; i++) {
+                double v = source.doubleValue(i) - min;
+                if (Math.abs(v % inc) > EPS) {
+                    do {
+                        final double r = (inc % v);     // Both 'inc' and 'v' are positive, so 'r' will be positive too.
+                        inc = v;
+                        v = r;
+                    } while (Math.abs(v) > EPS);
+                }
+            }
+        }
+        fromGrid.setElement(dim, dim, inc);
+        fromGrid.setElement(dim,   2, min);
+        final double n = span / inc;
+        if (n > 0.5 && n < 0.5 + Short.MAX_VALUE) {
+            return ((int) Math.round(n)) + 1;
+        }
+        throw new ArithmeticException(Resources.format(Resources.Keys.CanNotInferGridSizeFromValues_1, range));
+    }
+
+    /**
      * Sets the desired precision of <em>inverse</em> transformations, in units of source coordinates.
      * If a conversion from "real world" to grid coordinates {@linkplain #setSourceToGrid has been specified},
      * then the given precision is in "real world" units. Otherwise the precision is in units of grid cells.
@@ -122,6 +201,16 @@ public class LocalizationGridBuilder ext
     }
 
     /**
+     * Returns the desired precision of <em>inverse</em> transformations, in units of source coordinates.
+     * This is the precision sets by the last call to {@link #setDesiredPrecision(double)}.
+     *
+     * @return desired precision of the results of inverse transformations.
+     */
+    public double getDesiredPrecision() {
+        return precision;
+    }
+
+    /**
      * Defines relationship between "real-world" source coordinates and grid coordinates.
      * The given transform is usually two-dimensional, in which case conversions from (<var>x</var>,<var>y</var>)
      * source coordinates to ({@code gridX}, {@code gridY}) indices can be done with the following formulas:
@@ -173,9 +262,25 @@ public class LocalizationGridBuilder ext
     }
 
     /**
+     * Returns the current relationship between "real-world" source coordinates and grid coordinates.
+     * This is the value set by the last call to {@link #setSourceToGrid(LinearTransform)}.
+     * If that setter method has never been invoked, then this is an automatically computed transform
+     * if the grid coordinates {@linkplain #LocalizationGridBuilder(Vector, Vector) have been specified
+     * to the constructor}, or the identity transform {@linkplain #LocalizationGridBuilder(int, int) otherwise}.
+     *
+     * @return the current relationship between "real-world" source coordinates and grid coordinates.
+     */
+    public LinearTransform getSourceToGrid() {
+        return sourceToGrid;
+    }
+
+    /**
      * Sets a single matching control point pair. Source position is assumed precise and target position is assumed uncertain.
      * If the given source position was already associated with another target position, then the old target position is discarded.
      *
+     * <p>If a {@linkplain #getSourceToGrid() source to grid} conversion exists, it shall have been applied
+     * by the caller for computing the ({@code gridX}, {@code gridY}) coordinates given to this method.</p>
+     *
      * @param  gridX   the column index in the grid where to store the given target position.
      * @param  gridY   the row index in the grid where to store the given target position.
      * @param  target  the target coordinates, assumed uncertain.
@@ -231,7 +336,7 @@ public class LocalizationGridBuilder ext
             }
         }
         if (isExact) {
-            return gridToCoord;
+            return MathTransforms.concatenate(sourceToGrid, gridToCoord);
         }
         final int      width    = linear.gridSize(0);
         final int      height   = linear.gridSize(1);
@@ -282,7 +387,7 @@ public class LocalizationGridBuilder ext
             throw new FactoryException(e);                                          // Should never happen.
         }
         if (isLinear) {
-            return gridToCoord;
+            return MathTransforms.concatenate(sourceToGrid, gridToCoord);
         }
         return InterpolatedTransform.createGeodeticTransformation(nonNull(factory),
                 new ResidualGrid(sourceToGrid, gridToCoord, width, height, tgtDim, residual,

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -628,7 +628,7 @@ public class TransverseMercator extends
             final double tanφ = sinφ / cosφ;
             final double B    = cosφ * sinλ;
             /*
-             * Using Snyder's equation for calculating y, instead of the one used in Proj4.
+             * Using Snyder's equation for calculating y, instead of the one used in Proj.4.
              * Potential problems when y and x = 90 degrees, but behaves ok in tests.
              */
             if (dstPts != null) {

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=1803070&r1=1803069&r2=1803070&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] Wed Jul 26 16:14:09 2017
@@ -792,7 +792,7 @@ public abstract class AbstractMathTransf
     }
 
     /**
-     * Concatenates in an optimized way this math transform with the given one.
+     * Concatenates or pre-concatenates in an optimized way this math transform with the given one.
      * A new math transform is created to perform the combined transformation.
      * The {@code applyOtherFirst} value determines the transformation order as bellow:
      *
@@ -810,16 +810,20 @@ public abstract class AbstractMathTransf
      * {@link ConcatenatedTransform}.
      *
      * <p>The default implementation always returns {@code null}. This method is ought to be overridden
-     * by subclasses capable of concatenating some combination of transforms in a special way.
-     * Examples are {@link ExponentialTransform1D} and {@link LogarithmicTransform1D}.</p>
+     * by subclasses capable of concatenating some combination of transforms in a special way.</p>
      *
-     * @param  other            the math transform to apply.
      * @param  applyOtherFirst  {@code true} if the transformation order is {@code other} followed by {@code this}, or
      *                          {@code false} if the transformation order is {@code this} followed by {@code other}.
+     * @param  other            the other math transform to (pre-)concatenate with this transform.
      * @param  factory          the factory which is (indirectly) invoking this method, or {@code null} if none.
      * @return the combined math transform, or {@code null} if no optimized combined transform is available.
+     * @throws FactoryException if an error occurred while combining the transforms.
+     *
+     * @see DefaultMathTransformFactory#createConcatenatedTransform(MathTransform, MathTransform)
+     *
+     * @since 0.8
      */
-    MathTransform concatenate(MathTransform other, boolean applyOtherFirst, MathTransformFactory factory)
+    protected MathTransform tryConcatenate(boolean applyOtherFirst, MathTransform other, MathTransformFactory factory)
             throws FactoryException
     {
         return null;

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -330,13 +330,13 @@ class ConcatenatedTransform extends Abst
          * The main use case is Logarithmic vs Exponential transforms.
          */
         if (tr1 instanceof AbstractMathTransform) {
-            final MathTransform optimized = ((AbstractMathTransform) tr1).concatenate(tr2, false, factory);
+            final MathTransform optimized = ((AbstractMathTransform) tr1).tryConcatenate(false, tr2, factory);
             if (optimized != null) {
                 return optimized;
             }
         }
         if (tr2 instanceof AbstractMathTransform) {
-            final MathTransform optimized = ((AbstractMathTransform) tr2).concatenate(tr1, true, factory);
+            final MathTransform optimized = ((AbstractMathTransform) tr2).tryConcatenate(true, tr1, factory);
             if (optimized != null) {
                 return optimized;
             }

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=1803070&r1=1803069&r2=1803070&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] Wed Jul 26 16:14:09 2017
@@ -42,6 +42,7 @@ import org.opengis.parameter.InvalidPara
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.EllipsoidalCS;
+import org.opengis.referencing.cs.CartesianCS;
 import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
@@ -61,6 +62,8 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.referencing.provider.AbstractProvider;
 import org.apache.sis.internal.referencing.provider.VerticalOffset;
 import org.apache.sis.internal.referencing.provider.Providers;
+import org.apache.sis.internal.referencing.provider.GeographicToGeocentric;
+import org.apache.sis.internal.referencing.provider.GeocentricToGeographic;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.metadata.iso.citation.Citations;
@@ -848,7 +851,8 @@ public class DefaultMathTransformFactory
          * <p>The given method and parameters are stored in the {@link #provider} and {@link #parameters}
          * fields respectively. The actual stored values may differ from the values given to this method.</p>
          *
-         * @param  method  description of the transform to be created, or {@code null} if unknown.
+         * @param  factory  the enclosing factory.
+         * @param  method   description of the transform to be created, or {@code null} if unknown.
          * @return the exception if the operation failed, or {@code null} if none. This exception is not thrown now
          *         because the caller may succeed in creating the transform anyway, or otherwise may produce a more
          *         informative exception.
@@ -858,9 +862,23 @@ public class DefaultMathTransformFactory
          * @see #getCompletedParameters()
          */
         @SuppressWarnings("null")
-        final RuntimeException completeParameters(OperationMethod method, final ParameterValueGroup userParams)
-                throws IllegalArgumentException
+        final RuntimeException completeParameters(final DefaultMathTransformFactory factory, OperationMethod method,
+                final ParameterValueGroup userParams) throws FactoryException, IllegalArgumentException
         {
+            /*
+             * The "Geographic/geocentric conversions" conversion (EPSG:9602) can be either:
+             *
+             *    - "Ellipsoid_To_Geocentric"
+             *    - "Geocentric_To_Ellipsoid"
+             *
+             * EPSG defines both by a single operation, but Apache SIS needs to distinguish them.
+             */
+            if (method instanceof AbstractProvider) {
+                final String alt = ((AbstractProvider) method).resolveAmbiguity(this);
+                if (alt != null) {
+                    method = factory.getOperationMethod(alt);
+                }
+            }
             provider   = method;
             parameters = userParams;
             /*
@@ -896,8 +914,8 @@ public class DefaultMathTransformFactory
                 if (targetEllipsoid != null) n |= 2;
             }
             /*
-             * Set the ellipsoid axis-length parameter values. Those parameters may appear in the source
-             * ellipsoid, in the target ellipsoid or in both ellipsoids.
+             * Set the ellipsoid axis-length parameter values. Those parameters may appear in the source ellipsoid,
+             * in the target ellipsoid or in both ellipsoids.
              */
             switch (n) {
                 case 0: return null;
@@ -1020,7 +1038,7 @@ public class DefaultMathTransformFactory
                  * since the standard place where to provide this information is in the ellipsoid object.
                  */
                 if (context != null) {
-                    failure = context.completeParameters(method, parameters);
+                    failure    = context.completeParameters(this, method, parameters);
                     parameters = context.parameters;
                     method     = context.provider;
                 }
@@ -1048,8 +1066,10 @@ public class DefaultMathTransformFactory
             lastMethod.set(method);     // May be null in case of failure, which is intended.
             if (context != null) {
                 context.provider = null;
-                // For now we conservatively reset the provider information to null. But if we choose to make
-                // that information public in a future SIS version, then we would remove this code.
+                /*
+                 * For now we conservatively reset the provider information to null. But if we choose to
+                 * make that information public in a future SIS version, then we would remove this code.
+                 */
             }
         }
         return transform;
@@ -1190,13 +1210,71 @@ public class DefaultMathTransformFactory
      * @return a conversion from the given source to the given target coordinate system.
      * @throws FactoryException if the conversion can not be created.
      *
+     * @deprecated Replaced by {@link #createCoordinateSystemChange(CoordinateSystem, CoordinateSystem, Ellipsoid)}
+     *
      * @since 0.7
      */
+    @Deprecated
     public MathTransform createCoordinateSystemChange(final CoordinateSystem source, final CoordinateSystem target)
             throws FactoryException
     {
+        return createCoordinateSystemChange(source, target, null);
+    }
+
+    /**
+     * Creates a math transform that represent a change of coordinate system. If exactly one argument is
+     * an {@linkplain org.apache.sis.referencing.cs.DefaultEllipsoidalCS ellipsoidal coordinate systems},
+     * then the {@code ellipsoid} argument is mandatory. In all other cases (including the case where both
+     * coordinate systems are ellipsoidal), the ellipsoid argument is ignored and can be {@code null}.
+     *
+     * <div class="note"><b>Design note:</b>
+     * this method does not accept separated ellipsoid arguments for {@code source} and {@code target} because
+     * this method should not be used for datum shifts. If the two given coordinate systems are ellipsoidal,
+     * then they are assumed to use the same ellipsoid. If different ellipsoids are desired, then a
+     * {@linkplain #createParameterizedTransform parameterized transform} like <cite>"Molodensky"</cite>,
+     * <cite>"Geocentric translations"</cite>, <cite>"Coordinate Frame Rotation"</cite> or
+     * <cite>"Position Vector transformation"</cite> should be used instead.</div>
+     *
+     * @param  source     the source coordinate system.
+     * @param  target     the target coordinate system.
+     * @param  ellipsoid  the ellipsoid of {@code EllipsoidalCS}, or {@code null} if none.
+     * @return a conversion from the given source to the given target coordinate system.
+     * @throws FactoryException if the conversion can not be created.
+     *
+     * @since 0.8
+     */
+    public MathTransform createCoordinateSystemChange(final CoordinateSystem source, final CoordinateSystem target,
+            final Ellipsoid ellipsoid) throws FactoryException
+    {
         ArgumentChecks.ensureNonNull("source", source);
         ArgumentChecks.ensureNonNull("target", target);
+        if (ellipsoid != null) {
+            final boolean isEllipsoidalSource = (source instanceof EllipsoidalCS);
+            if (isEllipsoidalSource != (target instanceof EllipsoidalCS)) {
+                /*
+                 * For now we support only conversion between EllipsoidalCS and CartesianCS.
+                 * But future Apache SIS versions could add support for conversions between
+                 * EllipsoidalCS and SphericalCS or other coordinate systems.
+                 */
+                if ((isEllipsoidalSource ? target : source) instanceof CartesianCS) {
+                    final Context context = new Context();
+                    final EllipsoidalCS cs;
+                    final String operation;
+                    if (isEllipsoidalSource) {
+                        operation = GeographicToGeocentric.NAME;
+                        context.setSource(cs = (EllipsoidalCS) source, ellipsoid);
+                        context.setTarget(target);
+                    } else {
+                        operation = GeocentricToGeographic.NAME;
+                        context.setSource(source);
+                        context.setTarget(cs = (EllipsoidalCS) target, ellipsoid);
+                    }
+                    final ParameterValueGroup pg = getDefaultParameters(operation);
+                    if (cs.getDimension() < 3) pg.parameter("dim").setValue(2);       // Apache SIS specific parameter.
+                    return createParameterizedTransform(pg, context);
+                }
+            }
+        }
         return CoordinateSystemTransform.create(this, source, target);
         // No need to use unique(…) here.
     }
@@ -1313,6 +1391,9 @@ public class DefaultMathTransformFactory
      * Creates a math transform object from a
      * <a href="http://www.geoapi.org/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
      * Known Text</cite> (WKT)</a>.
+     * If the given text contains non-fatal anomalies (unknown or unsupported WKT elements,
+     * inconsistent unit definitions, <i>etc.</i>), warnings may be reported in a
+     * {@linkplain java.util.logging.Logger logger} named {@code "org.apache.sis.io.wkt"}.
      *
      * @param  text  math transform encoded in Well-Known Text format.
      * @return the math transform (never {@code null}).

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -915,7 +915,7 @@ next:   while (--numPts >= 0) {
          * </ul>
          */
         @Override
-        final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst,
+        protected MathTransform tryConcatenate(final boolean applyOtherFirst, final MathTransform other,
                 final MathTransformFactory factory) throws FactoryException
         {
             if (!applyOtherFirst && withHeight && other instanceof LinearTransform && other.getTargetDimensions() == 2) {
@@ -937,7 +937,7 @@ next:   while (--numPts >= 0) {
                     }
                 }
             }
-            return super.concatenate(other, applyOtherFirst, factory);
+            return super.tryConcatenate(applyOtherFirst, other, factory);
         }
 
         /**
@@ -993,9 +993,12 @@ next:   while (--numPts >= 0) {
      *   <li>Allow use of the more efficient {@link java.awt.geom.AffineTransform} before this transform
      *       instead than a transform based on a matrix of size 4×3.</li>
      * </ul>
+     *
+     * @return the combined math transform, or {@code null} if no optimized combined transform is available.
+     * @throws FactoryException if an error occurred while combining the transforms.
      */
     @Override
-    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst,
+    protected MathTransform tryConcatenate(final boolean applyOtherFirst, final MathTransform other,
             final MathTransformFactory factory) throws FactoryException
     {
         if (applyOtherFirst && withHeight && other instanceof LinearTransform && other.getSourceDimensions() == 2) {
@@ -1017,7 +1020,7 @@ next:   while (--numPts >= 0) {
                 }
             }
         }
-        return super.concatenate(other, applyOtherFirst, factory);
+        return super.tryConcatenate(applyOtherFirst, other, factory);
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ExponentialTransform1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ExponentialTransform1D.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ExponentialTransform1D.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ExponentialTransform1D.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -217,14 +217,14 @@ final class ExponentialTransform1D exten
      * {@code MathTransform}. This implementation can optimize some concatenation with
      * {@link LinearTransform1D} and {@link LogarithmicTransform1D}.
      *
-     * @param  other            the math transform to apply.
      * @param  applyOtherFirst  {@code true} if the transformation order is {@code other} followed by {@code this}, or
      *                          {@code false} if the transformation order is {@code this} followed by {@code other}.
+     * @param  other            the other math transform to (pre-)concatenate with this transform.
      * @param  factory          the factory which is (indirectly) invoking this method, or {@code null} if none.
      * @return the combined math transform, or {@code null} if no optimized combined transform is available.
      */
     @Override
-    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst,
+    protected MathTransform tryConcatenate(final boolean applyOtherFirst, final MathTransform other,
             final MathTransformFactory factory) throws FactoryException
     {
         if (other instanceof LinearTransform) {
@@ -243,7 +243,7 @@ final class ExponentialTransform1D exten
         } else if (other instanceof LogarithmicTransform1D) {
             return concatenateLog((LogarithmicTransform1D) other, applyOtherFirst);
         }
-        return super.concatenate(other, applyOtherFirst, factory);
+        return super.tryConcatenate(applyOtherFirst, other, factory);
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LogarithmicTransform1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LogarithmicTransform1D.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LogarithmicTransform1D.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LogarithmicTransform1D.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -113,14 +113,14 @@ class LogarithmicTransform1D extends Abs
      * {@code MathTransform}. This implementation can optimize some concatenation with
      * {@link LinearTransform1D} and {@link ExponentialTransform1D}.
      *
-     * @param  other            the math transform to apply.
      * @param  applyOtherFirst  {@code true} if the transformation order is {@code other} followed by {@code this}, or
      *                          {@code false} if the transformation order is {@code this} followed by {@code other}.
+     * @param  other            the other math transform to (pre-)concatenate with this transform.
      * @param  factory          the factory which is (indirectly) invoking this method, or {@code null} if none.
      * @return the combined math transform, or {@code null} if no optimized combined transform is available.
      */
     @Override
-    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst,
+    protected MathTransform tryConcatenate(final boolean applyOtherFirst, final MathTransform other,
             final MathTransformFactory factory) throws FactoryException
     {
         if (other instanceof LinearTransform1D) {
@@ -138,7 +138,7 @@ class LogarithmicTransform1D extends Abs
         } else if (other instanceof ExponentialTransform1D) {
             return ((ExponentialTransform1D) other).concatenateLog(this, !applyOtherFirst);
         }
-        return super.concatenate(other, applyOtherFirst, factory);
+        return super.tryConcatenate(applyOtherFirst, other, factory);
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PowerTransform1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PowerTransform1D.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PowerTransform1D.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PowerTransform1D.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -31,7 +31,7 @@ import org.apache.sis.util.ComparisonMod
  * Future version may expand on that.
  *
  * <p>Before to make this class public (if we do), we need to revisit the class name, define parameters
- * and improve the {@link #concatenate(MathTransform, boolean, MathTransformFactory)} method.</p>
+ * and improve the {@link #tryConcatenate(boolean, MathTransform, MathTransformFactory)} method.</p>
  *
  * <div class="section">Serialization</div>
  * Serialized instances of this class are not guaranteed to be compatible with future SIS versions.
@@ -171,21 +171,22 @@ final class PowerTransform1D extends Abs
     /**
      * Concatenates in an optimized way a {@link MathTransform} {@code other} to this {@code MathTransform}.
      *
-     * @param  other            the math transform to apply.
      * @param  applyOtherFirst  {@code true} if the transformation order is {@code other} followed by {@code this},
      *                          or {@code false} if the transformation order is {@code this} followed by {@code other}.
+     * @param  other            the other math transform to (pre-)concatenate with this transform.
      * @param  factory          the factory which is (indirectly) invoking this method, or {@code null} if none.
      * @return the combined math transform, or {@code null} if no optimized combined transform is available.
      */
     @Override
-    final MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst,
+    protected MathTransform tryConcatenate(final boolean applyOtherFirst, final MathTransform other,
             final MathTransformFactory factory) throws FactoryException
     {
         if (other instanceof PowerTransform1D) {
+            // Valid for both concatenation and pre-concatenation.
             return create(power + ((PowerTransform1D) other).power);
         }
         // TODO: more optimization could go here for logarithmic and exponential cases.
-        return super.concatenate(other, applyOtherFirst, factory);
+        return super.tryConcatenate(applyOtherFirst, other, factory);
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -541,7 +541,7 @@ public class TransformSeparator {
              * not accept arbitrary index for modified ordinates.
              */
             if (containsAll(dimensions, lower, subLower) && containsAll(dimensions, subUpper, upper)) {
-                return factory.createPassThroughTransform(subLower - lower, subTransform, upper - subUpper);
+                return factory.createPassThroughTransform(subLower - lower, subTransform, Math.max(0, upper - subUpper));
             }
         }
         /*
@@ -679,7 +679,7 @@ reduce:     for (int j=0; j <= numTgt; j
      * @return {@code true} if the full range was found in the sequence.
      */
     private static boolean containsAll(final int[] sequence, final int lower, int upper) {
-        if (lower == upper) {
+        if (lower >= upper) {
             return true;
         }
         if (sequence != null) {

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -110,6 +110,34 @@ public final strictfp class AxisDirectio
     }
 
     /**
+     * Tests {@link AxisDirections#isCardinal(AxisDirection)}.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testIsCardinal() {
+        assertTrue (AxisDirections.isCardinal(NORTH));
+        assertFalse(AxisDirections.isCardinal(NORTH_NORTH_EAST));
+        assertFalse(AxisDirections.isCardinal(NORTH_EAST));
+        assertFalse(AxisDirections.isCardinal(EAST_NORTH_EAST));
+        assertTrue (AxisDirections.isCardinal(EAST));
+        assertFalse(AxisDirections.isCardinal(EAST_SOUTH_EAST));
+        assertFalse(AxisDirections.isCardinal(SOUTH_EAST));
+        assertFalse(AxisDirections.isCardinal(SOUTH_SOUTH_EAST));
+        assertTrue (AxisDirections.isCardinal(SOUTH));
+        assertFalse(AxisDirections.isCardinal(SOUTH_SOUTH_WEST));
+        assertFalse(AxisDirections.isCardinal(SOUTH_WEST));
+        assertFalse(AxisDirections.isCardinal(WEST_SOUTH_WEST));
+        assertTrue (AxisDirections.isCardinal(WEST));
+        assertFalse(AxisDirections.isCardinal(WEST_NORTH_WEST));
+        assertFalse(AxisDirections.isCardinal(NORTH_WEST));
+        assertFalse(AxisDirections.isCardinal(NORTH_NORTH_WEST));
+        assertFalse(AxisDirections.isCardinal(UP));
+        assertFalse(AxisDirections.isCardinal(FUTURE));
+        assertFalse(AxisDirections.isCardinal(OTHER));
+    }
+
+    /**
      * Tests {@link AxisDirections#isIntercardinal(AxisDirection)}.
      */
     @Test

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolationTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolationTest.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolationTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolationTest.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.internal.referencing.provider;
 
-import java.net.URL;
 import java.net.URISyntaxException;
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -29,7 +28,6 @@ import org.opengis.geometry.Envelope;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.test.DependsOnMethod;
-import org.apache.sis.test.TestCase;
 import org.apache.sis.test.TestStep;
 import org.junit.Test;
 
@@ -48,7 +46,7 @@ import static org.opengis.test.Assert.*;
  * @since 0.7
  * @module
  */
-public final strictfp class FranceGeocentricInterpolationTest extends TestCase {
+public final strictfp class FranceGeocentricInterpolationTest extends DatumShiftTestCase {
     /**
      * Name of the file containing a small extract of the "{@code GR3DF97A.txt}" file.
      * The amount of data in this test file is less than 0.14% of the original file.
@@ -140,9 +138,7 @@ public final strictfp class FranceGeocen
     private static DatumShiftGridFile<Angle,Length> testGridAsFloats()
             throws URISyntaxException, IOException, FactoryException, TransformException
     {
-        final URL url = FranceGeocentricInterpolationTest.class.getResource(TEST_FILE);
-        assertNotNull("Test file \"" + TEST_FILE + "\" not found.", url);
-        final Path file = Paths.get(url.toURI());
+        final Path file = getResource(TEST_FILE);
         final DatumShiftGridFile.Float<Angle,Length> grid;
         try (BufferedReader in = Files.newBufferedReader(file)) {
             grid = FranceGeocentricInterpolation.load(in, file);
@@ -233,17 +229,15 @@ public final strictfp class FranceGeocen
     @Test
     @DependsOnMethod("testGrid")
     public void testGetOrLoad() throws URISyntaxException, FactoryException, TransformException {
-        final URL file = FranceGeocentricInterpolationTest.class.getResource(TEST_FILE);
-        assertNotNull("Test file \"" + TEST_FILE + "\" not found.", file);
         final DatumShiftGridFile<Angle,Length> grid = FranceGeocentricInterpolation.getOrLoad(
-                Paths.get(file.toURI()), new double[] {
+                getResource(TEST_FILE), new double[] {
                         FranceGeocentricInterpolation.TX,
                         FranceGeocentricInterpolation.TY,
                         FranceGeocentricInterpolation.TZ},
                         FranceGeocentricInterpolation.PRECISION);
         verifyGrid(grid);
         assertSame("Expected a cached value.", grid, FranceGeocentricInterpolation.getOrLoad(
-                Paths.get(file.toURI()), new double[] {
+                getResource(TEST_FILE), new double[] {
                         FranceGeocentricInterpolation.TX,
                         FranceGeocentricInterpolation.TY,
                         FranceGeocentricInterpolation.TZ},

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/Geographic3Dto2DTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/Geographic3Dto2DTest.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/Geographic3Dto2DTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/Geographic3Dto2DTest.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -34,7 +34,7 @@ import static org.opengis.test.Assert.*;
  * Tests the {@link Geographic3Dto2D} class.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -69,6 +69,18 @@ public final strictfp class Geographic3D
     }
 
     /**
+     * Tests {@link Geographic3Dto2D#redimension(int, int)}.
+     */
+    @Test
+    public void testRedimension() {
+        final Geographic3Dto2D provider = new Geographic3Dto2D();
+        assertSame  ("3 → 2", provider,                    provider.redimension(3, 2));
+        assertEquals("2 → 3", Geographic2Dto3D.class,      provider.redimension(2, 3).getClass());
+        assertEquals("3 → 3", GeographicRedimension.class, provider.redimension(3, 3).getClass());
+        assertEquals("2 → 2", GeographicRedimension.class, provider.redimension(2, 2).getClass());
+    }
+
+    /**
      * Creates a "Geographic 2D to 3D → Geocentric → Affine → Geographic → Geographic 3D to 2D" chain.
      * This method is used for integration tests.
      *

Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -19,10 +19,8 @@ package org.apache.sis.internal.referenc
 import java.util.Locale;
 import java.io.BufferedWriter;
 import java.io.IOException;
-import java.net.URL;
 import java.net.URISyntaxException;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.nio.file.Files;
 import javax.measure.quantity.Angle;
 import org.opengis.geometry.Envelope;
@@ -32,7 +30,6 @@ import org.apache.sis.referencing.operat
 import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.measure.Units;
-import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.opengis.test.Assert.*;
@@ -47,7 +44,7 @@ import static org.opengis.test.Assert.*;
  * @since   0.7
  * @module
  */
-public final strictfp class NADCONTest extends TestCase {
+public final strictfp class NADCONTest extends DatumShiftTestCase {
     /**
      * Returns the sample point for a step in the transformation from NAD27 to NAD83.
      * The sample point is the coordinate of Meades Ranch station, which was the point
@@ -102,12 +99,8 @@ public final strictfp class NADCONTest e
      */
     @Test
     public void testLoader() throws URISyntaxException, IOException, FactoryException, TransformException {
-        final URL latitudeShifts  = NADCONTest.class.getResource(TEST_FILE + ".laa");
-        final URL longitudeShifts = NADCONTest.class.getResource(TEST_FILE + ".loa");
-        assertNotNull("Test file \"" + TEST_FILE + ".laa\" not found.", latitudeShifts);
-        assertNotNull("Test file \"" + TEST_FILE + ".loa\" not found.", longitudeShifts);
-        testNADCON(Paths.get(latitudeShifts.toURI()),
-                   Paths.get(longitudeShifts.toURI()),
+        testNADCON(getResource(TEST_FILE + ".laa"),     // Latitude shifts
+                   getResource(TEST_FILE + ".loa"),     // Longitude shifts
                    -99.75, -98.0, 37.5, 39.75);
     }
 



Mime
View raw message