sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: First draft of Canvas.getGridGeometry() taking in account the supplemental dimensions (i.e. the dimensions of the Point Of Interest that are not in the objective CRS).
Date Mon, 10 Feb 2020 00:00:20 GMT
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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 62eb5d1  First draft of Canvas.getGridGeometry() taking in account the supplemental
dimensions (i.e. the dimensions of the Point Of Interest that are not in the objective CRS).
62eb5d1 is described below

commit 62eb5d1fc4c2bb206b5e50e425184b54bfa7fd3d
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon Feb 10 00:57:50 2020 +0100

    First draft of Canvas.getGridGeometry() taking in account the supplemental dimensions
    (i.e. the dimensions of the Point Of Interest that are not in the objective CRS).
---
 .../java/org/apache/sis/internal/map/Canvas.java   | 182 ++++++++++++++++-----
 .../org/apache/sis/internal/map/CanvasExtent.java  | 127 ++++++++++++++
 .../DefaultCoordinateOperationFactory.java         |   5 +-
 3 files changed, 271 insertions(+), 43 deletions(-)

diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
index 0c43c7c..50b8292 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Canvas.java
@@ -19,6 +19,7 @@ package org.apache.sis.internal.map;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.ArrayList;
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
@@ -31,6 +32,7 @@ import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.referencing.datum.PixelInCell;
 import org.opengis.util.FactoryException;
+import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.Localized;
 import org.apache.sis.util.ArgumentChecks;
