Author: desruisseaux
Date: Mon Oct 23 22:46:16 2017
New Revision: 1813110

URL: http://svn.apache.org/viewvc?rev=1813110&view=rev
Log:
Fix change of longitude range when there is also a change of ellipsoid.

Modified:
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
    
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1813110&r1=1813109&r2=1813110&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
 [UTF-8] Mon Oct 23 22:46:16 2017
@@ -1037,7 +1037,7 @@ check:      for (int isTarget=0; ; isTar
     private void setSource(final CoordinateReferenceSystem crs) {
         if (sourceCRS == null) {
             sourceCRS = crs;
-        } else {
+        } else if (!sourceCRS.equals(crs)) {                    // Could be 
defined by ConcatenatedOperation.
             
MetadataUtilities.propertyAlreadySet(AbstractCoordinateOperation.class, 
"setSource", "sourceCRS");
         }
     }
@@ -1056,7 +1056,7 @@ check:      for (int isTarget=0; ; isTar
     private void setTarget(final CoordinateReferenceSystem crs) {
         if (targetCRS == null) {
             targetCRS = crs;
-        } else {
+        } else if (!targetCRS.equals(crs)) {                    // Could be 
defined by ConcatenatedOperation.
             
MetadataUtilities.propertyAlreadySet(AbstractCoordinateOperation.class, 
"setTarget", "targetCRS");
         }
     }
@@ -1120,4 +1120,11 @@ check:      for (int isTarget=0; ; isTar
             
MetadataUtilities.propertyAlreadySet(AbstractCoordinateOperation.class, 
"setScope", "scope");
         }
     }
+
+    /**
+     * Invoked by JAXB after unmarshalling.
+     */
+    void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
+        computeTransientFields();
+    }
 }

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java?rev=1813110&r1=1813109&r2=1813110&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
 [UTF-8] Mon Oct 23 22:46:16 2017
@@ -70,7 +70,7 @@ import static org.apache.sis.util.Utilit
  * {@link DefaultPassThroughOperation}.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.6
  * @module
  */
@@ -513,7 +513,9 @@ class AbstractSingleOperation extends Ab
      *
      * @see <a href="http://issues.apache.org/jira/browse/SIS-291";>SIS-291</a>
      */
-    private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
+    @Override
+    final void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
+        super.afterUnmarshal(unmarshaller, parent);
         final CoordinateReferenceSystem sourceCRS = super.getSourceCRS();
         final CoordinateReferenceSystem targetCRS = super.getTargetCRS();
         if (transform == null && sourceCRS != null && targetCRS != null && 
parameters != null) try {

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java?rev=1813110&r1=1813109&r2=1813110&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
 [UTF-8] Mon Oct 23 22:46:16 2017
@@ -52,7 +52,7 @@ import static org.apache.sis.util.Utilit
  * reference system associated with the concatenated operation.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.6
  * @module
  */
@@ -113,18 +113,36 @@ final class DefaultConcatenatedOperation
             throw new 
IllegalArgumentException(Errors.getResources(properties).getString(
                     Errors.Keys.TooFewOccurrences_2, 2, 
CoordinateOperation.class));
         }
