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: Fix various bugs (e.g. NullPointerException) and anomalous behavior when switching between different visualized data.
Date Sat, 02 May 2020 21:32:34 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 bca03c4  Fix various bugs (e.g. NullPointerException) and anomalous behavior when
switching between different visualized data.
bca03c4 is described below

commit bca03c4b3c805f0c649af5e0b8bab8c809f05ad1
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat May 2 17:59:10 2020 +0200

    Fix various bugs (e.g. NullPointerException) and anomalous behavior when switching between
different visualized data.
---
 .../apache/sis/gui/coverage/CoverageCanvas.java    | 12 +++----
 .../apache/sis/gui/dataset/ResourceExplorer.java   | 19 ++++++++--
 .../java/org/apache/sis/gui/map/MapCanvas.java     | 19 ++++++----
 .../java/org/apache/sis/gui/map/StatusBar.java     | 41 +++++++++++++++++++---
 .../org/apache/sis/gui/referencing/MenuSync.java   |  2 +-
 .../org/apache/sis/internal/gui/GUIUtilities.java  |  6 ++--
 .../org/apache/sis/internal/gui/Resources.java     |  5 +++
 .../apache/sis/internal/gui/Resources.properties   |  1 +
 .../sis/internal/gui/Resources_fr.properties       |  1 +
 .../main/java/org/apache/sis/portrayal/Canvas.java | 21 ++++++-----
 .../org/apache/sis/portrayal/CanvasContext.java    | 10 ++++--
 11 files changed, 104 insertions(+), 33 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
index 61cc50d..769b90d 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
@@ -26,7 +26,6 @@ import javafx.scene.paint.Color;
 import javafx.scene.layout.Region;
 import javafx.scene.layout.Background;
 import javafx.scene.layout.BackgroundFill;
-import javafx.beans.value.ObservableValue;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.concurrent.Task;
@@ -113,8 +112,8 @@ public class CoverageCanvas extends MapCanvasAWT {
         sliceExtentProperty    = new SimpleObjectProperty<>(this, "sliceExtent");
         dataAlternatives       = new EnumMap<>(RangeType.class);
         currentDataAlternative = RangeType.DECLARED;
-        coverageProperty   .addListener(this::onImageSpecified);
-        sliceExtentProperty.addListener(this::onImageSpecified);
+        coverageProperty   .addListener((p,o,n) -> onImageSpecified());
+        sliceExtentProperty.addListener((p,o,n) -> onImageSpecified());
     }
 
     /**
@@ -194,12 +193,9 @@ public class CoverageCanvas extends MapCanvasAWT {
 
     /**
      * Invoked when a new coverage has been specified or when the slice extent changed.
-     *
-     * @param  property  the {@link #coverageProperty} or {@link #sliceExtentProperty} (ignored).
-     * @param  previous  ignored.
-     * @param  value     ignored.
+     * This method starts loading in a background thread.
      */
