You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2020/07/06 14:56:28 UTC
[sis] branch geoapi-4.0 updated (2802071 -> db63678)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git.
from 2802071 Fix wrong adjustement of number of dimensions when the coordinate system dimensions and transform dimensions do not match.
new f7d3c80 Deprecate DefaultOperationMethod.getSource/TargetDimensions(). Those attributes have been removed from ISO 19111:2019. Removing them from SIS would reduce complexity in management of OperationMethods working on variable number of dimensions.
new 2a6666a Following the deprecation of OperationMethod.getSource/TargetDimensions(), remove the dimension check in AbstractSingleOperation. This removal make easier to address https://issues.apache.org/jira/browse/SIS-462
new db63678 Fix a MismatchedDimensionException when a datum shift is applied between a two-dimensional GeographicCRS and a three-dimensional GeographicCRS. This is the first part of https://issues.apache.org/jira/browse/SIS-462
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../java/org/apache/sis/gui/map/StatusBar.java | 51 ++++++---
.../org/apache/sis/gui/referencing/MenuSync.java | 16 ++-
.../referencing/EllipsoidalHeightCombiner.java | 6 +-
.../sis/internal/referencing/provider/Affine.java | 3 +
.../referencing/provider/GeocentricAffine.java | 8 +-
.../GeocentricAffineBetweenGeographic.java | 8 +-
.../referencing/provider/GeodeticOperation.java | 6 +
.../referencing/provider/MapProjection.java | 6 +
.../referencing/provider/MapProjection3D.java | 9 ++
.../operation/AbstractSingleOperation.java | 124 +--------------------
.../operation/CoordinateOperationFinder.java | 48 +++++---
.../referencing/operation/DefaultConversion.java | 6 +-
.../operation/DefaultOperationMethod.java | 37 +++++-
.../transform/DefaultMathTransformFactory.java | 37 +++---
.../referencing/provider/ProviderMock.java | 4 +-
.../operation/AbstractSingleOperationTest.java | 82 --------------
.../operation/DefaultTransformationTest.java | 1 -
.../transform/DefaultMathTransformFactoryTest.java | 12 ++
.../sis/test/suite/ReferencingTestSuite.java | 1 -
19 files changed, 186 insertions(+), 279 deletions(-)
delete mode 100644 core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/AbstractSingleOperationTest.java
[sis] 01/03: Deprecate
DefaultOperationMethod.getSource/TargetDimensions(). Those attributes have
been removed from ISO 19111:2019. Removing them from SIS would reduce
complexity in management of OperationMethods working on variable number of
dimensions.
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit f7d3c80edf1d5c1ec10caeb755ce3dd2cc337828
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Jul 6 12:13:07 2020 +0200
Deprecate DefaultOperationMethod.getSource/TargetDimensions(). Those attributes have been removed from ISO 19111:2019.
Removing them from SIS would reduce complexity in management of OperationMethods working on variable number of dimensions.
---
.../sis/internal/referencing/provider/Affine.java | 3 ++
.../referencing/provider/GeodeticOperation.java | 6 ++++
.../referencing/provider/MapProjection.java | 6 ++++
.../referencing/provider/MapProjection3D.java | 9 ++++++
.../operation/DefaultOperationMethod.java | 37 +++++++++++++++++++++-
.../referencing/provider/ProviderMock.java | 4 +--
6 files changed, 62 insertions(+), 3 deletions(-)
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java
index 348b637..f44fb3b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java
@@ -212,8 +212,11 @@ public final class Affine extends AbstractProvider {
* @param sourceDimensions the desired number of input dimensions.
* @param targetDimensions the desired number of output dimensions.
* @return the redimensioned operation method, or {@code this} if no change is needed.
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
@Override
+ @Deprecated
public OperationMethod redimension(final int sourceDimensions, final int targetDimensions) {
return getProvider(sourceDimensions, targetDimensions, false);
}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeodeticOperation.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeodeticOperation.java
index 9367b3f..d57b33c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeodeticOperation.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeodeticOperation.java
@@ -53,7 +53,10 @@ abstract class GeodeticOperation extends AbstractProvider {
*
* <strong>Do not modify this array after construction</strong>, since the same array is shared by many
* objects and there is no synchronization.
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
+ @Deprecated
final GeodeticOperation[] redimensioned;
/**
@@ -100,8 +103,11 @@ abstract class GeodeticOperation extends AbstractProvider {
* @param sourceDimensions the desired number of input dimensions.
* @param targetDimensions the desired number of output dimensions.
* @return the redimensioned operation method, or {@code null} if none.
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
@Override
+ @Deprecated
public final OperationMethod redimension(final int sourceDimensions, final int targetDimensions) {
if (redimensioned != null && (sourceDimensions & ~1) == 2 && (targetDimensions & ~1) == 2) {
final GeodeticOperation m = redimensioned[((sourceDimensions & 1) << 1) | (targetDimensions & 1)];
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
index e7a1394..6094b46 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
@@ -174,7 +174,10 @@ public abstract class MapProjection extends AbstractProvider {
*
* @see #redimension(int, int)
* @see GeodeticOperation#redimensioned
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
+ @Deprecated
private OperationMethod redimensioned;
/**
@@ -204,8 +207,11 @@ public abstract class MapProjection extends AbstractProvider {
* @return the redimensioned projection method, or {@code this} if no change is needed.
*
* @since 0.8
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
@Override
+ @Deprecated
public final OperationMethod redimension(final int sourceDimensions, final int targetDimensions) {
if (sourceDimensions != 3 || targetDimensions != 3) {
return super.redimension(sourceDimensions, targetDimensions);
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection3D.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection3D.java
index 5d8983f..0796416 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection3D.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection3D.java
@@ -33,7 +33,10 @@ import org.opengis.util.FactoryException;
* @version 0.8
* @since 0.8
* @module
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
+@Deprecated
@XmlTransient
final class MapProjection3D extends AbstractProvider {
/**
@@ -43,7 +46,10 @@ final class MapProjection3D extends AbstractProvider {
/**
* The two-dimensional counterpart of this three-dimensional map projection.
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
+ @Deprecated
private final MapProjection redimensioned;
/**
@@ -57,8 +63,11 @@ final class MapProjection3D extends AbstractProvider {
/**
* Returns this operation method with the specified number of dimensions.
* The number of dimensions can be only 2 or 3, and must be the same for source and target CRS.
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
@Override
+ @Deprecated
public OperationMethod redimension(final int sourceDimensions, final int targetDimensions) {
if (sourceDimensions == 2 && targetDimensions == 2) {
return redimensioned;
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
index f50d2a4..94c68b2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
@@ -117,7 +117,7 @@ import static org.apache.sis.util.ArgumentChecks.*;
* {@link org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory}.
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 1.1
*
* @see DefaultConversion
* @see DefaultTransformation
@@ -166,7 +166,10 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* This field is modified only at unmarshalling time by {@link #setSourceDimensions(Integer)}</p>
*
* @see #getSourceDimensions()
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
+ @Deprecated
private Integer sourceDimensions;
/**
@@ -178,7 +181,10 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* This field is modified only at unmarshalling time by {@link #setTargetDimensions(Integer)}</p>
*
* @see #getTargetDimensions()
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
+ @Deprecated
private Integer targetDimensions;
/**
@@ -232,6 +238,19 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* </tr>
* </table>
*
+ * @param properties set of properties. Shall contain at least {@code "name"}.
+ * @param parameters description of parameters expected by this operation.
+ *
+ * @since 1.1
+ */
+ public DefaultOperationMethod(final Map<String,?> properties,
+ final ParameterDescriptorGroup parameters)
+ {
+ this(properties, null, null, parameters);
+ }
+
+ /**
+ * Constructs an operation method for the given number of dimensions.
* The source and target dimensions may be {@code null} if this method can work
* with any number of dimensions (e.g. <cite>Affine Transform</cite>).
*
@@ -239,7 +258,10 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* @param sourceDimensions number of dimensions in the source CRS of this operation method, or {@code null}.
* @param targetDimensions number of dimensions in the target CRS of this operation method, or {@code null}.
* @param parameters description of parameters expected by this operation.
+ *
+ * @deprecated ISO 19111:2019 removed "source dimensions" and "target dimensions" attributes.
*/
+ @Deprecated
public DefaultOperationMethod(final Map<String,?> properties,
final Integer sourceDimensions,
final Integer targetDimensions,
@@ -450,7 +472,11 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* @return the redimensioned operation method, or {@code null} if the given method was null,
* or {@code method} if no change is needed.
* @throws IllegalArgumentException if the given dimensions are illegal for the given operation method.
+ *
+ * @deprecated Not needed anymore since ISO 19111:2019 removed the
+ * "source dimensions" and "target dimensions" attributes.
*/
+ @Deprecated
public static OperationMethod redimension(OperationMethod method,
final int sourceDimensions, final int targetDimensions)
{
@@ -489,7 +515,10 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* @throws IllegalArgumentException if the given dimensions are illegal for this operation method.
*
* @since 0.6
+ *
+ * @deprecated ISO 19111:2019 removed source/target dimensions attributes.
*/
+ @Deprecated
public OperationMethod redimension(final int sourceDimensions, final int targetDimensions) {
return redimension(this, sourceDimensions, this.sourceDimensions,
targetDimensions, this.targetDimensions);
@@ -568,8 +597,11 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* @return the dimension of source CRS, or {@code null} if unknown.
*
* @see org.apache.sis.referencing.operation.transform.AbstractMathTransform#getSourceDimensions()
+ *
+ * @deprecated This attribute has been removed from ISO 19111:2019.
*/
@Override
+ @Deprecated
@XmlElement(name = "sourceDimensions")
@XmlSchemaType(name = "positiveInteger")
public Integer getSourceDimensions() {
@@ -583,8 +615,11 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* @return the dimension of target CRS, or {@code null} if unknown.
*
* @see org.apache.sis.referencing.operation.transform.AbstractMathTransform#getTargetDimensions()
+ *
+ * @deprecated This attribute has been removed from ISO 19111:2019.
*/
@Override
+ @Deprecated
@XmlElement(name = "targetDimensions")
@XmlSchemaType(name = "positiveInteger")
public Integer getTargetDimensions() {
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProviderMock.java b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProviderMock.java
index 1ab8e13..1d03d13 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProviderMock.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProviderMock.java
@@ -59,8 +59,8 @@ abstract strictfp class ProviderMock extends AbstractProvider {
@Override
public final MathTransform createMathTransform(MathTransformFactory factory, ParameterValueGroup parameters) {
return new AbstractMathTransform() {
- @Override public int getSourceDimensions() {return ProviderMock.this.getSourceDimensions();}
- @Override public int getTargetDimensions() {return ProviderMock.this.getTargetDimensions();}
+ @Override @Deprecated public int getSourceDimensions() {return ProviderMock.this.getSourceDimensions();}
+ @Override @Deprecated public int getTargetDimensions() {return ProviderMock.this.getTargetDimensions();}
@Override
public Matrix transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, boolean derivate) {
throw new UnsupportedOperationException();
[sis] 02/03: Following the deprecation of
OperationMethod.getSource/TargetDimensions(),
remove the dimension check in AbstractSingleOperation. This removal make
easier to address https://issues.apache.org/jira/browse/SIS-462
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 2a6666a8d96492f014031d31caf4f951ba9be384
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Jul 6 15:41:57 2020 +0200
Following the deprecation of OperationMethod.getSource/TargetDimensions(), remove the dimension check in AbstractSingleOperation.
This removal make easier to address https://issues.apache.org/jira/browse/SIS-462
---
.../operation/AbstractSingleOperation.java | 124 +--------------------
.../referencing/operation/DefaultConversion.java | 6 +-
.../operation/AbstractSingleOperationTest.java | 82 --------------
.../operation/DefaultTransformationTest.java | 1 -
.../sis/test/suite/ReferencingTestSuite.java | 1 -
5 files changed, 7 insertions(+), 207 deletions(-)
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
index 69ac527..6f7e81c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
@@ -30,7 +30,6 @@ import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue;
-import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.SingleOperation;
import org.opengis.referencing.operation.OperationMethod;
import org.opengis.referencing.operation.MathTransform;
@@ -39,18 +38,12 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.parameter.Parameters;
import org.apache.sis.parameter.Parameterized;
import org.apache.sis.parameter.DefaultParameterValueGroup;
-import org.apache.sis.referencing.IdentifiedObjects;
-import org.apache.sis.referencing.operation.transform.MathTransforms;
-import org.apache.sis.referencing.operation.transform.PassThroughTransform;
import org.apache.sis.internal.jaxb.referencing.CC_OperationParameterGroup;
import org.apache.sis.internal.jaxb.referencing.CC_OperationMethod;
import org.apache.sis.internal.jaxb.Context;
-import org.apache.sis.internal.referencing.Resources;
import org.apache.sis.internal.referencing.CoordinateOperations;
-import org.apache.sis.internal.referencing.ReferencingUtilities;
import org.apache.sis.internal.metadata.MetadataUtilities;
import org.apache.sis.internal.system.DefaultFactories;
-import org.apache.sis.internal.util.Constants;
import org.apache.sis.util.collection.Containers;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.ArgumentChecks;
@@ -123,7 +116,6 @@ class AbstractSingleOperation extends AbstractCoordinateOperation implements Sin
super(properties, sourceCRS, targetCRS, interpolationCRS, transform);
ArgumentChecks.ensureNonNull("method", method);
ArgumentChecks.ensureNonNull("transform", transform);
- checkDimensions(method, ReferencingUtilities.getDimension(interpolationCRS), transform, properties);
this.method = method;
/*
* Undocumented property, because SIS usually infers the parameters from the MathTransform.
@@ -135,16 +127,13 @@ class AbstractSingleOperation extends AbstractCoordinateOperation implements Sin
/**
* Creates a new coordinate operation initialized from the given properties.
- * It is caller's responsibility to:
+ * It is caller's responsibility to set the following fields:
*
* <ul>
- * <li>Set the following fields:<ul>
- * <li>{@link #sourceCRS}</li>
- * <li>{@link #targetCRS}</li>
- * <li>{@link #transform}</li>
- * <li>{@link #parameters}</li>
- * </ul></li>
- * <li>Invoke {@link #checkDimensions(Map)} after the above-cited fields have been set.</li>
+ * <li>{@link #sourceCRS}</li>
+ * <li>{@link #targetCRS}</li>
+ * <li>{@link #transform}</li>
+ * <li>{@link #parameters}</li>
* </ul>
*/
AbstractSingleOperation(final Map<String,?> properties, final OperationMethod method) {
@@ -169,109 +158,6 @@ class AbstractSingleOperation extends AbstractCoordinateOperation implements Sin
}
/**
- * Checks if an operation method and a math transform have a compatible number of source and target dimensions.
- * In the particular case of a {@linkplain PassThroughTransform pass through transform} with more dimensions
- * than what we would expect from the given method, the check will rather be performed against the
- * {@linkplain PassThroughTransform#getSubTransform() sub transform}.
- * The intent is to allow creation of a three-dimensional {@code ProjectedCRS} with a two-dimensional
- * {@code OperationMethod}, where the third-dimension just pass through.
- *
- * <p>This method tries to locates what seems to be the "core" of the given math transform. The definition
- * of "core" is imprecise and may be adjusted in future SIS versions. The current algorithm is as below:</p>
- *
- * <ul>
- * <li>If the given transform can be decomposed in {@linkplain MathTransforms#getSteps(MathTransform) steps},
- * then the steps for {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes axis
- * swapping and scaling} are ignored.</li>
- * <li>If the given transform or its non-ignorable step is a {@link PassThroughTransform}, then its sub-transform
- * is taken. Only one non-ignorable step may exist, otherwise we do not try to select any of them.</li>
- * </ul>
- *
- * @param method the operation method to compare to the math transform.
- * @param interpDim the number of interpolation dimension, or 0 if none.
- * @param transform the math transform to compare to the operation method.
- * @param properties properties of the caller object being constructed, used only for formatting error message.
- * @throws IllegalArgumentException if the number of dimensions are incompatible.
- */
- static void checkDimensions(final OperationMethod method, final int interpDim, MathTransform transform,
- final Map<String,?> properties) throws IllegalArgumentException
- {
- int actual = transform.getSourceDimensions();
- Integer expected = method.getSourceDimensions();
- if (expected != null && actual > expected + interpDim) {
- /*
- * The given MathTransform uses more dimensions than the OperationMethod.
- * Try to locate one and only one sub-transform, ignoring axis swapping and scaling.
- */
- MathTransform subTransform = null;
- for (final MathTransform step : MathTransforms.getSteps(transform)) {
- if (!isIgnorable(step)) {
- if (subTransform == null && step instanceof PassThroughTransform) {
- subTransform = ((PassThroughTransform) step).getSubTransform();
- } else {
- subTransform = null;
- break;
- }
- }
- }
- if (subTransform != null) {
- transform = subTransform;
- actual = transform.getSourceDimensions();
- }
- }
- /*
- * Now verify if the MathTransform dimensions are equal to the OperationMethod ones,
- * ignoring null java.lang.Integer instances. We do not specify whether the method
- * dimensions should include the interpolation dimensions or not, so we accept both.
- */
- int isTarget = 0; // 0 == false: the wrong dimension is the source one.
- if (expected == null || (actual == expected) || (actual == expected + interpDim)) {
- actual = transform.getTargetDimensions();
- expected = method.getTargetDimensions();
- if (expected == null || (actual == expected) || (actual == expected + interpDim)) {
- return;
- }
- isTarget = 1; // 1 == true: the wrong dimension is the target one.
- }
- /*
- * At least one dimension does not match. In principle this is an error, but we make an exception for the
- * "Affine parametric transformation" (EPSG:9624). The reason is that while OGC define that transformation
- * as two-dimensional, it can easily be extended to any number of dimensions. Note that Apache SIS already
- * has special handling for this operation (a TensorParameters dedicated class, etc.)
- */
- if (!IdentifiedObjects.isHeuristicMatchForName(method, Constants.AFFINE)) {
- throw new IllegalArgumentException(Resources.forProperties(properties).getString(
- Resources.Keys.MismatchedTransformDimension_3, isTarget, expected, actual));
- }
- }
-
- /**
- * Returns {@code true} if the specified transform is likely to exists only for axis swapping
- * and/or unit conversions. The heuristic rule checks if the transform is backed by a square
- * matrix with exactly one non-null value in each row and each column.
- */
- private static boolean isIgnorable(final MathTransform transform) {
- final Matrix matrix = MathTransforms.getMatrix(transform);
- if (matrix != null) {
- final int size = matrix.getNumRow();
- if (matrix.getNumCol() == size) {
- for (int j=0; j<size; j++) {
- int n1=0, n2=0;
- for (int i=0; i<size; i++) {
- if (matrix.getElement(j,i) != 0) n1++;
- if (matrix.getElement(i,j) != 0) n2++;
- }
- if (n1 != 1 || n2 != 1) {
- return false;
- }
- }
- return true;
- }
- }
- return false;
- }
-
- /**
* Returns a description of the operation method, including a list of expected parameter names.
* The returned object does not contains any parameter value.
*
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
index 85a3653..958b6d1 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
@@ -207,10 +207,8 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver
final ParameterValueGroup parameters)
{
super(properties, method);
- if (transform != null) {
- this.transform = transform;
- checkDimensions(method, 0, transform, properties);
- } else if (parameters == null) {
+ this.transform = transform;
+ if (transform == null && parameters == null) {
throw new IllegalArgumentException(Resources.forProperties(properties)
.getString(Resources.Keys.UnspecifiedParameterValues));
}
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/AbstractSingleOperationTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/AbstractSingleOperationTest.java
deleted file mode 100644
index 94bcf44..0000000
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/AbstractSingleOperationTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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 java.util.Collections;
-import java.util.Locale;
-import java.util.Map;
-import org.opengis.parameter.ParameterDescriptorGroup;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.OperationMethod;
-import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
-import org.apache.sis.referencing.operation.transform.MathTransformsTest;
-import org.apache.sis.test.DependsOn;
-import org.apache.sis.test.TestCase;
-import org.junit.Test;
-
-import static org.opengis.test.Assert.*;
-
-
-/**
- * Tests {@link AbstractSingleOperation}.
- *
- * @author Martin Desruisseaux (Geomatys)
- * @version 0.6
- * @since 0.6
- * @module
- */
-@DependsOn({
- DefaultOperationMethodTest.class,
- MathTransformsTest.class
-})
-public final strictfp class AbstractSingleOperationTest extends TestCase {
- /**
- * Creates a dummy operation method of the given dimensions.
- */
- private static OperationMethod createOperationMethod(final int sourceDimension, final int targetDimension) {
- final ParameterDescriptorGroup parameters = new DefaultParameterDescriptorGroup(
- Collections.singletonMap(ParameterDescriptorGroup.NAME_KEY, "Dummy"), 1, 1);
- return new DefaultOperationMethod(Collections.singletonMap(OperationMethod.NAME_KEY, parameters.getName()),
- sourceDimension, targetDimension, parameters);
- }
-
- /**
- * Tests {@link AbstractSingleOperation#checkDimensions(OperationMethod, int, MathTransform, Map)}
- * without interpolation dimension.
- */
- @Test
- public void testCheckDimensions() {
- final Map<String,?> properties = Collections.singletonMap(DefaultOperationMethod.LOCALE_KEY, Locale.ENGLISH);
- final MathTransform tr = MathTransformsTest.createConcatenateAndPassThrough();
- AbstractSingleOperation.checkDimensions(createOperationMethod(3, 3), 0, tr, properties);
- AbstractSingleOperation.checkDimensions(createOperationMethod(1, 1), 0, tr, properties);
- try {
- AbstractSingleOperation.checkDimensions(createOperationMethod(2, 2), 0, tr, properties);
- fail("MathTransform.sourceDimension == 3 shall be considered incompatible.");
- } catch (IllegalArgumentException e) {
- // This is the expected exception.
- assertEquals(e.getMessage(), "The transform has 1 source dimension, while 2 was expected.");
- }
- try {
- AbstractSingleOperation.checkDimensions(createOperationMethod(3, 1), 0, tr, properties);
- fail("MathTransform.targetDimension == 3 shall be considered incompatible.");
- } catch (IllegalArgumentException e) {
- // This is the expected exception.
- assertEquals(e.getMessage(), "The transform has 3 target dimensions, while 1 was expected.");
- }
- }
-}
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/DefaultTransformationTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/DefaultTransformationTest.java
index a65632c..2038949 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/DefaultTransformationTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/DefaultTransformationTest.java
@@ -49,7 +49,6 @@ import static org.apache.sis.test.ReferencingAssert.*;
* @module
*/
@DependsOn({
- AbstractSingleOperationTest.class,
org.apache.sis.referencing.crs.DefaultGeocentricCRSTest.class
})
public final strictfp class DefaultTransformationTest extends TestCase {
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
index 0040a49..438c672 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
@@ -147,7 +147,6 @@ import org.junit.BeforeClass;
org.apache.sis.referencing.operation.transform.SpecializableTransformTest.class,
org.apache.sis.referencing.operation.DefaultFormulaTest.class,
org.apache.sis.referencing.operation.DefaultOperationMethodTest.class,
- org.apache.sis.referencing.operation.AbstractSingleOperationTest.class,
org.apache.sis.referencing.operation.transform.OperationMethodSetTest.class,
// Registration of map projections and other math transforms.
[sis] 03/03: Fix a MismatchedDimensionException when a datum shift
is applied between a two-dimensional GeographicCRS and a three-dimensional
GeographicCRS. This is the first part of
https://issues.apache.org/jira/browse/SIS-462
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit db63678c02ffc47c8f54f1a8229353e6390fe600
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Jul 6 16:54:53 2020 +0200
Fix a MismatchedDimensionException when a datum shift is applied between a two-dimensional GeographicCRS and a three-dimensional GeographicCRS.
This is the first part of https://issues.apache.org/jira/browse/SIS-462
---
.../java/org/apache/sis/gui/map/StatusBar.java | 51 +++++++++++++++-------
.../org/apache/sis/gui/referencing/MenuSync.java | 16 ++++---
.../referencing/EllipsoidalHeightCombiner.java | 6 +--
.../referencing/provider/GeocentricAffine.java | 8 ++--
.../GeocentricAffineBetweenGeographic.java | 8 ++--
.../operation/CoordinateOperationFinder.java | 48 +++++++++++++-------
.../transform/DefaultMathTransformFactory.java | 37 +++++++---------
.../transform/DefaultMathTransformFactoryTest.java | 12 +++++
8 files changed, 117 insertions(+), 69 deletions(-)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
index c10986d..8cb0823 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
@@ -78,6 +78,7 @@ import org.apache.sis.util.ComparisonMode;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.gui.Widget;
import org.apache.sis.gui.referencing.RecentReferenceSystems;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
import org.apache.sis.internal.gui.BackgroundThreads;
import org.apache.sis.internal.gui.ExceptionReporter;
import org.apache.sis.internal.gui.GUIUtilities;
@@ -262,12 +263,14 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> {
private Predicate<MapCanvas> fullOperationSearchRequired;
/**
- * The source local indices before conversion to geospatial coordinates.
- * The number of dimensions is often {@value #BIDIMENSIONAL}.
- * Shall never be {@code null}.
+ * The source local indices before conversion to geospatial coordinates (never {@code null}).
+ * The number of dimensions is often {@value #BIDIMENSIONAL}. May be the same array than
+ * <code>{@linkplain #targetCoordinates}.coordinates</code> because some transforms are
+ * faster when the source and destination arrays are the same.
*
* @see #targetCoordinates
* @see #position
+ * @see #setTargetCRS(CoordinateReferenceSystem)
*/
private double[] sourceCoordinates;
@@ -635,7 +638,11 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> {
}
}
final boolean sameCRS = Utilities.equalsIgnoreMetadata(objectiveCRS, crs);
- ((LocalToObjective) localToObjectiveCRS).setNoCheck(localToCRS);
+ if (localToCRS == null) {
+ localToCRS = MathTransforms.identity(BIDIMENSIONAL);
+ }
+ final int srcDim = Math.max(localToCRS.getSourceDimensions(), BIDIMENSIONAL);
+ final int tgtDim = localToCRS.getTargetDimensions();
/*
* Remaining code should not fail, so we can start modifying the `StatusBar` fields.
* The buffers for source and target coordinates are recreated because the number of
@@ -645,14 +652,9 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> {
* So we can not use those values for updating the coordinates shown in status bar.
* Instead we will wait for the next mouse event to provide new local coordinates.
*/
- if (localToCRS != null) {
- sourceCoordinates = new double[Math.max(localToCRS.getSourceDimensions(), BIDIMENSIONAL)];
- targetCoordinates = new GeneralDirectPosition(localToCRS.getTargetDimensions());
- } else {
- localToCRS = MathTransforms.identity(BIDIMENSIONAL);
- targetCoordinates = new GeneralDirectPosition(BIDIMENSIONAL);
- sourceCoordinates = targetCoordinates.coordinates; // Okay to share array if same dimension.
- }
+ ((LocalToObjective) localToObjectiveCRS).setNoCheck(localToCRS);
+ targetCoordinates = new GeneralDirectPosition(tgtDim);
+ sourceCoordinates = (srcDim == tgtDim) ? targetCoordinates.coordinates : new double[srcDim];
objectiveCRS = crs;
localToPositionCRS = localToCRS; // May be updated again below.
inflatePrecisions = inflate;
@@ -689,7 +691,24 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> {
if (objectiveToPositionCRS != null) {
localToPositionCRS = MathTransforms.concatenate(localToObjectiveCRS.get(), objectiveToPositionCRS);
}
- targetCoordinates.setCoordinateReferenceSystem(format.getDefaultCRS());
+ setTargetCRS(format.getDefaultCRS());
+ }
+
+ /**
+ * Sets the CRS of {@link #targetCoordinates}.
+ * This method creates a new position if the number of dimensions changed.
+ */
+ private void setTargetCRS(final CoordinateReferenceSystem crs) {
+ final int tgtDim = ReferencingUtilities.getDimension(crs);
+ if (tgtDim != 0 && tgtDim != targetCoordinates.getDimension()) {
+ precisions = null;
+ targetCoordinates = new GeneralDirectPosition(tgtDim);
+ if (sourceCoordinates.length == tgtDim) {
+ // Sharing the same array make some transforms faster.
+ sourceCoordinates = targetCoordinates.coordinates;
+ }
+ }
+ targetCoordinates.setCoordinateReferenceSystem(crs);
}
/**
@@ -892,7 +911,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> {
position.setText(text);
}
outsideText = text;
- targetCoordinates.setCoordinateReferenceSystem(crs);
+ setTargetCRS(crs);
((PositionSystem) positionReferenceSystem).fireValueChangedEvent();
}
@@ -933,7 +952,9 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent> {
/**
* Overwrite previous value without any check. This method is invoked when the {@link #objectiveCRS}
- * is changed in same time the {@link #localToObjectiveCRS} transform.
+ * is changed in same time than the {@link #localToObjectiveCRS} transform, so the number of dimensions
+ * may be temporarily mismatched. This method does not invoke {@link #updateLocalToPositionCRS()};
+ * that call must be done by the caller when ready.
*/
final void setNoCheck(final MathTransform newValue) {
super.set(newValue);
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
index e841231..4ba7c6f 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
@@ -30,6 +30,8 @@ import javafx.scene.control.MenuItem;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.ToggleGroup;
import org.opengis.referencing.ReferenceSystem;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
import org.apache.sis.internal.gui.GUIUtilities;
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.util.resources.Vocabulary;
@@ -102,16 +104,18 @@ final class MenuSync extends SimpleObjectProperty<ReferenceSystem> implements Ev
}
/**
- * Sets the initial value to the first item in the {@code systems} list, if any.
- * This method is invoked in JavaFX thread at construction time or, if it didn't
- * work, at some later time when the systems list may contain an element.
+ * Sets the initial value to the first two-dimensional item in the {@code systems} list, if any.
+ * This method is invoked in JavaFX thread at construction time or, if it didn't work,
+ * at some later time when the systems list may contain an element.
* This method should not be invoked anymore after initialization succeeded.
*/
private void initialize(final ObservableList<? extends ReferenceSystem> systems) {
for (final ReferenceSystem system : systems) {
- if (system != RecentReferenceSystems.OTHER) {
- set(system);
- break;
+ if (system instanceof CoordinateReferenceSystem) {
+ if (ReferencingUtilities.getDimension((CoordinateReferenceSystem) system) == 2) {
+ set(system);
+ break;
+ }
}
}
}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/EllipsoidalHeightCombiner.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/EllipsoidalHeightCombiner.java
index 18d551e..c67accc 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/EllipsoidalHeightCombiner.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/EllipsoidalHeightCombiner.java
@@ -142,9 +142,9 @@ public final class EllipsoidalHeightCombiner {
axes[axisPosition %= 3] = vertical.getCoordinateSystem().getAxis(0);
final Map<String,?> csProps = IdentifiedObjects.getProperties(cs, CoordinateSystem.IDENTIFIERS_KEY);
final Map<String,?> crsProps = (components.length == 2) ? properties
- : IdentifiedObjects.getProperties(crs, CoordinateReferenceSystem.IDENTIFIERS_KEY);
+ : IdentifiedObjects.getProperties(crs, CoordinateReferenceSystem.IDENTIFIERS_KEY);
if (crs instanceof GeodeticCRS) {
- cs = factories.getCSFactory().createEllipsoidalCS(csProps, axes[0], axes[1], axes[2]);
+ cs = factories.getCSFactory() .createEllipsoidalCS(csProps, axes[0], axes[1], axes[2]);
crs = factories.getCRSFactory().createGeographicCRS(crsProps, ((GeodeticCRS) crs).getDatum(), (EllipsoidalCS) cs);
} else {
final ProjectedCRS proj = (ProjectedCRS) crs;
@@ -162,7 +162,7 @@ public final class EllipsoidalHeightCombiner {
fromBase = factories.getCoordinateOperationFactory().createDefiningConversion(
IdentifiedObjects.getProperties(fromBase),
fromBase.getMethod(), fromBase.getParameterValues());
- cs = factories.getCSFactory().createCartesianCS(csProps, axes[0], axes[1], axes[2]);
+ cs = factories.getCSFactory() .createCartesianCS(csProps, axes[0], axes[1], axes[2]);
crs = factories.getCRSFactory().createProjectedCRS(crsProps, base, fromBase, (CartesianCS) cs);
}
/*
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java
index e2b3c06..05b9de1 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java
@@ -56,7 +56,7 @@ import org.apache.sis.util.logging.Logging;
* "Geocentric translations" is an operation name defined by EPSG.</div>
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.1
* @since 0.7
* @module
*/
@@ -394,14 +394,14 @@ public abstract class GeocentricAffine extends GeodeticOperation {
* Following lines will set all Bursa-Wolf parameter values (scale, translation
* and rotation terms). In the particular case of Molodensky method, we have an
* additional parameter for the number of source and target dimensions (2 or 3).
+ * If the number of source and target dimensions are not the same, set to 3 and
+ * let the caller adds or removes an ellipsoidal height as needed.
*/
final Parameters values = createParameters(descriptor, parameters, isTranslation);
switch (method) {
case MOLODENSKY:
case ABRIDGED_MOLODENSKY: {
- if (dimension <= 3) {
- values.getOrCreate(Molodensky.DIMENSION).setValue(dimension);
- }
+ values.getOrCreate(Molodensky.DIMENSION).setValue(Math.min(dimension, 3));
break;
}
}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java
index 5c9d580..96a2e34 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java
@@ -59,9 +59,7 @@ public abstract class GeocentricAffineBetweenGeographic extends GeocentricAffine
* in this class) because this parameter is used with both two- and three-dimensional operation methods.
* If we want to provide a default value, we could but it would complicate a little bit the code since we
* could no longer reuse the same {@code PARAMETERS} constant for operation methods of any number of dimensions.
- * Furthermore it would not solve the case where the number of input dimensions is different than the number of
- * output dimensions. We can not afford to have wrong default values since it would confuse our interpretation
- * of user's parameters in {@link #createMathTransform(MathTransformFactory, ParameterValueGroup)}.</p>
+ * TODO: this paragraph will not apply anymore after {@link #redimensioned} is removed.</p>
*
* @see GeographicToGeocentric#DIMENSION
*
@@ -171,8 +169,8 @@ public abstract class GeocentricAffineBetweenGeographic extends GeocentricAffine
}
/**
- * Notifies {@code DefaultMathTransformFactory} that map projections require values for the
- * {@code "src_semi_major"}, {@code "src_semi_minor"} , {@code "tgt_semi_major"} and
+ * Notifies {@code DefaultMathTransformFactory} that this operation requires values for
+ * the {@code "src_semi_major"}, {@code "src_semi_minor"}, {@code "tgt_semi_major"} and
* {@code "tgt_semi_minor"} parameters.
*
* @return 3, meaning that the operation requires source and target ellipsoids.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
index 30016a5..0e98480 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
@@ -585,11 +585,15 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry {
* - [Abridged] Molodensky (as an approximation of geocentric translation)
* - Identity (if the desired accuracy is so large than we can skip datum shift)
*
- * TODO: if both CS are ellipsoidal but with different number of dimensions, then we should use
- * an intermediate 3D geographic CRS in order to enable the use of Molodensky method if desired.
+ * If both CS are ellipsoidal but with different number of dimensions, then a three-dimensional
+ * operation is used and `DefaultMathTransformFactory` will add an ellipsoidal height on-the-fly.
+ * We let the transform factory do this work instead than adding an intermediate "geographic 2D
+ * to 3D" operation here because the transform factory works with normalized CS, which avoid the
+ * need the search in which dimension to add the ellipsoidal height (it should always be last,
+ * but SIS is tolerant to unusual axis order).
*/
- final DatumShiftMethod preferredMethod = DatumShiftMethod.forAccuracy(desiredAccuracy);
- parameters = GeocentricAffine.createParameters(sourceCS, targetCS, datumShift, preferredMethod);
+ parameters = GeocentricAffine.createParameters(sourceCS, targetCS, datumShift,
+ DatumShiftMethod.forAccuracy(desiredAccuracy));
if (parameters == null) {
/*
* Failed to select a coordinate operation. Maybe because the coordinate system types are not the same.
@@ -634,18 +638,15 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry {
*/
final int sourceDim = sourceCS.getDimension();
final int targetDim = targetCS.getDimension();
- if ((sourceDim & ~1) == 2 // sourceDim == 2 or 3.
- && (sourceDim ^ targetDim) == 1 // abs(sourceDim - targetDim) == 1.
- && (sourceCS instanceof EllipsoidalCS)
- && (targetCS instanceof EllipsoidalCS))
- {
- parameters = (sourceDim == 2 ? Geographic2Dto3D.PARAMETERS
- : Geographic3Dto2D.PARAMETERS).createValue();
+ if (sourceDim == 2 && targetDim == 3 && sourceCS instanceof EllipsoidalCS) {
+ parameters = Geographic2Dto3D.PARAMETERS.createValue();
+ } else if (sourceDim == 3 && targetDim == 2 && targetCS instanceof EllipsoidalCS) {
+ parameters = Geographic3Dto2D.PARAMETERS.createValue();
} else {
/*
* TODO: instead than creating parameters for an identity operation, we should create the
* CoordinateOperation directly from the MathTransform created by mtFactory below.
- * The intent if to get the correct OperationMethod, which should not be "Affine"
+ * The intent is to get the correct OperationMethod, which should not be "Affine"
* if there is a CS type change.
*/
parameters = Affine.identity(targetDim);
@@ -758,7 +759,7 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry {
.createVerticalCS(derivedFrom(heightCS), expectedAxis));
}
}
- if (!isEllipsoidalHeight) { // 'false' if we need to change datum, unit or axis direction.
+ if (!isEllipsoidalHeight) { // `false` if we need to change datum, unit or axis direction.
heightCRS = toAuthorityDefinition(VerticalCRS.class, factorySIS.getCRSFactory()
.createVerticalCRS(derivedFrom(heightCRS), CommonCRS.Vertical.ELLIPSOIDAL.datum(), heightCS));
}
@@ -967,8 +968,7 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry {
} else if (stepComponents.length == 1) {
stepTargetCRS = target; // Slight optimization of the next block.
} else {
- final EllipsoidalHeightCombiner c = new EllipsoidalHeightCombiner(factorySIS.getCRSFactory(), factorySIS.getCSFactory(), factory);
- stepTargetCRS = toAuthorityDefinition(CoordinateReferenceSystem.class, c.createCompoundCRS(derivedFrom(target), stepComponents));
+ stepTargetCRS = createCompoundCRS(target, stepComponents);
}
int delta = source.getCoordinateSystem().getDimension();
final int startAtDimension = endAtDimension;
@@ -1041,6 +1041,24 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry {
}
/**
+ * Creates a compound CRS, but we special processing for (two-dimensional Geographic + ellipsoidal heights) tuples.
+ * If any such tuple is found, a three-dimensional geographic CRS is created instead than the compound CRS.
+ *
+ * @param template the CRS from which to inherit properties.
+ * @param components ordered array of {@code CoordinateReferenceSystem} objects.
+ * @return the coordinate reference system for the given properties.
+ * @throws FactoryException if the object creation failed.
+ *
+ * @see EllipsoidalHeightCombiner#createCompoundCRS(Map, CoordinateReferenceSystem...)
+ */
+ private CoordinateReferenceSystem createCompoundCRS(final CoordinateReferenceSystem template,
+ final CoordinateReferenceSystem[] components) throws FactoryException
+ {
+ EllipsoidalHeightCombiner c = new EllipsoidalHeightCombiner(factorySIS.getCRSFactory(), factorySIS.getCSFactory(), factory);
+ return toAuthorityDefinition(CoordinateReferenceSystem.class, c.createCompoundCRS(derivedFrom(template), components));
+ }
+
+ /**
* Concatenates two operation steps.
* The new concatenated operation gets an automatically generated name.
*
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
index ca1e6e6..ea43943 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
@@ -1236,25 +1236,24 @@ public class DefaultMathTransformFactory extends AbstractFactory implements Math
* Conversely if the source CS is missing a height, add a height with NaN values.
* After this block, the dimensions of `step1` and `step2` should match.
*
- * Note: when adding an ellipsoidal height, we set the height value to NaN instead than 0 (Earth surface)
- * because the given `parameterized` transform may be a Molodensky transform or anything else that could
- * use the height in its calculation. If we have to add a height, maybe the parameterized transform is a
- * 2D Molodensky instead than a 3D Molodensky and the height is just propagated to the output CS by a
- * "passthrough" transform. The result is not the same as if a 3D Molodensky was used in the first place.
+ * When adding an ellipsoidal height, there is two scenarios: the ellipsoidal height may be used by the
+ * parameterized operation, or it may be passed through (in which case the operation ignores the height).
+ * If the height is expected as operation input, set the height to 0. Otherwise (the pass through case),
+ * set the height to NaN. We do that way because the given `parameterized` transform may be a Molodensky
+ * transform or anything else that could use the height in its calculation. If we have to add a height as
+ * a pass through dimension, maybe the parameterized transform is a 2D Molodensky instead than a 3D Molodensky.
+ * The result of passing through the height is not the same as if a 3D Molodensky was used in the first place.
* A NaN value avoid to give a false sense of accuracy.
*/
final int sourceDim = step1.getTargetDimensions();
final int targetDim = step2.getSourceDimensions();
- final int change = targetDim - sourceDim;
- if (change != 0) {
- if (change > numTrailingCoordinates) {
- // If we add dimensions, they must be passthrough dimensions.
- throw new InvalidGeodeticParameterException(canNotAssociateToCS(parameterized, context));
- }
- ensureDimensionChangeAllowed(parameterized, context, change, targetDim);
+ int insertCount = targetDim - sourceDim;
+ if (insertCount != 0) {
+ ensureDimensionChangeAllowed(parameterized, context, insertCount, targetDim);
final Matrix resize = Matrices.createZero(targetDim+1, sourceDim+1);
for (int j=0; j<targetDim; j++) {
- resize.setElement(j, Math.min(j, sourceDim), (j < sourceDim) ? 1 : Double.NaN);
+ resize.setElement(j, Math.min(j, sourceDim), (j < sourceDim) ? 1 :
+ ((--insertCount >= numTrailingCoordinates) ? 0 : Double.NaN)); // See above note.
}
resize.setElement(targetDim, sourceDim, 1); // Element in the lower-right corner.
step1 = createConcatenatedTransform(step1, createAffineTransform(resize));
@@ -1303,13 +1302,9 @@ public class DefaultMathTransformFactory extends AbstractFactory implements Math
return;
}
}
- throw new InvalidGeodeticParameterException(canNotAssociateToCS(parameterized, context));
- }
-
- /**
- * Creates the error message for a transform that can not be associated with given coordinate systems.
- */
- private static String canNotAssociateToCS(final MathTransform parameterized, final Context context) {
+ /*
+ * Creates the error message for a transform that can not be associated with given coordinate systems.
+ */
String name = null;
if (parameterized instanceof Parameterized) {
name = IdentifiedObjects.getDisplayName(((Parameterized) parameterized).getParameterDescriptors(), null);
@@ -1324,7 +1319,7 @@ public class DefaultMathTransformFactory extends AbstractFactory implements Math
.append(parameterized.getTargetDimensions()).append("D)");
cs = context.getTargetCS();
if (cs != null) b.append(" → ").append(cs.getDimension()).append('D');
- return Resources.format(Resources.Keys.CanNotAssociateToCS_2, name, b);
+ throw new InvalidGeodeticParameterException(Resources.format(Resources.Keys.CanNotAssociateToCS_2, name, b));
}
/**
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java
index 7203e55..f117804 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java
@@ -314,6 +314,7 @@ public final strictfp class DefaultMathTransformFactoryTest extends TestCase {
}), MathTransforms.getMatrix(mt), STRICT);
/*
* Transform from 2D to 3D. Coordinate values in the height dimension are unknown (NaN).
+ * This case happen when the third dimension is handled as a "pass through" dimension.
*/
context.setSource(HardCodedCS.GEODETIC_2D);
context.setTarget(HardCodedCS.GEODETIC_3D);
@@ -325,6 +326,17 @@ public final strictfp class DefaultMathTransformFactoryTest extends TestCase {
0, 0, 1
}), MathTransforms.getMatrix(mt), STRICT);
/*
+ * Same transform from 2D to 3D, but this time with the height consumed by the parameterized operation.
+ * This is differentiated from the previous case by the fact that the parameterized operation is three-dimensional.
+ */
+ mt = factory.swapAndScaleAxes(MathTransforms.identity(3), context);
+ assertMatrixEquals("2D → 3D", Matrices.create(4, 3, new double[] {
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 0,
+ 0, 0, 1
+ }), MathTransforms.getMatrix(mt), STRICT);
+ /*
* Test error message when adding a dimension that is not ellipsoidal height.
*/
context.setSource(HardCodedCS.CARTESIAN_2D);