+        initialize(properties, operations, mtFactory);
+        checkDimensions(properties);
+    }
+
+    /**
+     * Initializes the {@link #sourceCRS}, {@link #targetCRS} and {@link 
#operations} fields.
+     * If the source or target CRS is already non-null (which may happen on 
JAXB unmarshalling),
+     * leaves that CRS unchanged.
+     *
+     * @param  properties   the properties specified at construction time, or 
{@code null} if unknown.
+     * @param  operations   the operations to concatenate.
+     * @param  mtFactory    the math transform factory to use, or {@code null} 
for not performing concatenation.
+     * @throws FactoryException if the factory can not concatenate the math 
transforms.
+     */
+    private void initialize(final Map<String,?>         properties,
+                            final CoordinateOperation[] operations,
+                            final MathTransformFactory  mtFactory)
+            throws FactoryException
+    {
         final List<CoordinateOperation> flattened = new 
ArrayList<>(operations.length);
-        initialize(properties, operations, flattened, mtFactory,
-                (coordinateOperationAccuracy == null), (domainOfValidity == 
null));
+        final CoordinateReferenceSystem crs = initialize(properties, 
operations, flattened, mtFactory,
+                (sourceCRS == null), (coordinateOperationAccuracy == null), 
(domainOfValidity == null));
+        if (targetCRS == null) {
+            targetCRS = crs;
+        }
         /*
          * At this point we should have flattened.size() >= 2, except if some 
operations
          * were omitted because their associated math transform were identity 
operation.
          */
-        operations      = flattened.toArray(new 
CoordinateOperation[flattened.size()]);
-        this.operations = UnmodifiableArrayList.wrap(operations);
-        this.sourceCRS  = operations[0].getSourceCRS();
-        this.targetCRS  = operations[operations.length - 1].getTargetCRS();
-        checkDimensions(properties);
+        this.operations = UnmodifiableArrayList.wrap(flattened.toArray(new 
CoordinateOperation[flattened.size()]));
     }
 
     /**
@@ -162,17 +180,20 @@ final class DefaultConcatenatedOperation
      * @param  operations   the operations to concatenate.
      * @param  flattened    the destination list in which to add the {@code 
SingleOperation} instances.
      * @param  mtFactory    the math transform factory to use, or {@code null} 
for not performing concatenation.
+     * @param  setSource    {@code true} for setting the {@link #sourceCRS} on 
the very first CRS (regardless if null or not).
      * @param  setAccuracy  {@code true} for setting the {@link 
#coordinateOperationAccuracy} field.
      * @param  setDomain    {@code true} for setting the {@link 
#domainOfValidity} field.
+     * @return the last target CRS, regardless if null or not.
      * @throws FactoryException if the factory can not concatenate the math 
transforms.
      */
