sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1740177 [6/8] - in /sis/trunk: ./ 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/sis-fea...
Date Wed, 20 Apr 2016 17:40:13 GMT
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=1740177&r1=1740176&r2=1740177&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 Apr 20 17:40:11 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.
      *
@@ -485,7 +550,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.
          *
@@ -565,6 +630,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());
@@ -602,14 +670,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.
@@ -618,7 +688,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
      */
@@ -627,7 +697,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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] Wed Apr 20 17:40:11 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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -34,6 +34,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;
 
@@ -280,6 +282,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/trunk/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?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/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 Apr 20 17:40:11 2016
@@ -19,13 +19,23 @@ package org.apache.sis.referencing.opera
 import java.util.Map;
 import java.util.HashMap;
 import javax.xml.bind.annotation.XmlTransient;
+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.referencing.ReferenceIdentifier;
+import org.opengis.metadata.Identifier;
 
 
 /**
@@ -58,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);
+            }
         }
-        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);
-        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);
     }
 }

Copied: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java (from r1740152, sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java)
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java?p2=sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java&p1=sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java&r1=1740152&r2=1740177&rev=1740177&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -26,6 +26,9 @@ import org.apache.sis.util.logging.Loggi
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 
+// Branch-dependent imports
+import org.apache.sis.referencing.crs.DefaultParametricCRS;
+
 
 /**
  * Information about the relationship between a source component and a target component
@@ -54,7 +57,7 @@ final class SubOperationInfo {
         {GeodeticCRS.class},
         {VerticalCRS.class, GeodeticCRS.class},
         {TemporalCRS.class},
-        {ParametricCRS.class},
+        {DefaultParametricCRS.class},
         {EngineeringCRS.class},
         {ImageCRS.class}
     };
@@ -65,7 +68,7 @@ final class SubOperationInfo {
      */
     private static Class<?> type(SingleCRS crs) {
         while (crs instanceof GeneralDerivedCRS) {
-            crs = ((GeneralDerivedCRS) crs).getBaseCRS();
+            crs = (SingleCRS) ((GeneralDerivedCRS) crs).getBaseCRS();
         }
         return crs.getClass();
     }
@@ -127,7 +130,7 @@ final class SubOperationInfo {
                                 if (failure == null) {
                                     failure = exception;
                                 } else {
-                                    failure.addSuppressed(exception);
+                                    // failure.addSuppressed(exception);
                                 }
                                 continue;
                             }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java [UTF-8] Wed Apr 20 17:40:11 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/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=1740177&r1=1740176&r2=1740177&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 Apr 20 17:40:11 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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] Wed Apr 20 17:40:11 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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] Wed Apr 20 17:40:11 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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java [UTF-8] Wed Apr 20 17:40:11 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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Wed Apr 20 17:40:11 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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java [UTF-8] Wed Apr 20 17:40:11 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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] Wed Apr 20 17:40:11 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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] Wed Apr 20 17:40:11 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>
  *

Modified: sis/trunk/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod [UTF-8] Wed Apr 20 17:40:11 2016
@@ -2,6 +2,7 @@
 # Heavier classes (e.g. having more dependencies) or classes less likely to be used, should be last.
 org.apache.sis.internal.referencing.provider.Affine
 org.apache.sis.internal.referencing.provider.Geographic3Dto2D
+org.apache.sis.internal.referencing.provider.Geographic2Dto3D
 org.apache.sis.internal.referencing.provider.GeographicOffsets
 org.apache.sis.internal.referencing.provider.GeographicOffsets2D
 org.apache.sis.internal.referencing.provider.VerticalOffset
@@ -11,12 +12,12 @@ org.apache.sis.internal.referencing.prov
 org.apache.sis.internal.referencing.provider.CoordinateFrameRotation
 org.apache.sis.internal.referencing.provider.GeographicToGeocentric
 org.apache.sis.internal.referencing.provider.GeocentricToGeographic
