sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1736701 - in /sis/branches/JDK8/core/sis-referencing/src: main/java/org/apache/sis/referencing/operation/ test/java/org/apache/sis/referencing/operation/ test/java/org/apache/sis/test/suite/
Date Sat, 26 Mar 2016 17:38:57 GMT
Author: desruisseaux
Date: Sat Mar 26 17:38:57 2016
New Revision: 1736701

URL: http://svn.apache.org/viewvc?rev=1736701&view=rev
Log:
Search of coordinate operation path now take in account map projections.
Begin tests.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
  (with props)
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java?rev=1736701&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
(added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
[UTF-8] Sat Mar 26 17:38:57 2016
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation;
+
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.IdentifiedObject;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Classes;
+
+// Branch-dependent imports
+import java.util.Objects;
+
+
+/**
+ * A pair of source-destination {@link CoordinateReferenceSystem} objects.
+ * Used as key in hash map.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+final class CRSPair {
+    /**
+     * The source and target CRS.
+     */
+    final CoordinateReferenceSystem sourceCRS, targetCRS;
+
+    /**
+     * Creates a {@code CRSPair} for the specified source and target CRS.
+     */
+    public CRSPair(final CoordinateReferenceSystem sourceCRS,
+                   final CoordinateReferenceSystem targetCRS)
+    {
+        this.sourceCRS = sourceCRS;
+        this.targetCRS = targetCRS;
+    }
+
+    /**
+     * Returns the hash code value.
+     */
+    @Override
+    public int hashCode() {
+        return sourceCRS.hashCode() * 31 + targetCRS.hashCode();
+    }
+
+    /**
+     * Compares this pair to the specified object for equality.
+     *
+     * {@note We perform the CRS comparison using strict equality, not using
+     *        <code>equalsIgnoreMetadata</code>, because metadata matter since
+     *        they are attributes of the <code>CoordinateOperation</code>
+     *        object to be created.}
+     */
+    @Override
+    public boolean equals(final Object object) {
+        if (object instanceof CRSPair) {
+            final CRSPair that = (CRSPair) object;
+            return Objects.equals(this.sourceCRS, that.sourceCRS) &&
+                   Objects.equals(this.targetCRS, that.targetCRS);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a name for the given object, truncating it if needed.
+     */
+    static String shortName(final IdentifiedObject object) {
+        String name = IdentifiedObjects.getName(object, null);
+        if (name == null) {
+            name = Classes.getShortClassName(object);
+        } else {
+            int i = 30;                 // Arbitrary length threshold.
+            if (name.length() >= i) {
+                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() + '…';
+            }
+        }
+        return name;
+    }
+
+    /**
+     * Return a string representation of this key.
+     */
+    @Override
+    public String toString() {
+        return shortName(sourceCRS) + " → " + shortName(targetCRS);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java?rev=1736701&r1=1736700&r2=1736701&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
[UTF-8] Sat Mar 26 17:38:57 2016
@@ -37,6 +37,8 @@ import org.opengis.parameter.ParameterDe
 import org.apache.sis.internal.metadata.ReferencingServices;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
+import org.apache.sis.internal.referencing.provider.GeographicToGeocentric;
+import org.apache.sis.internal.referencing.provider.GeocentricToGeographic;
 import org.apache.sis.internal.referencing.provider.GeocentricAffine;
 import org.apache.sis.internal.referencing.provider.Affine;
 import org.apache.sis.internal.system.DefaultFactories;
@@ -57,8 +59,6 @@ import org.apache.sis.referencing.operat
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
 import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.util.CharSequences;
-import org.apache.sis.util.Classes;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
 
@@ -171,6 +171,12 @@ public class CoordinateOperationInferenc
     private double desiredAccuracy;
 
     /**
+     * The pair of source and target CRS for which we already searched a coordinate operation.
+     * This is used as a safety against infinite recursivity.
+     */
+    private final Map<CRSPair,Boolean> previousSearches;
+
+    /**
      * Creates a new instance for the given factory and context.
      *
      * @param factory The factory to use for creating coordinate operations.
@@ -188,29 +194,18 @@ public class CoordinateOperationInferenc
             desiredAccuracy = context.getDesiredAccuracy();
             bbox            = context.getGeographicBoundingBox();
         }
-    }
-
-    /**
-     * If the domain of interest was not set, defines it to the domain of validity of the
given CRS.
-     */
-    private void updateDomainOfInterest(final CoordinateReferenceSystem sourceCRS,
-                                        final CoordinateReferenceSystem targetCRS)
-    {
-        if (bbox == null) {
-            bbox = Extents.intersection(CRS.getGeographicBoundingBox(sourceCRS),
-                                        CRS.getGeographicBoundingBox(targetCRS));
-            areaOfInterest = CoordinateOperationContext.setGeographicBoundingBox(areaOfInterest,
bbox);
-        }
+        previousSearches = new HashMap<>(4);
     }
 
     /**
      * Infers an operation for conversion or transformation between two coordinate reference
systems.
      * This method inspects the given CRS and delegates the work to one or many {@code createOperationStep(…)}
methods.
-     * This method fails if no path between the CRS is found.
+     * Note that some {@code createOperationStep(…)} methods may callback this {@code createOperation(…)}
method with
+     * another source or target CRS.
      *
-     * @param  sourceCRS Input coordinate reference system.
-     * @param  targetCRS Output coordinate reference system.
-     * @return A coordinate operation from {@code sourceCRS} to {@code targetCRS}.
+     * @param  sourceCRS  input coordinate reference system.
+     * @param  targetCRS  output coordinate reference system.
+     * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}.
      * @throws OperationNotFoundException if no operation path was found from {@code sourceCRS}
to {@code targetCRS}.
      * @throws FactoryException if the operation creation failed for some other reason.
      */
@@ -220,6 +215,18 @@ public class CoordinateOperationInferenc
     {
         ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS);
         ArgumentChecks.ensureNonNull("targetCRS", targetCRS);
+        if (!previousSearches.isEmpty()) {
+            // TODO: verify here if the path is defined in EPSG database.
+        }
+        final CRSPair key = new CRSPair(sourceCRS, targetCRS);
+        if (previousSearches.put(key, Boolean.TRUE) != null) {
+            throw new FactoryException(Errors.format(Errors.Keys.RecursiveCreateCallForCode_2,
CoordinateOperation.class, key));
+        }
+        if (bbox == null) {
+            bbox = Extents.intersection(CRS.getGeographicBoundingBox(sourceCRS),
+                                        CRS.getGeographicBoundingBox(targetCRS));
+            areaOfInterest = CoordinateOperationContext.setGeographicBoundingBox(areaOfInterest,
bbox);
+        }
         ////////////////////////////////////////////////////////////////////////////////
         ////                                                                        ////
         ////                        Compound  →  various CRS                        ////
@@ -245,19 +252,27 @@ public class CoordinateOperationInferenc
         }
         ////////////////////////////////////////////////////////////////////////////////
         ////                                                                        ////
-        ////                 Projected  →  Projected or Geographic                  ////
-        ////                                                                        ////
-        ////      This check needs to be done before the check for DerivedCRS       ////
-        ////      because ProjectedCRS is a particular kind of derived CRS.         ////
+        ////                       Derived  →  any Single CRS                       ////
         ////                                                                        ////
         ////////////////////////////////////////////////////////////////////////////////
-        if (sourceCRS instanceof ProjectedCRS) {
-            final ProjectedCRS source = (ProjectedCRS) sourceCRS;
-            if (targetCRS instanceof ProjectedCRS) {
-//              return createOperationStep(source, (ProjectedCRS) targetCRS);
+        if (sourceCRS instanceof GeneralDerivedCRS) {
+            final GeneralDerivedCRS source = (GeneralDerivedCRS) sourceCRS;
+            if (targetCRS instanceof GeneralDerivedCRS) {
+                return createOperationStep(source, (GeneralDerivedCRS) targetCRS);
             }
-            if (targetCRS instanceof GeographicCRS) {
-//              return createOperationStep(source, (GeographicCRS) targetCRS);
+            if (targetCRS instanceof SingleCRS) {
+                return createOperationStep(source, (SingleCRS) targetCRS);
+            }
+        }
+        ////////////////////////////////////////////////////////////////////////////////
+        ////                                                                        ////
+        ////                       any Single CRS  →  Derived                       ////
+        ////                                                                        ////
+        ////////////////////////////////////////////////////////////////////////////////
+        if (targetCRS instanceof GeneralDerivedCRS) {
+            final GeneralDerivedCRS target = (GeneralDerivedCRS) targetCRS;
+            if (sourceCRS instanceof SingleCRS) {
+                return createOperationStep((SingleCRS) sourceCRS, target);
             }
         }
         ////////////////////////////////////////////////////////////////////////////////
@@ -270,9 +285,6 @@ public class CoordinateOperationInferenc
             if (targetCRS instanceof GeodeticCRS) {
                 return createOperationStep(source, (GeodeticCRS) targetCRS);
             }
-            if (targetCRS instanceof ProjectedCRS) {
-//              return createOperationStep(source, (ProjectedCRS) targetCRS);
-            }
             if (targetCRS instanceof VerticalCRS) {
 //              return createOperationStep(source, (VerticalCRS) targetCRS);
             }
@@ -303,6 +315,89 @@ public class CoordinateOperationInferenc
     }
 
     /**
+     * Creates an operation from an arbitrary single CRS to a derived coordinate reference
system.
+     * Conversions from {@code GeographicCRS} to {@code ProjectedCRS} are also handled by
this method,
+     * since projected CRS are a special kind of {@code GeneralDerivedCRS}.
+     *
+     * <p>The default implementation constructs the following operation chain:</p>
+     * <blockquote><code>sourceCRS  →  {@linkplain GeneralDerivedCRS#getBaseCRS()
baseCRS}  →  targetCRS</code></blockquote>
+     *
+     * where the conversion from {@code baseCRS} to {@code targetCRS} is obtained from
+     * <code>targetCRS.{@linkplain GeneralDerivedCRS#getConversionFromBase() getConversionFromBase()}</code>.
+     *
+     * @param  sourceCRS  input coordinate reference system.
+     * @param  targetCRS  output coordinate reference system.
+     * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}.
+     * @throws FactoryException if the operation can not be constructed.
+     */
+    protected CoordinateOperation createOperationStep(final SingleCRS sourceCRS,
+                                                      final GeneralDerivedCRS targetCRS)
+            throws FactoryException
+    {
+        CoordinateOperation step1 = createOperation(sourceCRS, targetCRS.getBaseCRS());
+        return concatenate(step1, targetCRS.getConversionFromBase());
+    }
+
+    /**
+     * Creates an operation from a derived CRS to an arbitrary single coordinate reference
system.
+     * Conversions from {@code ProjectedCRS} to {@code GeographicCRS} are also handled by
this method,
+     * since projected CRS are a special kind of {@code GeneralDerivedCRS}.
+     *
+     * <p>The default implementation constructs the following operation chain:</p>
+     * <blockquote><code>sourceCRS  →  {@linkplain GeneralDerivedCRS#getBaseCRS()
baseCRS}  →  targetCRS</code></blockquote>
+     *
+     * where the conversion from {@code sourceCRS} to {@code baseCRS} is obtained from the
inverse of
+     * <code>sourceCRS.{@linkplain GeneralDerivedCRS#getConversionFromBase() getConversionFromBase()}</code>.
+     *
+     * @param  sourceCRS  input coordinate reference system.
+     * @param  targetCRS  output coordinate reference system.
+     * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}.
+     * @throws FactoryException if the operation can not be constructed.
+     */
+    protected CoordinateOperation createOperationStep(final GeneralDerivedCRS sourceCRS,
+                                                      final SingleCRS targetCRS)
+            throws FactoryException
+    {
+        final SingleCRS base = sourceCRS.getBaseCRS();
+        CoordinateOperation step2 = createOperation(base, targetCRS);
+        CoordinateOperation step1 = sourceCRS.getConversionFromBase();
+        MathTransform transform = step1.getMathTransform();
+        try {
+            transform = transform.inverse();
+        } catch (NoninvertibleTransformException exception) {
+            throw new OperationNotFoundException(notFoundMessage(sourceCRS, base), exception);
+        }
+        step1 = createFromMathTransform(properties(INVERSE_OPERATION), sourceCRS, base, transform,
null, null);
+        return concatenate(step1, step2);
+    }
+
+    /**
+     * Creates an operation between two derived coordinate reference systems.
+     * The default implementation performs three steps:
+     *
+     * <ol>
+     *   <li>Convert from {@code sourceCRS} to its base CRS.</li>
+     *   <li>Convert the source base CRS to target base CRS.</li>
+     *   <li>Convert from the target base CRS to the {@code targetCRS}.</li>
+     * </ol>
+     *
+     * @param  sourceCRS  input coordinate reference system.
+     * @param  targetCRS  output coordinate reference system.
+     * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}.
+     * @throws FactoryException if the operation can not be constructed.
+     */
+    protected CoordinateOperation createOperationStep(final GeneralDerivedCRS sourceCRS,
+                                                      final GeneralDerivedCRS targetCRS)
+            throws FactoryException
+    {
+        final SingleCRS sourceBase = sourceCRS.getBaseCRS();
+        final SingleCRS targetBase = targetCRS.getBaseCRS();
+        return concatenate(createOperation(sourceCRS,  sourceBase),
+                           createOperation(sourceBase, targetBase),
+                           createOperation(targetBase, targetCRS));
+    }
+
+    /**
      * Creates an operation between two geodetic (geographic or geocentric) coordinate reference
systems.
      * The default implementation can:
      *
@@ -324,21 +419,44 @@ public class CoordinateOperationInferenc
                                                       final GeodeticCRS targetCRS)
             throws FactoryException
     {
-        updateDomainOfInterest(sourceCRS, targetCRS);
         final GeodeticDatum sourceDatum = sourceCRS.getDatum();
         final GeodeticDatum targetDatum = targetCRS.getDatum();
         Matrix datumShift = null;
         /*
+         * If the prime meridian is not the same, we will concatenate a longitude rotation
before or after datum shift
+         * (that concatenation will be performed by the customized DefaultMathTransformFactory.Context
created below).
+         * Actually we do not know if the longitude rotation should be before or after datum
shift. But this ambiguity
+         * can usually be ignored because Bursa-Wolf parameters are always used with source
and target prime meridians
+         * set to Greenwich in EPSG dataset 8.9.  For safety, the SIS's DefaultGeodeticDatum
class ensures that if the
+         * prime meridian are not the same, then the target meridian must be Greenwich.
+         */
+        final DefaultMathTransformFactory.Context context = ReferencingUtilities.createTransformContext(
+                sourceCRS, targetCRS, new MathTransformContext(sourceDatum, targetDatum));
+        /*
          * If both CRS use the same datum and the same prime meridian, then the coordinate
operation is only axis
          * swapping, unit conversion or change of coordinate system type (Ellipsoidal ↔
Cartesian ↔ Spherical).
-         * Otherwise (if the datum are not the same), we need to perform a scale, translation
and rotation in
-         * Cartesian space using the Bursa-Wolf parameters. If the user does not require
the best accuracy,
+         * Otherwise (if the datum are not the same), we will need to perform a scale, translation
and rotation
+         * in Cartesian space using the Bursa-Wolf parameters. If the user does not require
the best accuracy,
          * then the Molodensky approximation may be used for avoiding the conversion step
to geocentric CRS.
          */
         Identifier identifier;
-        final DefaultMathTransformFactory mtFactory = factorySIS.getDefaultMathTransformFactory();
+        boolean isGeographicToGeocentric = false;
+        final CoordinateSystem sourceCS = context.getSourceCS();
+        final CoordinateSystem targetCS = context.getTargetCS();
         if (equalsIgnoreMetadata(sourceDatum, targetDatum)) {
-            identifier = AXIS_CHANGES;
+            final boolean isGeocentricToGeographic;
+            isGeographicToGeocentric = (sourceCS instanceof EllipsoidalCS && targetCS
instanceof CartesianCS);
+            isGeocentricToGeographic = (sourceCS instanceof CartesianCS && targetCS
instanceof EllipsoidalCS);
+            /*
+             * Above booleans should never be true in same time. If it nevertheless happen
(we are paranoiac;
+             * maybe a lazy user implemented all interfaces in a single class), do not apply
any geographic ↔
+             * geocentric conversion. Instead do as if the coordinate system types were the
same.
+             */
+            if (isGeocentricToGeographic ^ isGeographicToGeocentric) {
+                identifier = GEOCENTRIC_CONVERSION;
+            } else {
+                identifier = AXIS_CHANGES;
+            }
         } else {
             identifier = ELLIPSOID_CHANGE;
             if (sourceDatum instanceof DefaultGeodeticDatum) {
@@ -349,15 +467,6 @@ public class CoordinateOperationInferenc
             }
         }
         /*
-         * If there is a change of prime meridian, concatenate that change before or after
the datum shift.
-         * Actually we do not know if we should concatenate longitude rotation before or
after datum shift.
-         * But this ambiguity does not apply to EPSG dataset 8.9 because source and target
prime meridians
-         * are always Greenwich. For reducing ambiguity in other cases, the SIS DefaultGeodeticDatum
class
-         * ensures that if the prime meridian are not the same, then the target meridian
must be Greenwich.
-         */
-        final DefaultMathTransformFactory.Context context = ReferencingUtilities.createTransformContext(
-                sourceCRS, targetCRS, new MathTransformContext(sourceDatum, targetDatum));
-        /*
          * Conceptually, all transformations below could done by first converting from the
source coordinate
          * system to geocentric Cartesian coordinates (X,Y,Z), apply an affine transform
represented by the
          * datum shift matrix, then convert from the (X′,Y′,Z′) coordinates to the
target coordinate system.
@@ -365,15 +474,14 @@ public class CoordinateOperationInferenc
          *
          *   1) In the particular where both the source and target CS are ellipsoidal, we
may use the
          *      Molodensky approximation as a shortcut (if the desired accuracy allows).
+         *
          *   2) Even if we really go through the XYZ coordinates without Molodensky approximation,
there is
          *      at least 9 different ways to name this operation depending on whether the
source and target
          *      CRS are geocentric or geographic, 2- or 3-dimensional, whether there is a
translation or not,
          *      the rotation sign, etc. We try to use the most specific name if we can find
one, and fallback
          *      on an arbitrary name only in last resort.
          */
-        final CoordinateSystem sourceCS = context.getSourceCS();
-        final CoordinateSystem targetCS = context.getTargetCS();
-        final Map<String,?> properties = properties(identifier);
+        final DefaultMathTransformFactory mtFactory = factorySIS.getDefaultMathTransformFactory();
         MathTransform before = null, after = null;
         ParameterValueGroup parameters;
         if (datumShift != null) {
@@ -389,15 +497,18 @@ public class CoordinateOperationInferenc
              */
             parameters = GeocentricAffine.createParameters(sourceCS, targetCS, datumShift,
desiredAccuracy >= MOLODENSKY_ACCURACY);
             if (parameters == null) {
-                parameters = TensorParameters.WKT1.createValueGroup(properties, datumShift);
+                parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE),
datumShift);
                 final CoordinateSystem normalized = CommonCRS.WGS84.geocentric().getCoordinateSystem();
                 before = mtFactory.createCoordinateSystemChange(sourceCS, normalized);
                 after  = mtFactory.createCoordinateSystemChange(normalized, targetCS);
                 context.setSource(normalized);
                 context.setTarget(normalized);
             }
+        } else if (identifier == GEOCENTRIC_CONVERSION) {
+            parameters = (isGeographicToGeocentric ? GeographicToGeocentric.PARAMETERS
+                                                   : GeocentricToGeographic.PARAMETERS).createValue();
         } else {
-            parameters = TensorParameters.WKT1.createValueGroup(properties);            
   // Initialized to identity.
+            parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE));
 // Initialized to identity.
             parameters.parameter(Constants.NUM_COL).setValue(sourceCS.getDimension() + 1);
             parameters.parameter(Constants.NUM_ROW).setValue(targetCS.getDimension() + 1);
             before = mtFactory.createCoordinateSystemChange(sourceCS, targetCS);
@@ -424,7 +535,7 @@ public class CoordinateOperationInferenc
                 transform = mtFactory.createConcatenatedTransform(transform, after);
             }
         }
