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 9dde363825909c92cf7ac887931c498e9586bee3
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Mon Jan 24 12:05:00 2022 +0100

    Provide a way to alter parameter values of inverse operation
    in a call to `ContextualParameters.inverse(…)`.
---
 .../operation/transform/ContextualParameters.java  | 107 +++++++++++++++------
 .../transform/CoordinateSystemTransform.java       |   2 +-
 .../transform/EllipsoidToCentricTransform.java     |   6 +-
 .../operation/transform/package-info.java          |   2 +-
 4 files changed, 82 insertions(+), 35 deletions(-)

diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
index 853cfaa..4e1e01d 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
@@ -22,6 +22,7 @@ import java.util.Map;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.Objects;
+import java.util.function.BiPredicate;
 import java.io.Serializable;
 import org.opengis.util.FactoryException;
 import org.opengis.parameter.GeneralParameterValue;
@@ -54,6 +55,7 @@ import org.apache.sis.io.wkt.Formatter;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ArraysExt;
 
 
 /**
@@ -121,7 +123,7 @@ import org.apache.sis.util.ArgumentChecks;
  * Serialization should be used only for short term storage or RMI between 
applications running the same SIS version.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.2
  *
  * @see org.apache.sis.referencing.operation.projection.NormalizedProjection
  * @see AbstractMathTransform#getContextualParameters()
@@ -205,12 +207,17 @@ public class ContextualParameters extends Parameters 
implements Serializable {
      *
      * <p>This array is modifiable after construction, but is considered 
unmodifiable after
      * {@link #completeTransform(MathTransformFactory, MathTransform)} has 
been invoked.</p>
+     *
+     * @see #parameter(String)
+     * @see #freeze()
      */
     private ParameterValue<?>[] values;
 
     /**
      * If the inverse coordinate operation can be described by another {@code 
ContextualParameters} instance,
      * a reference to that instance. Otherwise {@code null}.
+     *
+     * @see #inverse(ParameterDescriptorGroup, BiPredicate)
      */
     private ContextualParameters inverse;
 
