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 a160c6f66af80ac0b45274a0e5e092db221b8ca9
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Jun 10 14:28:39 2022 +0200

    Addition of a method for getting changes in objective CRS.
    Refactoring: methods renaming and documentation updates.
---
 .../main/java/org/apache/sis/portrayal/Canvas.java |  77 ++++-----
 .../java/org/apache/sis/portrayal/MapLayer.java    |   1 -
 .../java/org/apache/sis/portrayal/MapLayers.java   |   2 -
 .../org/apache/sis/portrayal/PlanarCanvas.java     |  20 ++-
 .../apache/sis/portrayal/TransformChangeEvent.java | 175 ++++++++++++++++++---
 .../operation/matrix/AffineTransforms2D.java       |   2 +-
 6 files changed, 194 insertions(+), 83 deletions(-)

diff --git 
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Canvas.java 
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Canvas.java
index ce293db612..51c56e80ed 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Canvas.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Canvas.java
@@ -575,7 +575,7 @@ public class Canvas extends Observable implements Localized 
{
                      * normally it should just return the `result` as-is.
                      */
                     newObjectiveToDisplay = MathTransforms.tangent(result, 
poiInNew);
-                    updateObjectiveToDisplay(newObjectiveToDisplay);
+                    setObjectiveToDisplayImpl(newObjectiveToDisplay);
                     objectivePOI          = poiInNew;               // Set 
only after everything else succeeded.
                     multidimToObjective   = poiToNew;
                     augmentedObjectiveCRS = null;                   // Will be 
recomputed when first needed.
@@ -680,22 +680,23 @@ public class Canvas extends Observable implements 
Localized {
      */
     public LinearTransform getObjectiveToDisplay() {
         if (objectiveToDisplay == null) {
-            objectiveToDisplay = updateObjectiveToDisplay();
+            objectiveToDisplay = createObjectiveToDisplay();
         }
         return objectiveToDisplay;
     }
 
     /**
-     * Takes a snapshot of the <cite>objective to display</cite> conversion. 
This method needs
-     * to be overridden only by subclasses that use their own specialized 
class instead of
-     * {@link #objectiveToDisplay} for managing changes in the zooms or viewed 
area.
+     * Returns the current <cite>objective to display</cite> conversion 
managed by the subclass.
+     * This method is invoked only if {@link #objectiveToDisplay} is {@code 
null}, which may
+     * happen either at initialization time or if the subclass uses its own 
specialized field
+     * instead of {@link #objectiveToDisplay} for managing changes in the 
zooms or viewed area.
+     * This method needs to be overridden only by subclasses using such 
specialization.
      *
-     * @return snapshot of objective to display conversion, never null.
+     * @return objective to display conversion created from current value 
managed by subclass.
      *
-     * @see #updateObjectiveToDisplay(LinearTransform)
-     * @see #invalidateObjectiveToDisplay()
+     * @see #setObjectiveToDisplayImpl(LinearTransform)
      */
-    LinearTransform updateObjectiveToDisplay() {
+    LinearTransform createObjectiveToDisplay() {
         return MathTransforms.identity(getDisplayDimensions());
     }
 
@@ -712,7 +713,7 @@ public class Canvas extends Observable implements Localized 
{
      * @throws IllegalArgumentException if given the transform does not have 
the expected number of dimensions or is not affine.
      * @throws RenderException if the <cite>objective to display</cite> 
transform can not be set to the given value for another reason.
      */
-    public void setObjectiveToDisplay(LinearTransform newValue) throws 
RenderException {
+    public void setObjectiveToDisplay(final LinearTransform newValue) throws 
RenderException {
         ArgumentChecks.ensureNonNull(OBJECTIVE_TO_DISPLAY_PROPERTY, newValue);
         final int expected = getDisplayDimensions();
         int actual = newValue.getSourceDimensions();
@@ -721,11 +722,11 @@ public class Canvas extends Observable implements 
Localized {
             if (actual == expected) {
                 LinearTransform oldValue = objectiveToDisplay;      // Do not 
invoke user-overridable method.
                 if (oldValue == null) {
-                    oldValue = updateObjectiveToDisplay();
+                    oldValue = createObjectiveToDisplay();
                 }
                 if (!oldValue.equals(newValue)) {
-                    updateObjectiveToDisplay(newValue);
-                    fireTransformChange(oldValue, newValue);
+                    setObjectiveToDisplayImpl(newValue);
+                    firePropertyChange(new TransformChangeEvent(this, 
oldValue, newValue));
                 }
                 return;
             }
@@ -735,28 +736,23 @@ public class Canvas extends Observable implements 
Localized {
     }
 
     /**
-     * Sets the conversion from objective CRS to display coordinate system.
+     * Actually sets the conversion from objective CRS to display coordinate 
system.
      * Contrarily to other setter methods, this method does not notify 
listeners about that change;
-     * it is caller responsibility to send a {@value 
#OBJECTIVE_TO_DISPLAY_PROPERTY} change event.
+     * it is caller responsibility to fire a {@link TransformChangeEvent} 
after all fields are updated.
      * This design choice is because this method is usually invoked as part of 
a larger set of changes.
      *
-     * @see #updateObjectiveToDisplay()
-     */
-    void updateObjectiveToDisplay(final LinearTransform newValue) {
-        objectiveToDisplay = newValue;
-        gridGeometry       = null;
-        operationContext.clear();
-    }
-
-    /**
-     * Declares that the {@link #objectiveToDisplay} transform became invalid 
and will need to be recomputed.
-     * It is subclasses responsibility to recompute the transform in their 
{@link #updateObjectiveToDisplay()}
-     * method and to invoke {@link #fireTransformChange(LinearTransform, 
LinearTransform)} (or equivalent).
+     * <p>If the new value is {@code null}, then this method only declares 
that the {@link #objectiveToDisplay}
+     * transform became invalid and will need to be recomputed. It is 
subclasses responsibility to recompute the
+     * transform in their {@link #createObjectiveToDisplay()}.</p>
+     *
+     * @param  newValue  the new "objective to display" transform, or {@code 
null} if it will be computed later
+     *          by {@link #createObjectiveToDisplay()}. A null value is okay 
only when invoked by subclasses that
+     *          overrode {@link #createObjectiveToDisplay()}.
      *
-     * @see #updateObjectiveToDisplay()
+     * @see #createObjectiveToDisplay()
      */
-    final void invalidateObjectiveToDisplay() {
-        objectiveToDisplay = null;
+    void setObjectiveToDisplayImpl(final LinearTransform newValue) {
+        objectiveToDisplay = newValue;
         gridGeometry       = null;
         operationContext.clear();
     }
@@ -991,7 +987,7 @@ public class Canvas extends Observable implements Localized 
{
              * translation terms of the `gridToCRS` matrix.
              */
             if (objectiveToDisplay == null) {
-                objectiveToDisplay = updateObjectiveToDisplay();
+                objectiveToDisplay = createObjectiveToDisplay();
             }
             LinearTransform gridToCRS = objectiveToDisplay.inverse();
             if (supplementalDimensions != 0) {
@@ -1096,11 +1092,11 @@ public class Canvas extends Observable implements 
Localized {
             final LinearTransform           oldObjectiveToDisplay = 
objectiveToDisplay;
             final CoordinateReferenceSystem oldObjectiveCRS       = 
objectiveCRS;
             /*
-             * Set internal fields only after we successfully computed 
everything, in order to have a
-             * "all or nothing" behavior.
+             * Set internal fields only after we successfully computed 
everything,
+             * in order to have a "all or nothing" behavior.
              */
             displayBounds.setEnvelope(newBounds);
-            updateObjectiveToDisplay(newObjectiveToDisplay);
+            setObjectiveToDisplayImpl(newObjectiveToDisplay);
             pointOfInterest       = newPOI;
             objectivePOI          = newPOI;
             objectiveCRS          = newObjectiveCRS;
@@ -1143,21 +1139,10 @@ public class Canvas extends Observable implements 
Localized {
      */
     private void fireIfChanged(final LinearTransform oldValue, final 
LinearTransform newValue) {
         if (!Objects.equals(oldValue, newValue)) {
-            fireTransformChange(oldValue, newValue);
+            firePropertyChange(new TransformChangeEvent(this, oldValue, 
newValue));
         }
     }
 
-    /**
-     * Fires a {@link TransformChangeEvent}.
-     * It is caller responsibility to verify that the old and new values are 
different.
-     *
-     * @param  oldValue  the old "objective to display" transform.
-     * @param  newValue  the new transform, or {@code null} for lazy 
computation.
-     */
-    final void fireTransformChange(final LinearTransform oldValue, final 
LinearTransform newValue) {
-        firePropertyChange(new TransformChangeEvent(this, oldValue, newValue));
-    }
-
     /**
      * Returns the geographic bounding box encompassing the area shown on the 
display device.
      * If the {@linkplain #getObjectiveCRS() objective CRS} is not convertible 
to a geographic CRS,
diff --git 
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayer.java 
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayer.java
index 3c69cf7a7e..b2b97e41e7 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayer.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayer.java
@@ -260,5 +260,4 @@ public class MapLayer extends MapItem {
         }
         return Optional.empty();
     }
-
 }
diff --git 
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayers.java 
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayers.java
index 4908b101c3..0ead548e0b 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayers.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayers.java
@@ -177,7 +177,6 @@ public class MapLayers extends MapItem {
         for (MapItem i : components) {
             i.getEnvelope().ifPresent(envelopes::add);
         }
-
         switch (envelopes.size()) {
             case 0 : return Optional.empty();
             case 1 : return Optional.of(envelopes.get(0));
@@ -190,5 +189,4 @@ public class MapLayers extends MapItem {
             }
         }
     }
-
 }
diff --git 
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/PlanarCanvas.java 
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/PlanarCanvas.java
index f5c20bb139..345b638217 100644
--- 
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/PlanarCanvas.java
+++ 
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/PlanarCanvas.java
@@ -137,7 +137,7 @@ public abstract class PlanarCanvas extends Canvas {
      * @see Canvas#objectiveToDisplay
      */
     @Override
-    final LinearTransform updateObjectiveToDisplay() {
+    final LinearTransform createObjectiveToDisplay() {
         return new AffineTransform2D(objectiveToDisplay);
     }
 
@@ -152,9 +152,9 @@ public abstract class PlanarCanvas extends Canvas {
      * @throws IllegalArgumentException if the given transform is not 
two-dimensional or is not affine.
      */
     @Override
-    final void updateObjectiveToDisplay(final LinearTransform newValue) {
+    final void setObjectiveToDisplayImpl(final LinearTransform newValue) {
         
objectiveToDisplay.setTransform(AffineTransforms2D.castOrCopy(newValue.getMatrix()));
-        super.updateObjectiveToDisplay(newValue);
+        super.setObjectiveToDisplayImpl(newValue);
     }
 
     /**
@@ -163,14 +163,18 @@ public abstract class PlanarCanvas extends Canvas {
      * vector is in units of the {@linkplain #getObjectiveCRS() objective CRS} 
(typically metres on the map).
      *
      * @param  before  coordinate conversion to apply before the current 
<cite>objective to display</cite> transform.
+     *
+     * @see TransformChangeEvent#getObjectiveChange()
      */
     public void transformObjectiveCoordinates(final AffineTransform before) {
         if (!before.isIdentity()) {
             final LinearTransform old = 
hasListener(OBJECTIVE_TO_DISPLAY_PROPERTY) ? getObjectiveToDisplay() : null;
             objectiveToDisplay.concatenate(before);
-            invalidateObjectiveToDisplay();
+            super.setObjectiveToDisplayImpl(null);
             if (old != null) {
-                fireTransformChange(old, null);
+                final TransformChangeEvent event = new 
TransformChangeEvent(this, old, null);
+                event.objectiveChange2D = before;
+                firePropertyChange(event);
             }
         }
     }
@@ -181,15 +185,17 @@ public abstract class PlanarCanvas extends Canvas {
      * vector is in pixel units.
      *
      * @param  after  coordinate conversion to apply after the current 
<cite>objective to display</cite> transform.
+     *
+     * @see TransformChangeEvent#getDisplayChange()
      */
     public void transformDisplayCoordinates(final AffineTransform after) {
         if (!after.isIdentity()) {
             final LinearTransform old = 
hasListener(OBJECTIVE_TO_DISPLAY_PROPERTY) ? getObjectiveToDisplay() : null;
             objectiveToDisplay.preConcatenate(after);
-            invalidateObjectiveToDisplay();
+            super.setObjectiveToDisplayImpl(null);
             if (old != null) {
                 final TransformChangeEvent event = new 
TransformChangeEvent(this, old, null);
-                event.change = AffineTransforms2D.toMathTransform(after);
+                event.displayChange2D = after;
                 firePropertyChange(event);
             }
         }
diff --git 
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/TransformChangeEvent.java
 
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/TransformChangeEvent.java
index 756d7ee7fd..115f1a4692 100644
--- 
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/TransformChangeEvent.java
+++ 
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/TransformChangeEvent.java
@@ -16,16 +16,28 @@
  */
 package org.apache.sis.portrayal;
 
+import java.util.Optional;
+import java.util.logging.Logger;
+import java.awt.geom.AffineTransform;
 import java.beans.PropertyChangeEvent;
 import org.opengis.referencing.operation.NoninvertibleTransformException;
+import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
 import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.internal.system.Modules;
+import org.apache.sis.util.logging.Logging;
 
 
 /**
- * A change in the zoom, pan or translation applied for viewing a map. All 
events fired by
- * {@link Canvas} for the {@value Canvas#OBJECTIVE_TO_DISPLAY_PROPERTY} 
property are of this kind.
- * This specialization provides a method for computing the difference between 
the old and new state.
+ * A change in the "objective to display" transform that {@code Canvas} uses 
for rendering data.
+ * That transform is updated frequently following gestures events such as 
zoom, translation or rotation.
+ * All events fired by {@link Canvas} for the {@value 
Canvas#OBJECTIVE_TO_DISPLAY_PROPERTY} property
+ * are instances of this class.
+ * This specialization provides methods for computing the difference between 
the old and new state.
+ *
+ * <h2>Multi-threading</h2>
+ * This class is <strong>not</strong> thread-safe.
+ * All listeners should process this event in the same thread.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.3
@@ -39,15 +51,26 @@ public class TransformChangeEvent extends 
PropertyChangeEvent {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = 4065626270969827867L;
+    private static final long serialVersionUID = 4444752056666264066L;
 
     /**
-     * The change in display coordinates, computed when first needed.
-     * This field may be precomputed by the code that fired this event.
+     * The change from old coordinates to new coordinates, computed when first 
needed.
      *
-     * @see #getChangeInDisplayCoordinates()
+     * @see #getDisplayChange()
+     * @see #getObjectiveChange()
+     */
+    private transient LinearTransform displayChange, objectiveChange;
+
+    /**
+     * Value of {@link #displayChange} or {@link #objectiveChange} precomputed 
by the code that fired this event.
+     */
+    AffineTransform displayChange2D, objectiveChange2D;
+
+    /**
+     * Non-null if {@link #canNotCompute(String, 
NoninvertibleTransformException)} already reported an error.
+     * This is used for avoiding to report many times the same error.
      */
-    LinearTransform change;
+    private transient Exception error;
 
     /**
      * Creates a new event for a change of the "objective to display" property.
@@ -91,32 +114,132 @@ public class TransformChangeEvent extends 
PropertyChangeEvent {
      */
     @Override
     public LinearTransform getNewValue() {
-        LinearTransform transform = (LinearTransform) super.getNewValue();
-        if (transform == null) {
-            transform = getSource().getObjectiveToDisplay();
+        LinearTransform value = (LinearTransform) super.getNewValue();
+        if (value == null) {
+            value = getSource().getObjectiveToDisplay();
         }
-        return transform;
+        return value;
     }
 
     /**
-     * Returns the change in display coordinates from the old state to the new 
state.
-     * If the "objective to display" transform changed because the users did a 
zoom,
-     * pan or translation, this is the transform representing that change in 
display
-     * coordinates.
+     * Returns the change from old objective coordinates to new objective 
coordinates.
+     * When the "objective to display" transform changed (e.g. because the 
user did a zoom, translation or rotation),
+     * this method expresses how the "real world" coordinates (typically in 
metres) of any point on the screen changed.
+     *
+     * <div class="note"><b>Example:</b>
+     * if the map is shifted 10 metres toward the right side of the canvas, 
then (assuming no rotation or axis flip)
+     * the <var>x</var> translation coefficient of the change is +10 (same 
sign than {@link #getDisplayChange()}).
+     * Note that it may correspond to any amount of pixels, depending on the 
zoom factor.</div>
+     *
+     * The {@link #getObjectiveChange2D()} method gives the same transform as 
a Java2D object.
+     * That change can be replicated on another canvas by giving the transform 
to
+     * {@link PlanarCanvas#transformObjectiveCoordinates(AffineTransform)}.
+     *
+     * @return the change in objective coordinates. Usually not {@code null},
+     *         unless one of the canvas is initializing or has a 
non-invertible transform.
+     */
+    public LinearTransform getObjectiveChange() {
+        if (objectiveChange == null) {
+            if (objectiveChange2D != null) {
+                objectiveChange = 
AffineTransforms2D.toMathTransform(objectiveChange2D);
+            } else {
+                final LinearTransform oldValue = getOldValue();
+                if (oldValue != null) {
+                    final LinearTransform newValue = getNewValue();
+                    if (newValue != null) try {
+                        objectiveChange = (LinearTransform) 
MathTransforms.concatenate(newValue, oldValue.inverse());
+                    } catch (NoninvertibleTransformException e) {
+                        canNotCompute("getObjectiveChange", e);
+                    }
+                }
+            }
+        }
+        return objectiveChange;
+    }
+
+    /**
+     * Returns the change from old display coordinates to new display 
coordinates.
+     * When the "objective to display" transform changed (e.g. because the 
user did a zoom, translation or rotation),
+     * this method expresses how the display coordinates (typically pixels) of 
any given point on the map changed.
+     *
+     * <div class="note"><b>Example:</b>
+     * if the map is shifted 10 pixels toward the right side of the canvas, 
then (assuming no rotation or axis flip)
+     * the <var>x</var> translation coefficient of the change is +10: the 
points on the map which were located at
+     * <var>x</var>=0 pixel before the change are now located at 
<var>x</var>=10 pixels after the change.</div>
+     *
+     * The {@link #getDisplayChange2D()} method gives the same transform as a 
Java2D object.
+     * That change can be replicated on another canvas by giving the transform 
to
+     * {@link PlanarCanvas#transformDisplayCoordinates(AffineTransform)}.
      *
-     * @return the change in display coordinates, or {@code null} if the old 
or new transform is missing.
-     * @throws NoninvertibleTransformException if a singular matrix prevent 
the change to be computed.
+     * @return the change in display coordinates. Usually not {@code null},
+     *         unless one of the canvas is initializing or has a 
non-invertible transform.
      */
-    public LinearTransform getChangeInDisplayCoordinates() throws 
NoninvertibleTransformException {
-        if (change == null) {
-            final LinearTransform oldValue = getOldValue();
-            if (oldValue != null) {
-                final LinearTransform newValue = getNewValue();
-                if (newValue != null) {
-                    change = (LinearTransform) 
MathTransforms.concatenate(oldValue.inverse(), newValue);
+    public LinearTransform getDisplayChange() {
+        if (displayChange == null) {
+            if (displayChange2D != null) {
+                displayChange = 
AffineTransforms2D.toMathTransform(displayChange2D);
+            } else {
+                final LinearTransform oldValue = getOldValue();
+                if (oldValue != null) {
+                    final LinearTransform newValue = getNewValue();
+                    if (newValue != null) try {
+                        displayChange = (LinearTransform) 
MathTransforms.concatenate(oldValue.inverse(), newValue);
+                    } catch (NoninvertibleTransformException e) {
+                        canNotCompute("getDisplayChange", e);
+                    }
                 }
             }
         }
-        return change;
+        return displayChange;
+    }
+
+    /**
+     * Returns the change in objective coordinates as a Java2D affine 
transform.
+     * This method is suitable for two-dimensional canvas only.
+     * For performance reason, it does not clone the returned transform.
+     *
+     * @return the change in objective coordinates. <strong>Do not 
modify.</strong>
+     *
+     * @see #getObjectiveChange()
+     */
+    public Optional<AffineTransform> getObjectiveChange2D() {
+        if (objectiveChange2D == null) try {
+            objectiveChange2D = 
AffineTransforms2D.castOrCopy(getObjectiveChange());
+        } catch (IllegalArgumentException e) {
+            canNotCompute("getObjectiveChange2D", e);
+        }
+        return Optional.ofNullable(objectiveChange2D);
+    }
+
+    /**
+     * Returns the change in display coordinates as a Java2D affine transform.
+     * This method is suitable for two-dimensional canvas only.
+     * For performance reason, it does not clone the returned transform.
+     *
+     * @return the change in display coordinates. <strong>Do not 
modify.</strong>
+     *
+     * @see #getDisplayChange()
+     */
+    public Optional<AffineTransform> getDisplayChange2D() {
+        if (displayChange2D == null) try {
+            displayChange2D = 
AffineTransforms2D.castOrCopy(getDisplayChange());
+        } catch (IllegalArgumentException e) {
+            canNotCompute("getDisplayChange2D", e);
+        }
+        return Optional.ofNullable(displayChange2D);
+    }
+
+    /**
+     * Invoked when a change can not be computed. It should never happen 
because "objective to display"
+     * transforms should always be invertible. If this error nevertheless 
happens, consider the change
+     * as a missing optional information.
+     */
+    private void canNotCompute(final String method, final Exception e) {
+        if (error == null) {
+            error = e;
+            Logging.recoverableException(Logger.getLogger(Modules.PORTRAYAL), 
TransformChangeEvent.class, method, e);
+        } else {
+            error.addSuppressed(e);
+        }
     }
 }
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java
index b0575cbf33..387bab2757 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java
@@ -59,7 +59,7 @@ public final class AffineTransforms2D extends Static {
      *
      * @param  transform  the transform to convert, or {@code null}.
      * @return the transform argument if it can be safely casted (including 
{@code null} argument) or converted.
-     * @throws IllegalArgumentException if the given transform can not be 
caster or converted.
+     * @throws IllegalArgumentException if the given transform can not be 
casted or converted.
      *
      * @see #toMathTransform(AffineTransform)
      */

Reply via email to