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); /*