@@ -240,7 +247,10 @@ public class ContextualParameters extends Parameters 
implements Serializable {
      * See class javadoc for more information.
      *
      * @param  method  the non-linear operation method for which to define the 
parameter values.
+     *
+     * @deprecated Use the constructor with explicit number of dimensions 
instead.
      */
+    @Deprecated
     public ContextualParameters(final OperationMethod method) {
         ArgumentChecks.ensureNonNull("method", method);
         descriptor  = method.getParameters();
@@ -270,18 +280,42 @@ public class ContextualParameters extends Parameters 
implements Serializable {
     }
 
     /**
-     * Creates a {@code ContextualParameters} for the inverse operation.
+     * Creates a new and frozen {@code ContextualParameters} for the inverse 
operation.
+     * An optional {@code mapper} can be specified for setting the parameter 
values.
+     * If the mapper is-non null, then it will be invoked for each new 
parameters.
+     * The first argument given to the mapper is the {@code forward} operation,
+     * and the second argument is the parameter to configure.
+     * The return value is whether the parameter should be kept in the new 
operation.
      *
      * @param  desc     descriptor of the inverse operation.
      * @param  forward  the parameters created for the forward operation.
+     * @param  mapper   the function to invoke for setting parameter values,
+     *                  or {@code null} if the inverse operation uses the same 
parameter values.
+     *
+     * @see #inverse(ParameterDescriptorGroup, BiPredicate)
      */
-    private ContextualParameters(final ParameterDescriptorGroup desc, final 
ContextualParameters forward) {
+    private ContextualParameters(final ParameterDescriptorGroup desc, final 
ContextualParameters forward,
+                                 final BiPredicate<Parameters, 
ParameterValue<?>> mapper)
+    {
         descriptor  = desc;
+        inverse     = forward;
         normalize   = forward.getMatrix(MatrixRole.INVERSE_DENORMALIZATION);
         denormalize = forward.getMatrix(MatrixRole.INVERSE_NORMALIZATION);
-        values      = forward.values;
-        inverse     = forward;
-        isFrozen    = true;
+        if (mapper == null) {
+            values = forward.values;
+        } else {
+            final List<GeneralParameterDescriptor> descriptors = 
desc.descriptors();
+            final ParameterValue<?>[] values = new 
ParameterValue<?>[descriptors.size()];
+            int count = 0;
+            for (int i=0; i < values.length; i++) {
+                final ContextualParameter<?> p = new 
ContextualParameter<>((ParameterDescriptor<?>) descriptors.get(i));
+                if (mapper.test(forward, p)) {
+                    values[count++] = p;
+                }
+            }
+            this.values = ArraysExt.resize(values, count);
+        }
+        isFrozen = true;
     }
 
     /**
@@ -298,17 +332,29 @@ public class ContextualParameters extends Parameters 
implements Serializable {
     }
 
     /**
-     * Creates a {@code ContextualParameters} for the inverse operation.
-     *
-     * @param  desc  descriptor of the inverse operation.
+     * Creates a new and frozen {@code ContextualParameters} for the inverse 
of this operation.
+     * An optional {@code mapper} can be specified for setting the parameter 
values.
+     * If the mapper is-non null, then it will be invoked for each new 
parameters.
+     * The first argument given to the mapper will be {@code this} operation,
+     * and the second argument is the parameter to configure.
+     * The return value is whether the parameter should be kept in the new 
operation.
+     *
+     * <p>This method caches the inverse operation. It is caller 
responsibility to ensure that all
+     * arguments given to this method are constants for a given {@link 
MathTransform} instance.</p>
+     *
+     * @param  desc    descriptor of the inverse operation.
+     * @param  mapper  the function to invoke for setting parameter values,
+     *                 or {@code null} if the inverse operation uses the same 
parameter values.
      * @return parameters for the inverse operation.
      */
-    final synchronized ContextualParameters inverse(final 
ParameterDescriptorGroup desc) {
+    final synchronized ContextualParameters inverse(final 
ParameterDescriptorGroup desc,
+            final BiPredicate<Parameters, ParameterValue<?>> mapper)
+    {
         if (inverse == null) {
             if (!isFrozen) {
                 freeze();
             }
-            inverse = new ContextualParameters(desc, this);
+            inverse = new ContextualParameters(desc, this, mapper);
         }
         assert inverse.descriptor == desc;
         return inverse;
@@ -412,7 +458,7 @@ public class ContextualParameters extends Parameters 
implements Serializable {
         }
         /*
          * Following must be outside the synchronized block in order to avoid 
potential deadlock while invoking
-         * inverse.getMatrix(role). We do not cache the matrix here, but 
'inverse' is likely to have cached it.
+         * inverse.getMatrix(role). We do not cache the matrix here, but 
`inverse` is likely to have cached it.
          */
         final Matrix m;
         if (inverse != null) {
@@ -527,8 +573,8 @@ public class ContextualParameters extends Parameters 
implements Serializable {
         }
         /*
          * Following call must be outside the synchronized block for avoiding 
dead-lock. This is because the
-         * factory typically contains a WeakHashSet, which invoke in turn the 
'equals' methods in this class
-         * (indirectly, through 'kernel' comparison). We need to be outside 
the synchronized block for having
+         * factory typically contains a WeakHashSet, which invoke in turn the 
`equals` methods in this class
+         * (indirectly, through `kernel` comparison). We need to be outside 
the synchronized block for having
          * the locks taken in same order (WeakHashSet lock before the 
ContextualParameters lock).
          */
         return 
factory.createConcatenatedTransform(factory.createConcatenatedTransform(n, 
kernel), d);
@@ -543,7 +589,7 @@ public class ContextualParameters extends Parameters 
implements Serializable {
         isFrozen = true;
         /*
          * Sort the parameter values in the same order than the parameter 
descriptor. This is not essential,
-         * but makes easier to read 'toString()' output by ensuring a 
consistent order for most projections.
+         * but makes easier to read `toString()` output by ensuring a 
consistent order for most projections.
          * Some WKT parsers other than SIS may also require the parameter 
values to be listed in that specific
          * order. We proceed by first copying all parameters in a temporary 
HashMap:
          */
@@ -560,11 +606,11 @@ public class ContextualParameters extends Parameters 
implements Serializable {
             }
         }
         /*
-         * Then, copy all HashMap values back to the 'values' array in the 
order they are declared in the
+         * Then, copy all HashMap values back to the `values` array in the 
order they are declared in the
          * descriptor. Implementation note: the iteration termination 
condition uses the values array, not
          * the descriptors list, because the former is often shorter than the 
later. We should never reach
-         * the end of descriptors list before the end of values array because 
'descriptors' contains all
-         * 'parameters' keys. This is verified by the 'assert' below.
+         * the end of descriptors list before the end of values array because 
`descriptors` contains all
+         * `parameters` keys. This is verified by the `assert` below.
          */
         values = new ParameterValue<?>[parameters.size()];
         assert descriptor.descriptors().containsAll(parameters.keySet());
@@ -572,8 +618,8 @@ public class ContextualParameters extends Parameters 
implements Serializable {
         for (int i=0; i < values.length;) {
             /*
              * No need to check for it.hasNext(), since a 
NoSuchElementException below would be a bug in
-             * our algorithm (or a concurrent change in the 
'descriptor.descriptors()' list, which would
-             * be a contract violation). See above 'assert'.
+             * our algorithm (or a concurrent change in the 
`descriptor.descriptors()` list, which would
+             * be a contract violation). See above `assert`.
              */
             final ParameterValue<?> p = parameters.get(it.next());
             if (p != null) {
@@ -610,6 +656,7 @@ public class ContextualParameters extends Parameters 
implements Serializable {
         for (int i=0; i < values.length; i++) {
             ParameterValue<?> p = values[i];
             if (p == null) {
+                // Null values are always last, so it is okay to stop the 
search here.
                 values[i] = p = new 
ContextualParameter<>((ParameterDescriptor<?>) desc);
                 return p;
             }
@@ -618,7 +665,7 @@ public class ContextualParameters extends Parameters 
implements Serializable {
             }
         }
         /*
-         * We may reach this point if map projection construction is completed 
(i.e. 'completeTransform(…)' has
+         * We may reach this point if map projection construction is completed 
(i.e. `completeTransform(…)` has
          * been invoked) and the user asks for a parameter which is not one of 
the parameters that we retained.
          * Returns a parameter initialized to the default value, which is the 
actual value (otherwise we would
          * have stored that parameter).  Note: we do not bother making the 
parameter immutable for performance
@@ -868,14 +915,14 @@ public class ContextualParameters extends Parameters 
implements Serializable {
          * is often for changing axis order. Thanks to double-double 
arithmetic in SIS matrices,
          * the non-zero values are usually accurate. But the values that 
should be zero are much
          * harder to get right. Sometime we see small values (around 1E-12) in 
the last column of
-         * the 'before' matrix below. Since this column contains translation 
terms, those numbers
+         * the `before` matrix below. Since this column contains translation 
terms, those numbers
          * are in the unit of measurement of input values of the MathTransform 
after the matrix.
          *
          *   - For forward map projections, those values are conceptually in 
decimal degrees
-         *     (in fact the values are converted to radians but not by this 
'before' matrix).
+         *     (in fact the values are converted to radians but not by this 
`before` matrix).
          *
          *   - For inverse map projections, those values are conceptually in 
metres (in fact
-         *     converted to distances on a unitary ellipsoid but not by this 
'before' matrix).
+         *     converted to distances on a unitary ellipsoid but not by this 
`before` matrix).
          *
          *   - Geographic/Geocentric transformations behave like map 
projections in regard to units.
          *     Molodensky transformations conceptually use always decimal 
degrees. There is not much
@@ -883,7 +930,7 @@ public class ContextualParameters extends Parameters 
implements Serializable {
          *
          * Consequently we set the tolerance threshold to ANGULAR_TOLERANCE. 
We do not bother (at least
          * for now) to identify the cases where we could use LINEAR_TOLERANCE 
because just checking the
-         * 'inverse' flag is not sufficient (e.g. the Molodensky case). Since 
the angular tolerance is
+         * `inverse` flag is not sufficient (e.g. the Molodensky case). Since 
the angular tolerance is
          * smaller than the linear one, unconditional usage of 
ANGULAR_TOLERANCE is more conservative.
          */
         before = Matrices.isIdentity(userDefined, Formulas.ANGULAR_TOLERANCE) 
? null : userDefined;
@@ -902,20 +949,20 @@ public class ContextualParameters extends Parameters 
implements Serializable {
             userDefined = Matrices.multiply(after, userDefined);
         }
         /*
-         * Note on rounding error: same discussion than the "note on rounding 
error" of the 'before' matrix,
+         * Note on rounding error: same discussion than the "note on rounding 
error" of the `before` matrix,
          * with the following differences:
          *
          *   - For forward map projections, unit of measurements of 
translation terms are conceptually
-         *     metres (instead of degrees) multiplied by the scale factors in 
the 'after' matrix.
+         *     metres (instead of degrees) multiplied by the scale factors in 
the `after` matrix.
          *
          *   - For inverse map projections, unit of measurements of 
translation terms are conceptually
-         *     degrees (instead of metres) multiplied by the scale factors in 
the 'after' matrix.
+         *     degrees (instead of metres) multiplied by the scale factors in 
the `after` matrix.
          *
          *   - And so on for all cases: swap the units of the forward and 
inverse cases, then multiply
-         *     by the scale factor. Note that the multiplication step does not 
exist in the 'before' case.
+         *     by the scale factor. Note that the multiplication step does not 
exist in the `before` case.
          *
          * Since we are seeking for the identity matrix, the scale factor is 
1. We do not bother to distinguish
-         * the ANGULAR_TOLERANCE and LINEAR_TOLERANCE cases for the same 
reasons than for the 'before' matrix.
+         * the ANGULAR_TOLERANCE and LINEAR_TOLERANCE cases for the same 
reasons than for the `before` matrix.
          */
         after = Matrices.isIdentity(userDefined, Formulas.ANGULAR_TOLERANCE) ? 
null : userDefined;
         /*
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
index ae211e3..6a6390e 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
@@ -44,7 +44,7 @@ import 
org.apache.sis.referencing.operation.DefaultOperationMethod;
 
 /**
  * Base class of conversions between coordinate systems.
- * Each subclasses should have a singleton instance.
+ * Each subclass should have a singleton instance.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
index d4d4ed2..0b5dbd1 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
@@ -815,7 +815,7 @@ next:   while (--numPts >= 0) {
          */
         @Override
         protected ContextualParameters getContextualParameters() {
-            return forward.context.inverse(GeocentricToGeographic.PARAMETERS);
+            return forward.context.inverse(GeocentricToGeographic.PARAMETERS, 
null);
         }
 
         /**
@@ -842,8 +842,8 @@ next:   while (--numPts >= 0) {
         @Debug
         @Override
         public ParameterDescriptorGroup getParameterDescriptors() {
-            return new 
DefaultParameterDescriptorGroup(Collections.singletonMap(ParameterDescriptorGroup.NAME_KEY,
-                            new ImmutableIdentifier(Citations.SIS, 
Constants.SIS, "Centric to ellipsoid (radians domain)")),
+            ImmutableIdentifier name = new ImmutableIdentifier(Citations.SIS, 
Constants.SIS, "Centric to ellipsoid (radians domain)");
+            return new 
DefaultParameterDescriptorGroup(Collections.singletonMap(ParameterDescriptorGroup.NAME_KEY,
 name),
                     forward.getParameterDescriptors());
         }
 
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/package-info.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/package-info.java
index db70c02..5298191 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/package-info.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/package-info.java
@@ -61,7 +61,7 @@
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Adrian Custer (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   0.5
  * @module
  */

Reply via email to