sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1740152 [5/8] - in /sis/branches/JDK6: ./ core/sis-feature/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-feature/src/main/java/org/apache/sis/internal/ core/sis-feature/src/main/java/org/apache/sis/internal/feature/ core...
Date Wed, 20 Apr 2016 14:53:33 GMT
Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -18,6 +18,7 @@ package org.apache.sis.referencing.opera
 
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.IdentifiedObject;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Classes;
@@ -44,8 +45,8 @@ final class CRSPair {
     /**
      * Creates a {@code CRSPair} for the specified source and target CRS.
      */
-    public CRSPair(final CoordinateReferenceSystem sourceCRS,
-                   final CoordinateReferenceSystem targetCRS)
+    CRSPair(final CoordinateReferenceSystem sourceCRS,
+            final CoordinateReferenceSystem targetCRS)
     {
         this.sourceCRS = sourceCRS;
         this.targetCRS = targetCRS;
@@ -78,24 +79,34 @@ final class CRSPair {
     }
 
     /**
-     * Returns a name for the given object, truncating it if needed.
+     * Returns the name of the GeoAPI interface implemented by the specified object,
+     * followed by the name between brackets.
      */
-    static String shortName(final IdentifiedObject object) {
-        String name = IdentifiedObjects.getName(object, null);
-        if (name == null) {
-            name = Classes.getShortClassName(object);
+    static String label(final IdentifiedObject object) {
+        if (object == null) {
+            return null;
+        }
+        Class<? extends IdentifiedObject> type;
+        if (object instanceof AbstractIdentifiedObject) {
+            type = ((AbstractIdentifiedObject) object).getInterface();
         } else {
-            int i = 30;                 // Arbitrary length threshold.
+            type = Classes.getLeafInterfaces(object.getClass(), IdentifiedObject.class)[0];
+        }
+        String label = Classes.getShortName(type);
+        String name = IdentifiedObjects.getName(object, null);
+        if (name != null) {
+            int i = 30;                                         // Arbitrary length threshold.
             if (name.length() >= i) {
-                while (i > 15) {        // Arbitrary minimal length.
+                while (i > 15) {                                // Arbitrary minimal length.
                     final int c = name.codePointBefore(i);
                     if (Character.isSpaceChar(c)) break;
                     i -= Character.charCount(c);
                 }
                 name = CharSequences.trimWhitespaces(name, 0, i).toString() + '…';
             }
+            label = label + "[“" + name + "”]";
         }
-        return name;
+        return label;
     }
 
     /**
@@ -103,6 +114,6 @@ final class CRSPair {
      */
     @Override
     public String toString() {
-        return shortName(sourceCRS) + " → " + shortName(targetCRS);
+        return label(sourceCRS) + " → " + label(targetCRS);
     }
 }

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -19,11 +19,15 @@ package org.apache.sis.referencing.opera
 import java.io.Serializable;
 import org.opengis.metadata.extent.Extent;
 import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.opengis.referencing.operation.CoordinateOperation;
 import org.apache.sis.metadata.iso.extent.DefaultExtent;
 import org.apache.sis.metadata.iso.extent.Extents;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.util.ArgumentChecks;
 
+// Branch-dependent imports
+import org.apache.sis.internal.jdk8.Predicate;
+
 
 /**
  * Optional information about the context in which a requested coordinate operation will be used.
@@ -38,7 +42,7 @@ import org.apache.sis.util.ArgumentCheck
  * to choose the most suitable coordinate transformation between two CRS.
  *
  * <div class="note"><b>Example:</b>
- * if a transformation from NAD27 to NAD83 is requested without providing context, then Apache SIS will return the
+ * if a transformation from NAD27 to WGS84 is requested without providing context, then Apache SIS will return the
  * transformation applicable to the widest North American surface. But if the user provides a context saying that
  * he wants to transform coordinates in Texas, then Apache SIS may return another coordinate transformation with
  * different {@linkplain org.apache.sis.referencing.datum.BursaWolfParameters Bursa-Wolf parameters} more suitable
@@ -66,11 +70,6 @@ public class CoordinateOperationContext
     private Extent areaOfInterest;
 
     /**
-     * The geographic component of the area of interest, computed when first needed.
-     */
-    private transient GeographicBoundingBox bbox;
-
-    /**
      * The desired accuracy in metres, or 0 for the best accuracy available.
      * See {@link #getDesiredAccuracy()} for more details about what we mean by <cite>"best accuracy"</cite>.
      */
@@ -99,6 +98,8 @@ public class CoordinateOperationContext
      * Returns the spatio-temporal area of interest, or {@code null} if none.
      *
      * @return The spatio-temporal area of interest, or {@code null} if none.
+     *
+     * @see Extents#getGeographicBoundingBox(Extent)
      */
     public Extent getAreaOfInterest() {
         return areaOfInterest;
@@ -109,32 +110,24 @@ public class CoordinateOperationContext
      *
      * @param area The spatio-temporal area of interest, or {@code null} if none.
      */
-    public void setAreaOfInterest(final Extent area) {
-        areaOfInterest = area;
-    }
-
-    /**
-     * Returns the geographic component of the area of interest, or {@code null} if none.
-     * This convenience method extracts the bounding box from the spatio-temporal {@link Extent}.
-     *
-     * @return The geographic area of interest, or {@code null} if none.
-     */
-    public GeographicBoundingBox getGeographicBoundingBox() {
-        if (bbox == null) {
-            bbox = Extents.getGeographicBoundingBox(areaOfInterest);
+    public void setAreaOfInterest(Extent area) {
+        if (area != null) {
+            area = new DefaultExtent(area);
         }
-        return bbox;
+        areaOfInterest = area;
     }
 
     /**
      * Sets the geographic component of the area of interest, or {@code null} if none.
      * This convenience method set the bounding box into the spatio-temporal {@link Extent}.
      *
+     * <p>The reverse operation can be done with <code>{@linkplain Extents#getGeographicBoundingBox(Extent)
+     * Extents.getGeographicBoundingBox}({@linkplain #getAreaOfInterest()})</code>.</p>
+     *
      * @param area The geographic area of interest, or {@code null} if none.
      */
-    public void setGeographicBoundingBox(final GeographicBoundingBox area) {
+    public void setAreaOfInterest(final GeographicBoundingBox area) {
         areaOfInterest = setGeographicBoundingBox(areaOfInterest, area);
-        bbox = area;
     }
 
     /**
@@ -177,4 +170,13 @@ public class CoordinateOperationContext
         ArgumentChecks.ensurePositive("accuracy", accuracy);
         desiredAccuracy = accuracy;
     }
+
+    /**
+     * Returns a filter that can be used for applying additional restrictions on the coordinate operation.
+     *
+     * @todo Not yet implemented.
+     */
+    Predicate<CoordinateOperation> getOperationFilter() {
+        return null;
+    }
 }

Copied: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java (from r1740146, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java?p2=sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java&p1=sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java&r1=1740146&r2=1740152&rev=1740152&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -158,8 +158,8 @@ public class CoordinateOperationFinder e
                                      final CoordinateOperationContext          context) throws FactoryException
     {
         super(registry, factory, context);
-        identifierOfStepCRS = new HashMap<>(8);
-        previousSearches    = new HashMap<>(8);
+        identifierOfStepCRS = new HashMap<Identifier,Object>(8);
+        previousSearches    = new HashMap<CRSPair,Boolean>(8);
     }
 
     /**
@@ -642,7 +642,9 @@ public class CoordinateOperationFinder e
         final Matrix matrix;
         try {
             matrix = CoordinateSystems.swapAndScaleAxes(sourceCS, targetCS);
-        } catch (IllegalArgumentException | ConversionException exception) {
+        } catch (IllegalArgumentException exception) {
+            throw new OperationNotFoundException(notFoundMessage(sourceCRS, targetCRS), exception);
+        } catch (ConversionException exception) {
             throw new OperationNotFoundException(notFoundMessage(sourceCRS, targetCRS), exception);
         }
         return createFromAffineTransform(AXIS_CHANGES, sourceCRS, targetCRS, matrix);
@@ -688,7 +690,9 @@ public class CoordinateOperationFinder e
         final Matrix matrix;
         try {
             matrix = CoordinateSystems.swapAndScaleAxes(sourceCS, targetCS);
-        } catch (IllegalArgumentException | ConversionException exception) {
+        } catch (IllegalArgumentException exception) {
+            throw new OperationNotFoundException(notFoundMessage(sourceCRS, targetCRS), exception);
+        } catch (ConversionException exception) {
             throw new OperationNotFoundException(notFoundMessage(sourceCRS, targetCRS), exception);
         }
         final int translationColumn = matrix.getNumCol() - 1;           // Paranoiac check: should always be 1.
@@ -902,7 +906,7 @@ public class CoordinateOperationFinder e
         if (main instanceof SingleOperation) {
             final SingleOperation op = (SingleOperation) main;
             final MathTransform mt = factorySIS.getMathTransformFactory().createConcatenatedTransform(mt1, mt2);
-            main = createFromMathTransform(new HashMap<>(IdentifiedObjects.getProperties(main)),
+            main = createFromMathTransform(new HashMap<String,Object>(IdentifiedObjects.getProperties(main)),
                    sourceCRS, targetCRS, mt, op.getMethod(), op.getParameterValues(),
                    (main instanceof Transformation) ? Transformation.class :
                    (main instanceof Conversion) ? Conversion.class : SingleOperation.class);
@@ -922,7 +926,7 @@ public class CoordinateOperationFinder e
                     break;
                 }
             }
-            main = createFromMathTransform(new HashMap<>(IdentifiedObjects.getProperties(main)),
+            main = createFromMathTransform(new HashMap<String,Object>(IdentifiedObjects.getProperties(main)),
                     main.getSourceCRS(), main.getTargetCRS(), main.getMathTransform(), null, null, type);
         }
         return main;
@@ -988,7 +992,7 @@ public class CoordinateOperationFinder e
         identifierOfStepCRS.put(newID, oldID);
         identifierOfStepCRS.put(oldID, count);
 
-        final Map<String,Object> properties = new HashMap<>(4);
+        final Map<String,Object> properties = new HashMap<String,Object>(4);
         properties.put(IdentifiedObject.NAME_KEY, newID);
         properties.put(IdentifiedObject.REMARKS_KEY, Vocabulary.formatInternational(
                             Vocabulary.Keys.DerivedFrom_1, CRSPair.label(object)));

Copied: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java (from r1740146, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?p2=sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java&p1=sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java&r1=1740146&r2=1740152&rev=1740152&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -74,7 +74,7 @@ import org.apache.sis.util.resources.Voc
 import org.apache.sis.util.resources.Errors;
 
 // Branch-dependent imports
-import java.util.Objects;
+import org.apache.sis.internal.jdk7.Objects;
 import org.apache.sis.internal.jdk8.JDK8;
 import org.apache.sis.internal.jdk8.Predicate;
 
@@ -303,7 +303,10 @@ class CoordinateOperationRegistry {
                     }
                     return operation;
                 }
-            } catch (IllegalArgumentException | ConversionException e) {
+            } catch (IllegalArgumentException e) {
+                throw new FactoryException(Errors.format(
+                        Errors.Keys.CanNotInstantiate_1, new CRSPair(sourceCRS, targetCRS)), e);
+            } catch (ConversionException e) {
                 throw new FactoryException(Errors.format(
                         Errors.Keys.CanNotInstantiate_1, new CRSPair(sourceCRS, targetCRS)), e);
             }
@@ -366,7 +369,7 @@ class CoordinateOperationRegistry {
                     return null;
                 }
             }
-        } catch (NoSuchAuthorityCodeException | MissingFactoryResourceException exception) {
+        } catch (NoSuchAuthorityCodeException exception) {
             /*
              * sourceCode or targetCode is unknown to the underlying authority factory.
              * Ignores the exception and fallback on the generic algorithm provided by
@@ -374,6 +377,9 @@ class CoordinateOperationRegistry {
              */
             log(exception);
             return null;
+        } catch (MissingFactoryResourceException exception) {
+            log(exception);
+            return null;
         }
         /*
          * We will loop over all coordinate operations and select the one having the largest intersection
@@ -634,7 +640,7 @@ class CoordinateOperationRegistry {
         CoordinateReferenceSystem crs;
         if (Utilities.equalsApproximatively(sourceCRS, crs = operation.getSourceCRS())) sourceCRS = crs;
         if (Utilities.equalsApproximatively(targetCRS, crs = operation.getTargetCRS())) targetCRS = crs;
-        final Map<String,Object> properties = new HashMap<>(derivedFrom(operation));
+        final Map<String,Object> properties = new HashMap<String,Object>(derivedFrom(operation));
         /*
          * Determine whether the operation to create is a Conversion or a Transformation
          * (could also be a Conversion subtype like Projection, but this is less important).
@@ -668,8 +674,11 @@ class CoordinateOperationRegistry {
                     try {
                         method = factory.getOperationMethod(method.getName().getCode());
                         method = DefaultOperationMethod.redimension(method, sourceDimensions, targetDimensions);
-                    } catch (NoSuchIdentifierException | IllegalArgumentException se) {
-                        ex.addSuppressed(se);
+                    } catch (NoSuchIdentifierException se) {
+                        // ex.addSuppressed(se) on the JDK7 branch.
+                        throw ex;
+                    } catch (IllegalArgumentException se) {
+                        // ex.addSuppressed(se) on the JDK7 branch.
                         throw ex;
                     }
                 }
@@ -704,7 +713,7 @@ class CoordinateOperationRegistry {
     private CoordinateOperation propagateVertical(final CoordinateOperation operation,
             final boolean source3D, final boolean target3D) throws IllegalArgumentException, FactoryException
     {
-        final List<CoordinateOperation> operations = new ArrayList<>();
+        final List<CoordinateOperation> operations = new ArrayList<CoordinateOperation>();
         if (operation instanceof ConcatenatedOperation) {
             operations.addAll(((ConcatenatedOperation) operation).getOperations());
         } else {
@@ -863,7 +872,7 @@ class CoordinateOperationRegistry {
      * @return a modifiable map containing the given name. Callers can put other entries in this map.
      */
     static Map<String,Object> properties(final Identifier name) {
-        final Map<String,Object> properties = new HashMap<>(4);
+        final Map<String,Object> properties = new HashMap<String,Object>(4);
         properties.put(CoordinateOperation.NAME_KEY, name);
         if ((name == DATUM_SHIFT) || (name == ELLIPSOID_CHANGE)) {
             properties.put(CoordinateOperation.COORDINATE_OPERATION_ACCURACY_KEY, new PositionalAccuracy[] {

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -17,36 +17,31 @@
 package org.apache.sis.referencing.operation;
 
 import java.util.Map;
-import java.util.Set;
 import java.util.List;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedHashSet;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 import org.opengis.util.FactoryException;
-import org.opengis.metadata.quality.PositionalAccuracy;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.ConcatenatedOperation;
 import org.opengis.referencing.operation.Transformation;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
+import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
-import org.apache.sis.internal.util.CollectionsExt;
-import org.apache.sis.util.collection.Containers;
 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;
 
 // Branch-dependent imports
 import org.apache.sis.internal.jdk7.Objects;
-import org.apache.sis.io.wkt.Formatter;
 
 
 /**
@@ -114,9 +109,8 @@ final class DefaultConcatenatedOperation
     {
         super(properties);
         ArgumentChecks.ensureNonNull("operations", operations);
-        final boolean setAccuracy = (coordinateOperationAccuracy == null);
         final List<CoordinateOperation> flattened = new ArrayList<CoordinateOperation>(operations.length);
-        initialize(properties, operations, flattened, mtFactory, setAccuracy);
+        initialize(properties, operations, flattened, mtFactory, (coordinateOperationAccuracy == null));
         if (flattened.size() < 2) {
             throw new IllegalArgumentException(Errors.getResources(properties).getString(
                     Errors.Keys.TooFewOccurrences_2, 2, CoordinateOperation.class));
@@ -125,10 +119,6 @@ final class DefaultConcatenatedOperation
         this.operations = UnmodifiableArrayList.wrap(operations);
         this.sourceCRS  = operations[0].getSourceCRS();
         this.targetCRS  = operations[operations.length - 1].getTargetCRS();
-        if (setAccuracy) {
-            coordinateOperationAccuracy = CollectionsExt.unmodifiableOrCopy(
-                    (Set<PositionalAccuracy>) coordinateOperationAccuracy);
-        }
         checkDimensions(properties);
     }
 
@@ -147,11 +137,11 @@ final class DefaultConcatenatedOperation
      * in the given list. This should not happen according ISO 19111 standard, but we try to be safe.
      *
      * <div class="section">How coordinate operation accuracy is determined</div>
-     * If {@code setAccuracy} is {@code true}, then this method collects all accuracy information found in the
-     * {@link Transformation} instances. This method ignores instances of other kinds for the following reason:
+     * If {@code setAccuracy} is {@code true}, then this method copies accuracy information found in the single
+     * {@link Transformation} instance. This method ignores instances of other kinds for the following reason:
      * some {@link Conversion} instances declare an accuracy, which is typically close to zero. If a concatenated
      * operation contains such conversion together with a transformation with unknown accuracy, then we do not want
-     * to declare that a 0 meter error as the concatenated operation accuracy; it would be a false information.
+     * to declare "0 meter" as the concatenated operation accuracy; it would be a false information.
      * An other reason is that a concatenated operation typically contains an arbitrary amount of conversions,
      * but only one transformation. So considering only transformations usually means to pickup only one operation
      * in the given {@code operations} list, which make things clearer.
@@ -174,7 +164,7 @@ final class DefaultConcatenatedOperation
                             final CoordinateOperation[]     operations,
                             final List<CoordinateOperation> flattened,
                             final MathTransformFactory      mtFactory,
-                            final boolean                   setAccuracy)
+                            boolean                         setAccuracy)
             throws FactoryException
     {
         CoordinateReferenceSystem previous = null;
@@ -218,16 +208,22 @@ final class DefaultConcatenatedOperation
                 transform = (transform != null) ? mtFactory.createConcatenatedTransform(transform, step) : step;
             }
             /*
-             * Optionally update the coordinate operation accuracy.
-             * See javadoc for a rational about why we take only transformations in account.
+             * Optionally copy the coordinate operation accuracy from the transformation (or from a concatenated
+             * operation on the assumption that its accuracy was computed by the same algorithm than this method).
+             * See javadoc for a rational about why we take only transformations in account. If more than one
+             * transformation is found, clear the collection and abandon the attempt to set the accuracy information.
+             * Instead the user will get a better result by invoking PositionalAccuracyConstant.getLinearAccuracy(…)
+             * since that method conservatively computes the sum of all linear accuracy.
              */
-            if (setAccuracy && op instanceof Transformation) {
-                Collection<PositionalAccuracy> candidates = op.getCoordinateOperationAccuracy();
-                if (!Containers.isNullOrEmpty(candidates)) {
-                    if (coordinateOperationAccuracy == null) {
-                        coordinateOperationAccuracy = new LinkedHashSet<PositionalAccuracy>();
+            if (setAccuracy && (op instanceof Transformation || op instanceof ConcatenatedOperation)) {
+                if (coordinateOperationAccuracy == null) {
+                    setAccuracy = (PositionalAccuracyConstant.getLinearAccuracy(op) > 0);
+                    if (setAccuracy) {
+                        coordinateOperationAccuracy = op.getCoordinateOperationAccuracy();
                     }
-                    coordinateOperationAccuracy.addAll(candidates);
+                } else {
+                    coordinateOperationAccuracy = null;
+                    setAccuracy = false;
                 }
             }
         }
@@ -337,6 +333,7 @@ final class DefaultConcatenatedOperation
     protected String formatTo(final Formatter formatter) {
         super.formatTo(formatter);
         for (final CoordinateOperation component : operations) {
+            formatter.newLine();
             formatter.append(castOrCopy(component));
         }
         formatter.setInvalidWKT(this, null);
@@ -382,8 +379,7 @@ final class DefaultConcatenatedOperation
      */
     private void setSteps(final CoordinateOperation[] steps) throws FactoryException {
         final List<CoordinateOperation> flattened = new ArrayList<CoordinateOperation>(steps.length);
-        initialize(null, steps, flattened, DefaultFactories.forBuildin(MathTransformFactory.class), true);
+        initialize(null, steps, flattened, DefaultFactories.forBuildin(MathTransformFactory.class), coordinateOperationAccuracy == null);
         operations = UnmodifiableArrayList.wrap(flattened.toArray(new CoordinateOperation[flattened.size()]));
-        coordinateOperationAccuracy = CollectionsExt.unmodifiableOrCopy((Set<PositionalAccuracy>) coordinateOperationAccuracy);
     }
 }

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -25,16 +25,20 @@ import org.opengis.util.NoSuchIdentifier
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.referencing.operation.*;
+import org.opengis.referencing.AuthorityFactory;
 import org.opengis.referencing.IdentifiedObject;
 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.crs.CRSFactory;
+import org.opengis.referencing.cs.CSFactory;
 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.Constants;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
 import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
@@ -44,6 +48,7 @@ import org.apache.sis.util.iso.AbstractF
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Classes;
 import org.apache.sis.util.NullArgumentException;
 import org.apache.sis.util.Utilities;
 
@@ -77,6 +82,12 @@ import org.apache.sis.util.Utilities;
  */
 public class DefaultCoordinateOperationFactory extends AbstractFactory implements CoordinateOperationFactory {
     /**
+     * Whether this class is allowed to use the EPSG authority factory for searching coordinate operation paths.
+     * This flag should always be {@code true}, except temporarily for testing purposes.
+     */
+    static final boolean USE_EPSG_FACTORY = true;
+
+    /**
      * The default properties, or an empty map if none. This map shall not change after construction in
      * order to allow usage without synchronization in multi-thread context. But we do not need to wrap
      * in a unmodifiable map since {@code DefaultCoordinateOperationFactory} does not provide public
@@ -85,6 +96,22 @@ public class DefaultCoordinateOperationF
     private final Map<String,?> defaultProperties;
 
     /**
+     * The factory to use if {@link CoordinateOperationFinder} needs to create CRS for intermediate steps.
+     * Will be created only when first needed.
+     *
+     * @see #getCRSFactory()
+     */
+    private volatile CRSFactory crsFactory;
+
+    /**
+     * The factory to use if {@link CoordinateOperationFinder} needs to create CS for intermediate steps.
+     * Will be created only when first needed.
+     *
+     * @see #getCSFactory()
+     */
+    private volatile CSFactory csFactory;
+
+    /**
      * The math transform factory. Will be created only when first needed.
      *
      * @see #getMathTransformFactory()
@@ -101,8 +128,7 @@ public class DefaultCoordinateOperationF
      * Constructs a factory with no default properties.
      */
     public DefaultCoordinateOperationFactory() {
-        defaultProperties = Collections.emptyMap();
-        pool = new WeakHashSet<IdentifiedObject>(IdentifiedObject.class);
+        this(null, null);
     }
 
     /**
@@ -110,19 +136,36 @@ public class DefaultCoordinateOperationF
      * {@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.
      *
-     * @param properties The default properties, or {@code null} if none.
-     * @param mtFactory The factory to use for creating
+     * @param properties the default properties, or {@code null} if none.
+     * @param factory the factory to use for creating
      *        {@linkplain org.apache.sis.referencing.operation.transform.AbstractMathTransform math transforms},
      *        or {@code null} for the default factory.
      */
-    public DefaultCoordinateOperationFactory(Map<String,?> properties, final MathTransformFactory mtFactory) {
+    public DefaultCoordinateOperationFactory(Map<String,?> properties, final MathTransformFactory factory) {
         if (properties == null || properties.isEmpty()) {
             properties = Collections.emptyMap();
         } else {
-            properties = CollectionsExt.compact(new HashMap<String,Object>(properties));
+            String key   = null;
+            Object value = null;
+            properties   = new HashMap<String,Object>(properties);
+            /*
+             * Following use of properties is an undocumented feature for now. Current version documents only
+             * MathTransformFactory because math transforms are intimately related to coordinate operations.
+             */
+            try {
+                crsFactory = (CRSFactory)           (value = properties.remove(key = ReferencingServices.CRS_FACTORY));
+                csFactory  = (CSFactory)            (value = properties.remove(key = ReferencingServices.CS_FACTORY));
+                mtFactory  = (MathTransformFactory) (value = properties.remove(key = ReferencingServices.MT_FACTORY));
+            } catch (ClassCastException e) {
+                throw new IllegalArgumentException(Errors.getResources(properties)
+                        .getString(Errors.Keys.IllegalPropertyValueClass_2, key, Classes.getClass(value)));
+            }
+            properties = CollectionsExt.compact(properties);
         }
         defaultProperties = properties;
-        this.mtFactory = mtFactory;
+        if (factory != null) {
+            mtFactory = factory;
+        }
         pool = new WeakHashSet<IdentifiedObject>(IdentifiedObject.class);
     }
 
@@ -148,6 +191,28 @@ public class DefaultCoordinateOperationF
     }
 
     /**
+     * Returns the factory to use if {@link CoordinateOperationFinder} needs to create CRS for intermediate steps.
+     */
+    final CRSFactory getCRSFactory() {
+        CRSFactory factory = crsFactory;
+        if (factory == null) {
+            crsFactory = factory = DefaultFactories.forBuildin(CRSFactory.class);
+        }
+        return factory;
+    }
+
+    /**
+     * Returns the factory to use if {@link CoordinateOperationFinder} needs to create CS for intermediate steps.
+     */
+    final CSFactory getCSFactory() {
+        CSFactory factory = csFactory;
+        if (factory == null) {
+            csFactory = factory = DefaultFactories.forBuildin(CSFactory.class);
+        }
+        return factory;
+    }
+
+    /**
      * Returns the underlying math transform factory. This factory is used for constructing {@link MathTransform}
      * dependencies for all {@linkplain AbstractCoordinateOperation coordinate operations} instances.
      *
@@ -487,7 +552,7 @@ next:   for (int i=components.size(); --
         }
         /*
          * Now create the coordinate operation of the requested type. If we can not find a concrete class for the
-         * requested type, we will instantiate an SingleOperation in last resort. The later action is a departure
+         * requested type, we will instantiate a SingleOperation in last resort.  The later action is a departure
          * from ISO 19111 since 'SingleOperation' is conceptually abstract.  But we do that as a way to said that
          * we are missing this important piece of information but still go ahead.
          *
@@ -567,6 +632,9 @@ next:   for (int i=components.size(); --
     public CoordinateOperation createConcatenatedOperation(final Map<String,?> properties,
             final CoordinateOperation... operations) throws FactoryException
     {
+        if (operations != null && operations.length == 1) {
+            return operations[0];
+        }
         final CoordinateOperation op;
         try {
             op = new DefaultConcatenatedOperation(properties, operations, getMathTransformFactory());
@@ -604,14 +672,16 @@ next:   for (int i=components.size(); --
      * 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>
+     * <p>The default implementation is equivalent to the following code
+     * (omitting the {@code registry} type check and cast for brevity):</p>
      *
      * {@preformat java
-     *   return new CoordinateOperationInference(this, context).createOperation(sourceCRS, targetCRS);
+     *   CoordinateOperationAuthorityFactory registry = CRS.getAuthorityFactory("EPSG");    // Actually needs cast
+     *   return new CoordinateOperationFinder(registry, this, context).createOperation(sourceCRS, targetCRS);
      * }
      *
      * Subclasses can override this method if they need, for example, to use a custom
-     * {@link CoordinateOperationInference} implementation.
+     * {@link CoordinateOperationFinder} implementation.
      *
      * @param  sourceCRS  input coordinate reference system.
      * @param  targetCRS  output coordinate reference system.
@@ -620,7 +690,7 @@ next:   for (int i=components.size(); --
      * @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
+     * @see CoordinateOperationFinder
      *
      * @since 0.7
      */
@@ -629,7 +699,9 @@ next:   for (int i=components.size(); --
                                                final CoordinateOperationContext context)
             throws OperationNotFoundException, FactoryException
     {
-        return new CoordinateOperationInference(this, context).createOperation(sourceCRS, targetCRS);
+        final AuthorityFactory registry = USE_EPSG_FACTORY ? CRS.getAuthorityFactory(Constants.EPSG) : null;
+        return new CoordinateOperationFinder((registry instanceof CoordinateOperationAuthorityFactory) ?
+                (CoordinateOperationAuthorityFactory) registry : null, this, context).createOperation(sourceCRS, targetCRS);
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -259,7 +259,7 @@ public class DefaultOperationMethod exte
             formula = new DefaultFormula((CharSequence) value);
         } else {
             throw new IllegalArgumentException(Errors.getResources(properties)
-                    .getString(Errors.Keys.IllegalPropertyClass_2, FORMULA_KEY, value.getClass()));
+                    .getString(Errors.Keys.IllegalPropertyValueClass_2, FORMULA_KEY, value.getClass()));
         }
         this.parameters       = parameters;
         this.sourceDimensions = sourceDimensions;

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -33,6 +33,8 @@ import org.apache.sis.util.UnsupportedIm
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.io.wkt.FormattableObject;
+import org.apache.sis.io.wkt.Formatter;
 
 import static org.apache.sis.util.Utilities.deepEquals;
 
@@ -249,6 +251,33 @@ public class DefaultPassThroughOperation
         return super.computeHashCode() + 31 * operation.hashCode();
     }
 
+    /**
+     * Formats this coordinate operation in a pseudo-Well Known Text (WKT) format.
+     * Current format is specific to Apache SIS and may change in any future version
+     * if a standard format for pass through operations is defined.
+     *
+     * @param  formatter The formatter to use.
+     * @return Currently {@code "PassThroughOperation"} (may change in any future version).
+     *
+     * @since 0.7
+     */
+    @Override
+    protected String formatTo(final Formatter formatter) {
+        super.formatTo(formatter);
+        formatter.append(new FormattableObject() {
+            @Override protected String formatTo(final Formatter formatter) {
+                for (final int i : getModifiedCoordinates()) {
+                    formatter.append(i);
+                }
+                return "ModifiedCoordinates";
+            }
+        });
+        formatter.newLine();
+        formatter.append(castOrCopy(getOperation()));
+        formatter.setInvalidWKT(this, null);
+        return "PassThroughOperation";
+    }
+
 
 
 

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -19,12 +19,24 @@ 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 javax.measure.unit.Unit;
+import org.opengis.util.InternationalString;
+import org.opengis.parameter.ParameterValue;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.GeneralParameterValue;
+import org.opengis.parameter.GeneralParameterDescriptor;
 import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.SingleOperation;
+import org.apache.sis.internal.metadata.ReferencingServices;
+import org.apache.sis.internal.referencing.SignReversalComment;
 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.metadata.Identifier;
+
 
 /**
  * Description of the inverse of another method. This class should be used only when no operation is defined
@@ -56,24 +68,92 @@ final class InverseOperationMethod exten
     }
 
     /**
-     * Returns or create the inverse of the given operation method.
+     * Returns {@code true} if the given method flags itself as invertible.
+     */
+    private static boolean isInvertible(final OperationMethod method) {
+        return method instanceof AbstractProvider && ((AbstractProvider) method).isInvertible();
+    }
+
+    /**
+     * Returns or create the inverse of the given operation method. If the same operation method can be used
+     * for the inverse operation either with the exact same parameter values or with the sign of some values
+     * reversed, then the given method is returned as-is. Otherwise a synthetic method is created.
      */
     static OperationMethod create(final OperationMethod method) {
         if (method instanceof InverseOperationMethod) {
             return ((InverseOperationMethod) method).inverse;
         }
-        if (method instanceof AbstractProvider && ((AbstractProvider) method).isInvertible()) {
-            return method;
+        if (!isInvertible(method)) {
+            boolean useSameParameters = false;
+            for (final GeneralParameterDescriptor descriptor : method.getParameters().descriptors()) {
+                useSameParameters = (descriptor.getRemarks() instanceof SignReversalComment);
+                if (!useSameParameters) break;
+            }
+            if (!useSameParameters) {
+                Identifier name = method.getName();
+                name = new ImmutableIdentifier(null, null, "Inverse of " + name.getCode());
+                final Map<String,Object> properties = new HashMap<String,Object>(6);
+                properties.put(NAME_KEY,    name);
+                properties.put(FORMULA_KEY, method.getFormula());
+                properties.put(REMARKS_KEY, method.getRemarks());
+                if (method instanceof Deprecable) {
+                    properties.put(DEPRECATED_KEY, ((Deprecable) method).isDeprecated());
+                }
+                return new InverseOperationMethod(properties, method);
+            }
         }
-        Identifier 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);
-        properties.put(FORMULA_KEY, method.getFormula());
-        properties.put(REMARKS_KEY, method.getRemarks());
-        if (method instanceof Deprecable) {
-            properties.put(DEPRECATED_KEY, ((Deprecable) method).isDeprecated());
+        return method;
+    }
+
+    /**
+     * If the inverse of the given operation can be represented by inverting the sign of all numerical
+     * parameter values, copies those parameters in a {@code "parameters"} entry in the given map.
+     * Otherwise does nothing.
+     *
+     * @param source  the operation for which to get the inverse parameters.
+     * @param target  where to store the inverse parameters.
+     */
+    static void putParameters(final SingleOperation source, final Map<String,Object> target) {
+        final boolean isInvertible = isInvertible(source.getMethod());
+        final ParameterValueGroup parameters = source.getParameterValues();
+        final ParameterValueGroup copy = parameters.getDescriptor().createValue();
+        for (final GeneralParameterValue gp : parameters.values()) {
+            if (gp instanceof ParameterValue<?>) {
+                final ParameterValue<?> src = (ParameterValue<?>) gp;
+                final Object value = src.getValue();
+                if (value instanceof Number) {
+                    final ParameterDescriptor<?> descriptor = src.getDescriptor();
+                    final InternationalString remarks = descriptor.getRemarks();
+                    if (remarks != SignReversalComment.SAME) {
+                        boolean isOpposite = (remarks == SignReversalComment.OPPOSITE);
+                        if (!isOpposite) {
+                            /*
+                             * If the parameter descriptor does not contain an information about whether the
+                             * inverse operation uses values of opposite sign or not, use heuristic rules.
+                             */
+                            if (!isInvertible) {
+                                return;                 // Can not create inverse parameter values - abandon.
+                            }
+                            final Comparable<?> minimum = descriptor.getMinimumValue();
+                            isOpposite = (minimum == null || (minimum instanceof Number && ((Number) minimum).doubleValue() < 0));
+                        }
+                        if (isOpposite) {
+                            final ParameterValue<?> tgt = copy.parameter(descriptor.getName().getCode());
+                            final Unit<?> unit = src.getUnit();
+                            if (unit != null) {
+                                tgt.setValue(-src.doubleValue(), unit);
+                            } else if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
+                                tgt.setValue(-src.intValue());
+                            } else {
+                                tgt.setValue(-src.doubleValue());
+                            }
+                            continue;
+                        }
+                    }
+                }
+            }
+            copy.values().add(gp);
         }
-        return new InverseOperationMethod(properties, method);
+        target.put(ReferencingServices.PARAMETERS_KEY, copy);
     }
 }

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -178,7 +178,7 @@ public final class AffineTransforms2D ex
      *
      * @return The direct transform of the {@code bounds} rectangle, or {@code null} if {@code bounds} was null.
      *
-     * @see org.apache.sis.referencing.CRS#transform(MathTransform2D, Rectangle2D, Rectangle2D)
+     * @see org.apache.sis.geometry.Envelopes#transform(MathTransform2D, Rectangle2D, Rectangle2D)
      */
     public static Rectangle2D transform(final AffineTransform transform,
             final Rectangle2D bounds, final Rectangle2D dest)

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -29,6 +29,7 @@ import org.apache.sis.util.ComparisonMod
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.internal.metadata.AxisDirections;
 import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix;
 
@@ -559,8 +560,9 @@ public final class Matrices extends Stat
      *
      * <p>The given sub-matrix shall have the following properties:</p>
      * <ul>
-     *   <li>The last column contains translation terms, except in the last row.</li>
-     *   <li>The last row often (but not necessarily) contains 0 values except in the last column.</li>
+     *   <li>The last row often (but not necessarily) contains 0 values everywhere except in the last column.</li>
+     *   <li>Values in the last column are translation terms, except in the last row.</li>
+     *   <li>All other values are scale or shear terms.</li>
      * </ul>
      *
      * A square matrix complying with the above conditions is often {@linkplain #isAffine(Matrix) affine},
@@ -608,22 +610,34 @@ public final class Matrices extends Stat
      * @param  firstAffectedOrdinate The lowest index of the affected ordinates.
      * @param  subMatrix The matrix to use for affected ordinates.
      * @param  numTrailingOrdinates Number of trailing ordinates to pass through.
-     * @return A matrix
+     * @return A matrix for the same transform than the given matrix,
+     *         augmented with leading and trailing pass-through coordinates.
      *
      * @see org.apache.sis.referencing.operation.DefaultMathTransformFactory#createPassThroughTransform(int, MathTransform, int)
      */
     public static MatrixSIS createPassThrough(final int firstAffectedOrdinate,
             final Matrix subMatrix, final int numTrailingOrdinates)
     {
+        ArgumentChecks.ensureNonNull ("subMatrix",             subMatrix);
         ArgumentChecks.ensurePositive("firstAffectedOrdinate", firstAffectedOrdinate);
         ArgumentChecks.ensurePositive("numTrailingOrdinates",  numTrailingOrdinates);
         final int  expansion = firstAffectedOrdinate + numTrailingOrdinates;
-        int sourceDimensions = subMatrix.getNumCol();
+        int sourceDimensions = subMatrix.getNumCol();           // Will become the number of dimensions later.
         int targetDimensions = subMatrix.getNumRow();
+        /*
+         * Get data from the source matrix, together with the error terms if present.
+         * The 'stride' and 'length' values will be used for computing indices in that array.
+         * The DoubleDouble temporary object is used only if the array contains error terms.
+         */
+        final int      stride  = sourceDimensions;
+        final int      length  = sourceDimensions * targetDimensions;
+        final double[] sources = getExtendedElements(subMatrix);
+        final DoubleDouble transfer = (sources.length > length) ? new DoubleDouble() : null;
         final MatrixSIS matrix = createZero(targetDimensions-- + expansion,
-                                            sourceDimensions-- + expansion);
+                                            sourceDimensions-- + expansion,
+                                            transfer != null);
         /*
-         * Following code process for upper row to lower row.
+         * Following code processes from upper row to lower row.
          * First, set the diagonal elements on leading new dimensions.
          */
         for (int j=0; j<firstAffectedOrdinate; j++) {
@@ -634,12 +648,14 @@ public final class Matrices extends Stat
          * which are unconditionally stored in the last column.
          */
         final int lastColumn = sourceDimensions + expansion;
-        for (int j=0; j<targetDimensions; j++) {
-            for (int i=0; i<sourceDimensions; i++) {
-                matrix.setElement(firstAffectedOrdinate + j, firstAffectedOrdinate + i, subMatrix.getElement(j, i));
-            }
-            matrix.setElement(firstAffectedOrdinate + j, lastColumn, subMatrix.getElement(j, sourceDimensions));
-        }
+        matrix.setElements(sources, length, stride, transfer,
+                0,                     0,                           // Source (row, colum)
+                firstAffectedOrdinate, firstAffectedOrdinate,       // Target (row, column)
+                targetDimensions,      sourceDimensions);           // Number of rows and columns to copy.
+        matrix.setElements(sources, length, stride, transfer,
+                0,                     sourceDimensions,            // Source (row, colum):  last column
+                firstAffectedOrdinate, lastColumn,                  // Target (row, column): part of last column
+                targetDimensions,      1);                          // Copy some rows of only 1 column.
         /*
          * Set the pseudo-diagonal elements on the trailing new dimensions.
          * 'diff' is zero for a square matrix and non-zero for rectangular matrix.
@@ -653,14 +669,70 @@ public final class Matrices extends Stat
          * this row contains only 0 element except for the last one, which is 1.
          */
         final int lastRow = targetDimensions + expansion;
-        for (int i=0; i<sourceDimensions; i++) {
-            matrix.setElement(lastRow, i + firstAffectedOrdinate, subMatrix.getElement(targetDimensions, i));
-        }
-        matrix.setElement(lastRow, lastColumn, subMatrix.getElement(targetDimensions, sourceDimensions));
+        matrix.setElements(sources, length, stride, transfer,
+                targetDimensions, 0,                                // Source (row, colum):  last row
+                lastRow,          firstAffectedOrdinate,            // Target (row, column): part of last row
+                1,                sourceDimensions);                // Copy some columns of only 1 row.
+        matrix.setElements(sources, length, stride, transfer,
+                targetDimensions, sourceDimensions,
+                lastRow,          lastColumn,
+                1,                1);
         return matrix;
     }
 
     /**
+     * Returns a matrix with the same content than the given matrix but a different size, assuming an affine transform.
+     * This method can be invoked for adding or removing the <strong>last</strong> dimensions of an affine transform.
+     * More specifically:
+     *
+     * <ul class="verbose">
+     *   <li>If the given {@code numCol} is <var>n</var> less than the number of columns in the given matrix,
+     *       then the <var>n</var> columns <em>before the last column</em> are removed.
+     *       The last column is left unchanged because it is assumed to contain the translation terms.</li>
+     *   <li>If the given {@code numCol} is <var>n</var> more than the number of columns in the given matrix,
+     *       then <var>n</var> columns are inserted <em>before the last column</em>.
+     *       All values in the new columns will be zero.</li>
+     *   <li>If the given {@code numRow} is <var>n</var> less than the number of rows in the given matrix,
+     *       then the <var>n</var> rows <em>before the last row</em> are removed.
+     *       The last row is left unchanged because it is assumed to contain the usual [0 0 0 … 1] terms.</li>
+     *   <li>If the given {@code numRow} is <var>n</var> more than the number of rows in the given matrix,
+     *       then <var>n</var> rows are inserted <em>before the last row</em>.
+     *       The corresponding offset and scale factors will be 0 and 1 respectively.
+     *       In other words, new dimensions are propagated unchanged.</li>
+     * </ul>
+     *
+     * @param  matrix  the matrix to resize. This matrix will never be changed.
+     * @param  numRow  the new number of rows. This is equal to the desired number of target dimensions plus 1.
+     * @param  numCol  the new number of columns. This is equal to the desired number of source dimensions plus 1.
+     * @return a new matrix of the given size, or the given {@code matrix} if no resizing was needed.
+     */
+    public static Matrix resizeAffine(final Matrix matrix, int numRow, int numCol) {
+        ArgumentChecks.ensureNonNull         ("matrix", matrix);
+        ArgumentChecks.ensureStrictlyPositive("numRow", numRow);
+        ArgumentChecks.ensureStrictlyPositive("numCol", numCol);
+        int srcRow = matrix.getNumRow();
+        int srcCol = matrix.getNumCol();
+        if (numRow == srcRow && numCol == srcCol) {
+            return matrix;
+        }
+        final int      stride  = srcCol;
+        final int      length  = srcCol * srcRow;
+        final double[] sources = getExtendedElements(matrix);
+        final DoubleDouble transfer = (sources.length > length) ? new DoubleDouble() : null;
+        final MatrixSIS resized = createZero(numRow, numCol, transfer != null);
+        final int copyRow = Math.min(--numRow, --srcRow);
+        final int copyCol = Math.min(--numCol, --srcCol);
+        for (int j=copyRow; j<numRow; j++) {
+            resized.setElement(j, j, 1);
+        }
+        resized.setElements(sources, length, stride, transfer, 0,      0,       0,      0,       copyRow, copyCol);    // Shear and scale terms.
+        resized.setElements(sources, length, stride, transfer, 0,      srcCol,  0,      numCol,  copyRow, 1);          // Translation column.
+        resized.setElements(sources, length, stride, transfer, srcRow, 0,       numRow, 0,       1,       copyCol);    // Last row.
+        resized.setElements(sources, length, stride, transfer, srcRow, srcCol,  numRow, numCol,  1,       1);          // Last row.
+        return resized;
+    }
+
+    /**
      * Returns {@code true} if the given matrix is likely to use extended precision.
      * A value of {@code true} is not a guarantee that the matrix uses extended precision,
      * but a value of {@code false} is a guarantee that it does not.
@@ -671,6 +743,17 @@ public final class Matrices extends Stat
     }
 
     /**
+     * Returns the elements of the given matrix, together with error terms if available.
+     */
+    private static double[] getExtendedElements(final Matrix matrix) {
+        if (matrix instanceof ExtendedPrecisionMatrix) {
+            return ((ExtendedPrecisionMatrix) matrix).getExtendedElements();
+        } else {
+            return MatrixSIS.castOrCopy(matrix).getElements();
+        }
+    }
+
+    /**
      * Creates a new matrix which is a copy of the given matrix.
      *
      * <div class="note"><b>Implementation note:</b>

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -249,6 +249,41 @@ public abstract class MatrixSIS implemen
     public abstract void setElements(final double[] elements);
 
     /**
+     * Sets elements in a sub-region of this matrix, optionally including error terms.
+     *
+     * @param source    Row-major values as given by {@link ExtendedPrecisionMatrix#getExtendedElements()}.
+     * @param length    Number of elements ({@code numRow} × {@code numCol}) in the source matrix, not including error terms.
+     * @param stride    Number of columns in the source matrix, used for computing indices in {@code source} array.
+     * @param srcRow    Index of the first row from the {@code source} to copy in {@code this}.
+     * @param srcCol    Index of the first column from the {@code source} to copy in {@code this}.
+     * @param dstRow    Index of the first row in {@code this} where to copy the {@code source} values.
+     * @param dstCol    Index of the first column in {@code this} where to copy the {@code source} values.
+     * @param numRow    Number of rows to copy.
+     * @param numCol    Number of columns to copy.
+     * @param transfer  If both {@code source} and {@code this} use extended precision,
+     *                  the temporary object to use for transferring values. Otherwise {@code null}.
+     */
+    final void setElements(final double[] source, final int length, final int stride, final DoubleDouble transfer,
+                           int srcRow, final int srcCol,
+                           int dstRow, final int dstCol,
+                           int numRow, final int numCol)
+    {
+        while (--numRow >= 0) {
+            final int valueOffset = srcRow*stride + srcCol;
+            for (int i=0; i<numCol; i++) {
+                if (transfer != null) {
+                    transfer.setFrom(source, valueOffset + i, length);
+                    set(dstRow, dstCol + i, transfer);
+                } else {
+                    setElement(dstRow, dstCol + i, source[valueOffset + i]);
+                }
+            }
+            srcRow++;
+            dstRow++;
+        }
+    }
+
+    /**
      * Sets this matrix to the values of another matrix.
      * The given matrix must have the same size.
      *

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -56,7 +56,7 @@
  * <div class="section"><cite>Early binding</cite> versus <cite>late binding</cite> implementations</div>
  * There is sometime multiple ways of transforming coordinates for a given pair of source and target CRS.
  * For example the {@linkplain org.apache.sis.referencing.datum.BursaWolfParameters Bursa-Wolf parameters}
- * may vary depending on the area of interest, like in the transformations from NAD27 to NAD83.
+ * may vary depending on the area of interest, like in the transformations from NAD27 to WGS84.
  * Even for a fixed set of Bursa-Wolf parameter, there is various ways to use them (<cite>Molodensky</cite>,
  * <cite>Abridged Molodensky</cite>, <cite>Geocentric translation</cite>, <cite>etc.</cite>).
  *
@@ -86,7 +86,7 @@
  * exists in the form of the {@link org.apache.sis.referencing.datum.DefaultGeodeticDatum#getBursaWolfParameters()}
  * method for those who really need it. This means that when searching for a coordinate operation between a given
  * pair of CRS, Apache SIS will query {@link org.apache.sis.referencing.factory.sql.EPSGFactory} before to try to
- * {@linkplain org.apache.sis.referencing.operation.CoordinateOperationInference infer the operation path by itelf}.
+ * {@linkplain org.apache.sis.referencing.operation.CoordinateOperationFinder infer the operation path by itelf}.
  * The {@link org.apache.sis.referencing.operation.CoordinateOperationContext} can be used for further refinements,
  * for example by specifying the area of interest.
  *

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -150,4 +150,82 @@ public abstract class AbstractMathTransf
     public MathTransform1D inverse() throws NoninvertibleTransformException {
         return (MathTransform1D) super.inverse();
     }
+
+    /**
+     * Base class for implementation of inverse math transforms.
+     * This inner class is the inverse of the enclosing {@link AbstractMathTransform1D}.
+     *
+     * <div class="section">Serialization</div>
+     * Instances of this class are serializable only if the enclosing math transform is also serializable.
+     * Serialized math transforms are not guaranteed to be compatible with future SIS versions.
+     * Serialization, if allowed, should be used only for short term storage or RMI between applications
+     * running the same SIS version.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.7
+     * @version 0.7
+     * @module
+     */
+    protected abstract class Inverse extends AbstractMathTransform.Inverse implements MathTransform1D {
+        /**
+         * Serial number for inter-operability with different versions.
+         */
+        private static final long serialVersionUID = 2018412413506158560L;
+
+        /**
+         * Constructs an inverse math transform.
+         */
+        protected Inverse() {
+            AbstractMathTransform1D.this.super();
+        }
+
+        /**
+         * Returns the enclosing math transform.
+         */
+        @Override
+        public MathTransform1D inverse() {
+            return (MathTransform1D) super.inverse();
+        }
+
+        /**
+         * Transforms a single point in the given array and opportunistically computes its derivative if requested.
+         * The default implementation delegates to {@link #transform(double)} and potentially to {@link #derivative(double)}.
+         * Subclasses may override this method for performance reason.
+         *
+         * @return {@inheritDoc}
+         * @throws TransformException {@inheritDoc}
+         */
+        @Override
+        public Matrix transform(final double[] srcPts, final int srcOff,
+                                final double[] dstPts, final int dstOff,
+                                final boolean derivate) throws TransformException
+        {
+            final double ordinate = srcPts[srcOff];
+            if (dstPts != null) {
+                dstPts[dstOff] = transform(ordinate);
+            }
+            return derivate ? new Matrix1(derivative(ordinate)) : null;
+        }
+
+        /**
+         * Gets the derivative of this transform at a point. The default implementation ensures that
+         * {@code point} is one-dimensional, then delegates to {@link #derivative(double)}.
+         *
+         * @param  point The coordinate point where to evaluate the derivative, or {@code null}.
+         * @return The derivative at the specified point (never {@code null}).
+         * @throws MismatchedDimensionException if {@code point} does not have the expected dimension.
+         * @throws TransformException if the derivative can not be evaluated at the specified point.
+         */
+        @Override
+        public Matrix derivative(final DirectPosition point) throws TransformException {
+            final double ordinate;
+            if (point == null) {
+                ordinate = Double.NaN;
+            } else {
+                ensureDimensionMatches("point", 1, point);
+                ordinate = point.getOrdinate(0);
+            }
+            return new Matrix1(derivative(ordinate));
+        }
+    }
 }

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -61,6 +61,7 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.referencing.j2d.ParameterizedAffine;
 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.system.Loggers;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.parameter.DefaultParameterValueGroup;
@@ -270,7 +271,7 @@ public class DefaultMathTransformFactory
          *
          * Wrapping the ServiceLoader in a LazySet avoid this issue.
          */
-        this(new LazySet<OperationMethod>(ServiceLoader.load(OperationMethod.class)));
+        this(new Providers());
     }
 
     /**
@@ -387,7 +388,10 @@ public class DefaultMathTransformFactory
         ArgumentChecks.ensureNonEmpty("identifier", identifier);
         OperationMethod method = methodsByName.get(identifier);
         if (method == null) {
-            method = ReferencingServices.getInstance().getOperationMethod(methods, identifier);
+            final ReferencingServices services = ReferencingServices.getInstance();
+            synchronized (methods) {
+                method = services.getOperationMethod(methods, identifier);
+            }
             if (method == null) {
                 throw new NoSuchIdentifierException(Errors.format(Errors.Keys.NoSuchOperationMethod_1, identifier), identifier);
             }
@@ -845,25 +849,60 @@ public class DefaultMathTransformFactory
          * if available. This method writes semi-major and semi-minor parameter values only if they do not
          * already exists in the given parameters.
          *
+         * <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.
          * @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.
          * @throws IllegalArgumentException if the operation fails because a parameter has a unrecognized name or an
          *         illegal value.
+         *
+         * @see #getCompletedParameters()
          */
-        final RuntimeException setEllipsoids(final OperationMethod method) throws IllegalArgumentException {
-            ensureCompatibleParameters(false);
+        @SuppressWarnings("null")
+        final RuntimeException completeParameters(OperationMethod method, final ParameterValueGroup userParams)
+                throws IllegalArgumentException
+        {
+            provider   = method;
+            parameters = userParams;
+            /*
+             * Get the operation method for the appropriate number of dimensions. For example the default Molodensky
+             * operation expects two-dimensional source and target CRS. If a given CRS is three-dimensional, we need
+             * a provider variant which will not concatenate a "geographic 3D to 2D" operation before the Molodensky
+             * one. It is worth to perform this check only if the provider is a subclass of DefaultOperationMethod,
+             * since it needs to override the 'redimension(int, int)' method.
+             */
+            if (method instanceof DefaultOperationMethod && method.getClass() != DefaultOperationMethod.class) {
+                final Integer sourceDim = (sourceCS != null) ? sourceCS.getDimension() : method.getSourceDimensions();
+                final Integer targetDim = (targetCS != null) ? targetCS.getDimension() : method.getTargetDimensions();
+                if (sourceDim != null && targetDim != null) {
+                    method = ((DefaultOperationMethod) method).redimension(sourceDim, targetDim);
+                    if (method instanceof MathTransformProvider) {
+                        provider = method;
+                    }
+                }
+            }
+            ensureCompatibleParameters(false);      // Invoke only after we set 'provider' to its final instance.
+            /*
+             * Get a mask telling us if we need to set parameters for the source and/or target ellipsoid.
+             * This information should preferably be given by the provider. But if the given provider is
+             * not a SIS implementation, use as a fallback whether ellipsoids are provided. This fallback
+             * may be less reliable.
+             */
             int n;
-            if (method instanceof AbstractProvider) {
-                n = ((AbstractProvider) method).getEllipsoidsMask();
+            if (provider instanceof AbstractProvider) {
+                n = ((AbstractProvider) provider).getEllipsoidsMask();
             } else {
-                // Fallback used only when the information is not available in
-                // a more reliable way from AbstractProvider.getEllipsoidsMask().
                 n = 0;
                 if (sourceEllipsoid != null) n  = 1;
                 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.
+             */
             switch (n) {
                 case 0: return null;
                 case 1: return setEllipsoid(getSourceEllipsoid(), Constants.SEMI_MAJOR, Constants.SEMI_MINOR, true, null);
@@ -872,7 +911,7 @@ public class DefaultMathTransformFactory
                     RuntimeException failure = null;
                     if (sourceCS != null) try {
                         ensureCompatibleParameters(true);
-                        final ParameterValue<?> p = parameters.parameter("dim");
+                        final ParameterValue<?> p = parameters.parameter("dim");    // Really 'parameters', not 'userParams'.
                         if (p.getValue() == null) {
                             p.setValue(sourceCS.getDimension());
                         }
@@ -987,10 +1026,9 @@ public class DefaultMathTransformFactory
                  * since the standard place where to provide this information is in the ellipsoid object.
                  */
                 if (context != null) {
-                    context.provider   = method;
-                    context.parameters = parameters;
-                    failure = context.setEllipsoids(method);
+                    failure = context.completeParameters(method, parameters);
                     parameters = context.parameters;
+                    method     = context.provider;
                 }
                 transform = ((MathTransformProvider) method).createMathTransform(this, parameters);
             } catch (RuntimeException exception) {  // (IllegalArgumentException | IllegalStateException) on the JDK7 branch.
@@ -1389,9 +1427,9 @@ public class DefaultMathTransformFactory
     public void reload() {
         synchronized (methods) {
             methodsByName.clear();
-            Iterable<? extends OperationMethod> m = methods;
+            final Iterable<? extends OperationMethod> m = methods;
             if (m instanceof LazySet<?>) { // Workaround for JDK bug. See DefaultMathTransformFactory() constructor.
-                m = ((LazySet<? extends OperationMethod>) m).reload();
+                ((LazySet<? extends OperationMethod>) m).reload();
             }
             if (m instanceof ServiceLoader<?>) {
                 ((ServiceLoader<?>) m).reload();

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -47,7 +47,7 @@ import static java.lang.Double.doubleToR
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see LogarithmicTransform1D
@@ -60,6 +60,11 @@ class LinearTransform1D extends Abstract
     private static final long serialVersionUID = -7595037195668813000L;
 
     /**
+     * A transform that just reverse the sign of input values.
+     */
+    static final LinearTransform1D NEGATE = new LinearTransform1D(-1, 0);
+
+    /**
      * The value which is multiplied to input values.
      */
     final double scale;
@@ -99,8 +104,9 @@ class LinearTransform1D extends Abstract
      * @see MathTransforms#linear(double, double)
      */
     public static LinearTransform1D create(final double scale, final double offset) {
-        if (offset == 0 && scale == 1) {
-            return IdentityTransform1D.INSTANCE;
+        if (offset == 0) {
+            if (scale == +1) return IdentityTransform1D.INSTANCE;
+            if (scale == -1) return NEGATE;
         }
         if (scale == 0) {
             if (offset == 0) return ConstantTransform1D.ZERO;
@@ -111,6 +117,19 @@ class LinearTransform1D extends Abstract
     }
 
     /**
+     * Creates a constant function having value <var>y</var>, and for which the inverse is <var>x</var>.
+     *
+     * @since 0.7
+     */
+    static LinearTransform1D constant(final double x, final double y) {
+        final LinearTransform1D tr = create(0, y);
+        if (!Double.isNaN(x)) {
+            tr.inverse = create(0, x);
+        }
+        return tr;
+    }
+
+    /**
      * Returns the parameter descriptors for this math transform.
      */
     @Override

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -147,6 +147,33 @@ public final class MathTransforms extend
     }
 
     /**
+     * Creates a transform for the <i>y=f(x)</i> function where <var>y</var> are computed by a linear interpolation.
+     * Both {@code preimage} (the <var>x</var>) and {@code values} (the <var>y</var>) arguments can be null:
+     *
+     * <ul>
+     *   <li>If both {@code preimage} and {@code values} arrays are non-null, then the must have the same length.</li>
+     *   <li>If both {@code preimage} and {@code values} arrays are null, then this method returns the identity transform.</li>
+     *   <li>If only {@code preimage} is null, then the <var>x</var> values are taken as {0, 1, 2, …, {@code values.length} - 1}.</li>
+     *   <li>If only {@code values} is null, then the <var>y</var> values are taken as {0, 1, 2, …, {@code preimage.length} - 1}.</li>
+     * </ul>
+     *
+     * All {@code preimage} elements shall be real numbers (not NaN) sorted in increasing or decreasing order.
+     * Elements in the {@code values} array do not need to be ordered, but the returned transform will be invertible
+     * only if all values are real numbers sorted in increasing or decreasing order.
+     * Furthermore the returned transform is affine (i.e. implement the {@link LinearTransform} interface)
+     * if the interval between each {@code preimage} and {@code values} element is constant.
+     *
+     * @param preimage the input values (<var>x</var>) in the function domain, or {@code null}.
+     * @param values the output values (<var>y</var>) in the function range, or {@code null}.
+     * @return the <i>y=f(x)</i> function.
+     *
+     * @since 0.7
+     */
+    public static MathTransform1D interpolate(final double[] preimage, final double[] values) {
+        return LinearInterpolator1D.create(preimage, values);
+    }
+
+    /**
      * Puts together a list of independent math transforms, each of them operating on a subset of ordinate values.
      * This method is often used for defining 4-dimensional (<var>x</var>,<var>y</var>,<var>z</var>,<var>t</var>)
      * transform as an aggregation of 3 simpler transforms operating on (<var>x</var>,<var>y</var>), (<var>z</var>)

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java?rev=1740152&r1=1740151&r2=1740152&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] Wed Apr 20 14:53:31 2016
@@ -26,19 +26,41 @@
  * postal address. This package is the root for both kinds, with an emphasis on the one for coordinates.</p>
  *
  * <div class="section">Fetching geodetic object instances</div>
- * Geodetic objects can be instantiated either directly by specifying all information to a factory method
- * or constructor, or indirectly by specifying the identifier of an entry in a database. In particular,
- * the <a href="http://www.epsg.org">EPSG</a> database provides definitions for many geodetic objects,
+ * Geodetic objects can be instantiated either
+ * {@linkplain org.apache.sis.referencing.factory.GeodeticObjectFactory directly by specifying all information to a factory method or constructor}, or
+ * {@linkplain org.apache.sis.referencing.factory.GeodeticAuthorityFactory indirectly by specifying the identifier of an entry in a database}.
+ * In particular, the <a href="http://www.epsg.org">EPSG</a> database provides definitions for many geodetic objects,
  * and Apache SIS provides convenience shortcuts for some of them in the
- * {@link org.apache.sis.referencing.CommonCRS} enumerations.
+ * {@link org.apache.sis.referencing.CommonCRS} enumerations. Other convenience methods are
+ * {@link org.apache.sis.referencing.CRS#forCode(String)},
+ * {@link org.apache.sis.referencing.CRS#fromWKT(String)} and
+ * {@link org.apache.sis.referencing.CRS#fromXML(String)}
+ *
+ * <div class="section">Usage example</div>
+ * The following example projects a (<var>latitude</var>, <var>longitude</var>) coordinate to
+ * a <cite>Universal Transverse Mercator</cite> projection in the zone of the coordinate:
+ *
+ * {@preformat java
+ *   GeographicCRS source = CommonCRS.WGS84.geographic();
+ *   ProjectedCRS  target = CommonCRS.WGS84.UTM(20, 30);                        // 20°N 30°E   (watch out axis order!)
+ *   CoordinateOperation operation = CRS.findOperation(source, target, null);
+ *   if (CRS.getLinearAccuracy(operation) > 100) {
+ *       // If the accuracy is coarser than 100 metres (or any other threshold at application choice)
+ *       // maybe the operation is not suitable. Decide here what to do (throw an exception, etc).
+ *   }
+ *   MathTransform mt = operation.getMathTransform();
+ *   DirectPosition position = new DirectPosition2D(20, 30);                    // 20°N 30°E   (watch out axis order!)
+ *   position = mt.transform(position, position);
+ *   System.out.println(position);
+ * }
  *
  * <div class="section">The EPSG database</div>
  * The EPSG geodetic parameter dataset is a structured database required to:
  *
  * <ul>
- *   <li>define {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem Coordinate Reference Systems}
+ *   <li>define {@linkplain org.apache.sis.referencing.crs.AbstractCRS Coordinate Reference Systems}
  *       (CRS) such that coordinates describe positions unambiguously;</li>
- *   <li>define {@linkplain org.opengis.referencing.operation.CoordinateOperation Coordinate Operations}
+ *   <li>define {@linkplain org.apache.sis.referencing.operation.AbstractCoordinateOperation Coordinate Operations}
  *       that allow coordinates to be changed from one CRS to another CRS.</li>
  * </ul>
  *




Mime
View raw message