-    private void onImageSpecified(final ObservableValue<?> property, final Object previous,
final Object value) {
+    private void onImageSpecified() {
         data = null;
         dataAlternatives.clear();
         final GridCoverage coverage = getCoverage();
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
index 388cbe6..da80440 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
@@ -264,6 +264,7 @@ public class ResourceExplorer extends WindowManager {
         Region       table = null;
         FeatureSet   data  = null;
         ImageRequest grid  = null;
+        CoverageExplorer.View type = null;
         if (resource instanceof GridCoverageResource) {
             grid = new ImageRequest((GridCoverageResource) resource, null, 0);
             if (coverage == null) {
@@ -271,6 +272,7 @@ public class ResourceExplorer extends WindowManager {
             }
             image = coverage.getDataView(CoverageExplorer.View.IMAGE);
             table = coverage.getDataView(CoverageExplorer.View.TABLE);
+            type  = viewTab.isSelected() ? CoverageExplorer.View.IMAGE : CoverageExplorer.View.TABLE;
         } else if (resource instanceof FeatureSet) {
             data = (FeatureSet) resource;
             if (features == null) {
@@ -288,6 +290,7 @@ public class ResourceExplorer extends WindowManager {
         if (table    != null) tableTab.setContent(table);
         if (isDataTabSet) {
             setNewWindowDisabled(image == null && table == null);
+            updateControls(type);
         }
     }
 
@@ -300,14 +303,26 @@ public class ResourceExplorer extends WindowManager {
      * @param  visual    {@code true} for visual, or {@code false} for tabular data.
      */
     private void dataTabShown(final Boolean selected, final boolean visual) {
-        Region controlPanel = null;
+        CoverageExplorer.View type = null;
         if (selected) {
             if (!isDataTabSet) {
                 isDataTabSet = true;                    // Must be set before to invoke `updateDataTab(…)`.
                 updateDataTab(selectedResource.get());
             }
-            controlPanel = coverage.getControls(visual ? CoverageExplorer.View.IMAGE : CoverageExplorer.View.TABLE);
+            if (coverage != null) {                     // May still be null if selected
resource is not a coverage.
+                type = visual ? CoverageExplorer.View.IMAGE : CoverageExplorer.View.TABLE;
+            }
         }
+        updateControls(type);
+    }
+
+    /**
+     * Adds or removes controls for the given view.
+     *
+     * @param  type  the view for which to provide controls, or {@code null} if none.
+     */
+    private void updateControls(final CoverageExplorer.View type) {
+        final Region controlPanel = (type != null) ? coverage.getControls(type) : null;
         final ObservableList<Node> items = controls.getItems();
         if (items.size() >= 2) {
             if (controlPanel != null) {
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
index 9d83ecb..db377f0 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
@@ -41,20 +41,24 @@ import javafx.scene.shape.Rectangle;
 import javafx.scene.transform.Affine;
 import javafx.scene.transform.NonInvertibleTransformException;
 import org.opengis.geometry.Envelope;
+import org.opengis.referencing.datum.PixelInCell;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
 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.geometry.Envelope2D;
 import org.apache.sis.geometry.ImmutableEnvelope;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.gui.BackgroundThreads;
 import org.apache.sis.portrayal.PlanarCanvas;
 import org.apache.sis.portrayal.RenderException;
-import org.apache.sis.internal.system.Modules;
 
 
 /**
@@ -662,11 +666,11 @@ public abstract class MapCanvas extends PlanarCanvas {
              */
             if (invalidObjectiveToDisplay) {
                 invalidObjectiveToDisplay = false;
-                LinearTransform tr;
+                final LinearTransform tr;
                 CoordinateReferenceSystem crs;
+                final Envelope2D target = getDisplayBounds();
                 if (objectiveBounds != null) {
                     crs = objectiveBounds.getCoordinateReferenceSystem();
-                    final Envelope2D target = getDisplayBounds();
                     final MatrixSIS m = Matrices.createTransform(objectiveBounds, target);
                     Matrices.forceUniformScale(m, 0, new double[] {target.width / 2, target.height
/ 2});
                     tr = MathTransforms.linear(m);
@@ -678,11 +682,14 @@ public abstract class MapCanvas extends PlanarCanvas {
                     // TODO: build an EngineeringCRS reflecting better the data.
                     crs = getDisplayCRS();
                 }
-                setObjectiveCRS(crs);
-                setObjectiveToDisplay(tr);
+                final GridExtent extent = new GridExtent(null,
+                        new long[] {Math.round(target.getMinX()), Math.round(target.getMinY())},
+                        new long[] {Math.round(target.getMaxX()), Math.round(target.getMaxY())},
false);
+                setGridGeometry(new GridGeometry(extent, PixelInCell.CELL_CORNER, tr.inverse(),
crs));
                 transform.setToIdentity();
             }
-        } catch (RenderException ex) {
+        } catch (NoninvertibleTransformException | RenderException ex) {
+            floatingPane.setCursor(Cursor.CROSSHAIR);
             errorOccurred(ex);
             return;
         }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
index e0690b5..75e5d65 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
@@ -320,6 +320,15 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
     private ChangeListener<Boolean> renderingListener;
 
     /**
+     * The listener registered on {@link MapCanvas#errorProperty()}, or {@code null} if the
+     * listener has not yet been registered. This listener is remembered for allowing removal.
+     *
+     * @see #canvas
+     * @see #onCanvasSpecified(MapCanvas, MapCanvas)
+     */
+    private ChangeListener<Throwable> errorListener;
+
+    /**
      * Whether the mouse listeners have been registered. Those listeners are registered the
      * first time that {@link #apply(GridGeometry)} is invoked on a newly initialized canvas.
      */
@@ -415,11 +424,13 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
             previous.floatingPane.removeEventHandler(MouseEvent.MOUSE_EXITED,  this);
             previous.floatingPane.removeEventHandler(MouseEvent.MOUSE_MOVED,   this);
             previous.renderingProperty().removeListener(renderingListener);
+            previous.errorProperty().removeListener(errorListener);
             renderingListener = null;
             isMouseListenerRegistered = false;
         }
         if (value != null) {
             value.renderingProperty().addListener(renderingListener = new RenderingListener());
+            value.errorProperty().addListener(errorListener = (p,o,n) -> setRenderingError(n));
         }
         position.setText(null);
         registerMouseListeners(value);
@@ -469,7 +480,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
                  * Do not try to rewrite position neither since `lastX` and `lastY` are not
valid anymore.
                  */
             } catch (RenderException e) {
-                setErrorMessage(null, e);
+                setRenderingError(e);
             }
         }
     }
@@ -696,9 +707,10 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
                  * the coordinates will appear in red for telling user that there is a problem.
                  */
                 @Override protected void failed() {
-                    final Locale locale = format.getLocale(Locale.Category.DISPLAY);
+                    final Locale locale = getLocale();
                     setErrorMessage(Resources.forLocale(locale).getString(Resources.Keys.CanNotUseRefSys_1,
                                     IdentifiedObjects.getDisplayName(crs, locale)), getException());
+                    selectedSystem.set(format.getDefaultCRS());
                     resetPositionCRS(Styles.ERROR_TEXT);
                 }
             });
@@ -731,6 +743,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
      * @param  operation  the new value to assign to {@link #objectiveToPositionCRS}
      */
     private void setPositionCRS(final CoordinateReferenceSystem crs, final CoordinateOperation
operation) {
+        setErrorMessage(null, null);
         Length accuracy = null;
         double a = CRS.getLinearAccuracy(operation);
         if (a > 0) {
@@ -771,7 +784,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
     private void setFormatCRS(final CoordinateReferenceSystem crs, final Length accuracy)
{
         format.setDefaultCRS(crs);
         format.setGroundAccuracy(accuracy);
-        String text = IdentifiedObjects.getDisplayName(crs, format.getLocale(Locale.Category.DISPLAY));
+        String text = IdentifiedObjects.getDisplayName(crs, getLocale());
         Tooltip tp = null;
         if (text != null) {
             tp = position.getTooltip();
@@ -1041,7 +1054,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
         text = Strings.trimOrNull(text);
         Button more = null;
         if (details != null) {
-            final Locale locale = format.getLocale(Locale.Category.DISPLAY);
+            final Locale locale = getLocale();
             if (text == null) {
                 text = Exceptions.getLocalizedMessage(details, locale);
                 if (text == null) {
@@ -1058,4 +1071,24 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
         message.setText(text);
         message.setTextFill(Styles.ERROR_TEXT);
     }
+
+    /**
+     * Shown an error message that occurred in the context of rendering the {@link #canvas}
content.
+     * This method should not be invoked for other context like an error during transformation
of
+     * coordinates shown is the status bar.
+     */
+    private void setRenderingError(final Throwable details) {
+        String text = null;
+        if (details != null) {
+            text = Resources.forLocale(getLocale()).getString(Resources.Keys.CanNotRender);
+        }
+        setErrorMessage(text, details);
+    }
+
+    /**
+     * Returns the locale for error messages.
+     */
+    private Locale getLocale() {
+        return format.getLocale(Locale.Category.DISPLAY);
+    }
 }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
index 61c5313..1127282 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
@@ -108,7 +108,7 @@ final class MenuSync extends SimpleObjectProperty<ReferenceSystem>
implements Ev
     /**
      * Sets the initial value to the first item in the {@code systems} list, if any.
      * This method is invoked in JavaFX thread at construction time or, if it didn't
-     * work at some later time when the systems list may contain an element.
+     * work, at some later time when the systems list may contain an element.
      * This method should not be invoked anymore after initialization succeeded.
      */
     private void initialize(final ObservableList<? extends ReferenceSystem> systems)
{
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/GUIUtilities.java
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/GUIUtilities.java
index 7c13412..4db10f3 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/GUIUtilities.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/GUIUtilities.java
@@ -170,6 +170,8 @@ public final class GUIUtilities extends Static {
      * <a href="https://en.wikipedia.org/wiki/Longest_common_subsequence_problem">Longest
common subsequence problem</a>
      */
     static <E> List<? extends E> longestCommonSubsequence(List<? extends E>
x, List<? extends E> y) {
+        final List<? extends E> ox = x;     // The whole list, before sublisting that
may be applied.
+        final List<? extends E> oy = y;     // Idem.
         /*
          * This method can be optimized by excluding the common prefix and common suffix
before
          * to build the matrix. It can reduce a lot the matrix size and the number of iterations.
@@ -198,8 +200,8 @@ public final class GUIUtilities extends Static {
         for (int i=0; ; i++) {
             final int sx = nx - i;
             final int sy = ny - i;
-            if (sx == 0) return x;
-            if (sy == 0) return y;
+            if (sx == 0) return ox;                 // Concatenation of prefix + suffix =
original list.
+            if (sy == 0) return oy;
             if (x.get(sx - 1) != y.get(sy - 1)) {
                 if (i == 0) {
                     suffix = Collections.emptyList();
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
index 27dd8f2..75c8a6c 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
@@ -101,6 +101,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short CanNotReadResource = 55;
 
         /**
+         * An error occurred while rendering the data.
+         */
+        public static final short CanNotRender = 60;
+
+        /**
          * Can not use the “{0}” reference system.
          */
         public static final short CanNotUseRefSys_1 = 58;
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
index bd02914..968f3e3 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
@@ -28,6 +28,7 @@ CanNotClose_1          = Can not close \u201c{0}\u201d. Data may be lost.
 CanNotCreateCRS_1      = Can not create reference system \u201c{0}\u201d.
 CanNotCreateXML        = Can not create XML document.
 CanNotReadResource     = A resource contained in the file can not be read. The cause is given
below.
+CanNotRender           = An error occurred while rendering the data.
 CanNotUseRefSys_1      = Can not use the \u201c{0}\u201d reference system.
 CellGeometry           = Cell geometry
 Close                  = Close
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
index 0088dc3..7e1dbbf 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
@@ -33,6 +33,7 @@ CanNotClose_1          = Ne peut pas fermer \u00ab\u202f{0}\u202f\u00bb.
Il pour
 CanNotCreateCRS_1      = Ne peut pas cr\u00e9er le syst\u00e8me de r\u00e9f\u00e9rence \u00ab\u202f{0}\u202f\u00bb.
 CanNotCreateXML        = Ne peut pas cr\u00e9er le document XML.
 CanNotReadResource     = Une ressource contenue dans le fichier ne peut pas \u00eatre lue.
La cause est donn\u00e9e ci-dessous.
+CanNotRender           = Une erreur est survenue lors de l\u2019affichage des donn\u00e9es.
 CanNotUseRefSys_1      = Ne peut pas utiliser le syst\u00e8me de r\u00e9f\u00e9rence \u00ab\u202f{0}\u202f\u00bb.
 CellGeometry           = G\u00e9om\u00e9trie des cellules
 Close                  = Fermer
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 c8de211..0707e0f 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
@@ -480,11 +480,15 @@ public class Canvas extends Observable implements Localized {
 
     /**
      * Sets the Coordinate Reference System in which all data are transformed before displaying.
-     * The given CRS should have a domain of validity wide enough for encompassing all data
+     * The new CRS must be compatible with the previous CRS, i.e. a coordinate operation
between
+     * the two CRSs shall exist. If this is not the case (e.g. for rendering completely new
data),
+     * use {@link #setGridGeometry(GridGeometry)} instead.
+     *
+     * <p>The given CRS should have a domain of validity wide enough for encompassing
all data
      * (the {@link CRS#suggestCommonTarget CRS.suggestCommonTarget(…)} method may be helpful
      * for choosing an objective CRS from a set of data CRS).
      * If the given value is different than the previous value, then a change event is sent
to
-     * all listeners registered for the {@value #OBJECTIVE_CRS_PROPERTY} property.
+     * all listeners registered for the {@value #OBJECTIVE_CRS_PROPERTY} property.</p>
      *
      * <p>If the transform between old and new CRS is not identity, then this method
recomputes
      * the <cite>objective to display</cite> conversion in a way preserving the
display coordinates
@@ -528,8 +532,8 @@ public class Canvas extends Observable implements Localized {
                      * (same location, same Jacobian matrix) in the neighborhood of the point
of interest,
                      * so that we can apply the old `objectiveToCRS` transform. For achieving
that goal,
                      * we apply a local affine transform which cancel the effect of "old
CRS → new CRS"
-                     * transformation around the point of interest. The effect of CRS change
will appear
-                     * as we look further from the point of interest.
+                     * transformation around the point of interest. The deformations caused
by CRS change
+                     * will be more visible as we look further from the point of interest.
                      */
                     final MathTransform  poiToNew = findTransform(pointOfInterest.getCoordinateReferenceSystem(),
newValue);
                     final DirectPosition poiInNew = poiToNew.transform(pointOfInterest, allocatePosition());
@@ -1061,6 +1065,9 @@ public class Canvas extends Observable implements Localized {
     /**
      * Computes the value for {@link #objectiveToGeographic}. The value is not stored by
this method for
      * giving caller a chance to validate other properties before to write them in a "all
or nothing" way.
+     *
+     * @param  crs  the new objective CRS in process of being set by the caller.
+     * @return the conversion from given CRS to geographic CRS, or {@code null} if none.
      */
     private CoordinateOperation objectiveToGeographic(final CoordinateReferenceSystem crs)
throws FactoryException {
         final GeographicCRS geoCRS = ReferencingUtilities.toNormalizedGeographicCRS(crs,
false, false);
@@ -1073,14 +1080,12 @@ public class Canvas extends Observable implements Localized {
      * CRS may differ depending on which area is currently visible in the canvas. All requests
for a coordinate
      * operation should invoke this method instead than {@link CRS#findOperation(CoordinateReferenceSystem,
      * CoordinateReferenceSystem, GeographicBoundingBox)}.
-     *
-     * @todo verify if bounding box/resolution are up-to-date.
      */
     private MathTransform findTransform(final CoordinateReferenceSystem source,
                                         final CoordinateReferenceSystem target)
-            throws FactoryException, RenderException
+            throws FactoryException, TransformException, RenderException
     {
-        operationContext.refresh();
+        operationContext.refresh(this);
         return coordinateOperationFactory.createOperation(source, target, operationContext).getMathTransform();
     }
 
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasContext.java
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasContext.java
index d9efe45..4a26338 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasContext.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasContext.java
@@ -95,6 +95,8 @@ final class CanvasContext extends CoordinateOperationContext {
 
     /**
      * Sets the operation from {@link Canvas#objectiveCRS} to geographic CRS.
+     *
+     * @param  op  the conversion from objective CRS to geographic CRS, or {@code null} if
none.
      */
     final void setObjectiveToGeographic(final CoordinateOperation op) {
         objectiveToGeographic = op;
@@ -152,10 +154,13 @@ final class CanvasContext extends CoordinateOperationContext {
 
     /**
      * Recomputes {@link #geographicArea} and {@link #resolution} fields that are not valid.
-     * This method assumes that {@link #objectiveToGeographic} is valid.
+     * This method assumes that {@link #objectiveToGeographic} is {@code null} or valid.
      */
     @SuppressWarnings("fallthrough")
     private void recompute(final Canvas canvas) throws TransformException {
+        if (objectiveToGeographic == null) {
+            return;
+        }
         final LinearTransform objectiveToDisplay = canvas.getObjectiveToDisplay();
         final MathTransform displayToGeographic = MathTransforms.concatenate(
                             objectiveToDisplay.inverse(),
@@ -207,7 +212,8 @@ final class CanvasContext extends CoordinateOperationContext {
      * Sets the {@link CoordinateOperationContext} object to the desired area and accuracy
      * of the coordinate operation to obtain.
      */
-    final void refresh() {
+    final void refresh(final Canvas canvas) throws TransformException {
+        recompute(canvas);
         setAreaOfInterest(geographicArea);                          // null for default behavior.
         setDesiredAccuracy(resolution * DISPLAY_RESOLUTION);        // 0 for default behavior.
     }


Mime
View raw message