-        return createFromMathTransform(properties, sourceCRS, targetCRS, transform, method,
null);
+        return createFromMathTransform(properties(identifier), sourceCRS, targetCRS, transform,
method, null);
     }
 
     /**
@@ -443,7 +554,6 @@ public class CoordinateOperationInferenc
                                                       final VerticalCRS targetCRS)
             throws FactoryException
     {
-        updateDomainOfInterest(sourceCRS, targetCRS);
         final VerticalDatum sourceDatum = sourceCRS.getDatum();
         final VerticalDatum targetDatum = targetCRS.getDatum();
         if (!equalsIgnoreMetadata(sourceDatum, targetDatum)) {
@@ -474,7 +584,6 @@ public class CoordinateOperationInferenc
                                                       final TemporalCRS targetCRS)
             throws FactoryException
     {
-        updateDomainOfInterest(sourceCRS, targetCRS);
         final TemporalDatum sourceDatum = sourceCRS.getDatum();
         final TemporalDatum targetDatum = targetCRS.getDatum();
         final TimeCS sourceCS = sourceCRS.getCoordinateSystem();
@@ -645,8 +754,6 @@ public class CoordinateOperationInferenc
                                             final CoordinateOperation step2)
             throws FactoryException
     {
-        if (step1 == null)     return step2;
-        if (step2 == null)     return step1;
         if (isIdentity(step1)) return step2;
         if (isIdentity(step2)) return step1;
         final MathTransform mt1 = step1.getMathTransform();
@@ -671,13 +778,38 @@ public class CoordinateOperationInferenc
     }
 
     /**
+     * Concatenates three transformation steps. If the first and/or the last operation is
an {@link #AXIS_CHANGES},
+     * then it will be included as part of the second operation instead of creating a {@link
ConcatenatedOperation}.
+     * If a concatenated operation is created, it will get an automatically generated name.
+     *
+     * @param  step1 The first  step, or {@code null} for the identity operation.
+     * @param  step2 The second step, or {@code null} for the identity operation.
+     * @param  step3 The third  step, or {@code null} for the identity operation.
+     * @return A concatenated operation, or {@code null} if all arguments were null.
+     * @throws FactoryException if the operation can not be constructed.
+     */
+    protected CoordinateOperation concatenate(final CoordinateOperation step1,
+                                              final CoordinateOperation step2,
+                                              final CoordinateOperation step3)
+            throws FactoryException
+    {
+        if (isIdentity(step1)) return concatenate(step2, step3);
+        if (isIdentity(step2)) return concatenate(step1, step3);
+        if (isIdentity(step3)) return concatenate(step1, step2);
+        if (step1.getName() == AXIS_CHANGES) return concatenate(concatenate(step1, step2),
step3);
+        if (step3.getName() == AXIS_CHANGES) return concatenate(step1, concatenate(step2,
step3));
+        final Map<String,?> properties = defaultName(step1.getSourceCRS(), step3.getTargetCRS());
+        return factory.createConcatenatedOperation(properties, step1, step2, step3);
+    }
+
+    /**
      * Returns {@code true} if the specified operation is an identity conversion.
      * This method always returns {@code false} for transformations even if their
      * associated math transform is an identity one, because such transformations
      * are usually datum shift and must be visible.
      */
     private static boolean isIdentity(final CoordinateOperation operation) {
-        return (operation instanceof Conversion) && operation.getMathTransform().isIdentity();
+        return (operation == null) || ((operation instanceof Conversion) && operation.getMathTransform().isIdentity());
     }
 
     /**
@@ -706,32 +838,17 @@ public class CoordinateOperationInferenc
     }
 
     /**
-     * Returns a name for the given object, truncating it if needed.
+     * Returns the given name in a singleton map.
      */
