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 3164e8db34 When opening a large image in the JavaFX application, if 
there is no overview, zoom in the middle of the image as an initial view. We do 
that for avoiding to load too much data when the initial view is the whole 
image.
3164e8db34 is described below

commit 3164e8db34c0631aef307e617ea4580dbab7d764
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Feb 27 16:07:58 2025 +0100

    When opening a large image in the JavaFX application, if there is no 
overview, zoom in the middle of the image as an initial view.
    We do that for avoiding to load too much data when the initial view is the 
whole image.
---
 .../coverage/MultiResolutionCoverageLoader.java    |  2 +-
 .../org/apache/sis/util/privy/CollectionsExt.java  | 18 ++++++
 .../apache/sis/gui/coverage/CoverageCanvas.java    | 68 ++++++++++++++++++----
 .../gui/coverage/MultiResolutionImageLoader.java   |  2 +-
 .../main/org/apache/sis/gui/map/MapCanvas.java     |  6 +-
 .../main/org/apache/sis/gui/map/package-info.java  |  2 +-
 6 files changed, 80 insertions(+), 18 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/MultiResolutionCoverageLoader.java
 
b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/MultiResolutionCoverageLoader.java
index 2fda32ee32..ef1276110e 100644
--- 
a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/MultiResolutionCoverageLoader.java
+++ 
b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/MultiResolutionCoverageLoader.java
@@ -176,7 +176,7 @@ public class MultiResolutionCoverageLoader {
          * Build the arrays of resolutions from finest to coarsest.
          * The `base` array is cloned then updated to become the base of next 
level.
          */
-        final double[][] resolutions = new double[numLevels][];
+        final var resolutions = new double[numLevels][];
         resolutions[0] = base;
         for (int j=1; j<numLevels; j++) {
             resolutions[j] = base = base.clone();
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/CollectionsExt.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/CollectionsExt.java
index d332b9b431..9d2068257a 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/CollectionsExt.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/CollectionsExt.java
@@ -139,6 +139,24 @@ public final class CollectionsExt extends Static {
         return null;
     }
 
+    /**
+     * Returns the last non-null element of the given list.
+     *
+     * @param  <T>   the type of elements contained in the list.
+     * @param  list  the list from which to get the last non-null element, or 
{@code null}.
+     * @return the last non-null element, or {@code null} if the given list is 
null or empty.
+     */
+    public static <T> T lastNonNull(final List<T> collection) {
+        if (collection != null) {
+            int i = collection.size();
+            while (--i >= 0) {
+                T e = collection.get(i);
+                if (e != null) return e;
+            }
+        }
+        return null;
+    }
+
     /**
      * If the given iterable contains exactly one non-null element, returns 
that element.
      * Otherwise returns {@code null}.
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/CoverageCanvas.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/CoverageCanvas.java
index bdf4c9a529..965841c4a6 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/CoverageCanvas.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/CoverageCanvas.java
@@ -58,6 +58,7 @@ import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.PixelInCell;
+import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.geometry.Shapes2D;
 import org.apache.sis.image.Colorizer;
@@ -77,6 +78,7 @@ import org.apache.sis.gui.internal.LogHandler;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Debug;
 import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.privy.CollectionsExt;
 import org.apache.sis.io.TableAppender;
 import org.apache.sis.measure.Units;
 import static org.apache.sis.gui.internal.LogHandler.LOGGER;
@@ -88,7 +90,7 @@ import static org.apache.sis.gui.internal.LogHandler.LOGGER;
  * instance (given by {@link #coverageProperty}) will change automatically 
according the zoom level.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.4
+ * @version 1.5
  *
  * @see CoverageExplorer
  *
@@ -103,6 +105,15 @@ public class CoverageCanvas extends MapCanvasAWT {
      */
     private static final int OVERFLOW_SAFETY_MARGIN = 10_000_000;
 
+    /**
+     * Maximal surface or volume (in number of cells) of the data to load.
+     * If showing the full image would cause a larger amount of data to be 
loaded,
+     * then the widget will zoom on a smaller area by default. This is based 
on the
+     * assumption that data are tiled, and therefore zooming will reduce the 
amount
+     * of data to load.
+     */
+    private static final int MAXIMAL_CELL_COUNT = 5000 * 5000;
+
     /**
      * Whether to print debug information. If {@code true}, we use {@link 
System#out} instead of logging
      * because the log messages are intercepted and rerouted to the "logging" 
tab in the explorer widget.
@@ -581,24 +592,57 @@ public class CoverageCanvas extends MapCanvasAWT {
                     GridGeometry domain;
                     final Long id = LogHandler.loadingStart(resource);
                     try {
+                        double[] scales;
                         if (coverage != null) {
                             domain = coverage.getGridGeometry();
                             ranges = coverage.getSampleDimensions();
+                            scales = null;
                         } else {
                             domain = resource.getGridGeometry();
                             ranges = resource.getSampleDimensions();
+                            scales = 
CollectionsExt.lastNonNull(resource.getResolutions());
                         }
-                        /*
-                         * The domain should never be null and should always 
be complete (including envelope).
-                         * Nevertheless we try to be safe, since 
`setNewSource(…)` wants a complete geometry.
-                         * So if the envelope is missing but the extent is 
present, then the missing part was
-                         * the "grid to CRS" transform. We use an identity 
transform with a "display CRS".
-                         */
-                        if (domain != null && 
!domain.isDefined(GridGeometry.ENVELOPE) && 
domain.isDefined(GridGeometry.EXTENT)) {
-                            final GridExtent extent = domain.getExtent();
-                            final int dimension = extent.getDimension();
-                            domain = new GridGeometry(extent, 
PixelInCell.CELL_CORNER, MathTransforms.identity(dimension),
-                                            (dimension == BIDIMENSIONAL) ? 
CommonCRS.Engineering.DISPLAY.crs() : null);
+                        if (domain != null) {
+                            /*
+                             * The domain should never be null and should 
always be complete (including envelope).
+                             * Nevertheless we try to be safe, since 
`setNewSource(…)` wants a complete geometry.
+                             * So if the envelope is missing but the extent is 
present, then the missing part was
+                             * the "grid to CRS" transform. We use an identity 
transform with a "display CRS".
+                             */
+                            if (!domain.isDefined(GridGeometry.ENVELOPE) && 
domain.isDefined(GridGeometry.EXTENT)) {
+                                final GridExtent extent = domain.getExtent();
+                                final int dimension = extent.getDimension();
+                                domain = new GridGeometry(extent, 
PixelInCell.CELL_CORNER, MathTransforms.identity(dimension),
+                                                (dimension == BIDIMENSIONAL) ? 
CommonCRS.Engineering.DISPLAY.crs() : null);
+                            }
+                            /*
+                             * Compute the maximum zoom out. Usually, we want 
to show the full image.
+                             * But if the image has no pyramid, showing the 
full image may cause the
+                             * loading of a large amount of data. We are 
better to limit the zoom to
+                             * a small area.
+                             */
+                            if (domain.isDefined(GridGeometry.ENVELOPE | 
GridGeometry.RESOLUTION)) {
+                                if (scales == null) {
+                                    scales = domain.getResolution(true);
+                                }
+                                double ratio = MAXIMAL_CELL_COUNT;
+                                final Envelope bounds = domain.getEnvelope();
+                                final int dimension = Math.min(BIDIMENSIONAL, 
Math.min(bounds.getDimension(), scales.length));
+                                for (int i=0; i<dimension; i++) {
+                                    ratio *= scales[i] / bounds.getSpan(i);  
// Equivalent to /= span_in_pixels.
+                                }
+                                if (ratio < 1) {
+                                    ratio = Math.pow(ratio, 1d / dimension);
+                                    final double out = (1 - ratio) / 2;      
// Fraction of bounds to take out on each side.
+                                    final var zoomArea = new 
GeneralEnvelope(bounds);
+                                    for (int i=0; i<dimension; i++) {
+                                        final double margin = 
zoomArea.getSpan(i) * out;
+                                        zoomArea.setRange(i, 
zoomArea.getLower(i) + margin, zoomArea.getUpper(i) - margin);
+                                    }
+                                    // Pretend that the data domain is smaller 
than reality.
+                                    domain = domain.derive().subgrid(zoomArea, 
null).build();
+                                }
+                            }
                         }
                     } finally {
                         LogHandler.loadingStop(id);
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/MultiResolutionImageLoader.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/MultiResolutionImageLoader.java
index 4946b49614..2ca4bb5b28 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/MultiResolutionImageLoader.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/MultiResolutionImageLoader.java
@@ -69,7 +69,7 @@ final class MultiResolutionImageLoader extends 
MultiResolutionCoverageLoader {
                 cached = CACHE.get(resource);
             }
             if (cached == null) {
-                final MultiResolutionImageLoader loader = new 
MultiResolutionImageLoader(resource);
+                final var loader = new MultiResolutionImageLoader(resource);
                 synchronized (CACHE) {
                     cached = CACHE.putIfAbsent(resource, loader);
                 }
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/MapCanvas.java 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/MapCanvas.java
index 2ee421ddc0..7e29d85e0b 100644
--- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/MapCanvas.java
+++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/MapCanvas.java
@@ -131,7 +131,7 @@ import org.opengis.coordinate.MismatchedDimensionException;
  * </ol>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.4
+ * @version 1.5
  * @since   1.1
  */
 public abstract class MapCanvas extends PlanarCanvas {
@@ -200,7 +200,7 @@ public abstract class MapCanvas extends PlanarCanvas {
 
     /**
      * The data bounds to use for computing the initial value of {@link 
#objectiveToDisplay}.
-     * We differ this recomputation until all parameters are known.
+     * We differ this computation until all parameters are known.
      *
      * @see #setObjectiveBounds(Envelope)
      * @see #invalidObjectiveToDisplay
@@ -1195,7 +1195,7 @@ public abstract class MapCanvas extends PlanarCanvas {
                     return;
                 }
                 invalidObjectiveToDisplay = false;
-                final GridExtent extent = new GridExtent(null,
+                final var 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);
                 /*
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/package-info.java 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/package-info.java
index 43f51967bf..1f53783039 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/package-info.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/package-info.java
@@ -22,7 +22,7 @@
  * {@link org.apache.sis.gui.map.MapCanvasAWT} is a specialization for 
painting the map using Java2D.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.4
+ * @version 1.5
  * @since   1.1
  */
 package org.apache.sis.gui.map;

Reply via email to