sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1737107 [3/6] - in /sis/trunk: ./ application/sis-console/src/main/artifact/conf/ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/ core/sis-metadata/src...
Date Wed, 30 Mar 2016 12:35:11 GMT
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/TimeDependentBWP.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/TimeDependentBWP.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/TimeDependentBWP.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/TimeDependentBWP.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.datum
 import java.util.Date;
 import org.opengis.metadata.extent.Extent;
 import org.opengis.referencing.datum.GeodeticDatum;
+import org.opengis.referencing.datum.PrimeMeridian;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.internal.util.DoubleDouble;
 
@@ -132,8 +133,8 @@ public class TimeDependentBWP extends Bu
      * Verifies parameters validity after initialization.
      */
     @Override
-    void verify() {
-        super.verify();
+    void verify(final PrimeMeridian pm) throws IllegalArgumentException {
+        super.verify(pm);
         ensureFinite("dtX", dtX);
         ensureFinite("dtY", dtY);
         ensureFinite("dtZ", dtZ);

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/package-info.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/package-info.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/package-info.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -40,6 +40,18 @@
  *       DefaultGeodeticDatum.getPositionVectorTransformation(GeodeticDatum, Extent)}</li>
  * </ul>
  *
+ * <div class="section">Datum shifts</div>
+ * Three classes are provided in support of coordinate transformations between different datums:
+ * <ul>
+ *   <li>{@link org.apache.sis.referencing.datum.BursaWolfParameters} performs an approximation
+ *       based on a translation, rotation and scale of geocentric coordinates.</li>
+ *   <li>{@link org.apache.sis.referencing.datum.TimeDependentBWP} is like {@code BursaWolfParameters},
+ *       but varies with time for taking in account the motion of plate tectonic.</li>
+ *   <li>{@link org.apache.sis.referencing.datum.DatumShiftGrid} is used for more accurate transformations
+ *       than what {@code BursaWolfParameters} allows, by interpolating the geographic or geocentric translations
+ *       in a grid (e.g. NADCON or NTv2) instead than apply the same transformation for every points.</li>
+ * </ul>
+ *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Cédric Briançon (Geomatys)
  * @since   0.4

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -640,6 +640,11 @@ public class CommonAuthorityFactory exte
                         return unit;
                     }
 
+                    @Override public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> ignored) {
+                        assert SI.METRE.equals(ignored) : ignored;
+                        return unit;
+                    }
+
                     @Override public boolean accept(CoordinateSystemAxis axis) {
                         return true;
                     }
@@ -647,6 +652,10 @@ public class CommonAuthorityFactory exte
                     @Override public AxisDirection getDirectionReplacement(AxisDirection direction) {
                         return direction;
                     }
+
+                    @Override public AxisDirection getDirectionReplacement(CoordinateSystemAxis axis, AxisDirection direction) {
+                        return direction;
+                    }
                 });
             }
             /*

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -26,6 +26,8 @@ import java.util.logging.Logger;
 import java.util.logging.LogRecord;
 import java.util.concurrent.atomic.AtomicReference;
 import java.lang.reflect.Constructor;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import javax.measure.unit.Unit;
 import javax.measure.quantity.Angle;
 import javax.measure.quantity.Length;
@@ -186,8 +188,9 @@ import org.apache.sis.xml.XML;
  * since localizations are applied by the {@link InternationalString#toString(Locale)} method.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @author  Guilhem Legal (Geomatys)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  */
 public class GeodeticObjectFactory extends AbstractFactory implements CRSFactory, CSFactory, DatumFactory, Parser {
@@ -1524,7 +1527,13 @@ public class GeodeticObjectFactory exten
             if (c == null) {
                 c = Class.forName("org.apache.sis.io.wkt.GeodeticObjectParser").asSubclass(Parser.class)
                          .getConstructor(Map.class, ObjectFactory.class, MathTransformFactory.class);
-                c.setAccessible(true);
+                final Constructor<?> cp = c;     // For allowing use in inner class or lambda expression.
+                AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                    @Override public Void run() {
+                        cp.setAccessible(true);
+                        return null;
+                    }
+                });
                 parserConstructor = c;
             }
             p = c.newInstance(defaultProperties, this, getMathTransformFactory());

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -751,7 +751,7 @@ public class MultiAuthoritiesFactory ext
                 if (code == null) {
                     message = Errors.format(Errors.Keys.MissingComponentInElement_2, s, "code");
                 } else {
-                    message = Errors.format(Errors.Keys.CanNotCreateObjectOfType_2, type, uri.type);
+                    message = Errors.format(Errors.Keys.CanNotCreateObjectAsInstanceOf_2, type, uri.type);
                 }
                 throw new NoSuchAuthorityCodeException(message, authority, code, s);
             }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -17,14 +17,21 @@
 package org.apache.sis.referencing.factory.sql;
 
 import java.util.Locale;
+import java.util.List;
+import java.util.Map;
+import java.util.LinkedHashMap;
 import java.sql.ResultSet;
 import javax.measure.unit.Unit;
 import javax.measure.unit.NonSI;
 import javax.measure.unit.SI;
 import javax.measure.converter.ConversionException;
+import org.opengis.util.FactoryException;
+import org.opengis.metadata.extent.Extent;
+import org.apache.sis.internal.referencing.ExtentSelector;
 import org.apache.sis.referencing.datum.BursaWolfParameters;
 import org.apache.sis.referencing.datum.TimeDependentBWP;
 import org.apache.sis.referencing.factory.FactoryDataException;
+import org.apache.sis.referencing.factory.GeodeticAuthorityFactory;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.measure.Units;
 
