This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 953ad86847 When invoking `CRS.findOperation(sourceCRS, targetCRS,
context)` with a pair of CRSs created by `GridGeometry.createGridCRS(…)`, try
to delegate to `GridGeometry.createTransformTo(…)`.
953ad86847 is described below
commit 953ad868472f1f9072b777a801a687bdde01e7a6
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon May 11 00:45:13 2026 +0200
When invoking `CRS.findOperation(sourceCRS, targetCRS, context)` with a
pair of CRSs created
by `GridGeometry.createGridCRS(…)`, try to delegate to
`GridGeometry.createTransformTo(…)`.
---
.../coverage/grid/CoordinateOperationFinder.java | 44 +++----
.../apache/sis/coverage/grid/GridCRSBuilder.java | 128 +++++++++++++++++----
.../apache/sis/coverage/grid/GridGeometryTest.java | 48 ++++++++
.../internal/ParameterizedTransformBuilder.java | 4 +-
.../internal/shared/CoordinateOperations.java | 4 +-
.../internal/shared/OperationMethodExt.java | 73 ++++++++++++
.../operation/AbstractCoordinateOperation.java | 7 +-
.../operation/AbstractSingleOperation.java | 4 +-
.../operation/CoordinateOperationContext.java | 2 +-
.../operation/CoordinateOperationFinder.java | 69 +++++++----
.../operation/CoordinateOperationRegistry.java | 14 +--
.../operation/DefaultConcatenatedOperation.java | 4 +-
.../DefaultCoordinateOperationFactory.java | 19 +--
.../sis/referencing/operation/DefaultFormula.java | 9 +-
.../operation/DefaultOperationMethod.java | 4 +-
.../operation/transform/MathTransformProvider.java | 31 ++---
.../operation/DefaultTransformationTest.java | 3 +-
17 files changed, 346 insertions(+), 121 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/CoordinateOperationFinder.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/CoordinateOperationFinder.java
index 5d8d8aacc3..a37c8fe347 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/CoordinateOperationFinder.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/CoordinateOperationFinder.java
@@ -335,41 +335,22 @@ final class CoordinateOperationFinder extends
CoordinateOperationContext {
*/
private CoordinateOperation changeOfCRS() throws FactoryException,
TransformException {
if (!knowChangeOfCRS) {
- changeOfCRS = changeOfCRS(source, target, this);
+ if (source.isDefined(GridGeometry.CRS) &&
target.isDefined(GridGeometry.CRS)) try {
+ /*
+ * Unconditionally create the operation even if the CRS are
equivalent. A non-null operation
+ * trigs the check for wraparound axes, which is necessary
even if the transform is identity.
+ */
+ addAreaOfInterest(source.envelope);
+ addAreaOfInterest(target.envelope);
+ changeOfCRS =
CRS.findOperation(source.getCoordinateMetadata(),
target.getCoordinateMetadata(), this);
+ } catch (BackingStoreException e) {
+ throw e.unwrapOrRethrow(TransformException.class);
+ }
knowChangeOfCRS = true;
}
return changeOfCRS;
}
- /**
- * Computes the change of <abbr>CRS</abbr> between the given pair of grid
geometries.
- *
- * @param source the grid geometry which is the source of the
coordinate operation to find.
- * @param target the grid geometry which is the target of the
coordinate operation to find.
- * @param context contains coordinate values to use as constant if a
source CRS is missing.
- * @return coordinate operation from source to target CRS, or {@code null}
if none.
- * @throws FactoryException if no operation can be found between the
source and target CRS.
- * @throws TransformException if some coordinates cannot be transformed to
the specified target.
- */
- private static CoordinateOperation changeOfCRS(final GridGeometry source,
final GridGeometry target,
- final
CoordinateOperationContext context)
- throws FactoryException, TransformException
- {
- CoordinateOperation changeOfCRS = null;
- if (source.isDefined(GridGeometry.CRS) &&
target.isDefined(GridGeometry.CRS)) try {
- /*
- * Unconditionally create the operation even if the CRS are
equivalent. A non-null operation
- * trigs the check for wraparound axes, which is necessary even if
the transform is identity.
- */
- context.addAreaOfInterest(source.envelope);
- context.addAreaOfInterest(target.envelope);
- changeOfCRS = CRS.findOperation(source.getCoordinateMetadata(),
target.getCoordinateMetadata(), context);
- } catch (BackingStoreException e) {
- throw e.unwrapOrRethrow(TransformException.class);
- }
- return changeOfCRS;
- }
-
/**
* Computes the transform from “grid coordinates of the source” to “grid
coordinates of the target”.
* This is a concatenation of {@link #gridToCRS()} with target "CRS to
grid" transform.
@@ -468,7 +449,8 @@ apply: if (forwardChangeOfCRS == null) {
inverseChangeOfCRS =
changeOfCRS.getMathTransform().inverse();
}
} else {
- final CoordinateOperation inverse = changeOfCRS(target,
source, new CoordinateOperationContext());
+ // Really need the `CoordinateOperationFinder` subclass
because of `instanceof` checks elsewhere.
+ final CoordinateOperation inverse = new
CoordinateOperationFinder(target, source).changeOfCRS();
if (inverse != null) {
sourceCRS = inverse.getTargetCRS();
inverseChangeOfCRS = inverse.getMathTransform();
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
index 1f96d230e7..5421b8639c 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
+import java.util.EnumSet;
import java.util.Locale;
import java.util.Optional;
import org.opengis.util.FactoryException;
@@ -40,7 +41,7 @@ import org.opengis.referencing.crs.DerivedCRS;
import org.opengis.referencing.crs.EngineeringCRS;
import org.opengis.referencing.datum.EngineeringDatum;
import org.opengis.referencing.operation.Matrix;
-import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.Conversion;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.opengis.referencing.operation.NoninvertibleTransformException;
@@ -52,16 +53,20 @@ import org.apache.sis.referencing.cs.AbstractCS;
import org.apache.sis.referencing.cs.CoordinateSystems;
import org.apache.sis.referencing.operation.DefiningConversion;
import org.apache.sis.referencing.operation.DefaultOperationMethod;
+import org.apache.sis.referencing.operation.CoordinateOperationContext;
import org.apache.sis.referencing.operation.transform.TransformSeparator;
import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
import org.apache.sis.referencing.internal.shared.AxisDirections;
import org.apache.sis.referencing.internal.shared.DirectPositionView;
+import org.apache.sis.referencing.internal.shared.OperationMethodExt;
import org.apache.sis.referencing.internal.shared.ReferencingFactoryContainer;
import org.apache.sis.feature.internal.Resources;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.Characters;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.collection.Containers;
+import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.util.iso.Types;
import org.apache.sis.measure.Units;
@@ -82,21 +87,88 @@ import org.opengis.referencing.ObjectDomain;
* @author Martin Desruisseaux (IRD, Geomatys)
*/
final class GridCRSBuilder extends ReferencingFactoryContainer {
+ /**
+ * Name of the parameter specifying the grid geometry.
+ * In principle, this parameter should be mandatory. However, it is
defined as optional for now
+ * for avoiding the need to separate grid geometries when the
<abbr>CRS</abbr> is compound.
+ */
+ private static final String GRID_PARAM = "Grid geometry";
+
/**
* Name of the parameter specifying which part (center or corner)
- * of the call is associated with the coverage data attributes.
+ * of the cell is associated with the coverage data attributes.
*/
private static final String ANCHOR_PARAM = "Pixel in cell";
/**
* Description of the "<abbr>CRS</abbr> to grid indices" operation method.
*/
- private static final OperationMethod METHOD;
- static {
- final ParameterBuilder b = new ParameterBuilder().setRequired(true);
- final ParameterDescriptor<?> anchor =
b.addName(ANCHOR_PARAM).create(PixelInCell.class, PixelInCell.CELL_CENTER);
- final ParameterDescriptorGroup params = b.addName("CRS to grid
indices").createGroup(anchor);
- METHOD = new DefaultOperationMethod(Map.of(IdentifiedObject.NAME_KEY,
params.getName()), params);
+ private static final class Method extends DefaultOperationMethod
implements OperationMethodExt {
+ /** For cross-version compatibility. */
+ private static final long serialVersionUID = -404891574462494877L;
+
+ /** Copy of {@link
org.apache.sis.referencing.operation.DefaultConcatenatedOperation#TRANSFORM_KEY}.
*/
+ private static final String TRANSFORM_KEY = "transform";
+
+ /** The unique instance. */
+ static final Method INSTANCE;
+ static {
+ final ParameterBuilder b = new
ParameterBuilder().setRequired(true);
+ final ParameterDescriptor<?> anchor =
b.addName(ANCHOR_PARAM).create(PixelInCell.class, PixelInCell.CELL_CENTER);
+ final ParameterDescriptor<?> grid =
b.addName(GRID_PARAM).setRequired(false).create(GridGeometry.class, null);
+ INSTANCE = new Method(b.addName("CRS to grid
indices").createGroup(grid, anchor));
+ }
+
+ /** Creates the unique instance. */
+ private Method(final ParameterDescriptorGroup params) {
+ super(Map.of(IdentifiedObject.NAME_KEY, params.getName()), params);
+ }
+
+ /**
+ * If the given <abbr>CRS</abbr> has been built by this method,
returns the grid geometry.
+ * Otherwise, returns {@code null}. The {@code anchor} argument is
used for verifying that
+ * the two operations use the same "pixel in cell" configuration.
+ */
+ private static GridGeometry grid(final CoordinateReferenceSystem crs,
final EnumSet<PixelInCell> anchor) {
+ if (crs instanceof DerivedCRS) {
+ final Conversion conversion = ((DerivedCRS)
crs).getConversionFromBase();
+ if (conversion.getMethod() instanceof Method) {
+ final ParameterValueGroup values =
conversion.getParameterValues();
+ if (anchor.isEmpty() == anchor.add((PixelInCell)
values.parameter(ANCHOR_PARAM).getValue())) {
+ return (GridGeometry)
values.parameter(GRID_PARAM).getValue();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * If the given pair of <abbr>CRS</abbr>s is derived from grid
geometries, finds a transform between them.
+ * Compared to the default transform, the returned transform may
handle the anti-meridian crossing.
+ */
+ @Override
+ public boolean completeOperationMetadata(final
CoordinateOperationContext context,
+ final
CoordinateReferenceSystem sourceCRS,
+ final
CoordinateReferenceSystem targetCRS,
+ final Map<String, Object>
properties)
+ {
+ // The `instanceof` check is important for preventing never ending
recursive invocations.
+ if (!(context instanceof CoordinateOperationFinder ||
properties.containsKey(TRANSFORM_KEY))) {
+ final EnumSet<PixelInCell> anchor =
EnumSet.noneOf(PixelInCell.class);
+ final GridGeometry source = grid(sourceCRS, anchor);
+ if (source != null) {
+ final GridGeometry target = grid(targetCRS, anchor);
+ if (target != null) try {
+ // The set should always have exactly one element. If
not, it would be a bug in `grid(…)`.
+ properties.put(TRANSFORM_KEY,
source.createTransformTo(target, Containers.peekIfSingleton(anchor)));
+ return true;
+ } catch (TransformException e) {
+ throw new BackingStoreException(e);
+ }
+ }
+ }
+ return false;
+ }
}
/**
@@ -106,9 +178,12 @@ final class GridCRSBuilder extends
ReferencingFactoryContainer {
private static final InternationalString SCOPE =
Resources.formatInternational(Resources.Keys.CrsToGridConversion);
/**
- * The extent of the grid geometry, or {@code null} if none.
+ * The grid geometry for which to create a derived or compound
<abbr>CRS</abbr> for cell indices.
+ * This is kept constant after initialization, i.e. this field is not
updated when descending in
+ * <abbr>CRS</abbr> components. May be {@code null} if the
<abbr>CRS</abbr> is built only from a
+ * grid extent.
*/
- private GridExtent extent;
+ private GridGeometry fullGrid;
/**
* The cell part (center or corner) to map.
@@ -176,9 +251,7 @@ final class GridCRSBuilder extends
ReferencingFactoryContainer {
grid.getGeographicExtent().ifPresent((domain) -> {
properties.put(ObjectDomain.DOMAIN_OF_VALIDITY_KEY, new
DefaultExtent(null, domain, null, null));
});
- if (grid.isDefined(GridGeometry.EXTENT)) {
- extent = grid.getExtent();
- }
+ fullGrid = grid;
if (derived || grid.isDefined(GridGeometry.CRS |
GridGeometry.GRID_TO_CRS)) try {
separator = new
TransformSeparator(grid.getGridToCRS(anchor).inverse());
return forComponent(name, grid.getCoordinateReferenceSystem(), 0,
0);
@@ -192,8 +265,8 @@ final class GridCRSBuilder extends
ReferencingFactoryContainer {
*/
final int dimension = grid.getDimension();
final DimensionNameType[] dimensionNames;
- if (extent != null) {
- dimensionNames = Arrays.copyOf(extent.getAxisTypes(), dimension);
+ if (grid.isDefined(GridGeometry.EXTENT)) {
+ dimensionNames = Arrays.copyOf(grid.getExtent().getAxisTypes(),
dimension);
} else {
dimensionNames = new DimensionNameType[dimension];
}
@@ -210,11 +283,11 @@ final class GridCRSBuilder extends
ReferencingFactoryContainer {
* Caller can get the dimensions that have been used. Caller shall invoke
{@code transform.clear()}
* before to invoke this method again.</p>
*
- * @param name name of the <abbr>CRS</abbr> to create.
+ * @param name name of the derived or compound <abbr>CRS</abbr> to
create.
* @param baseCRS real world <abbr>CRS</abbr> or component of that
<abbr>CRS</abbr>.
* @param srcDim dimension of the first axis of {@code baseCRS}
relatively to the full real world <abbr>CRS</abbr>.
* @param tgtDim dimension of the first axis of the return value
relatively to the full derived <abbr>CRS</abbr>.
- * @return grid extent <abbr>CRS</abbr> derived from the given {@code
baseCRS}.
+ * @return <abbr>CRS</abbr> for cell indices derived from the
<abbr>CRS</abbr> of the given grid geometry.
* @throws FactoryException if an error occurred during the use of a
referencing factory.
*/
private CoordinateReferenceSystem forComponent(final Object name, final
CoordinateReferenceSystem baseCRS, int srcDim, int tgtDim)
@@ -263,9 +336,6 @@ final class GridCRSBuilder extends
ReferencingFactoryContainer {
final MathTransform crsToGrid = separator.separate();
final int[] dispatch = separator.getTargetDimensions();
final var dimensionNames = new DimensionNameType[dispatch.length];
- if (extent != null) {
- Arrays.setAll(dimensionNames, (i) ->
extent.getAxisType(dispatch[i]).orElse(null));
- }
/*
* Get the directions of the axes of the coverage coordinate system,
but in the order of grid dimensions.
* The direction array may contain null elements if directions could
not be inferred for some dimensions.
@@ -276,7 +346,9 @@ final class GridCRSBuilder extends
ReferencingFactoryContainer {
AxisDirection[] directions;
toGrid: try {
final Matrix derivative;
- if (extent != null) {
+ if (fullGrid.isDefined(GridGeometry.EXTENT)) {
+ final GridExtent extent = fullGrid.getExtent();
+ Arrays.setAll(dimensionNames, (i) ->
extent.getAxisType(dispatch[i]).orElse(null));
derivative = crsToGrid.derivative(new
DirectPositionView.Double(extent.getPointOfInterest(anchor), srcDim,
dimension));
} else try {
derivative = crsToGrid.derivative(null);
@@ -302,12 +374,18 @@ toGrid: try {
directions = directions(dimensionNames);
}
/*
- * Creates the coordinate system, then the conversion, and finally the
derived CRS.
+ * Create the coordinate system, then the conversion, and finally the
derived CRS.
+ * The `GRID_PARAM` parameter should be mandatory, but for now it is
not clear that
+ * it is worth to pay the cost of creating sub-grids.
*/
- final CoordinateSystem cs = createCS(dispatch.length, dimensionNames,
directions, tgtDim + 1, true);
- final ParameterValueGroup params =
METHOD.getParameters().createValue();
+ final Method method = Method.INSTANCE;
+ final ParameterValueGroup params =
method.getParameters().createValue();
+ if (srcDim == 0 && dimension == fullGrid.getDimension()) {
+ params.parameter(GRID_PARAM).setValue(fullGrid);
+ }
params.parameter(ANCHOR_PARAM).setValue(anchor);
- final var conversion = new
DefiningConversion(properties(METHOD.getName()), METHOD, crsToGrid, params);
+ final var conversion = new
DefiningConversion(properties(method.getName()), method, crsToGrid, params);
+ final CoordinateSystem cs = createCS(dispatch.length, dimensionNames,
directions, tgtDim + 1, true);
return getCRSFactory().createDerivedCRS(properties(name), baseCRS,
conversion, cs);
}
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
index 4c03cc09fe..4c9c242138 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -33,6 +33,7 @@ import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.opengis.referencing.operation.NoninvertibleTransformException;
+import org.opengis.referencing.operation.CoordinateOperation;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.ImmutableIdentifier;
@@ -43,6 +44,7 @@ import org.apache.sis.referencing.operation.matrix.Matrix4;
import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.referencing.operation.transform.WraparoundTransform;
import org.apache.sis.referencing.internal.shared.ExtendedPrecisionMatrix;
import org.apache.sis.util.ComparisonMode;
@@ -883,6 +885,52 @@ public final class GridGeometryTest extends TestCase {
assertAxisDirectionsEqual(cs, AxisDirection.NORTH, AxisDirection.WEST,
AxisDirection.FUTURE);
}
+ /**
+ * Tests coordinate operation between two derived <abbr>CRS</abbr>s
+ * created by {@link GridGeometry#createGridCRS(Identifier, PixelInCell)}.
+ * Apache <abbr>SIS</abbr> should recognize this special case and redirects
+ * to {@link GridGeometry#createTransformTo(GridGeometry, PixelInCell)}.
+ *
+ * @throws FactoryException if the <abbr>CRS</abbr> cannot be built.
+ */
+ @Test
+ public void testCreateConcatenatedOperation() throws FactoryException {
+ final var extent = new GridExtent(90, 30);
+ final var crsPair = new CoordinateReferenceSystem[2];
+ for (int i=0; i<crsPair.length; i++) {
+ MathTransform gridToCRS = MathTransforms.linear(new Matrix3(
+ 1, 0, 150 + i, // Longitude overlapping the anti-meridian.
+ 0, 1, 25, // Latitude
+ 0, 0, 1));
+ if (i == 0) {
+ /*
+ * Add an arbitrary non-linear transform while still
pretending that the CRS is WGS84.
+ * This is not correct as the CRS is Mercator. But we don't
declare that CRS because,
+ * for the purpose of this test, we don't want the projected
CRS to be reversed, thus
+ * cancelling our artificial non-linear part.
+ */
+ gridToCRS = MathTransforms.concatenate(gridToCRS,
+
HardCodedConversions.mercator().getConversionFromBase().getMathTransform());
+ }
+ final var name = new ImmutableIdentifier(null, null, "Grid #" + i);
+ final var grid = new GridGeometry(extent, PixelInCell.CELL_CORNER,
gridToCRS, HardCodedCRS.WGS84);
+ crsPair[i] = grid.createGridCRS(name, PixelInCell.CELL_CENTER);
+ }
+ /*
+ * Ask for the transform between the two grids and verify that it
contains a `WraparoundTransform` step.
+ * The presence of this step is what differentiate the result from
what we would get without the special
+ * case.
+ */
+ int wraparound = 0;
+ final CoordinateOperation op = CRS.findOperation(crsPair[0],
crsPair[1], null);
+ for (MathTransform step :
MathTransforms.getSteps(op.getMathTransform())) {
+ if (step instanceof WraparoundTransform) {
+ wraparound++;
+ }
+ }
+ assertEquals(1, wraparound);
+ }
+
/**
* Tests {@link GridGeometry#createTransformTo(GridGeometry, PixelInCell)}.
*
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/ParameterizedTransformBuilder.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/ParameterizedTransformBuilder.java
index 68f4d7c600..1db2c40fca 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/ParameterizedTransformBuilder.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/ParameterizedTransformBuilder.java
@@ -136,7 +136,7 @@ public class ParameterizedTransformBuilder extends
MathTransformBuilder implemen
*
* @see #getContextualParameters()
*/
- private final Map<String,Boolean> contextualParameters;
+ private final Map<String, Boolean> contextualParameters;
/**
* Whether the user-specified parameters have been completed with the
contextual parameters.
@@ -403,7 +403,7 @@ public class ParameterizedTransformBuilder extends
MathTransformBuilder implemen
* @return names of parameters inferred from context.
*/
@Override
- public Map<String,Boolean> getContextualParameters() {
+ public Map<String, Boolean> getContextualParameters() {
return Collections.unmodifiableMap(contextualParameters);
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/CoordinateOperations.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/CoordinateOperations.java
index 22d9716c6b..6f7937bdee 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/CoordinateOperations.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/CoordinateOperations.java
@@ -135,7 +135,7 @@ public final class CoordinateOperations {
}
properties = Map.of();
}
- final HashMap<String,Object> p = new HashMap<>(properties);
+ final var p = new HashMap<String, Object>(properties);
p.putIfAbsent(ReferencingFactoryContainer.CRS_FACTORY, crsFactory);
p.putIfAbsent(ReferencingFactoryContainer.CS_FACTORY, csFactory);
properties = p;
@@ -370,7 +370,7 @@ public final class CoordinateOperations {
* unmodifiable List<Integer>. The list is for public API; internally,
Apache SIS will use toBitMask(…).
*/
long r = changes;
- final Integer[] indices = new Integer[Long.bitCount(r)];
+ final var indices = new Integer[Long.bitCount(r)];
for (int i=0; i<indices.length; i++) {
final int dim = Long.numberOfTrailingZeros(r);
indices[i] = dim;
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/OperationMethodExt.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/OperationMethodExt.java
new file mode 100644
index 0000000000..f737645394
--- /dev/null
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/shared/OperationMethodExt.java
@@ -0,0 +1,73 @@
+/*
+ * 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.internal.shared;
+
+import java.util.Map;
+import org.opengis.metadata.extent.Extent;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.SingleOperation;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.referencing.operation.CoordinateOperationContext;
+import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
+import org.apache.sis.metadata.iso.extent.Extents;
+
+
+/**
+ * Extension of {@code OperationMethod} kept in a separated interface
+ * because we are not sure if it is ready for public <abbr>API</abbr>.
+ * Note: it could as well be a protected method in {@code
DefaultOperationMethod}.
+ */
+public interface OperationMethodExt extends OperationMethod {
+ /**
+ * Optionally updates the metadata of a coordinate operation between a
given pair of <abbr>CRS</abbr>s.
+ * This method may be invoked when a coordinate operation is or contains a
{@link SingleOperation} step
+ * which uses this {@code OperationMethod}.
+ * The {@code source} and {@code target} arguments contains the
<abbr>CRS</abbr>s of the operation which
+ * will be constructed with the given {@code properties} map.
+ *
+ * <p>This method can be implemented for purposes such as restricting the
domain of validity or reducing the
+ * declared accuracy of any coordinate operation which uses (potentially
indirectly) this {@code OperationMethod}.
+ * The given {@code properties} map contains the metadata that the caller
intends to give to the operation to create.
+ * This method can update the given {@code properties} map and returns
{@code true},
+ * or returns {@code false} if this method did nothing.</p>
+ *
+ * <p>This method may need to merge map values instead of replacing them.
+ * For example, a domain of validity may need to be set to the {@linkplain
Extents#intersection(Extent, Extent)
+ * intersection} of the domain provided by this method with the domain
already contained in the map (if any).</p>
+ *
+ * <p>The recognized keys and the valid values of the {@code properties}
map are documented in the
+ * {@linkplain
AbstractCoordinateOperation#AbstractCoordinateOperation(Map,
CoordinateReferenceSystem,
+ * CoordinateReferenceSystem, CoordinateReferenceSystem, MathTransform)
operation constructor}.</p>
+ *
+ * @param context context of the coordinate operation to create, or
{@code null} if none.
+ * @param source the source <abbr>CRS</abbr> of the operation which
will use the given {@code properties}.
+ * @param target the target <abbr>CRS</abbr> of the operation which
will use the given {@code properties}.
+ * @param properties a modifiable map of metadata to be given to the
operation between the given <abbr>CRS</abbr>s.
+ * @return whether this method has modified the {@code properties} map.
+ *
+ * @todo If this method move to public <abbr>API</abbr>, {@code
CoordinateReferenceSystem}
+ * should be replaced by {@code CoordinateMetadata}.
+ */
+ default boolean completeOperationMetadata(CoordinateOperationContext
context,
+ CoordinateReferenceSystem
source,
+ CoordinateReferenceSystem
target,
+ Map<String, Object> properties)
+ {
+ return false;
+ }
+}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
index 436f95a10b..6362c40b19 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
@@ -197,8 +197,8 @@ public class AbstractCoordinateOperation extends
AbstractIdentifiedObject implem
* to positions in the {@linkplain #getTargetCRS target coordinate
reference system}.
*
* <p><b>Consider this field as final!</b>
- * This field is non-final only for the convenience of constructors and
for initialization
- * at XML unmarshalling time by {@link
AbstractSingleOperation#afterUnmarshal(Unmarshaller, Object)}.</p>
+ * This field is non-final for the convenience of constructors and for
initialization at <abbr>XML</abbr>
+ * unmarshalling time by {@link
AbstractSingleOperation#afterUnmarshal(Unmarshaller, Object)}.</p>
*/
@SuppressWarnings("serial") // Most SIS implementations are
serializable.
MathTransform transform;
@@ -491,7 +491,10 @@ check: for (int isTarget=0; ; isTarget++) { //
0 == source check; 1
* When this method returns {@code true}, the source and target CRS are
not marshalled in XML documents.
*
* @return {@code true} if this coordinate operation is for the definition
of a derived or projected CRS.
+ *
+ * @deprecated Replaced by the {@link DefiningConversion} class.
*/
+ @Deprecated(since = "1.7", forRemoval = true)
public boolean isDefiningConversion() {
/*
* Trick: we do not need to verify if (this instanceof Conversion)
because:
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractSingleOperation.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractSingleOperation.java
index 3d954d8889..2fe5df6bf1 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractSingleOperation.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractSingleOperation.java
@@ -230,8 +230,8 @@ class AbstractSingleOperation extends
AbstractCoordinateOperation implements Sin
* This situation happens when this operation has been initialized from a
<em>defining conversion</em>
* and the caller refined the parameters using information provided by the
math transform factory.
* On one hand, we want to take advantage of additional information
present in {@code definition}
- * such as OGC aliases (those information are often missing in {@link
#method} if the latter
- * is not a {@link
org.apache.sis.referencing.operation.transform.MathTransformProvider}).
+ * such as <abbr>OGC</abbr> aliases (those information are often missing
in {@link #method} if the
+ * latter is not a {@link
org.apache.sis.referencing.operation.transform.MathTransformProvider}).
* But on the other hand, {@code definition} may contain contextual
parameters (ellipsoid semi-axis lengths)
* which are unknown to {@link #method} and would cause an {@link
InvalidParameterValueException} if we try
* to set them. We could replace {@link #method}, but if the latter was
created from EPSG database it also
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationContext.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationContext.java
index 93c24183af..aab9161cbd 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationContext.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationContext.java
@@ -47,7 +47,7 @@ import org.apache.sis.measure.Longitude;
* </ul>
*
* While optional, those information can help {@link
DefaultCoordinateOperationFactory}
- * to choose the most suitable coordinate transformation between two CRS.
+ * to choose the most suitable coordinate transformation between two
<abbr>CRS</abbr>s.
*
* <p>{@code CoordinateOperationContext} is part of the <abbr>API</abbr>
* used by <abbr>SIS</abbr> for implementing the <i>late binding</i> model.
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
index 53fc23a90d..baa510d453 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Optional;
+import java.util.function.Consumer;
import java.time.Duration;
import javax.measure.Unit;
import javax.measure.IncommensurableException;
@@ -50,6 +51,7 @@ import org.apache.sis.referencing.internal.AnnotatedMatrix;
import org.apache.sis.referencing.internal.PositionalAccuracyConstant;
import org.apache.sis.referencing.internal.Resources;
import org.apache.sis.referencing.internal.shared.AxisDirections;
+import org.apache.sis.referencing.internal.shared.OperationMethodExt;
import org.apache.sis.referencing.internal.shared.CoordinateOperations;
import org.apache.sis.referencing.internal.shared.EllipsoidalHeightCombiner;
import org.apache.sis.referencing.internal.shared.ReferencingUtilities;
@@ -65,6 +67,7 @@ import
org.apache.sis.referencing.operation.provider.DatumShiftMethod;
import org.apache.sis.referencing.operation.provider.GeocentricAffine;
import org.apache.sis.util.Utilities;
import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.util.internal.shared.Constants;
import org.apache.sis.util.internal.shared.DoubleDouble;
import org.apache.sis.util.resources.Vocabulary;
@@ -1127,7 +1130,7 @@ public class CoordinateOperationFinder extends
CoordinateOperationRegistry {
if (isAxisChange2 && mt2.getSourceDimensions() ==
mt2.getTargetDimensions()) main = step1;
}
if (main instanceof SingleOperation) {
- final SingleOperation op = (SingleOperation) main;
+ final var op = (SingleOperation) main;
main = createFromMathTransform(
new HashMap<>(IdentifiedObjects.getProperties(main)),
sourceCRS,
@@ -1137,9 +1140,7 @@ public class CoordinateOperationFinder extends
CoordinateOperationRegistry {
op.getParameterValues(),
typeOf(op));
} else {
- main = factory.createConcatenatedOperation(
- defaultName(sourceCRS, targetCRS),
- sourceCRS, targetCRS, step1, step2);
+ main = createConcatenatedOperation(sourceCRS, targetCRS, step1,
step2);
}
/*
* Sometimes we get a concatenated operation made of an operation
followed by its inverse.
@@ -1181,8 +1182,50 @@ public class CoordinateOperationFinder extends
CoordinateOperationRegistry {
if (isIdentity(step3)) return concatenate(step1, step2);
if (canHide(step1.getName())) return concatenate(concatenate(step1,
step2), step3);
if (canHide(step3.getName())) return concatenate(step1,
concatenate(step2, step3));
- final Map<String, ?> properties = defaultName(step1.getSourceCRS(),
step3.getTargetCRS());
- return factory.createConcatenatedOperation(properties, null, null,
step1, step2, step3);
+ return createConcatenatedOperation(step1.getSourceCRS(),
step3.getTargetCRS(), step1, step2, step3);
+ }
+
+ /**
+ * Creates an ordered sequence of two or more single coordinate operations.
+ * The {@code sourceCRS} and {@code targetCRS} arguments of this method are
+ * needed for detecting whether the source or last step needs to be
reversed.
+ *
+ * <p>If any operation step uses a method that implements the {@link
OperationMethodExt} interface,
+ * it will be used for enriching the metadata. It may go as far as
overriding the default algorithm for
+ * computing the transform if a method supplies an {@value
DefaultConcatenatedOperation#TRANSFORM_KEY}
+ * value.</p>
+ *
+ * @param sourceCRS the source <abbr>CRS</abbr>, or {@code null} for
the source of the first step.
+ * @param targetCRS the target <abbr>CRS</abbr>, or {@code null} for
the target of the last effective step.
+ * @param operations the sequence of operations. Should contain at least
two operations.
+ * @return the concatenated operation created from the given arguments.
+ * @throws FactoryException if the object creation failed.
+ */
+ private CoordinateOperation createConcatenatedOperation(
+ final CoordinateReferenceSystem sourceCRS,
+ final CoordinateReferenceSystem targetCRS,
+ final CoordinateOperation... operations) throws FactoryException
+ {
+ final var properties = new HashMap<String, Object>(4);
+ properties.put(IdentifiedObject.NAME_KEY, new CRSPair(sourceCRS,
targetCRS).toString());
+ final var merge = new Consumer<CoordinateOperation>() {
+ @Override public void accept(final CoordinateOperation operation) {
+ final OperationMethod method =
CoordinateOperations.getMethod(operation);
+ if (method instanceof OperationMethodExt) {
+ final var provider = (OperationMethodExt) method;
+ provider.completeOperationMetadata(context, sourceCRS,
targetCRS, properties);
+ }
+ if (operation instanceof ConcatenatedOperation) {
+ ((ConcatenatedOperation)
operation).getOperations().stream().forEach(this);
+ }
+ }
+ };
+ try {
+ for (CoordinateOperation step : operations) merge.accept(step);
+ return factory.createConcatenatedOperation(properties, sourceCRS,
targetCRS, operations);
+ } catch (BackingStoreException e) {
+ throw e.unwrapOrRethrow(FactoryException.class);
+ }
}
/**
@@ -1219,13 +1262,6 @@ public class CoordinateOperationFinder extends
CoordinateOperationRegistry {
return (id == AXIS_CHANGES) || (id == IDENTITY);
}
- /**
- * Returns the given name in a singleton map.
- */
- private static Map<String, ?> properties(final String name) {
- return Map.of(IdentifiedObject.NAME_KEY, name);
- }
-
/**
* Returns a name for an object derived from the specified one.
* This method builds a name of the form "{@literal <original identifier>}
(step 1)"
@@ -1249,13 +1285,6 @@ public class CoordinateOperationFinder extends
CoordinateOperationRegistry {
return properties;
}
- /**
- * Returns a name for a transformation between two CRS.
- */
- private static Map<String, ?> defaultName(CoordinateReferenceSystem
source, CoordinateReferenceSystem target) {
- return properties(new CRSPair(source, target).toString());
- }
-
/**
* Returns the given operation as a list of one element. We cannot use
{@link Collections#singletonList(Object)}
* because the list needs to be modifiable, as required by {@link
#createOperations(CoordinateReferenceSystem,
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
index d84eb81c72..d64f692bd4 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
@@ -787,7 +787,7 @@ class CoordinateOperationRegistry {
final CoordinateReferenceSystem targetCRS = op.getTargetCRS();
final MathTransform transform = op.getMathTransform().inverse();
final OperationMethod method =
InverseOperationMethod.create(op.getMethod(), this);
- final Map<String,Object> properties = properties(INVERSE_OPERATION);
+ final Map<String, Object> properties = properties(INVERSE_OPERATION);
InverseOperationMethod.properties(op, properties);
inverse = createFromMathTransform(properties, targetCRS, sourceCRS,
transform, method, null, typeOf(op));
AbstractCoordinateOperation.setCachedInverse(op, inverse);
@@ -821,7 +821,7 @@ class CoordinateOperationRegistry {
if (operation instanceof ConcatenatedOperation) {
final CoordinateOperation[] inverted =
getSteps((ConcatenatedOperation) operation, true);
ArraysExt.reverse(inverted);
- final Map<String,Object> properties =
properties(INVERSE_OPERATION);
+ final Map<String, Object> properties =
properties(INVERSE_OPERATION);
final MathTransform transform = operation.getMathTransform();
if (transform != null) {
properties.put(DefaultConcatenatedOperation.TRANSFORM_KEY,
transform.inverse());
@@ -1016,7 +1016,7 @@ class CoordinateOperationRegistry {
CoordinateReferenceSystem crs;
if (Utilities.equalsApproximately(sourceCRS, crs =
operation.getSourceCRS())) sourceCRS = crs;
if (Utilities.equalsApproximately(targetCRS, crs =
operation.getTargetCRS())) targetCRS = crs;
- final Map<String,Object> properties = new
HashMap<>(derivedFrom(operation));
+ final var properties = new HashMap<String,
Object>(derivedFrom(operation));
properties.put(CoordinateOperations.OPERATION_TYPE_KEY,
typeOf(operation));
/*
* Reuse the same operation method, but we may need to change its
number of dimension.
@@ -1024,7 +1024,7 @@ class CoordinateOperationRegistry {
* The capability to resize an operation method is specific to Apache
SIS.
*/
if (operation instanceof SingleOperation) {
- final SingleOperation single = (SingleOperation) operation;
+ final var single = (SingleOperation) operation;
properties.put(CoordinateOperations.PARAMETERS_KEY,
single.getParameterValues());
if (method == null) {
method = single.getMethod();
@@ -1281,8 +1281,8 @@ class CoordinateOperationRegistry {
* @param name the name to put in a map.
* @return a modifiable map containing the given name. Callers can put
other entries in this map.
*/
- static Map<String,Object> properties(final Identifier name) {
- final var properties = new HashMap<String,Object>(4);
+ static Map<String, Object> properties(final Identifier name) {
+ final var properties = new HashMap<String, Object>(4);
properties.put(CoordinateOperation.NAME_KEY, name);
return properties;
}
@@ -1347,7 +1347,7 @@ class CoordinateOperationRegistry {
* @return a coordinate operation using the specified math transform.
* @throws FactoryException if the operation cannot be created.
*/
- final CoordinateOperation createFromMathTransform(final Map<String,Object>
properties,
+ final CoordinateOperation createFromMathTransform(final Map<String,
Object> properties,
final
CoordinateReferenceSystem sourceCRS,
final
CoordinateReferenceSystem targetCRS,
final MathTransform
transform,
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
index 916bb702d3..c9a43c030f 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
@@ -210,8 +210,8 @@ final class DefaultConcatenatedOperation extends
AbstractCoordinateOperation imp
* <li>Set the {@link #coordinateOperationAccuracy} field, but only if
{@code setAccuracy} is {@code true}.</li>
* </ul>
*
- * This method invokes itself recursively if there is nested {@code
ConcatenatedOperation} instances
- * in the given list. This should not happen according ISO 19111 standard,
but we try to be safe.
+ * This method invokes itself recursively if there are nested {@code
ConcatenatedOperation} instances
+ * in the given list. This should not happen according <abbr>ISO</abbr>
19111 standard, but we try to be safe.
*
* <h4>How coordinate operation accuracy is determined</h4>
* If {@code setAccuracy} is {@code true}, then this method copies
accuracy information found in the single
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
index 99a1fb9c27..0604dff9fa 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
@@ -443,7 +443,7 @@ next: for (SingleCRS component :
CRS.getSingleComponents(targetCRS)) {
/*
* Undocumented (for now) feature: if the `transform` argument is null
but parameters are
* found in the given properties, create the MathTransform instance
from those parameters.
- * This is needed for WKT parsing of CoordinateOperation[…] among
others.
+ * This is needed for WKT parsing of "CoordinateOperation[…]" among
other user cases.
*/
if (transform == null) {
final ParameterValueGroup parameters =
Containers.property(properties,
@@ -611,16 +611,19 @@ next: for (SingleCRS component :
CRS.getSingleComponents(targetCRS)) {
* cause the lost of the original CRS with the desired longitude
range.
*/
if (single instanceof SingleOperation) {
- final Map<String,Object> merge = new HashMap<>(
+ final var modifiedProperties = new HashMap<String, Object>(
IdentifiedObjects.getProperties(single,
CoordinateOperation.IDENTIFIERS_KEY));
- merge.put(CoordinateOperations.PARAMETERS_KEY,
((SingleOperation) single).getParameterValues());
+ modifiedProperties.put(CoordinateOperations.PARAMETERS_KEY,
((SingleOperation) single).getParameterValues());
if (single instanceof AbstractIdentifiedObject) {
- merge.put(CoordinateOperations.OPERATION_TYPE_KEY,
((AbstractIdentifiedObject) single).getInterface());
+
modifiedProperties.put(CoordinateOperations.OPERATION_TYPE_KEY,
((AbstractIdentifiedObject) single).getInterface());
}
- merge.putAll(properties);
- return createSingleOperation(merge, op.getSourceCRS(),
op.getTargetCRS(),
- op.getInterpolationCRS().orElse(null),
- ((SingleOperation) single).getMethod(),
op.getMathTransform());
+ modifiedProperties.putAll(properties);
+ return createSingleOperation(modifiedProperties,
+ op.getSourceCRS(),
+ op.getTargetCRS(),
+
op.getInterpolationCRS().orElse(null),
+ ((SingleOperation)
single).getMethod(),
+ op.getMathTransform());
}
}
return single;
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultFormula.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultFormula.java
index 4f66c2f9fe..cc7771e763 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultFormula.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultFormula.java
@@ -129,6 +129,8 @@ public class DefaultFormula extends FormattableObject
implements Formula, Serial
/**
* Returns the formula(s) or procedure used by the operation method, or
{@code null} if none.
+ *
+ * @return a textual description of the formula.
*/
@Override
public InternationalString getFormula() {
@@ -138,6 +140,8 @@ public class DefaultFormula extends FormattableObject
implements Formula, Serial
/**
* Returns the reference to a publication giving the formula(s) or
procedure used by the
* coordinate operation method, or {@code null} if none.
+ *
+ * @return reference to the publication giving the formula.
*/
@Override
public Citation getCitation() {
@@ -146,6 +150,8 @@ public class DefaultFormula extends FormattableObject
implements Formula, Serial
/**
* Returns a hash code value for this formula.
+ *
+ * @return hash code value computed from the textual representation or
reference to the formula.
*/
@Override
public int hashCode() {
@@ -167,7 +173,7 @@ public class DefaultFormula extends FormattableObject
implements Formula, Serial
return true;
}
if (object != null && object.getClass() == getClass()) {
- final DefaultFormula that = (DefaultFormula) object;
+ final var that = (DefaultFormula) object;
return Objects.equals(this.formula, that.formula) &&
Objects.equals(this.citation, that.citation);
}
@@ -188,6 +194,7 @@ public class DefaultFormula extends FormattableObject
implements Formula, Serial
@Override
protected String formatTo(final Formatter formatter) {
InternationalString text = null;
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final Citation citation = getCitation(); // Gives to users a chance
to override properties.
if (citation != null) {
text = citation.getTitle();
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultOperationMethod.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultOperationMethod.java
index acf3d1d4fc..21cb4ba6e1 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultOperationMethod.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultOperationMethod.java
@@ -275,8 +275,8 @@ public class DefaultOperationMethod extends
AbstractIdentifiedObject implements
* or {@code null} if it is not going to have any
declared authority.
* @return the identified object properties in a mutable map.
*/
- private static Map<String,Object> getProperties(final IdentifiedObject
info, final Citation authority) {
- final Map<String,Object> properties = new
HashMap<>(IdentifiedObjects.getProperties(info));
+ private static Map<String, Object> getProperties(final IdentifiedObject
info, final Citation authority) {
+ final var properties = new HashMap<String,
Object>(IdentifiedObjects.getProperties(info));
properties.put(NAME_KEY, new NamedIdentifier(authority,
info.getName().getCode()));
properties.remove(IDENTIFIERS_KEY);
return properties;
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformProvider.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformProvider.java
index bb7d4ce36c..9a37ae59ef 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformProvider.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformProvider.java
@@ -28,12 +28,13 @@ import org.opengis.referencing.datum.Ellipsoid;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.OperationMethod;
/**
* An object capable to create {@code MathTransform} instances from given
parameter values.
- * This interface is the Apache SIS mechanism by which
- * {@linkplain org.apache.sis.referencing.operation.DefaultFormula formula}
are concretized as Java code.
+ * This interface is the Apache <abbr>SIS</abbr> mechanism by which
+ * {@linkplain org.opengis.referencing.operation.Formula formula} are
concretized as Java code.
* A math transform provider ignores the source and target <abbr>CRS</abbr>
and works with coordinates in
* predefined axis order and units — typically (east, north, up) in degrees or
meters — although some
* variations are allowed in the number of dimensions (typically the "up"
dimension being optional).
@@ -41,21 +42,21 @@ import
org.opengis.referencing.operation.MathTransformFactory;
*
* <p>This interface is generally not used directly. The recommended way to
get a {@link MathTransform}
* is to {@linkplain org.apache.sis.referencing.CRS#findOperation find the
coordinate operation}
- * (generally from a pair of <var>source</var> and <var>target</var> CRS),
then to invoke
- * {@link
org.opengis.referencing.operation.CoordinateOperation#getMathTransform()}.
+ * (generally from a pair of <var>source</var> and <var>target</var>
<abbr>CRS</abbr>s),
+ * then to invoke {@link
org.opengis.referencing.operation.CoordinateOperation#getMathTransform()}.
* Alternatively, one can also use a {@linkplain DefaultMathTransformFactory
math transform factory}.</p>
*
- * <p>Implementations of this interface usually extend {@link
org.apache.sis.referencing.operation.DefaultOperationMethod},
- * but this is not mandatory. This interface can also be used alone since
{@link MathTransform} instances can be created
- * for other purpose than coordinate operations.</p>
+ * <p>Implementations of this interface usually implement also the {@link
OperationMethod} interface,
+ * but this is not mandatory. This interface can be used alone since {@link
MathTransform} instances
+ * can be created for other purposes than coordinate operations.</p>
*
*
- * <h2>How to add custom coordinate operations to Apache SIS</h2>
- * {@link DefaultMathTransformFactory} can discover automatically new
coordinate operations
- * (including map projections) by scanning the module path. To define a custom
coordinate operation,
- * one needs to define a <strong>thread-safe</strong> class implementing
<strong>both</strong> this
- * {@code MathTransformProvider} interface and the {@link
org.opengis.referencing.operation.OperationMethod} one.
- * While not mandatory, we suggest to extend {@link
org.apache.sis.referencing.operation.DefaultOperationMethod}.
+ * <h2>How to add custom coordinate operation methods to Apache
<abbr>SIS</abbr></h2>
+ * {@link DefaultMathTransformFactory} can discover automatically new
coordinate operation methods
+ * (including map projections) by scanning the module path. To define a custom
method, one needs to
+ * define a <em>thread-safe</em> class implementing <em>both</em> this {@code
MathTransformProvider}
+ * interface and the {@link OperationMethod} interface. The latter can be
implemented indirectly by
+ * extending {@link
org.apache.sis.referencing.operation.DefaultOperationMethod}.
* Example:
*
* {@snippet lang="java" :
@@ -124,7 +125,7 @@ public interface MathTransformProvider {
throws InvalidParameterNameException, ParameterNotFoundException,
InvalidParameterValueException, FactoryException
{
- return createMathTransform(new MathTransformProvider.Context() {
+ return createMathTransform(new Context() {
@Override public MathTransformFactory getFactory() {
return (factory != null) ? factory :
Context.super.getFactory();
}
@@ -279,7 +280,7 @@ public interface MathTransformProvider {
*
* @return names of parameters inferred from context.
*/
- default Map<String,Boolean> getContextualParameters() {
+ default Map<String, Boolean> getContextualParameters() {
return Map.of();
}
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultTransformationTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultTransformationTest.java
index c9fdc5f560..359c0810b9 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultTransformationTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultTransformationTest.java
@@ -46,6 +46,7 @@ import static
org.apache.sis.referencing.Assertions.assertWktEquals;
*
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class DefaultTransformationTest extends TestCase {
/**
* Creates a new test case.
@@ -93,7 +94,7 @@ public final class DefaultTransformationTest extends TestCase
{
* did not bothered to define a specialized MathTransform class for
our case. So we will help
* a little bit DefaultTransformation by telling it the parameters
that we used.
*/
- final Map<String, Object> properties = new HashMap<>(4);
+ final var properties = new HashMap<String, Object>(4);
properties.put(DefaultTransformation.NAME_KEY, "Tokyo to JGD2000
(GSI)");
properties.put(DefaultTransformation.OPERATION_VERSION_KEY, "GSI-Jpn");
properties.put(CoordinateOperations.PARAMETERS_KEY, pg);