-    private void initialize(final Map<String,?>             properties,
-                            final CoordinateOperation[]     operations,
-                            final List<CoordinateOperation> flattened,
-                            final MathTransformFactory      mtFactory,
-                            boolean                         setAccuracy,
-                            boolean                         setDomain)
-            throws FactoryException
+    private CoordinateReferenceSystem initialize(
+            final Map<String,?>             properties,
+            final CoordinateOperation[]     operations,
+            final List<CoordinateOperation> flattened,
+            final MathTransformFactory      mtFactory,
+            boolean                         setSource,
+            boolean                         setAccuracy,
+            boolean                         setDomain) throws FactoryException
     {
         CoordinateReferenceSystem previous = null;
         for (int i=0; i<operations.length; i++) {
@@ -182,17 +203,19 @@ final class DefaultConcatenatedOperation
              * Verify consistency of user argument: for each coordinate 
operation, the number of dimensions of the
              * source CRS shall be equals to the number of dimensions of the 
target CRS in the previous operation.
              */
-            if (previous != null) {
-                final CoordinateReferenceSystem next = op.getSourceCRS();
-                if (next != null) {
-                    final int dim1 = 
previous.getCoordinateSystem().getDimension();
-                    final int dim2 = next.getCoordinateSystem().getDimension();
-                    if (dim1 != dim2) {
-                        throw new 
IllegalArgumentException(Errors.getResources(properties).getString(
-                                Errors.Keys.MismatchedDimension_3, 
"operations[" + i + "].sourceCRS", dim1, dim2));
-                    }
+            final CoordinateReferenceSystem next = op.getSourceCRS();
+            if (previous != null && next != null) {
+                final int dim1 = previous.getCoordinateSystem().getDimension();
+                final int dim2 = next.getCoordinateSystem().getDimension();
+                if (dim1 != dim2) {
+                    throw new 
IllegalArgumentException(Errors.getResources(properties).getString(
+                            Errors.Keys.MismatchedDimension_3, "operations[" + 
i + "].sourceCRS", dim1, dim2));
                 }
             }
+            if (setSource) {
+                setSource = false;
+                sourceCRS = next;                                              
 // Take even if null.
+            }
             previous = op.getTargetCRS();                                      
 // For next iteration cycle.
             /*
              * Now that we have verified the CRS dimensions, we should be able 
to concatenate the transforms.
@@ -207,11 +230,11 @@ final class DefaultConcatenatedOperation
                 final List<? extends CoordinateOperation> children = 
((ConcatenatedOperation) op).getOperations();
                 @SuppressWarnings("SuspiciousToArrayCall")
                 final CoordinateOperation[] asArray = children.toArray(new 
CoordinateOperation[children.size()]);
-                initialize(properties, asArray, flattened, (step == null) ? 
mtFactory : null, setAccuracy, setDomain);
+                initialize(properties, asArray, flattened, (step == null) ? 
mtFactory : null, false, setAccuracy, setDomain);
             } else if (!step.isIdentity()) {
                 flattened.add(op);
             }
-            if (mtFactory != null) {
+            if (mtFactory != null && step != null) {
                 transform = (transform != null) ? 
mtFactory.createConcatenatedTransform(transform, step) : step;
             }
             /*
@@ -248,6 +271,7 @@ final class DefaultConcatenatedOperation
                 }
             }
         }
+        return previous;
     }
 
     /**
@@ -388,7 +412,6 @@ final class DefaultConcatenatedOperation
      * Returns the operations to marshal. We use this private methods instead 
than annotating
      * {@link #getOperations()} in order to force JAXB to invoke the setter 
method on unmarshalling.
      */
-    @SuppressWarnings("SuspiciousToArrayCall")
     @XmlElement(name = "coordOperation", required = true)
     private CoordinateOperation[] getSteps() {
         final List<? extends CoordinateOperation> operations = getOperations();
@@ -399,9 +422,6 @@ final class DefaultConcatenatedOperation
      * Invoked by JAXB for setting the operations.
      */
     private void setSteps(final CoordinateOperation[] steps) throws 
FactoryException {
-        final List<CoordinateOperation> flattened = new 
ArrayList<>(steps.length);
-        initialize(null, steps, flattened, 
DefaultFactories.forBuildin(MathTransformFactory.class),
-                (coordinateOperationAccuracy == null), (domainOfValidity == 
null));
-        operations = UnmodifiableArrayList.wrap(flattened.toArray(new 
CoordinateOperation[flattened.size()]));
+        initialize(null, steps, 
DefaultFactories.forBuildin(MathTransformFactory.class));
     }
 }

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java?rev=1813110&r1=1813109&r2=1813110&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
 [UTF-8] Mon Oct 23 22:46:16 2017
@@ -20,6 +20,7 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.ServiceLoader;
 import org.opengis.util.FactoryException;
 import org.opengis.util.NoSuchIdentifierException;
@@ -44,6 +45,8 @@ import org.apache.sis.internal.util.Coll
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.referencing.LazySet;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
 import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
 import 
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
@@ -676,6 +679,13 @@ next:   for (int i=components.size(); --
     public CoordinateOperation createConcatenatedOperation(final Map<String,?> 
properties,
             final CoordinateOperation... operations) throws FactoryException
     {
+        /*
+         * If the user specified a single operation, there is no need to 
create a ConcatenatedOperation;
+         * the operation to return will be the specified one. The metadata 
given in argument are ignored
+         * on the assumption that the single operation has more complete 
metadata (in particular an EPSG
+         * code, in which case we do not want to modify any other metadata in 
order to stay compliant
+         * with EPSG definition).
+         */
         if (operations != null && operations.length == 1) {
             return operations[0];
         }
@@ -688,14 +698,39 @@ next:   for (int i=components.size(); --
         /*
          * Verifies again the number of single operations.  We may have a 
singleton if some operations
          * were omitted because their associated math transform were identity. 
This happen for example
-         * if a "Geographic 3D to 2D conversion" as been redimensioned to a 
"3D to 3D" operation.
+         * if a "Geographic 3D to 2D conversion" has been redimensioned to a 
"3D to 3D" operation.
          */
         final List<? extends CoordinateOperation> co = op.getOperations();
-        if (co.size() == 1) {
-            assert op.getMathTransform().equals(co.get(0).getMathTransform()) 
: op;
-            return co.get(0);
+        if (co.size() != 1) {
+            return pool.unique(op);
         }
-        return pool.unique(op);
+        final CoordinateOperation single = co.get(0);
+        assert op.getMathTransform().equals(single.getMathTransform()) : op;
+        if (!Objects.equals(single.getSourceCRS(), op.getSourceCRS()) ||
+            !Objects.equals(single.getTargetCRS(), op.getTargetCRS()))
+        {
+            /*
+             * The CRS of the single operation may be different than the CRS 
of the concatenated operation
+             * if the first or the last operation was an identity operation. 
It happens for example if the
+             * sole purpose of an operation step was to change the longitude 
range from [-180 … +180]° to
+             * [0 … 360]°: the MathTransform is identity (because Apache SIS 
does not handle those changes
+             * in MathTransform; we handle that elsewhere, for example in the 
Envelopes utility class),
+             * but omitting the transform should not cause the lost of the CRS 
with desired longitude range.
+             */
+            if (single instanceof SingleOperation) {
+                final Map<String,Object> merge = new HashMap<>(
+                        IdentifiedObjects.getProperties(single, 
CoordinateOperation.IDENTIFIERS_KEY));
+                merge.put(ReferencingServices.PARAMETERS_KEY, 
((SingleOperation) single).getParameterValues());
+                if (single instanceof AbstractIdentifiedObject) {
+                    merge.put(ReferencingServices.OPERATION_TYPE_KEY, 
((AbstractIdentifiedObject) single).getInterface());
+                }
+                merge.putAll(properties);
+                return createSingleOperation(merge, op.getSourceCRS(), 
op.getTargetCRS(),
+                        AbstractCoordinateOperation.getInterpolationCRS(op),
+                        ((SingleOperation) single).getMethod(), 
single.getMathTransform());
+            }
+        }
+        return single;
     }
 
     /**

Modified: 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java?rev=1813110&r1=1813109&r2=1813110&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
 [UTF-8] Mon Oct 23 22:46:16 2017
@@ -190,11 +190,33 @@ public final strictfp class EnvelopesTes
     @Test
     @DependsOnMethod("testAxisRangeChange")
     public void testAxisRangeChange3D() throws FactoryException, 
TransformException {
+        testAxisRangeChange3D(HardCodedCRS.WGS84);
+    }
+
+    /**
+     * Tests a change of longitude axis range together with change of 
ellipsoid. This is the same test
+     * than {@link #testAxisRangeChange3D()} with an additional complexity: a 
change of ellipsoid.
+     * This causes the execution of different code branches in {@code 
ConcatenatedOperation} creation.
+     *
+     * @throws FactoryException if an error occurred while creating the 
operation.
+     * @throws TransformException if an error occurred while transforming the 
envelope.
+     *
+     * @since 0.8
+     */
+    @Test
+    @DependsOnMethod("testAxisRangeChange3D")
+    public void testAxisRangeChangeWithDatumShift() throws FactoryException, 
TransformException {
+        testAxisRangeChange3D(HardCodedCRS.SPHERE);
+    }
+
+    /**
+     * Implementation of {@link #testAxisRangeChange3D()} and {@link 
#testAxisRangeChangeWithDatumShift()}.
+     */
+    private void testAxisRangeChange3D(final GeographicCRS targetCRS) throws 
FactoryException, TransformException {
         final GeneralEnvelope envelope  = new GeneralEnvelope(new double[] { 
-0.5, -90, 1000},
                                                               new double[] 
{354.5, +90, 1002});
         envelope.setCoordinateReferenceSystem(CRS.compound(
                 
HardCodedCRS.WGS84.forConvention(AxesConvention.POSITIVE_RANGE), 
HardCodedCRS.TIME));
-        final GeographicCRS  targetCRS = HardCodedCRS.WGS84;
         final GeneralEnvelope expected = createFromExtremums(targetCRS, -0.5, 
-90, -5.5, 90);
         assertEnvelopeEquals(expected, Envelopes.transform(envelope, 
targetCRS), STRICT, STRICT);
         /*


Reply via email to