@@ -123,11 +130,22 @@ final class BursaWolfInfo {
     final int target;
 
     /**
+     * The value of {@code AREA_OF_USE_CODE}.
+     */
+    private final int domainOfValidity;
+
+    /**
+     * The domain of validity as an {@code Extent} object.
+     */
+    private Extent extent;
+
+    /**
      * Fills a structure with the specified values.
      */
-    BursaWolfInfo(final int operation, final int method, final int targetCRS) {
-        this.operation = operation;
-        this.method    = method;
+    BursaWolfInfo(final int operation, final int method, final int targetCRS, final int domainOfValidity) {
+        this.operation        = operation;
+        this.method           = method;
+        this.domainOfValidity = domainOfValidity;
         switch (targetCRS) {
             case TARGET_CRS: target = TARGET_DATUM; break;
             // More codes may be added in future SIS version.
@@ -155,4 +173,44 @@ final class BursaWolfInfo {
     public String toString() {
         return String.valueOf(operation);
     }
+
+    /**
+     * Gets the domain of validity. The result is cached.
+     *
+     * @param factory The factory to use for creating {@code Extent} instances.
+     */
+    Extent getDomainOfValidity(final GeodeticAuthorityFactory factory) throws FactoryException {
+        if (extent == null && domainOfValidity != 0) {
+            extent = factory.createExtent(String.valueOf(domainOfValidity));
+        }
+        return extent;
+    }
+
+    /**
+     * Given an array of {@code BursaWolfInfo} instances, retains only the instances having the largest
+     * domain of validity for each target datum. If two instances have the same domain of validity, the
+     * first one is retained. This presume that the instances have already been sorted for preference order
+     * before to invoke this method.
+     *
+     * @param factory     The factory to use for creating {@code Extent} instances.
+     * @param candidates  The Bursa-Wolf parameters candidates.
+     * @param addTo       Where to add the instances retained by this method.
+     */
+    static void filter(final GeodeticAuthorityFactory factory, final BursaWolfInfo[] candidates,
+            final List<BursaWolfInfo> addTo) throws FactoryException
+    {
+        final Map<Integer,ExtentSelector<BursaWolfInfo>> added = new LinkedHashMap<Integer,ExtentSelector<BursaWolfInfo>>();
+        for (BursaWolfInfo candidate : candidates) {
+            final Integer target = candidate.target;
+            ExtentSelector<BursaWolfInfo> selector = added.get(target);
+            if (selector == null) {
+                selector = new ExtentSelector<BursaWolfInfo>(null);
+                added.put(target, selector);
+            }
+            selector.evaluate(candidate.getDomainOfValidity(factory), candidate);
+        }
+        for (final ExtentSelector<BursaWolfInfo> select : added.values()) {
+            addTo.add(select.best());
+        }
+    }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -19,7 +19,6 @@ package org.apache.sis.referencing.facto
 import java.util.Set;
 import java.util.Map;
 import java.util.List;
-import java.util.HashSet;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedHashMap;
@@ -40,7 +39,6 @@ import java.sql.ResultSetMetaData;
 import java.sql.Statement;
 import java.sql.SQLException;
 import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.text.ParseException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -72,6 +70,7 @@ import org.apache.sis.internal.metadata.
 import org.apache.sis.internal.metadata.sql.SQLUtilities;
 import org.apache.sis.internal.referencing.DeprecatedCode;
 import org.apache.sis.internal.referencing.EPSGParameterDomain;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.system.Semaphores;
@@ -107,17 +106,21 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Localized;
 import org.apache.sis.util.Version;
+import org.apache.sis.util.Workaround;
+import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.collection.Containers;
 import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.measure.Units;
 
+import static org.apache.sis.util.Utilities.equalsIgnoreMetadata;
 import static org.apache.sis.internal.referencing.ServicesForMetadata.CONNECTION;
 
 // Branch-dependent imports
 import org.apache.sis.internal.jdk7.JDK7;
 import org.apache.sis.internal.jdk8.JDK8;
 import org.apache.sis.internal.jdk7.AutoCloseable;
+import org.apache.sis.internal.util.StandardDateFormat;
 
 
 /**
@@ -176,6 +179,7 @@ public class EPSGDataAccess extends Geod
      *
      * @see #replaceDeprecatedCS
      */
+    @Workaround(library = "EPSG:6401-6420", version = "8.9")        // Deprecated in 2002 but still present in 2016.
     private static final Map<Integer,Integer> DEPRECATED_CS = deprecatedCS();
     static Map<Integer,Integer> deprecatedCS() {
         final Map<Integer,Integer> m = new HashMap<Integer,Integer>(24);
@@ -1640,7 +1644,7 @@ addURIs:    for (int i=0; ; i++) {
                         properties = new HashMap<String,Object>(properties);         // Protect from changes
                         final Ellipsoid ellipsoid    = owner.createEllipsoid    (getString(code, result, 10));
                         final PrimeMeridian meridian = owner.createPrimeMeridian(getString(code, result, 11));
-                        final BursaWolfParameters[] param = createBursaWolfParameters(epsg);
+                        final BursaWolfParameters[] param = createBursaWolfParameters(meridian, epsg);
                         if (param != null) {
                             properties.put(DefaultGeodeticDatum.BURSA_WOLF_KEY, param);
                         }
@@ -1665,7 +1669,7 @@ addURIs:    for (int i=0; ; i++) {
                             throw new FactoryDataException(error().getString(Errors.Keys.DatumOriginShallBeDate));
                         }
                         if (dateFormat == null) {
-                            dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.CANADA);
+                            dateFormat = new StandardDateFormat();
                             dateFormat.setCalendar(getCalendar());          // Use UTC timezone.
                         }
                         try {
@@ -1713,10 +1717,13 @@ addURIs:    for (int i=0; ; i++) {
      * That legacy format had a {@code TOWGS84} element which needs the information provided by this method.
      * Note that {@code TOWGS84} is a deprecated element as of WKT 2 (ISO 19162).</p>
      *
-     * @param  code The EPSG code of the {@link GeodeticDatum}.
+     * @param  meridian The source datum prime meridian, used for discarding any target datum using a different meridian.
+     * @param  code The EPSG code of the source {@link GeodeticDatum}.
      * @return an array of Bursa-Wolf parameters, or {@code null}.
      */
-    private BursaWolfParameters[] createBursaWolfParameters(final Integer code) throws SQLException, FactoryException {
+    private BursaWolfParameters[] createBursaWolfParameters(final PrimeMeridian meridian, final Integer code)
+            throws SQLException, FactoryException
+    {
         /*
          * We do not provide TOWGS84 information for WGS84 itself or for any other datum on our list of target datum,
          * in order to avoid infinite recursivity. The 'ensureNonRecursive' call is an extra safety check which should
@@ -1729,7 +1736,8 @@ addURIs:    for (int i=0; ; i++) {
         ResultSet result = executeQuery("BursaWolfParametersSet",
                 "SELECT COORD_OP_CODE," +
                       " COORD_OP_METHOD_CODE," +
-                      " TARGET_CRS_CODE" +
+                      " TARGET_CRS_CODE," +
+                      " AREA_OF_USE_CODE"+
                 " FROM [Coordinate_Operation]" +
                " WHERE DEPRECATED=0" +           // Do not put spaces around "=" - SQLTranslator searches for this exact match.
                  " AND TARGET_CRS_CODE = "       + BursaWolfInfo.TARGET_CRS +
@@ -1743,7 +1751,8 @@ addURIs:    for (int i=0; ; i++) {
                 final BursaWolfInfo info = new BursaWolfInfo(
                         getInteger(code, result, 1),                // Operation
                         getInteger(code, result, 2),                // Method
-                        getInteger(code, result, 3));               // Target datum
+                        getInteger(code, result, 3),                // Target datum
+                        getInteger(code, result, 4));               // Domain of validity
                 if (info.target != code) {                          // Paranoiac check.
                     bwInfos.add(info);
                 }
@@ -1764,12 +1773,7 @@ addURIs:    for (int i=0; ; i++) {
             final BursaWolfInfo[] codes = bwInfos.toArray(new BursaWolfInfo[size]);
             sort("Coordinate_Operation", codes);
             bwInfos.clear();
-            final Set<Integer> added = new HashSet<Integer>();
-            for (BursaWolfInfo candidate : codes) {
-                if (added.add(candidate.target)) {
-                    bwInfos.add(candidate);
-                }
-            }
+            BursaWolfInfo.filter(owner, codes, bwInfos);
             size = bwInfos.size();
         }
         /*
@@ -1777,6 +1781,7 @@ addURIs:    for (int i=0; ; i++) {
          */
         final BursaWolfParameters[] parameters = new BursaWolfParameters[size];
         final Locale locale = getLocale();
+        int count = 0;
         for (int i=0; i<size; i++) {
             final BursaWolfInfo info = bwInfos.get(i);
             final GeodeticDatum datum;
@@ -1786,7 +1791,17 @@ addURIs:    for (int i=0; ; i++) {
             } finally {
                 endOfRecursivity(BursaWolfParameters.class, code);
             }
-            final BursaWolfParameters bwp = new BursaWolfParameters(datum, null);
+            /*
+             * Accept only Bursa-Wolf parameters between datum that use the same prime meridian.
+             * This is for avoiding ambiguity about whether longitude rotation should be applied
+             * before or after the datum change. This check is useless for EPSG dataset 8.9 since
+             * all datum seen by this method use Greenwich. But we nevertheless perform this check
+             * as a safety for future evolution or customized EPSG dataset.
+             */
+            if (!equalsIgnoreMetadata(meridian, datum.getPrimeMeridian())) {
+                continue;
+            }
+            final BursaWolfParameters bwp = new BursaWolfParameters(datum, info.getDomainOfValidity(owner));
             result = executeQuery("BursaWolfParameters",
                 "SELECT PARAMETER_CODE," +
                       " PARAMETER_VALUE," +
@@ -1809,9 +1824,9 @@ addURIs:    for (int i=0; ; i++) {
                 // except for the sign of rotation parameters.
                 bwp.reverseRotation();
             }
-            parameters[i] = bwp;
+            parameters[count++] = bwp;
         }
-        return parameters;
+        return ArraysExt.resize(parameters, count);
     }
 
     /**
@@ -2313,7 +2328,8 @@ addURIs:    for (int i=0; ; i++) {
                     throw new FactoryDataException(exception.getLocalizedMessage(), exception);
                 }
                 final AxisName an = getAxisName(nameCode);
-                final CoordinateSystemAxis axis = owner.csFactory.createCoordinateSystemAxis(createProperties("Coordinate Axis", an.name, epsg, an.description, false),
+                final CoordinateSystemAxis axis = owner.csFactory.createCoordinateSystemAxis(
+                        createProperties("Coordinate Axis", an.name, epsg, an.description, false),
                         abbreviation, direction, owner.createUnit(unit));
                 returnValue = ensureSingleton(axis, returnValue, code);
             }
@@ -2832,7 +2848,7 @@ next:               while (r.next()) {
                         sourceDimensions = sourceCRS.getCoordinateSystem().getDimension();
                     } else {
                         sourceCRS = null;
-                        sourceDimensions = 2;       // Acceptable default for projections only.
+                        sourceDimensions = 2;           // Acceptable default for projections only.
                         isDimensionKnown = false;
                     }
                     if (targetCode != null) {
@@ -2840,7 +2856,7 @@ next:               while (r.next()) {
                         targetDimensions = targetCRS.getCoordinateSystem().getDimension();
                     } else {
                         targetCRS = null;
-                        targetDimensions = 2;       // Acceptable default for projections only.
+                        targetDimensions = 2;           // Acceptable default for projections only.
                         isDimensionKnown = false;
                     }
                     /*
@@ -2930,10 +2946,8 @@ next:               while (r.next()) {
                         final MathTransform mt;
                         final MathTransformFactory mtFactory = owner.mtFactory;
                         if (mtFactory instanceof DefaultMathTransformFactory) {
-                            DefaultMathTransformFactory.Context context = new DefaultMathTransformFactory.Context();
-                            context.setSource(sourceCRS);
-                            context.setTarget(targetCRS);
-                            mt = ((DefaultMathTransformFactory) mtFactory).createParameterizedTransform(parameters, context);
+                            mt = ((DefaultMathTransformFactory) mtFactory).createParameterizedTransform(parameters,
+                                    ReferencingUtilities.createTransformContext(sourceCRS, targetCRS, null));
                         } else {
                             // Fallback for non-SIS implementations. Work for map projections but not for Molodensky.
                             mt = mtFactory.createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem());

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -350,8 +350,9 @@ public class EPSGFactory extends Concurr
      *     If no provider is specified, then this method will search on the classpath (with {@link java.util.ServiceLoader})
      *     for user-provided implementations of {@code InstallationScriptProvider}.
      *     If no user-specified provider is found, then this method will search for
-     *     {@code "EPSG_Tables.sql"}, {@code "EPSG_Data.sql"} and {@code "EPSG_FKeys.sql"} files in the
-     *     {@code $SIS_DATA/Databases/ExternalSources} directory.</li>
+     *     {@code "EPSG_*Tables.sql"}, {@code "EPSG_*Data.sql"} and {@code "EPSG_*FKeys.sql"} files in the
+     *     {@code $SIS_DATA/Databases/ExternalSources} directory where {@code *} stands for any characters
+     *     provided that there is no ambiguity.</li>
      * </ul>
      *
      * <p><b>Legal constraint:</b>
@@ -374,28 +375,33 @@ public class EPSGFactory extends Concurr
             if (ac) {
                 connection.setAutoCommit(false);
             }
-            boolean success = false;
             try {
-                if (!"".equals(schema)) {                                               // Schema may be null.
-                    installer.setSchema(schema != null ? schema : Constants.EPSG);
-                    if (catalog != null && !catalog.isEmpty()) {
-                        installer.prependNamespace(catalog);
+                boolean success = false;
+                try {
+                    if (!"".equals(schema)) {                                           // Schema may be null.
+                        installer.setSchema(schema != null ? schema : Constants.EPSG);
+                        if (catalog != null && !catalog.isEmpty()) {
+                            installer.prependNamespace(catalog);
+                        }
                     }
-                }
-                installer.run(scriptProvider);
-                success = true;
-            } finally {
-                if (ac) {
-                    if (success) {
-                        connection.commit();
-                    } else {
-                        connection.rollback();
+                    installer.run(scriptProvider, locale);
+                    success = true;
+                } finally {
+                    if (ac) {
+                        if (success) {
+                            connection.commit();
+                        } else {
+                            connection.rollback();
+                        }
+                        connection.setAutoCommit(true);
                     }
-                    connection.setAutoCommit(true);
-                }
-                if (!success) {
-                    installer.logFailure(locale);
                 }
+            } catch (IOException e) {
+                installer.logFailure(locale, e);
+                throw e;
+            } catch (SQLException e) {
+                installer.logFailure(locale, e);
+                throw e;
             }
         } finally {
             installer.close();

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -32,11 +32,10 @@ import java.io.BufferedReader;
 import org.apache.sis.util.StringBuilders;
 import org.apache.sis.internal.metadata.sql.ScriptRunner;
 import org.apache.sis.internal.metadata.sql.SQLUtilities;
-import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.util.Fallback;
 import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.resources.Messages;
-import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.logging.PerformanceLevel;
 import org.apache.sis.setup.InstallationResources;
 
@@ -251,12 +250,12 @@ final class EPSGInstaller extends Script
      * @throws IOException if an error occurred while reading an input.
      * @throws SQLException if an error occurred while executing a SQL statement.
      */
-    public void run(InstallationResources scriptProvider) throws SQLException, IOException {
+    public void run(InstallationResources scriptProvider, final Locale locale) throws SQLException, IOException {
         long time = System.nanoTime();
-        log(Messages.getResources(null).getLogRecord(Level.INFO, Messages.Keys.CreatingSchema_2, EPSG,
-                SQLUtilities.getSimplifiedURL(getConnection().getMetaData())));
+        InstallationScriptProvider.log(Messages.getResources(locale).getLogRecord(Level.INFO,
+                Messages.Keys.CreatingSchema_2, EPSG, SQLUtilities.getSimplifiedURL(getConnection().getMetaData())));
         if (scriptProvider == null) {
-            scriptProvider = lookupProvider();
+            scriptProvider = lookupProvider(locale);
         }
         final String[] scripts = scriptProvider.getResourceNames(EPSG);
         int numRows = 0;
@@ -269,7 +268,7 @@ final class EPSGInstaller extends Script
             }
         }
         time = System.nanoTime() - time;
-        log(Messages.getResources(null).getLogRecord(
+        InstallationScriptProvider.log(Messages.getResources(locale).getLogRecord(
                 PerformanceLevel.forDuration(time, TimeUnit.NANOSECONDS),
                 Messages.Keys.InsertDuration_2, numRows, time / 1E9f));
     }
@@ -277,7 +276,7 @@ final class EPSGInstaller extends Script
     /**
      * Searches for a SQL script provider on the classpath before to fallback on the default provider.
      */
-    private static InstallationResources lookupProvider() {
+    private static InstallationResources lookupProvider(final Locale locale) throws IOException {
         InstallationResources fallback = null;
         for (final InstallationResources provider : ServiceLoader.load(InstallationResources.class)) {
             if (provider.getAuthorities().contains(EPSG)) {
@@ -287,7 +286,7 @@ final class EPSGInstaller extends Script
                 fallback = provider;
             }
         }
-        return (fallback != null) ? fallback : new InstallationScriptProvider.Default();
+        return (fallback != null) ? fallback : new InstallationScriptProvider.Default(locale);
     }
 
     /**
@@ -296,21 +295,13 @@ final class EPSGInstaller extends Script
      * lets the exception propagate. Another code (for example {@link org.apache.sis.referencing.CRS#forCode(String)})
      * may catch that exception and log another record with the exception message.
      */
-    final void logFailure(final Locale locale) {
+    final void logFailure(final Locale locale, final Exception cause) {
         String message = Messages.getResources(locale).getString(Messages.Keys.CanNotCreateSchema_1, EPSG);
         String status = status(locale);
         if (status != null) {
             message = message + ' ' + status;
         }
-        log(new LogRecord(Level.WARNING, message));
-    }
-
-    /**
-     * Logs the given record. This method pretend that the record has been logged by
-     * {@code EPSGFactory.install(…)} because it is the public API using this class.
-     */
-    private static void log(final LogRecord record) {
-        record.setLoggerName(Loggers.CRS_FACTORY);
-        Logging.log(EPSGFactory.class, "install", record);
+        message = Exceptions.formatChainedMessages(locale, message, cause);
+        InstallationScriptProvider.log(new LogRecord(Level.WARNING, message));
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -19,6 +19,8 @@ package org.apache.sis.referencing.facto
 import java.util.Set;
 import java.util.Locale;
 import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import java.sql.Connection;
 import java.io.BufferedReader;
 import java.io.LineNumberReader;
@@ -28,14 +30,17 @@ import java.io.IOException;
 import java.io.FileNotFoundException;
 import java.nio.charset.Charset;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.setup.InstallationResources;
 import org.apache.sis.internal.system.DataDirectory;
+import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.util.Constants;
 
 // Branch-dependent imports
 import org.apache.sis.internal.jdk7.StandardCharsets;
+import org.apache.sis.internal.jdk7.DirectoryStream;
 import org.apache.sis.internal.jdk7.Files;
 import org.apache.sis.internal.jdk7.Path;
 
@@ -261,17 +266,27 @@ public abstract class InstallationScript
      */
     protected abstract InputStream openStream(final String name) throws IOException;
 
+    /**
+     * Logs the given record. This method pretend that the record has been logged by
+     * {@code EPSGFactory.install(…)} because it is the public API using this class.
+     */
+    static void log(final LogRecord record) {
+        record.setLoggerName(Loggers.CRS_FACTORY);
+        Logging.log(EPSGFactory.class, "install", record);
+    }
+
 
 
 
     /**
      * The default implementation which use the scripts in the {@code $SIS_DATA/Databases/ExternalSources}
-     * directory, if present. This class expects the files to have those exact names:
+     * directory, if present. This class expects the files to have those exact names where {@code *} stands
+     * for any characters provided that there is no ambiguity:
      *
      * <ul>
-     *   <li>{@code EPSG_Tables.sql}</li>
-     *   <li>{@code EPSG_Data.sql}</li>
-     *   <li>{@code EPSG_FKeys.sql}</li>
+     *   <li>{@code EPSG_*Tables.sql}</li>
+     *   <li>{@code EPSG_*Data.sql}</li>
+     *   <li>{@code EPSG_*FKeys.sql}</li>
      * </ul>
      *
      * @author  Martin Desruisseaux (Geomatys)
@@ -283,27 +298,61 @@ public abstract class InstallationScript
         /**
          * The directory containing the scripts, or {@code null} if it does not exist.
          */
-        private final Path directory;
+        private Path directory;
+
+        /**
+         * Index of the first real file in the array given to the constructor.
+         * We set the value to 1 for skipping the {@code PREPARE} pseudo-file.
+         */
+        private static final int FIRST_FILE = 1;
 
         /**
          * Creates a default provider.
          */
-        Default() {
+        Default(final Locale locale) throws IOException {
             super(Constants.EPSG,
                     PREPARE,
-                    "EPSG_Tables.sql",
-                    "EPSG_Data.sql",
-                    "EPSG_FKeys.sql",
+                    "Tables",
+                    "Data",
+                    "FKeys",
                     FINISH);
 
             Path dir = DataDirectory.DATABASES.getDirectory();
             if (dir != null) {
                 dir = dir.resolve("ExternalSources");
-                if (!Files.isRegularFile(dir.resolve("EPSG_Tables.sql"))) {
-                    dir = null;
+                if (Files.isDirectory(dir)) {
+                    final String[] resources = super.resources;
+                    final String[] found = new String[resources.length - FIRST_FILE - 1];
+                    DirectoryStream stream = Files.newDirectoryStream(dir, "EPSG_*.sql");
+                    try {
+                        for (final Path path : stream) {
+                            final String name = path.getFileName().toString();
+                            for (int i=0; i<found.length; i++) {
+                                final String part = resources[FIRST_FILE + i];
+                                if (name.contains(part)) {
+                                    if (found[i] != null) {
+                                        log(Errors.getResources(locale)
+                                                  .getLogRecord(Level.WARNING, Errors.Keys.DuplicatedElement_1, part));
+                                        return;                         // Stop the search because of duplicated file.
+                                    }
+                                    found[i] = name;
+                                }
+                            }
+                        }
+                    } finally {
+                        stream.close();
+                    }
+                    for (int i=0; i<found.length; i++) {
+                        final String file = found[i];
+                        if (file != null) {
+                            resources[FIRST_FILE + i] = file;
+                        } else {
+                            dir = null;
+                        }
+                    }
+                    directory = dir;
                 }
             }
-            directory = dir;
         }
 
         /**
@@ -337,7 +386,7 @@ public abstract class InstallationScript
          */
         @Override
         protected InputStream openStream(final String name) throws IOException {
-            return Files.newInputStream(directory.resolve(name));
+            return (directory != null) ? Files.newInputStream(directory.resolve(name)) : null;
         }
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -31,6 +31,7 @@ import org.opengis.metadata.quality.Posi
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.crs.GeneralDerivedCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.ConcatenatedOperation;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.referencing.operation.MathTransform;
@@ -650,16 +651,29 @@ check:      for (int isTarget=0; ; isTar
      * {@linkplain #transform}, if possible. If no descriptor can be inferred from the math transform,
      * then this method fallback on the {@link OperationMethod} parameters.
      */
-    ParameterDescriptorGroup getParameterDescriptors() throws UnsupportedOperationException {
-        MathTransform mt = transform;
-        while (mt != null) {
-            if (mt instanceof Parameterized) {
+    ParameterDescriptorGroup getParameterDescriptors() {
+        ParameterDescriptorGroup descriptor = getParameterDescriptors(transform);
+        if (descriptor == null) {
+            final OperationMethod method = getMethod();
+            if (method != null) {
+                descriptor = method.getParameters();
+            }
+        }
+        return descriptor;
+    }
+
+    /**
+     * Returns the parameter descriptors for the given transform, or {@code null} if unknown.
+     */
+    static ParameterDescriptorGroup getParameterDescriptors(MathTransform transform) {
+        while (transform != null) {
+            if (transform instanceof Parameterized) {
                 final ParameterDescriptorGroup param;
                 if (Semaphores.queryAndSet(Semaphores.ENCLOSED_IN_OPERATION)) {
-                    throw new AssertionError(); // Should never happen.
+                    throw new AssertionError();                                     // Should never happen.
                 }
                 try {
-                    param = ((Parameterized) mt).getParameterDescriptors();
+                    param = ((Parameterized) transform).getParameterDescriptors();
                 } finally {
                     Semaphores.clear(Semaphores.ENCLOSED_IN_OPERATION);
                 }
@@ -667,14 +681,13 @@ check:      for (int isTarget=0; ; isTar
                     return param;
                 }
             }
-            if (mt instanceof PassThroughTransform) {
-                mt = ((PassThroughTransform) mt).getSubTransform();
+            if (transform instanceof PassThroughTransform) {
+                transform = ((PassThroughTransform) transform).getSubTransform();
             } else {
                 break;
             }
         }
-        final OperationMethod method = getMethod();
-        return (method != null) ? method.getParameters() : null;
+        return null;
     }
 
     /**
@@ -685,13 +698,13 @@ check:      for (int isTarget=0; ; isTar
      * @throws UnsupportedOperationException if the parameter values can not
      *         be determined for the current math transform implementation.
      */
-    ParameterValueGroup getParameterValues() {
+    ParameterValueGroup getParameterValues() throws UnsupportedOperationException {
         MathTransform mt = transform;
         while (mt != null) {
             if (mt instanceof Parameterized) {
                 final ParameterValueGroup param;
                 if (Semaphores.queryAndSet(Semaphores.ENCLOSED_IN_OPERATION)) {
-                    throw new AssertionError(); // Should never happen.
+                    throw new AssertionError();                                     // Should never happen.
                 }
                 try {
                     param = ((Parameterized) mt).getParameterValues();
@@ -844,32 +857,45 @@ check:      for (int isTarget=0; ; isTar
     protected String formatTo(final Formatter formatter) {
         super.formatTo(formatter);
         formatter.newLine();
-        append(formatter, getSourceCRS(), WKTKeywords.SourceCRS);
-        append(formatter, getTargetCRS(), WKTKeywords.TargetCRS);
-        formatter.append(DefaultOperationMethod.castOrCopy(getMethod()));
-        ParameterValueGroup parameters;
-        try {
-            parameters = getParameterValues();
-        } catch (UnsupportedOperationException e) {
-            final IdentifiedObject c = getParameterDescriptors();
-            formatter.setInvalidWKT(c != null ? c : this, e);
-            parameters = null;
+        /*
+         * If the WKT is a component of a ConcatenatedOperation, do not format the source and target CRS.
+         * This decision SIS-specific since the WKT 2 specification does not define concatenated operations.
+         * The choice of content to omit may change in any future version.
+         */
+        final boolean isComponent = (formatter.getEnclosingElement(1) instanceof ConcatenatedOperation);
+        if (!isComponent) {
+            append(formatter, getSourceCRS(), WKTKeywords.SourceCRS);
+            append(formatter, getTargetCRS(), WKTKeywords.TargetCRS);
         }
-        if (parameters != null) {
-            formatter.newLine();
-            for (final GeneralParameterValue param : parameters.values()) {
-                WKTUtilities.append(param, formatter);
+        final OperationMethod method = getMethod();
+        if (method != null) {
+            formatter.append(DefaultOperationMethod.castOrCopy(method));
+            ParameterValueGroup parameters;
+            try {
+                parameters = getParameterValues();
+            } catch (UnsupportedOperationException e) {
+                final IdentifiedObject c = getParameterDescriptors();
+                formatter.setInvalidWKT(c != null ? c : this, e);
+                parameters = null;
             }
-        }
-        append(formatter, getInterpolationCRS(), WKTKeywords.InterpolationCRS);
-        final double accuracy = getLinearAccuracy();
-        if (accuracy > 0) {
-            formatter.append(new FormattableObject() {
-                @Override protected String formatTo(final Formatter formatter) {
-                    formatter.append(accuracy);
-                    return WKTKeywords.OperationAccuracy;
+            if (parameters != null) {
+                formatter.newLine();
+                for (final GeneralParameterValue param : parameters.values()) {
+                    WKTUtilities.append(param, formatter);
                 }
-            });
+            }
+        }
+        if (!isComponent) {
+            append(formatter, getInterpolationCRS(), WKTKeywords.InterpolationCRS);
+            final double accuracy = getLinearAccuracy();
+            if (accuracy > 0) {
+                formatter.append(new FormattableObject() {
+                    @Override protected String formatTo(final Formatter formatter) {
+                        formatter.append(accuracy);
+                        return WKTKeywords.OperationAccuracy;
+                    }
+                });
+            }
         }
         if (formatter.getConvention().majorVersion() == 1) {
             formatter.setInvalidWKT(this, null);

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -225,14 +225,14 @@ class AbstractSingleOperation extends Ab
          * ignoring null java.lang.Integer instances.  We do not specify whether the method
          * dimensions should include the interpolation dimensions or not, so we accept both.
          */
-        int isTarget = 0;   // 0 == false: the wrong dimension is the source one.
+        int isTarget = 0;               // 0 == false: the wrong dimension is the source one.
         if (expected == null || (actual == expected) || (actual == expected + interpDim)) {
             actual = transform.getTargetDimensions();
             expected = method.getTargetDimensions();
             if (expected == null || (actual == expected) || (actual == expected + interpDim)) {
                 return;
             }
-            isTarget = 1;   // 1 == true: the wrong dimension is the target one.
+            isTarget = 1;               // 1 == true: the wrong dimension is the target one.
         }
         /*
          * At least one dimension does not match.  In principle this is an error, but we make an exception for the

Copied: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java (from r1737105, sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java)
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java?p2=sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java&p1=sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java&r1=1737105&r2=1737107&rev=1737107&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -720,10 +720,10 @@ public class CoordinateOperationInferenc
                 if (descriptor != null) {
                     final Identifier name = descriptor.getName();
                     if (name != null) {
-                        method = factory.getOperationMethod(name.getCode());
+                        method = factorySIS.getOperationMethod(name.getCode());
                     }
                     if (method == null) {
-                        method = factory.createOperationMethod(properties,
+                        method = factorySIS.createOperationMethod(properties,
                                 sourceCRS.getCoordinateSystem().getDimension(),
                                 targetCRS.getCoordinateSystem().getDimension(),
                                 descriptor);

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -41,6 +41,7 @@ import org.apache.sis.util.collection.Co
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.io.wkt.Formatter;
 
 import static org.apache.sis.util.Utilities.deepEquals;
 
@@ -334,6 +335,23 @@ final class DefaultConcatenatedOperation
         return super.computeHashCode() + 37 * Objects.hashCode(operations);
     }
 
+    /**
+     * Formats this coordinate operation in pseudo-WKT. This is specific to Apache SIS since
+     * there is no concatenated operation in the Well Known Text (WKT) version 2 format.
+     *
+     * @param  formatter The formatter to use.
+     * @return {@code "ConcatenatedOperation"}.
+     */
+    @Override
+    protected String formatTo(final Formatter formatter) {
+        super.formatTo(formatter);
+        for (final CoordinateOperation component : operations) {
+            formatter.append(castOrCopy(component));
+        }
+        formatter.setInvalidWKT(this, null);
+        return "ConcatenatedOperation";
+    }
+
 
 
 

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -254,12 +254,12 @@ public class DefaultConversion extends A
                  * CoordinateReferenceSystem because the targetCRS is typically under construction when this
                  * method in invoked, and attempts to use it can cause NullPointerException.
                  */
-                final DefaultMathTransformFactory.Context context = new DefaultMathTransformFactory.Context();
-                context.setSource(source);
+                final DefaultMathTransformFactory.Context context;
                 if (target instanceof GeneralDerivedCRS) {
+                    context = ReferencingUtilities.createTransformContext(source, null, null);
                     context.setTarget(target.getCoordinateSystem());    // Using 'target' would be unsafe here.
                 } else {
-                    context.setTarget(target);
+                    context = ReferencingUtilities.createTransformContext(source, target, null);
                 }
                 transform = ((DefaultMathTransformFactory) factory).createParameterizedTransform(parameters, context);
                 parameters = Parameters.unmodifiable(context.getCompletedParameters());

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.opera
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Collections;
+import java.util.List;
 import org.opengis.util.FactoryException;
 import org.opengis.util.NoSuchIdentifierException;
 import org.opengis.parameter.ParameterValueGroup;
@@ -28,11 +29,13 @@ import org.opengis.referencing.Identifie
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.ProjectedCRS;
+import org.opengis.referencing.crs.SingleCRS;
+import org.opengis.referencing.datum.Datum;
 import org.apache.sis.internal.referencing.MergedProperties;
 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.Utilities;
+import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
 import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
 import org.apache.sis.util.collection.WeakHashSet;
@@ -42,6 +45,7 @@ import org.apache.sis.util.resources.Err
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.NullArgumentException;
+import org.apache.sis.util.Utilities;
 
 
 /**
@@ -68,7 +72,7 @@ import org.apache.sis.util.NullArgumentE
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  */
 public class DefaultCoordinateOperationFactory extends AbstractFactory implements CoordinateOperationFactory {
@@ -149,7 +153,7 @@ public class DefaultCoordinateOperationF
      *
      * @return The underlying math transform factory.
      */
-    private MathTransformFactory getMathTransformFactory() {
+    final MathTransformFactory getMathTransformFactory() {
         MathTransformFactory factory = mtFactory;
         if (factory == null) {
             mtFactory = factory = DefaultFactories.forBuildin(MathTransformFactory.class);
@@ -158,6 +162,18 @@ public class DefaultCoordinateOperationF
     }
 
     /**
+     * Returns the Apache SIS implementation of math transform factory.
+     * This method is used only when we need SIS-specific methods.
+     */
+    final DefaultMathTransformFactory getDefaultMathTransformFactory() {
+        MathTransformFactory factory = getMathTransformFactory();
+        if (factory instanceof DefaultMathTransformFactory) {
+            return (DefaultMathTransformFactory) factory;
+        }
+        return DefaultFactories.forBuildin(MathTransformFactory.class, DefaultMathTransformFactory.class);
+    }
+
+    /**
      * 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"}).
@@ -312,6 +328,39 @@ public class DefaultCoordinateOperationF
     }
 
     /**
+     * Returns {@code true} if the given CRS are using equivalent (ignoring metadata) datum.
+     * If the CRS are {@link CompoundCRS}, then this method verifies that all datum in the
+     * target CRS exists in the source CRS, but not necessarily in the same order.
+     * The target CRS may have less datum than the source CRS.
+     *
+     * @param sourceCRS The target CRS.
+     * @param targetCRS The source CRS.
+     * @return {@code true} if all datum in the {@code targetCRS} exists in the {@code sourceCRS}.
+     */
+    private static boolean isConversion(final CoordinateReferenceSystem sourceCRS,
+                                        final CoordinateReferenceSystem targetCRS)
+    {
+        List<SingleCRS> components = CRS.getSingleComponents(sourceCRS);
+        int n = components.size();                      // Number of remaining datum from sourceCRS to verify.
+        final Datum[] datum = new Datum[n];
+        for (int i=0; i<n; i++) {
+            datum[i] = components.get(i).getDatum();
+        }
+        components = CRS.getSingleComponents(targetCRS);
+next:   for (int i=components.size(); --i >= 0;) {
+            final Datum d = components.get(i).getDatum();
+            for (int j=n; --j >= 0;) {
+                if (Utilities.equalsIgnoreMetadata(d, datum[j])) {
+                    System.arraycopy(datum, j+1, datum, j, --n - j);  // Remove the datum from the list.
+                    continue next;
+                }
+            }
+            return false;                               // Datum from 'targetCRS' not found in 'sourceCRS'.
+        }
+        return true;
+    }
+
+    /**
      * Creates a transformation or conversion from the given properties.
      * This method infers by itself if the operation to create is a
      * {@link Transformation}, a {@link Conversion} or a {@link Projection} sub-type
@@ -399,7 +448,7 @@ public class DefaultCoordinateOperationF
         }
         if (method instanceof DefaultOperationMethod) {
             final Class<? extends SingleOperation> c = ((DefaultOperationMethod) method).getOperationType();
-            if (c != null) {  // Paranoiac check (above method should not return null).
+            if (c != null) {                        // Paranoiac check (above method should not return null).
                 if (baseType.isAssignableFrom(c)) {
                     baseType = c;
                 } else if (!c.isAssignableFrom(baseType)) {
@@ -422,7 +471,7 @@ public class DefaultCoordinateOperationF
          * could be different, which we want to allow.
          */
         if (baseType == SingleOperation.class) {
-            if (OperationPathFinder.isConversion(sourceCRS, targetCRS)) {
+            if (isConversion(sourceCRS, targetCRS)) {
                 if (interpolationCRS == null && sourceCRS instanceof GeographicCRS
                                              && targetCRS instanceof ProjectedCRS)
                 {
@@ -471,7 +520,7 @@ public class DefaultCoordinateOperationF
             op = new AbstractSingleOperation(properties, sourceCRS, targetCRS, interpolationCRS, method, transform);
         }
         if (!baseType.isInstance(op)) {
-            throw new FactoryException(Errors.format(Errors.Keys.CanNotInstantiate_1, baseType));
+            throw new FactoryException(Errors.format(Errors.Keys.CanNotCreateObjectAsInstanceOf_2, baseType, op.getName()));
         }
         return pool.unique(op);
     }
@@ -526,57 +575,87 @@ public class DefaultCoordinateOperationF
     }
 
     /**
-     * Not yet implemented.
-     *
-     * @param  sourceCRS Input coordinate reference system.
-     * @param  targetCRS Output coordinate reference system.
-     * @param  method the algorithmic method for conversion or transformation.
-     * @return A coordinate operation from {@code sourceCRS} to {@code targetCRS}.
-     * @throws FactoryException if the operation creation failed.
+     * Finds or creates an operation for conversion or transformation between two coordinate reference systems.
+     * If an operation exists, it is returned. If more than one operation exists, the operation having the widest
+     * domain of validity is returned. If no operation exists, then an exception is thrown.
+     *
+     * <p>The default implementation delegates to <code>{@linkplain #createOperation(CoordinateReferenceSystem,
+     * CoordinateReferenceSystem, CoordinateOperationContext) createOperation}(sourceCRS, targetCRS, null)}</code>.</p>
+     *
+     * @param  sourceCRS  input coordinate reference system.
+     * @param  targetCRS  output coordinate reference system.
+     * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}.
+     * @throws OperationNotFoundException if no operation path was found from {@code sourceCRS} to {@code targetCRS}.
+     * @throws FactoryException if the operation creation failed for some other reason.
      */
     @Override
     public CoordinateOperation createOperation(final CoordinateReferenceSystem sourceCRS,
-                                               final CoordinateReferenceSystem targetCRS,
-                                               final OperationMethod method)
-            throws FactoryException
+                                               final CoordinateReferenceSystem targetCRS)
+            throws OperationNotFoundException, FactoryException
     {
-        return delegate().createOperation(sourceCRS, targetCRS, method);
+        return createOperation(sourceCRS, targetCRS, (CoordinateOperationContext) null);
     }
 
     /**
-     * Not yet implemented.
+     * Finds or creates an operation for conversion or transformation between two coordinate reference systems.
+     * If an operation exists, it is returned. If more than one operation exists, then the operation having the
+     * widest intersection between its {@linkplain AbstractCoordinateOperation#getDomainOfValidity() domain of
+     * validity} and the {@linkplain CoordinateOperationContext#getAreaOfInterest() area of interest} is returned.
+     *
+     * <p>The default implementation is as below:</p>
      *
-     * @param  sourceCRS Input coordinate reference system.
-     * @param  targetCRS Output coordinate reference system.
-     * @return A coordinate operation from {@code sourceCRS} to {@code targetCRS}.
+     * {@preformat java
+     *   return new CoordinateOperationInference(this, context).createOperation(sourceCRS, targetCRS);
+     * }
+     *
+     * Subclasses can override this method if they need, for example, to use a custom
+     * {@link CoordinateOperationInference} implementation.
+     *
+     * @param  sourceCRS  input coordinate reference system.
+     * @param  targetCRS  output coordinate reference system.
+     * @param  context    area of interest and desired accuracy, or {@code null}.
+     * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}.
      * @throws OperationNotFoundException if no operation path was found from {@code sourceCRS} to {@code targetCRS}.
      * @throws FactoryException if the operation creation failed for some other reason.
+     *
+     * @see CoordinateOperationInference
+     *
+     * @since 0.7
      */
-    @Override
     public CoordinateOperation createOperation(final CoordinateReferenceSystem sourceCRS,
-                                               final CoordinateReferenceSystem targetCRS)
+                                               final CoordinateReferenceSystem targetCRS,
+                                               final CoordinateOperationContext context)
             throws OperationNotFoundException, FactoryException
     {
-        return delegate().createOperation(sourceCRS, targetCRS);
+        return new CoordinateOperationInference(this, context).createOperation(sourceCRS, targetCRS);
     }
 
     /**
-     * Temporarily returns a third-party factory for operation not yet implemented by this class.
-     * This method will be removed when the missing implementation will have been ported to SIS.
+     * Returns an operation using a particular method for conversion or transformation between
+     * two coordinate reference systems. If an operation exists using the given method, then it
+     * is returned. If no operation using the given method is found, then the implementation has
+     * the option of inferring the operation from the argument objects.
+     *
+     * <p>Current implementation ignores the {@code method} argument.
+     * This behavior may change in a future Apache SIS version.</p>
+     *
+     * @param  sourceCRS  input coordinate reference system.
+     * @param  targetCRS  output coordinate reference system.
+     * @param  method     the algorithmic method for conversion or transformation.
+     * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}.
+     * @throws OperationNotFoundException if no operation path was found from {@code sourceCRS} to {@code targetCRS}.
+     * @throws FactoryException if the operation creation failed for some other reason.
+     *
+     * @deprecated Replaced by {@link #createOperation(CoordinateReferenceSystem, CoordinateReferenceSystem, CoordinateOperationContext)}.
      */
-    private synchronized CoordinateOperationFactory delegate() throws FactoryException {
-        if (delegate != null) {
-            return delegate;
-        }
-        for (final CoordinateOperationFactory factory : java.util.ServiceLoader.load(CoordinateOperationFactory.class)) {
-            if (!Utilities.isSIS(factory.getClass())) {
-                delegate = factory;
-                return factory;
-            }
-        }
-        throw new FactoryException(Errors.format(Errors.Keys.MissingRequiredModule_1, "geotk-referencing")); // This is temporary.
+    @Override
+    @Deprecated
+    public CoordinateOperation createOperation(final CoordinateReferenceSystem sourceCRS,
+                                               final CoordinateReferenceSystem targetCRS,
+                                               final OperationMethod method)
+            throws FactoryException
+    {
+        ArgumentChecks.ensureNonNull("method", method);     // As a matter of principle.
+        return createOperation(sourceCRS, targetCRS, (CoordinateOperationContext) null);
     }
-
-    /** Temporary, to be deleted in a future SIS version. */
-    private transient CoordinateOperationFactory delegate;
 }

Copied: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java (from r1737105, sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java)
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java?p2=sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java&p1=sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java&r1=1737105&r2=1737107&rev=1737107&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -19,12 +19,14 @@ package org.apache.sis.referencing.opera
 import java.util.Map;
 import java.util.HashMap;
 import javax.xml.bind.annotation.XmlTransient;
-import org.opengis.metadata.Identifier;
 import org.opengis.referencing.operation.OperationMethod;
 import org.apache.sis.internal.referencing.provider.AbstractProvider;
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
 import org.apache.sis.util.Deprecable;
 
+// Branch-dependent imports
+import org.opengis.referencing.ReferenceIdentifier;
+
 
 /**
  * Description of the inverse of another method. This class should be used only when no operation is defined
@@ -65,7 +67,7 @@ final class InverseOperationMethod exten
         if (method instanceof AbstractProvider && ((AbstractProvider) method).isInvertible()) {
             return method;
         }
-        Identifier name = method.getName();
+        ReferenceIdentifier name = method.getName();
         name = new ImmutableIdentifier(null, name.getCodeSpace(), "Inverse " + name.getCode());
         final Map<String,Object> properties = new HashMap<String,Object>(6);
         properties.put(NAME_KEY,    name);

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -191,7 +191,7 @@ class GeneralMatrix extends MatrixSIS im
      */
     static int indexOfErrors(final int numRow, final int numCol, final double[] elements) {
         assert elements.length % (numRow * numCol) == 0;
-        return (numRow * numCol) % elements.length; // A % B is for getting 0 without branching if A == B.
+        return (numRow * numCol) % elements.length;         // A % B is for getting 0 without branching if A == B.
     }
 
     /**
@@ -337,7 +337,7 @@ class GeneralMatrix extends MatrixSIS im
                 if (copy) {
                     elements = elements.clone();
                 }
-                return elements; // Internal array already uses extended precision.
+                return elements;                                // Internal array already uses extended precision.
             } else {
                 elements = Arrays.copyOf(elements, length);
             }
@@ -415,7 +415,7 @@ class GeneralMatrix extends MatrixSIS im
      * @see Matrices#create(int, int, Number[])
      */
     final boolean setElements(final Number[] newValues) {
-        final int numRow = this.numRow; // Protection against accidental changes.
+        final int numRow = this.numRow;                         // Protection against accidental changes.
         final int numCol = this.numCol;
         final int length = numRow * numCol;
         if (newValues.length != length) {
@@ -494,9 +494,18 @@ class GeneralMatrix extends MatrixSIS im
      */
     @Override
     public final boolean isAffine() {
-        final int numRow = this.numRow; // Protection against accidental changes.
+        return isAffine(true);
+    }
+
+    /**
+     * Implementation of {@link #isAffine()} with control on whether we require the matrix to be square.
+     *
+     * @param square {@code true} if the matrix must be square, or {@code false} for allowing non-square matrices.
+     */
+    final boolean isAffine(final boolean square) {
+        final int numRow = this.numRow;                     // Protection against accidental changes.
         final int numCol = this.numCol;
-        if (numRow == numCol) {
+        if (numRow == numCol || !square) {
             int i = numRow * numCol;
             if (elements[--i] == 1) {
                 final int base = (numRow - 1) * numCol;
@@ -526,7 +535,7 @@ class GeneralMatrix extends MatrixSIS im
      */
     @Override
     public final boolean isIdentity() {
-        final int numRow = this.numRow; // Protection against accidental changes.
+        final int numRow = this.numRow;                     // Protection against accidental changes.
         final int numCol = this.numCol;
         if (numRow != numCol) {
             return false;
@@ -553,9 +562,9 @@ class GeneralMatrix extends MatrixSIS im
      */
     @Override
     public void transpose() {
-        final int numRow = this.numRow; // Protection against accidental changes.
+        final int numRow = this.numRow;                                 // Protection against accidental changes.
         final int numCol = this.numCol;
-        final int errors = indexOfErrors(numRow, numCol, elements); // Where error values start, or 0 if none.
+        final int errors = indexOfErrors(numRow, numCol, elements);     // Where error values start, or 0 if none.
         for (int j=0; j<numRow; j++) {
             for (int i=0; i<j; i++) {
                 final int lo = j*numCol + i;
@@ -574,7 +583,7 @@ class GeneralMatrix extends MatrixSIS im
      * The matrix sizes much match - this is not verified unless assertions are enabled.
      */
     final void setToProduct(final Matrix A, final Matrix B) {
-        final int numRow = this.numRow; // Protection against accidental changes.
+        final int numRow = this.numRow;         // Protection against accidental changes.
         final int numCol = this.numCol;
         final int nc = A.getNumCol();
         assert B.getNumRow() == nc;
@@ -585,7 +594,7 @@ class GeneralMatrix extends MatrixSIS im
          */
         final double[] eltA   = getExtendedElements(A, numRow, nc, false);
         final double[] eltB   = getExtendedElements(B, nc, numCol, false);
-        final int errorOffset = numRow * numCol; // Where error terms start.
+        final int errorOffset = numRow * numCol;            // Where error terms start.
         final int errA        = numRow * nc;
         final int errB        = nc * numCol;
         /*
@@ -597,20 +606,20 @@ class GeneralMatrix extends MatrixSIS im
             for (int i=0; i<numCol; i++) {
                 sum.clear();
                 double max = 0;
-                int iB = i;       // Index of values in a single column of B.
-                int iA = j * nc;  // Index of values in a single row of A.
+                int iB = i;                                 // Index of values in a single column of B.
+                int iA = j * nc;                            // Index of values in a single row of A.
                 final int nextRow = iA + nc;
                 while (iA < nextRow) {
                     dot.setFrom (eltA, iA, errA);
                     dot.multiply(eltB, iB, errB);
                     sum.add(dot);
-                    iB += numCol; // Move to next row of B.
-                    iA++;         // Move to next column of A.
+                    iB += numCol;                           // Move to next row of B.
+                    iA++;                                   // Move to next column of A.
                     final double value = Math.abs(dot.value);
                     if (value > max) max = value;
                 }
                 if (Math.abs(sum.value) < Math.ulp(max) * ZERO_THRESHOLD) {
-                    sum.clear(); // Sum is not significant according double arithmetic.
+                    sum.clear();                            // Sum is not significant according double arithmetic.
                 }
                 sum.storeTo(elements, k++, errorOffset);
             }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -69,7 +69,7 @@ import org.apache.sis.internal.jdk7.Obje
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see org.apache.sis.parameter.TensorParameters
@@ -736,7 +736,7 @@ public final class Matrices extends Stat
      */
     public static MatrixSIS multiply(final Matrix m1, final Matrix m2) throws MismatchedMatrixSizeException {
         if (m1 instanceof MatrixSIS) {
-            return ((MatrixSIS) m1).multiply(m2);  // Maybe the subclass override that method.
+            return ((MatrixSIS) m1).multiply(m2);           // Maybe the subclass overrides that method.
         }
         final int nc = m2.getNumCol();
         MatrixSIS.ensureNumRowMatch(m1.getNumCol(), m2.getNumRow(), nc);
@@ -777,7 +777,6 @@ public final class Matrices extends Stat
      * @return {@code true} if the matrix represents an affine transform.
      *
      * @see MatrixSIS#isAffine()
-     * @see AffineTransforms2D#castOrCopy(Matrix)
      */
     public static boolean isAffine(final Matrix matrix) {
         if (matrix instanceof MatrixSIS) {
@@ -788,6 +787,32 @@ public final class Matrices extends Stat
     }
 
     /**
+     * Returns {@code true} if the given matrix represents a translation.
+     * This method returns {@code true} if the given matrix {@linkplain #isAffine(Matrix) is affine}
+     * and differs from the identity matrix only in the last column.
+     *
+     * @param matrix The matrix to test.
+     * @return {@code true} if the matrix represents a translation.
+     *
+     * @since 0.7
+     */
+    public static boolean isTranslation(final Matrix matrix) {
+        if (!isAffine(matrix)) {
+            return false;
+        }
+        final int numRow = matrix.getNumRow() - 1;      // Excluding translation column.
+        final int numCol = matrix.getNumCol() - 1;      // Excluding last row in affine transform.
+        for (int j=0; j<numRow; j++) {
+            for (int i=0; i<numCol; i++) {
+                if (matrix.getElement(j,i) != ((i == j) ? 1 : 0)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
      * Returns {@code true} if the given matrix is close to an identity matrix, given a tolerance threshold.
      * This method is equivalent to computing the difference between the given matrix and an identity matrix
      * of identical size, and returning {@code true} if and only if all differences are smaller than or equal

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrix.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrix.java?rev=1737107&r1=1737106&r2=1737107&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrix.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrix.java [UTF-8] Wed Mar 30 12:35:10 2016
@@ -227,28 +227,39 @@ next:   do {
     }
 
     /**
-     * Inverse a matrix for a transform where target points has more ordinates than source points.
+     * Inverses a matrix for a transform where target points has more ordinates than source points.
      * In other words, the target matrices will be a transform that discard some ordinate values.
      * We will discard the ones for which the row contains only 0 or NaN elements.
+     *
+     * <p>In the special case where the last row is of the form [0 0 … 0 1] as in affine transforms,
+     * this method also omits rows that contain only a translation term. We allow that because if we
+     * do not omit those rows, then the matrix will be non-invertible anyway. This is true only when
+     * the last row contains only zero except in the last column ([0 0 … 0 n] where <var>n</var> can
+     * be any value). We restrict <var>n</var> to 1 for now because a different value may indicate a
+     * matrix created for another purpose than coordinate conversions.</p>
      */
     private MatrixSIS inverseDimensionIncrease() throws NoninvertibleMatrixException {
-        final int numRow = this.numRow; // Protection against accidental changes.
+        final int numRow = this.numRow;                     // Protection against accidental changes.
         final int numCol = this.numCol;
         int j  = numRow;
-        int oi = numRow - numCol; // Initialized to the maximal amount of rows that we may discard.
+        int oi = numRow - numCol;   // Initialized to the maximal amount of rows that we may discard.
         final int[] omitted = new int[oi];
+        final boolean ignoreTranslation = isAffine(false);
+        if (ignoreTranslation) j--;                         // Last row already verified by isAffine().
 next:   do {
             if (--j < 0) {
-                throw nonInvertible(); // Not enough rows that we can omit.
+                throw nonInvertible();                      // Not enough rows that we can omit.
             }
             final int offset = j * numCol;
-            for (int i=offset + numCol; --i >= offset;) {
+            int i = offset + numCol;
+            if (ignoreTranslation) i--;
+            while (--i >= offset) {
                 final double element = elements[i];
                 if (element != 0 && !Double.isNaN(element)) {
                     continue next;
                 }
             }
-            omitted[--oi] = j; // Found a row which contains only 0 or NaN elements.
+            omitted[--oi] = j;                  // Found a row which contains only 0 or NaN elements.
         } while (oi != 0);
         /*
          * Found enough rows containing only zero elements. Create a square matrix omitting those rows,
@@ -258,7 +269,7 @@ next:   do {
         int i = 0;
         for (j=0; j<numRow; j++) {
             if (oi != omitted.length && j == omitted[oi]) oi++;
-            else copyRow(this, j, squareMatrix, i++); // Copy only if not skipped.
+            else copyRow(this, j, squareMatrix, i++);                   // Copy only if not skipped.
         }
         if (indexOfErrors(numRow, numCol, elements) == 0) {
             inferErrors(squareMatrix.elements);
@@ -329,6 +340,7 @@ next:   do {
      * {@inheritDoc}
      */
     @Override
+    @SuppressWarnings("CloneDoesntCallSuperClone")
     public MatrixSIS clone() {
         return new NonSquareMatrix(this);
     }



Mime
View raw message