-org.apache.sis.internal.referencing.provider.GeocentricTranslation2D
 org.apache.sis.internal.referencing.provider.GeocentricTranslation3D
-org.apache.sis.internal.referencing.provider.PositionVector7Param2D
+org.apache.sis.internal.referencing.provider.GeocentricTranslation2D
 org.apache.sis.internal.referencing.provider.PositionVector7Param3D
-org.apache.sis.internal.referencing.provider.CoordinateFrameRotation2D
+org.apache.sis.internal.referencing.provider.PositionVector7Param2D
 org.apache.sis.internal.referencing.provider.CoordinateFrameRotation3D
+org.apache.sis.internal.referencing.provider.CoordinateFrameRotation2D
 org.apache.sis.internal.referencing.provider.Molodensky
 org.apache.sis.internal.referencing.provider.AbridgedMolodensky
 org.apache.sis.internal.referencing.provider.Equirectangular
@@ -42,3 +43,4 @@ org.apache.sis.internal.referencing.prov
 org.apache.sis.internal.referencing.provider.NTv2
 org.apache.sis.internal.referencing.provider.NADCON
 org.apache.sis.internal.referencing.provider.FranceGeocentricInterpolation
+org.apache.sis.internal.referencing.provider.Interpolation1D

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -90,8 +90,9 @@ public final strictfp class ReferencingU
      */
     @Test
     public void testGetPropertiesForModifiedCRS() {
-        assertEquals("WGS 84", getPropertiesForModifiedCRS(HardCodedCRS.WGS84_3D).get(IdentifiedObject.NAME_KEY));
-        assertEquals("WGS 84", getPropertiesForModifiedCRS(HardCodedCRS.GEOID_4D).get(IdentifiedObject.NAME_KEY));
+        assertEquals("WGS 84",      getPropertiesForModifiedCRS(HardCodedCRS.WGS84_3D).get(IdentifiedObject.NAME_KEY));
+        assertEquals("WGS 84",      getPropertiesForModifiedCRS(HardCodedCRS.GEOID_4D).get(IdentifiedObject.NAME_KEY));
+        assertEquals("NTF (Paris)", getPropertiesForModifiedCRS(HardCodedCRS.NTF)     .get(IdentifiedObject.NAME_KEY));
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/PoleRotationMock.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/PoleRotationMock.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/PoleRotationMock.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/PoleRotationMock.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -36,7 +36,7 @@ import org.apache.sis.parameter.Paramete
  * @module
  */
 @SuppressWarnings("serial")
-public final strictfp class PoleRotationMock extends MockProvider {
+public final strictfp class PoleRotationMock extends ProviderMock {
     /**
      * The group of all parameters expected by this coordinate operation.
      */

Copied: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java (from r1740152, sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java)
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java?p2=sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java&p1=sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java&r1=1740152&r2=1740177&rev=1740177&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -126,10 +126,10 @@ public final strictfp class ProvidersTes
      * Ensures that every parameter instance is unique. Actually this test is not strong requirement.
      * This is only for sharing existing resources by avoiding unnecessary objects duplication.
      *
-     * @throws ReflectiveOperationException if the instantiation of a service provider failed.
+     * @throws Exception if the instantiation of a service provider failed.
      */
     @Test
-    public void ensureParameterUniqueness() throws ReflectiveOperationException {
+    public void ensureParameterUniqueness() throws Exception {
         final Map<GeneralParameterDescriptor, String> groupNames = new IdentityHashMap<GeneralParameterDescriptor, String>();
         final Map<GeneralParameterDescriptor, GeneralParameterDescriptor> parameters = new HashMap<GeneralParameterDescriptor, GeneralParameterDescriptor>();
         final Map<Object, Object> namesAndIdentifiers = new HashMap<Object, Object>();

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/SeismicBinGridMock.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/SeismicBinGridMock.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/SeismicBinGridMock.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/SeismicBinGridMock.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -38,7 +38,7 @@ import org.apache.sis.parameter.Paramete
  * @module
  */
 @SuppressWarnings("serial")
-public final strictfp class SeismicBinGridMock extends MockProvider {
+public final strictfp class SeismicBinGridMock extends ProviderMock {
     /**
      * The group of all parameters expected by this coordinate operation.
      */

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TopocentricConversionMock.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TopocentricConversionMock.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TopocentricConversionMock.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TopocentricConversionMock.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -36,7 +36,7 @@ import org.apache.sis.parameter.Paramete
  * @module
  */
 @SuppressWarnings("serial")
-public final strictfp class TopocentricConversionMock extends MockProvider {
+public final strictfp class TopocentricConversionMock extends ProviderMock {
     /**
      * The group of all parameters expected by this coordinate operation.
      */

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/GeodeticObjectParserTest.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -91,14 +91,14 @@ public final strictfp class GeodeticObje
     }
 
     /**
-     * Parses the given text.
+     * Parses the given text. It is caller's responsibility to verify if some warnings have been emitted.
      *
      * @param  type The expected object type.
      * @param  text The WKT string to parse.
      * @return The parsed object.
      * @throws ParseException if an error occurred during the parsing.
      */
-    private <T> T parse(final Class<T> type, final String text) throws ParseException {
+    private <T> T parseIgnoreWarnings(final Class<T> type, final String text) throws ParseException {
         if (parser == null) {
             newParser(Convention.DEFAULT);
         }
@@ -106,13 +106,26 @@ public final strictfp class GeodeticObje
         final Object obj = parser.parseObject(text, position);
         assertEquals("errorIndex", -1, position.getErrorIndex());
         assertEquals("index", text.length(), position.getIndex());
-        assertNull("warnings", parser.getAndClearWarnings(obj));
-        assertTrue("ignoredElements", parser.ignoredElements.isEmpty());
         assertInstanceOf("GeodeticObjectParser.parseObject", type, obj);
         return type.cast(obj);
     }
 
     /**
+     * Parses the given text and ensure that no warnings have been emitted.
+     *
+     * @param  type The expected object type.
+     * @param  text The WKT string to parse.
+     * @return The parsed object.
+     * @throws ParseException if an error occurred during the parsing.
+     */
+    private <T> T parse(final Class<T> type, final String text) throws ParseException {
+        final T obj = parseIgnoreWarnings(type, text);
+        assertNull("warnings", parser.getAndClearWarnings(obj));
+        assertTrue("ignoredElements", parser.ignoredElements.isEmpty());
+        return obj;
+    }
+
+    /**
      * Asserts that the name and (optionally) the EPSG identifier of the given object are equal to the given strings.
      * As a special case if the given EPSG code is 0, then this method verifies that the given object has no identifier.
      *
@@ -438,6 +451,48 @@ public final strictfp class GeodeticObje
     }
 
     /**
+     * Tests parsing of a CRS with a prime meridian having implicit unit in grads but axes having explicit unit
+     * in degrees. The specification in §8.2.2 (ii) said:
+     *
+     *     "(snip) the prime meridian’s {@literal <irm longitude>} value shall be given in
+     *     the same angular units as those for the horizontal axes of the geographic CRS."
+     *
+     * Consequently we expect the prime meridian to be in decimal degrees even if the WKT used in this test has
+     * an {@code Unit[“grade”, 0.015707963267948967]} element, because this WK also declare the axis as being in
+     * degrees. Since this can be confusing, we expect the parser to emit a warning.
+     *
+     * @throws ParseException if the parsing failed.
+     */
+    @Test
+    @DependsOnMethod("testGeographicWithParisMeridian")
+    public void testMismatchedAngularUnits() throws ParseException {
+        String wkt = "GeodeticCRS[“NTF (Paris)”,\n" +
+                     "  Datum[“Nouvelle Triangulation Française (Paris)”,\n" +
+                     "    Ellipsoid[“Clarke 1880 (IGN)”, 6378249.2, 293.4660212936269]],\n" +
+                     "    PrimeMeridian[“Paris”, 2.33722917],\n" +              // In units of the longitude axis.
+                     "  CS[ellipsoidal, 2],\n" +
+                     "    Axis[“Latitude (φ)”, NORTH, Unit[“degree”, 0.017453292519943295]],\n" +
+                     "    Axis[“Longitude (λ)”, EAST, Unit[“degree”, 0.017453292519943295]],\n" +
+                     "    Unit[“grade”, 0.015707963267948967]\n," +             // Inconsistent with axis units.
+                     "  Id[“EPSG”, 4807]]";
+
+        GeographicCRS crs = parseIgnoreWarnings(GeographicCRS.class, wkt);
+        final Warnings warnings = parser.getAndClearWarnings(crs);
+        assertTrue("ignoredElements", parser.ignoredElements.isEmpty());
+        assertNotNull("warnings", warnings);
+        assertEquals("warnings.numMessages", 1, warnings.getNumMessages());
+
+        assertNameAndIdentifierEqual("NTF (Paris)", 4807, crs);
+        PrimeMeridian pm = crs.getDatum().getPrimeMeridian();
+        assertEquals("angularUnit", NonSI.DEGREE_ANGLE, pm.getAngularUnit());
+        assertEquals("greenwichLongitude", 2.33722917, pm.getGreenwichLongitude(), STRICT);
+        EllipsoidalCS cs = crs.getCoordinateSystem();
+        assertEquals("dimension", 2, cs.getDimension());
+        assertAxisEquals(AxisNames.GEODETIC_LATITUDE,  "φ", AxisDirection.NORTH,  -90,  +90, NonSI.DEGREE_ANGLE, RangeMeaning.EXACT,      cs.getAxis(0));
+        assertAxisEquals(AxisNames.GEODETIC_LONGITUDE, "λ", AxisDirection.EAST,  -180, +180, NonSI.DEGREE_ANGLE, RangeMeaning.WRAPAROUND, cs.getAxis(1));
+    }
+
+    /**
      * Implementation of {@link #testGeographicCRS()} and {@link #testWithAxisSwapping()}.
      * This test expects no {@code AUTHORITY} element on any component.
      *
@@ -1041,17 +1096,14 @@ public final strictfp class GeodeticObje
     @Test
     @DependsOnMethod("testGeographicWithImplicitAxes")
     public void testWarnings() throws ParseException {
-        newParser(Convention.DEFAULT);
-        final ParsePosition position = new ParsePosition(0);
-        final GeographicCRS crs = (GeographicCRS) parser.parseObject(
+        final GeographicCRS crs = parseIgnoreWarnings(GeographicCRS.class,
                "GEOGCS[“WGS 84”,\n" +
                "  DATUM[“World Geodetic System 1984”,\n" +
                "    SPHEROID[“WGS84”, 6378137.0, 298.257223563, Ext1[“foo”], Ext2[“bla”]]],\n" +
                "    PRIMEM[“Greenwich”, 0.0, Intruder[“unknown”]],\n" +
-               "  UNIT[“degree”, 0.017453292519943295], Intruder[“foo”]]", position);
+               "  UNIT[“degree”, 0.017453292519943295], Intruder[“foo”]]");
 
         verifyGeographicCRS(0, crs);
-        assertEquals("errorIndex", -1, position.getErrorIndex());
         final Warnings warnings = parser.getAndClearWarnings(crs);
         assertNotNull("warnings", warnings);
 

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -29,6 +29,7 @@ import org.opengis.parameter.ParameterVa
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.cs.HardCodedCS;
+import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
 import org.apache.sis.internal.referencing.GeodeticObjectBuilder;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.system.Loggers;
@@ -88,6 +89,25 @@ public final strictfp class DefaultProje
     private static final String XML_FILE = "ProjectedCRS.xml";
 
     /**
+     * Creates a projected CRS and verifies its parameters.
+     * Verifies also that the constructor does not accept invalid base CRS.
+     *
+     * @throws FactoryException if the CRS creation failed.
+     */
+    @Test
+    public void testConstructor() throws FactoryException {
+        final ProjectedCRS crs = create(HardCodedCRS.NTF);
+        verifyParameters(crs.getConversionFromBase().getParameterValues());
+        try {
+            create(HardCodedCRS.WGS84_3D);
+            fail("Should not accept a three-dimensional base geodetic CRS.");
+        } catch (InvalidGeodeticParameterException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("Lambert Conic Conformal (1SP)"));
+        }
+    }
+
+    /**
      * Creates the "NTF (Paris) / Lambert zone II" CRS. The prime meridian is always in grades,
      * but the axes can be in degrees or in grades depending if the {@code baseCRS} argument is
      * {@link HardCodedCRS.NTF_NORMALIZED_AXES} or {@link HardCodedCRS.NTF} respectively.
@@ -109,11 +129,24 @@ public final strictfp class DefaultProje
     }
 
     /**
+     * Verifies the parameters of a {@code ProjectedCRS} created by the {@link #create(GeographicCRS)} method
+     * or something equivalent.
+     */
+    private static void verifyParameters(final ParameterValueGroup pg) {
+        assertEquals("Latitude of natural origin",    52,          pg.parameter("Latitude of natural origin")    .doubleValue(NonSI.GRADE), STRICT);
+        assertEquals("Longitude of natural origin",    0,          pg.parameter("Longitude of natural origin")   .doubleValue(NonSI.GRADE), STRICT);
+        assertEquals("Scale factor at natural origin", 0.99987742, pg.parameter("Scale factor at natural origin").doubleValue(),            STRICT);
+        assertEquals("False easting",             600000,          pg.parameter("False easting")                 .doubleValue(SI.METRE),    STRICT);
+        assertEquals("False northing",           2200000,          pg.parameter("False northing")                .doubleValue(SI.METRE),    STRICT);
+    }
+
+    /**
      * Tests WKT 1 formatting.
      *
      * @throws FactoryException if the CRS creation failed.
      */
     @Test
+    @DependsOnMethod("testConstructor")
     public void testWKT1() throws FactoryException {
         final ProjectedCRS crs = create(HardCodedCRS.NTF);
         assertWktEquals(Convention.WKT1,
@@ -447,16 +480,10 @@ public final strictfp class DefaultProje
         assertAxisDirectionsEqual("coordinateSystem", crs.getCoordinateSystem(), AxisDirection.EAST, AxisDirection.NORTH);
 
         final Projection conversion = crs.getConversionFromBase();
-        final ParameterValueGroup pg = conversion.getParameterValues();
         assertEpsgNameAndIdentifierEqual("Lambert zone II", 18082, conversion);
         assertEpsgNameAndIdentifierEqual("Lambert Conic Conformal (1SP)", 9801, conversion.getMethod());
-        assertEquals("Latitude of natural origin",    52,          pg.parameter("Latitude of natural origin")    .doubleValue(NonSI.GRADE), STRICT);
-        assertEquals("Longitude of natural origin",    0,          pg.parameter("Longitude of natural origin")   .doubleValue(NonSI.GRADE), STRICT);
-        assertEquals("Scale factor at natural origin", 0.99987742, pg.parameter("Scale factor at natural origin").doubleValue(),            STRICT);
-        assertEquals("False easting",             600000,          pg.parameter("False easting")                 .doubleValue(SI.METRE),    STRICT);
-        assertEquals("False northing",           2200000,          pg.parameter("False northing")                .doubleValue(SI.METRE),    STRICT);
-
         assertNotNull("conversion.mathTransform", conversion.getMathTransform());
+        verifyParameters(conversion.getParameterValues());
         /*
          * Test marshalling and compare with the original file. The comparison ignores the <gml:name> nodes because the
          * marshalled CRS contains many operation method and parameter aliases which were not in the original XML file.

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java?rev=1740177&r1=1740176&r2=1740177&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java [UTF-8] Wed Apr 20 17:40:11 2016
@@ -36,7 +36,7 @@ import static org.apache.sis.referencing
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.5
+ * @version 0.7
  * @module
  */
 public final strictfp class HardCodedCRS {
@@ -109,6 +109,38 @@ public final strictfp class HardCodedCRS
             HardCodedDatum.NTF, HardCodedCS.GEODETIC_2D);
 
     /**
+     * A three-dimensional geographic coordinate reference system using the Tokyo datum.
+     * This CRS uses (<var>longitude</var>, <var>latitude</var>, <var>height</var>) ordinates
+     * with longitude values increasing towards the East, latitude values increasing towards
+     * the North and ellipsoidal eight increasing toward up.
+     * The angular units are decimal degrees and the linear units are metres.
+     *
+     * <p>This CRS is equivalent to {@code EPSG:4301} except for axis order and the addition
+     * of ellipsoidal height.</p>
+     *
+     * @since 0.7
+     */
+    public static final DefaultGeographicCRS TOKYO = new DefaultGeographicCRS(
+            Collections.singletonMap(DefaultGeographicCRS.NAME_KEY, "Tokyo"),
+            HardCodedDatum.TOKYO, HardCodedCS.GEODETIC_3D);
+
+    /**
+     * A two-dimensional geographic coordinate reference system using the JGD2000 datum.
+     * This CRS uses (<var>longitude</var>, <var>latitude</var>, <var>height</var>) ordinates
+     * with longitude values increasing towards the East, latitude values increasing towards
+     * the North and ellipsoidal eight increasing toward up.
+     * The angular units are decimal degrees and the linear units are metres.
+     *
+     * <p>This CRS is equivalent to {@code EPSG:4612} except for axis order and the addition
+     * of ellipsoidal height.</p>
+     *
+     * @since 0.7
+     */
+    public static final DefaultGeographicCRS JGD2000 = new DefaultGeographicCRS(
+            Collections.singletonMap(DefaultGeographicCRS.NAME_KEY, "JGD2000"),
+            HardCodedDatum.JGD2000, HardCodedCS.GEODETIC_3D);
+
+    /**
      * A two-dimensional geographic coordinate reference system using a spherical datum.
      * This CRS uses (<var>longitude</var>, <var>latitude</var>) ordinates with longitude values
      * increasing towards the East and latitude values increasing towards the North.
@@ -161,6 +193,18 @@ public final strictfp class HardCodedCRS
             getProperties(HardCodedCS.ELLIPSOIDAL_HEIGHT), HardCodedDatum.ELLIPSOID, HardCodedCS.ELLIPSOIDAL_HEIGHT);
 
     /**
+     * A vertical coordinate reference system using ellipsoidal datum.
+     * Ellipsoidal heights are measured along the normal to the ellipsoid used in the definition of horizontal datum.
+     *
+     * <p>This is not a valid vertical CRS according ISO 19111.
+     * This CRS is used by Apache SIS for internal calculation.</p>
+     *
+     * @since 0.7
+     */
+    public static final DefaultVerticalCRS ELLIPSOIDAL_HEIGHT_cm = new DefaultVerticalCRS(
+            getProperties(HardCodedCS.ELLIPSOIDAL_HEIGHT_cm), HardCodedDatum.ELLIPSOID, HardCodedCS.ELLIPSOIDAL_HEIGHT_cm);
+
+    /**
      * A vertical coordinate reference system using Mean Sea Level datum.
      */
     public static final DefaultVerticalCRS GRAVITY_RELATED_HEIGHT = new DefaultVerticalCRS(




Mime
View raw message