-    private static String shortName(final IdentifiedObject object) {
-        String name = IdentifiedObjects.getName(object, null);
-        if (name == null) {
-            name = Classes.getShortClassName(object);
-        } else {
-            int i = 30;                 // Arbitrary length threshold.
-            if (name.length() >= i) {
-                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() + '…';
-            }
-        }
-        return name;
+    private static Map<String,?> properties(final String name) {
+        return Collections.singletonMap(IdentifiedObject.NAME_KEY, name);
     }
 
     /**
-     * Returns a temporary name for a transformation between two CRS.
+     * Returns a name for a transformation between two CRS.
      */
     private static Map<String,?> defaultName(CoordinateReferenceSystem source, CoordinateReferenceSystem
target) {
-        final String name = shortName(source) + " → " + shortName(target);
-        return Collections.singletonMap(IdentifiedObject.NAME_KEY, name);
+        return properties(CRSPair.shortName(source) + " → " + CRSPair.shortName(target));
     }
 
     /**
@@ -743,6 +860,6 @@ public class CoordinateOperationInferenc
      * @return A default error message.
      */
     private static String notFoundMessage(final IdentifiedObject source, final IdentifiedObject
target) {
-        return Errors.format(Errors.Keys.CoordinateOperationNotFound_2, shortName(source),
shortName(target));
+        return Errors.format(Errors.Keys.CoordinateOperationNotFound_2, CRSPair.shortName(source),
CRSPair.shortName(target));
     }
 }

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java?rev=1736701&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
(added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
[UTF-8] Sat Mar 26 17:38:57 2016
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation;
+
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.apache.sis.referencing.CommonCRS;
+
+// Test dependencies
+import org.apache.sis.referencing.operation.transform.MathTransformTestCase;
+import org.apache.sis.test.DependsOn;
+import org.junit.BeforeClass;
+import org.junit.AfterClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests {@link CoordinateOperationInference}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+@DependsOn({
+    DefaultConversionTest.class,
+    DefaultTransformationTest.class,
+    DefaultPassThroughOperationTest.class,
+    DefaultConcatenatedOperationTest.class
+})
+public final strictfp class CoordinateOperationInferenceTest extends MathTransformTestCase
{
+    /**
+     * The transformation factory to use for testing.
+     */
+    private static DefaultCoordinateOperationFactory factory;
+
+    /**
+     * Creates a new {@link DefaultCoordinateOperationFactory} to use for testing purpose.
+     * The same factory will be used for all tests in this class.
+     */
+    @BeforeClass
+    public static void createFactory() {
+        factory = new DefaultCoordinateOperationFactory();
+    }
+
+    /**
+     * Disposes the factory created by {@link #createFactory()} after all tests have been
executed.
+     */
+    @AfterClass
+    public static void disposeFactory() {
+        factory = null;
+    }
+
+    /**
+     * Makes sure that {@code createOperation(sourceCRS, targetCRS)} returns an identity
transform
+     * when {@code sourceCRS} and {@code targetCRS} are identical.
+     *
+     * @throws FactoryException if the operation can not be created.
+     */
+    @Test
+    public void testIdentityTransform() throws FactoryException {
+        CoordinateReferenceSystem crs = CommonCRS.WGS84.normalizedGeographic();
+        CoordinateOperation operation = factory.createOperation(crs, crs);
+        assertSame("sourceCRS",  crs, operation.getSourceCRS());
+        assertSame("targetCRS",  crs, operation.getTargetCRS());
+        assertTrue("isIdentity", operation.getMathTransform().isIdentity());
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1736701&r1=1736700&r2=1736701&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
[UTF-8] Sat Mar 26 17:38:57 2016
@@ -170,6 +170,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.SingleOperationMarshallingTest.class,
     org.apache.sis.referencing.operation.DefaultPassThroughOperationTest.class,
     org.apache.sis.referencing.operation.DefaultConcatenatedOperationTest.class,
+    org.apache.sis.referencing.operation.CoordinateOperationInferenceTest.class,
     org.apache.sis.referencing.crs.DefaultProjectedCRSTest.class,
     org.apache.sis.referencing.crs.DefaultDerivedCRSTest.class,
     org.apache.sis.referencing.crs.SubTypesTest.class,




Mime
View raw message