@@ -201,6 +203,7 @@ public class Canvas extends Observable implements Localized {
      * @see #OBJECTIVE_CRS_PROPERTY
      * @see #getObjectiveCRS()
      * @see #setObjectiveCRS(CoordinateReferenceSystem)
+     * @see #augmentedObjectiveCRS
      */
     private CoordinateReferenceSystem objectiveCRS;
 
@@ -240,10 +243,47 @@ public class Canvas extends Observable implements Localized {
      * The point of interest transformed to the objective CRS, or {@code null} if {@link
#pointOfInterest}
      * has not yet been provided. This point shall be updated immediately when {@link #pointOfInterest}
is
      * updated, as a way to verify that the point is valid.
+     *
+     * <p>There is no setter method for this property. It is computed from {@link #pointOfInterest}
+     * and {@link #objectiveCRS} (indirectly, through {@link #multidimToObjective}) and should
be
+     * recomputed when any of those properties changed.</p>
      */
     private DirectPosition objectivePOI;
 
     /**
+     * The transform from the multi-dimensional CRS of {@link #pointOfInterest} to the objective
CRS.
+     * This is the transform used for computing {@link #objectivePOI}. This transform may
reduce the
+     * number of dimensions.
+     *
+     * <p>There is no setter method for this property. It is computed from {@link #pointOfInterest}
+     * and {@link #objectiveCRS} and should be recomputed when any of those properties changed.</p>
+     */
+    private MathTransform multidimToObjective;
+
+    /**
+     * The {@link #objectiveCRS} augmented with additional dimensions found in {@link #pointOfInterest}.
+     * This field is initially {@code null} and computed only if needed. It may be reset
to {@code null}
+     * at any time if the properties used for computing this value changed.
+     *
+     * <p>If the point of interest has no supplemental dimension, then this CRS is
{@link #objectiveCRS}.
+     * Otherwise {@linkplain #supplementalDimensions supplemental dimensions} are added on
a best effort
+     * basis: some supplemental dimensions may be missing if we have not been able to separate
components
+     * from the Point Of Interest CRS.</p>
+     */
+    private CoordinateReferenceSystem augmentedObjectiveCRS;
+
+    /**
+     * The dimensions in the Point Of Interest CRS that are not in the {@link #objectiveCRS}.
+     * Those dimensions are encoded as a bitmask: if dimension <var>n</var> is
a supplemental
+     * dimension, then the bit {@code 1L << n} is set to 1. This encoding implies that
we can
+     * not handle more than {@value Long#SIZE} dimensions at this time.
+     *
+     * <p>The value of this field is invalid if {@link #augmentedObjectiveCRS} is {@code
null}.
+     * Those two fields are computed together.</p>
+     */
+    private long supplementalDimensions;
+
+    /**
      * The factory to use for creating coordinate operations. This factory allow us to specify
the area
      * of interest (the geographic region shown by this {@code Canvas}) and the desired resolution.
      *
@@ -376,7 +416,7 @@ public class Canvas extends Observable implements Localized {
         final CoordinateReferenceSystem oldValue = objectiveCRS;
         LinearTransform oldObjectiveToDisplay = null;
         LinearTransform newObjectiveToDisplay = null;
-        if (!Objects.equals(oldValue, newValue)) {
+        if (!newValue.equals(oldValue)) {
             if (oldValue != null) try {
                 /*
                  * Compute the change unconditionally as a way to verify that the new CRS
is compatible with
@@ -406,7 +446,9 @@ public class Canvas extends Observable implements Localized {
                      */
                     newObjectiveToDisplay = MathTransforms.tangent(result, poiInNew);
                     updateObjectiveToDisplay(newObjectiveToDisplay);
-                    objectivePOI = poiInNew;    // Set only after everything else succeeded.
+                    objectivePOI          = poiInNew;               // Set only after everything
else succeeded.
+                    multidimToObjective   = poiToNew;
+                    augmentedObjectiveCRS = null;                   // Will be recomputed
when first needed.
                 }
             } catch (FactoryException | TransformException e) {
                 throw new RenderException(errors().getString(Errors.Keys.CanNotSetPropertyValue_1,
OBJECTIVE_CRS_PROPERTY), e);
@@ -448,6 +490,8 @@ public class Canvas extends Observable implements Localized {
      * to be overridden only by subclasses that use their own specialized class instead than
      * {@link #objectiveToDisplay} for managing changes in the zooms or viewed area.
      *
+     * @return snapshot of objective to display conversion, never null.
+     *
      * @see #updateObjectiveToDisplay(LinearTransform)
      */
     LinearTransform updateObjectiveToDisplay() {
@@ -478,7 +522,7 @@ public class Canvas extends Observable implements Localized {
                 if (oldValue == null) {
                     oldValue = updateObjectiveToDisplay();
                 }
-                if (!Objects.equals(oldValue, newValue)) {
+                if (!oldValue.equals(newValue)) {
                     updateObjectiveToDisplay(newValue);
                     firePropertyChange(OBJECTIVE_TO_DISPLAY_PROPERTY, oldValue, newValue);
                 }
@@ -531,20 +575,19 @@ public class Canvas extends Observable implements Localized {
      */
     public void setDisplayBounds(final Envelope newValue) throws RenderException {
         ArgumentChecks.ensureNonNull(DISPLAY_BOUNDS_PROPERTY, newValue);
-        if (objectiveCRS != null) {
-            final CoordinateReferenceSystem crs = newValue.getCoordinateReferenceSystem();
-            if (crs != null && !Utilities.equalsIgnoreMetadata(objectiveCRS, crs))
{
-                throw new MismatchedReferenceSystemException(errors().getString(
-                        Errors.Keys.IllegalCoordinateSystem_1, IdentifiedObjects.getDisplayName(crs,
getLocale())));
-            }
+        final CoordinateReferenceSystem crs = newValue.getCoordinateReferenceSystem();
+        if (crs != null && !Utilities.equalsIgnoreMetadata(getDisplayCRS(), crs))
{
+            throw new MismatchedReferenceSystemException(errors().getString(
+                    Errors.Keys.IllegalCoordinateSystem_1, IdentifiedObjects.getDisplayName(crs,
getLocale())));
         }
         final GeneralEnvelope oldValue = new GeneralEnvelope(displayBounds);
         displayBounds.setEnvelope(newValue);
+        displayBounds.setCoordinateReferenceSystem(oldValue.getCoordinateReferenceSystem());
         if (displayBounds.isEmpty()) {
             displayBounds.setEnvelope(oldValue);
             throw new IllegalArgumentException(errors().getString(Errors.Keys.EmptyProperty_1,
DISPLAY_BOUNDS_PROPERTY));
         }
-        if (!Objects.equals(oldValue, displayBounds)) {
+        if (!oldValue.equals(displayBounds)) {
             firePropertyChange(DISPLAY_BOUNDS_PROPERTY, oldValue, newValue);    // Do not
publish reference to `displayBounds`.
         }
     }
@@ -575,16 +618,42 @@ public class Canvas extends Observable implements Localized {
      *
      * @param  newValue  the new coordinates of the point to show typically in the center
of display area.
      * @throws NullPointerException if the given position is null.
+     * @throws IllegalArgumentException if the given position does not have a CRS.
      * @throws RenderException if the point of interest can not be set to the given value.
      */
     public void setPointOfInterest(final DirectPosition newValue) throws RenderException
{
         ArgumentChecks.ensureNonNull(POINT_OF_INTEREST_PROPERTY, newValue);
         final GeneralDirectPosition copy = new GeneralDirectPosition(newValue);
+        final CoordinateReferenceSystem crs = copy.getCoordinateReferenceSystem();
+        if (crs == null) {
+            throw new IllegalArgumentException(errors().getString(Errors.Keys.UnspecifiedCRS));
+        }
         final GeneralDirectPosition oldValue = pointOfInterest;
-        if (!Objects.equals(oldValue, copy)) try {
-            final MathTransform mt = findTransform(newValue.getCoordinateReferenceSystem(),
objectiveCRS);
-            objectivePOI = mt.transform(copy, allocatePosition());
-            pointOfInterest = copy;
+        if (!copy.equals(oldValue)) try {
+            /*
+             * If the user has not yet specified an objective CRS, takes the Point Of Interest
CRS
+             * (only the number of dimensions that the display device can show).
+             */
+            if (objectiveCRS == null) {
+                objectiveCRS = CRS.getComponentAt(crs, 0, getDisplayDimensions());
+                if (objectiveCRS == null) {
+                    throw new IllegalArgumentException("Can not infer objective CRS.");
+                    // Message not localized yet because we should probably try harder.
+                }
+            }
+            /*
+             * Transform the Point Of Interest to the objective CRS as a way to test its
validity.
+             * All canvas fields will be updated only if this operation succeeds.
+             * Note: `oldValue` can not be null if `mt` is non-null.
+             */
+            MathTransform mt = multidimToObjective;
+            if (mt == null || !Utilities.equalsIgnoreMetadata(crs, oldValue.getCoordinateReferenceSystem()))
{
+                mt = findTransform(crs, objectiveCRS);
+            }
+            objectivePOI          = mt.transform(copy, allocatePosition());
+            pointOfInterest       = copy;                                           // Set
only after transform succeeded.
+            multidimToObjective   = mt;
+            augmentedObjectiveCRS = null;                                           // Will
be recomputed when first needed.
             firePropertyChange(POINT_OF_INTEREST_PROPERTY, oldValue, newValue);     // Do
not publish reference to `copy`.
         } catch (FactoryException | TransformException e) {
             throw new RenderException(errors().getString(Errors.Keys.CanNotSetPropertyValue_1,
POINT_OF_INTEREST_PROPERTY), e);
@@ -592,17 +661,18 @@ public class Canvas extends Observable implements Localized {
     }
 
     /**
-     * Returns canvas properties (objective CRS, display bounds, conversion) encapsulated
in a grid geometry.
-     * This is a convenience method for interoperability with grid coverage API. Properties
are mapped as below:
+     * Returns canvas properties (CRS, display bounds, conversion) encapsulated in a grid
geometry.
+     * This is a convenience method for interoperability with grid coverage API.
+     * The grid geometry elements are mapped to canvas properties as below:
      *
      * <table>
      *   <caption>Canvas to grid geometry properties</caption>
      *   <tr>
-     *     <th>Grid geometry value</th>
-     *     <th>Canvas value</th>
+     *     <th>Grid geometry element</th>
+     *     <th>Canvas property</th>
      *   </tr><tr>
      *     <td>{@link GridGeometry#getCoordinateReferenceSystem()}</td>
-     *     <td>{@link #getObjectiveCRS()}</td>
+     *     <td>{@link #getObjectiveCRS()}.</td>
      *   </tr><tr>
      *     <td>{@link GridGeometry#getExtent()}</td>
      *     <td>{@link #getDisplayBounds()} rounded to enclosing integers</td>
@@ -612,32 +682,62 @@ public class Canvas extends Observable implements Localized {
      *   </tr>
      * </table>
      *
-     * @param  allDimensions  if {@code true}, all dimensions from the point of interest
are included in
-     *         the returned grid geometry. If {@code false}, only the displayed dimensions
are included.
+     * All those elements are augmented with supplemental dimensions found in the point of
interest.
+     * For example if the canvas shows only (<var>x</var>,<var>y</var>)
coordinates but the point of
+     * interest also includes a <var>t</var> value, then a third dimension for
<var>t</var> is added
+     * to the CRS, extent and transform of the returned grid geometry.
+     *
      * @return a grid geometry encapsulating canvas properties.
      * @throws RenderException if the grid geometry can not be computed.
      */
-    public GridGeometry getGridGeometry(final boolean allDimensions) throws RenderException
{
-        final GridExtent extent;
-        if (displayBounds.isEmpty()) {
-            extent = null;
-        } else {
-            // TODO: take allDimensions in account.
-            final int dimension = displayBounds.getDimension();
-            final long[] lower = new long[dimension];
-            final long[] upper = new long[dimension];
-            for (int i=0; i<dimension; i++) {
-                lower[i] = (long) Math.floor(displayBounds.getMinimum(i));
-                upper[i] = (long) Math.ceil (displayBounds.getMaximum(i));
-            }
-            final DimensionNameType[] axisTypes = getDisplayAxes();
-            extent = new GridExtent(axisTypes, lower, upper, false);
-        }
+    public GridGeometry getGridGeometry() throws RenderException {
         try {
-            // TODO: take allDimensions in account.
-            return new GridGeometry(extent, PixelInCell.CELL_CENTER, objectiveToDisplay.inverse(),
objectiveCRS);
-        } catch (TransformException e) {
-            throw new RenderException(errors().getString(Errors.Keys.CanNotCompute_1, POINT_OF_INTEREST_PROPERTY),
e);
+            /*
+             * If not already done, create a multi-dimensional CRS composed of `objectiveCRS`
+             * with supplemental dimensions appended. This CRS needs to be recreated only
if
+             * the Point of Interest changed, or if the objective CRS changed.
+             */
+            if (augmentedObjectiveCRS == null) {
+                if (pointOfInterest != null) {
+                    final ArrayList<CoordinateReferenceSystem> components = new ArrayList<>(4);
+                    components.add(objectiveCRS);
+                    supplementalDimensions = CanvasExtent.findSupplementalDimensions(
+                            pointOfInterest.getCoordinateReferenceSystem(),
+                            multidimToObjective.derivative(pointOfInterest), components);
+                    augmentedObjectiveCRS = CRS.compound(components.toArray(new CoordinateReferenceSystem[components.size()]));
+                } else {
+                    augmentedObjectiveCRS = objectiveCRS;
+                }
+            }
+            /*
+             * Create the `gridToCRS` using the "display to objective" transform augmented
with POI coordinate
+             * values in supplemental dimensions. Note: `getObjectiveToDisplay()` should
never return null.
+             */
+            LinearTransform gridToCRS = getObjectiveToDisplay().inverse();
+            if (supplementalDimensions != 0) {
+                gridToCRS = CanvasExtent.createGridToCRS(gridToCRS.getMatrix(), pointOfInterest,
supplementalDimensions);
+            }
+            /*
+             * Create the grid extent with a number of dimensions that include the supplemental
dimensions.
+             * The cell indices range of all supplemental dimensions is [0 … 0].
+             */
+            final GridExtent extent;
+            if (displayBounds.isEmpty()) {
+                extent = null;
+            } else {
+                final int dimension = gridToCRS.getSourceDimensions();
+                final long[] lower = new long[dimension];
+                final long[] upper = new long[dimension];
+                for (int i = displayBounds.getDimension(); --i >= 0;) {
+                    lower[i] = (long) Math.floor(displayBounds.getMinimum(i));
+                    upper[i] = (long) Math.ceil (displayBounds.getMaximum(i));
+                }
+                final DimensionNameType[] axisTypes = ArraysExt.resize(getDisplayAxes(),
dimension);
+                extent = new GridExtent(axisTypes, lower, upper, false);
+            }
+            return new GridGeometry(extent, PixelInCell.CELL_CORNER, gridToCRS, augmentedObjectiveCRS);
+        } catch (FactoryException | TransformException e) {
+            throw new RenderException(errors().getString(Errors.Keys.CanNotCompute_1, "gridGeometry"),
e);
         }
     }
 
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasExtent.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasExtent.java
new file mode 100644
index 0000000..28616c6
--- /dev/null
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/CanvasExtent.java
@@ -0,0 +1,127 @@
+/*
+ * 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.internal.map;
+
+import java.util.List;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.referencing.CRS;
+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.LinearTransform;
+import org.apache.sis.internal.util.Numerics;
+
+
+/**
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+final class CanvasExtent {
+    private CanvasExtent() {
+    }
+
+    /**
+     * Finds the dimensions in the given CRS that are not included in the objective CRS.
+     * Those dimensions are discovered by inspection of the derivative of the transform
+     * from the given CRS to the objective CRS.  In addition, this method also adds the
+     * CRS component of those supplemental dimensions in the given list. If a component
+     * can not be separated from the CRS, then current implementation excludes it from
+     * the set of supplemental dimensions.
+     *
+     * @param  crs         the coordinate reference system of the Point Of Interest (POI).
+     * @param  derivative  derivative of the transform from Point Of Interest (POI) to objective
CRS.
+     * @param  addTo       the list where to add the CRS component for supplemental dimensions.
+     * @return a bitmask of supplemental dimensions.
+     *
+     * @see Canvas#supplementalDimensions
+     */
+    static long findSupplementalDimensions(final CoordinateReferenceSystem crs, final Matrix
derivative,
+                                           final List<CoordinateReferenceSystem> addTo)
+    {
+        /*
+         * Creates a mask with bits set to 1 for all source dimensions (columns)
+         * that are used by at least one target dimension (row). After the loop,
+         * that mask will be reverted and become the set of dimensions NOT used.
+         */
+        final int srcDim = Math.min(derivative.getNumCol(), Long.SIZE);
+        final int dstDim = derivative.getNumRow();
+        long mask = 0;                                  // Sources used by any target dimension.
+        for (int j=0; j<dstDim; j++) {
+            for (int i=0; i<srcDim; i++) {
+                if (derivative.getElement(j,i) != 0) {
+                    mask |= (1L << i);
+                    break;
+                }
+            }
+        }
+        mask ^= Numerics.bitmask(srcDim) - 1;           // Sources NOT used by any target
dimension.
+        /*
+         * Now we know the source dimensions of the CRS components to add in the specified
list.
+         * We must ask for CRS components using ranges as much as possible. For example if
some
+         * supplemental dimensions are 1,2,3 then we must as the component in range 1 inclusive
+         * to 4 exclusive. This is done easily we bits arithmetic. If we can get all components,
+         * the `supplementalDimensions` final value will be the `mask` initial value. If
we had
+         * to discard some components, then those long values will differ.
+         */
+        long supplementalDimensions = 0;
+        while (mask != 0) {
+            final int  lower = Long.numberOfTrailingZeros(mask);
+            final int  upper = Long.numberOfTrailingZeros(mask + (1L << lower));
+            final long clear = -(1L << upper);   // All bits on the right of `upper`
are zero.
+            final CoordinateReferenceSystem component = CRS.getComponentAt(crs, lower, upper);
+            if (component != null) {
+                addTo.add(component);
+                supplementalDimensions |= (mask & ~clear);
+            }
+            mask &= clear;
+        }
+        return supplementalDimensions;
+    }
+
+    /**
+     * Creates the "grid to CRS" transform of a grid geometry as the inverse of the
+     * "objective to display" transform augmented with supplemental dimensions.
+     *
+     * @param  displayToObjective      inverse of {@link Canvas#getObjectiveToDisplay()}.
+     * @param  pointOfInterest         value of {@link Canvas#getPointOfInterest()}.
+     * @param  supplementalDimensions  value of {@link #findSupplementalDimensions(CoordinateReferenceSystem,
Matrix, List)}.
+     * @return the "grid to CRS" transform of a grid geometry for a {@link Canvas}.
+     */
+    static LinearTransform createGridToCRS(final Matrix displayToObjective,
+            final DirectPosition pointOfInterest, long supplementalDimensions)
+    {
+        final int srcDim = displayToObjective.getNumCol();
+        final int tgtDim = displayToObjective.getNumRow() + Long.bitCount(supplementalDimensions);
+        final MatrixSIS gridToCRS = Matrices.createIdentity(tgtDim + 1);
+        while (supplementalDimensions != 0) {
+            final int n = Long.numberOfTrailingZeros(supplementalDimensions);
+            gridToCRS.setElement(n, tgtDim, pointOfInterest.getOrdinate(n));
+            supplementalDimensions &= ~(1L << n);
+        }
+        for (int j=0; j<tgtDim; j++) {
+            for (int i=0; i<srcDim; i++) {
+                gridToCRS.setElement(j,i, displayToObjective.getElement(j,i));
+            }
+        }
+        return MathTransforms.linear(gridToCRS);
+    }
+}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
index 128a808..d1e6807 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
@@ -247,8 +247,9 @@ public class DefaultCoordinateOperationFactory extends AbstractFactory
implement
     }
 
     /**
-     * Returns the underlying math transform factory. This factory is used for constructing
{@link MathTransform}
-     * dependencies for all {@linkplain AbstractCoordinateOperation coordinate operations}
instances.
+     * Returns the underlying math transform factory. This factory is used for constructing
the {@link MathTransform}
+     * instances doing the actual mathematical work of {@linkplain AbstractCoordinateOperation
coordinate operations}
+     * instances.
      *
      * @return the underlying math transform factory.
      */


Mime
View raw message