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 ad92a26248 Initialize new windows to the same zoom level and map
projection than the original window.
ad92a26248 is described below
commit ad92a262484ad348393f3d79a5157d99b0325567
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sun Jun 12 15:35:30 2022 +0200
Initialize new windows to the same zoom level and map projection than the
original window.
---
.../apache/sis/gui/coverage/CoverageCanvas.java | 47 +++---
.../apache/sis/gui/coverage/CoverageExplorer.java | 75 ++++------
.../org/apache/sis/gui/coverage/ImageRequest.java | 15 +-
.../org/apache/sis/gui/dataset/WindowHandler.java | 21 ++-
.../java/org/apache/sis/gui/map/MapCanvas.java | 157 ++++++++++++++-------
.../org/apache/sis/internal/gui/PrivateAccess.java | 14 +-
6 files changed, 203 insertions(+), 126 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 b21bdc0f40..1b741939d2 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
@@ -50,20 +50,20 @@ import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.TransformException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.referencing.operation.transform.LinearTransform;
+import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
+import org.apache.sis.coverage.Category;
import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.coverage.SubspaceNotSpecifiedException;
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.referencing.operation.matrix.AffineTransforms2D;
-import org.apache.sis.referencing.operation.transform.LinearTransform;
-import org.apache.sis.referencing.operation.transform.MathTransforms;
-import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.geometry.Shapes2D;
import org.apache.sis.image.PlanarImage;
import org.apache.sis.image.Interpolation;
-import org.apache.sis.coverage.Category;
-import org.apache.sis.coverage.SubspaceNotSpecifiedException;
import org.apache.sis.storage.GridCoverageResource;
import org.apache.sis.gui.map.MapCanvas;
import org.apache.sis.gui.map.MapCanvasAWT;
@@ -158,7 +158,7 @@ public class CoverageCanvas extends MapCanvasAWT {
* This is used for preventing never-ending loop when a change of resource
causes a change of coverage
* or conversely.
*
- * @see #onPropertySpecified(ObjectProperty)
+ * @see #onPropertySpecified(GridCoverageResource, GridCoverage,
ObjectProperty, GridGeometry)
*/
private boolean isCoverageAdjusting;
@@ -265,9 +265,9 @@ public class CoverageCanvas extends MapCanvasAWT {
coverageProperty = new SimpleObjectProperty<>(this, "coverage");
sliceExtentProperty = new SimpleObjectProperty<>(this,
"sliceExtent");
interpolationProperty = new SimpleObjectProperty<>(this,
"interpolation", data.processor.getInterpolation());
- resourceProperty .addListener((p,o,n) -> onPropertySpecified(n,
null, coverageProperty));
- coverageProperty .addListener((p,o,n) -> onPropertySpecified(null,
n, resourceProperty));
- sliceExtentProperty .addListener((p,o,n) ->
onPropertySpecified(getResource(), getCoverage(), null));
+ resourceProperty .addListener((p,o,n) -> onPropertySpecified(n,
null, coverageProperty, null));
+ coverageProperty .addListener((p,o,n) -> onPropertySpecified(null,
n, resourceProperty, null));
+ sliceExtentProperty .addListener((p,o,n) ->
onPropertySpecified(getResource(), getCoverage(), null, null));
interpolationProperty.addListener((p,o,n) ->
onInterpolationSpecified(n));
}
@@ -459,6 +459,7 @@ public class CoverageCanvas extends MapCanvasAWT {
public void setObjectiveCRS(final CoordinateReferenceSystem newValue,
DirectPosition anchor) throws RenderException {
final Long id = LogHandler.loadingStart(getResource());
try {
+ // With `LogHandler` because this call may cause searches in EPSG
database.
super.setObjectiveCRS(newValue, anchor);
} finally {
LogHandler.loadingStop(id);
@@ -478,6 +479,7 @@ public class CoverageCanvas extends MapCanvasAWT {
public void setGridGeometry(final GridGeometry newValue) throws
RenderException {
final Long id = LogHandler.loadingStart(getResource());
try {
+ // With `LogHandler` because this call may cause searches in EPSG
database.
super.setGridGeometry(newValue);
} finally {
LogHandler.loadingStop(id);
@@ -495,14 +497,17 @@ public class CoverageCanvas extends MapCanvasAWT {
final GridCoverageResource resource;
final GridCoverage coverage;
final GridExtent sliceExtent;
+ final GridGeometry zoom;
if (request != null) {
resource = request.resource;
coverage = request.coverage;
sliceExtent = request.slice;
+ zoom = request.zoom;
} else {
resource = null;
coverage = null;
sliceExtent = null;
+ zoom = null;
}
if (getResource() != resource || getCoverage() != coverage ||
getSliceExtent() != sliceExtent) {
final boolean p = isCoverageAdjusting;
@@ -514,7 +519,7 @@ public class CoverageCanvas extends MapCanvasAWT {
} finally {
isCoverageAdjusting = p;
}
- onPropertySpecified(resource, coverage, null);
+ onPropertySpecified(resource, coverage, null, zoom);
}
}
@@ -527,9 +532,10 @@ public class CoverageCanvas extends MapCanvasAWT {
* @param resource the new resource, or {@code null} if none.
* @param coverage the new coverage, or {@code null} if none.
* @param toClear the property which is an alternative to the property
that has been set.
+ * @param zoom initial "objective to display" transform to use, or
{@code null} for automatic.
*/
private void onPropertySpecified(final GridCoverageResource resource,
final GridCoverage coverage,
- final ObjectProperty<?> toClear)
+ final ObjectProperty<?> toClear, final
GridGeometry zoom)
{
hasCoverageOrResource = (resource != null || coverage != null);
if (isCoverageAdjusting) {
@@ -593,7 +599,7 @@ public class CoverageCanvas extends MapCanvasAWT {
*/
@Override protected void succeeded() {
runAfterRendering(() -> {
- setNewSource(getValue(), ranges);
+ setNewSource(getValue(), ranges, zoom);
requestRepaint(); // Cause `Worker`
class to be executed.
});
}
@@ -637,8 +643,9 @@ public class CoverageCanvas extends MapCanvasAWT {
*
* @param domain the multi-dimensional grid geometry, or {@code null} if
there is no data.
* @param ranges descriptions of bands, or {@code null} if there is no
data.
+ * @param zoom initial "objective to display" transform to use, or
{@code null} for automatic.
*/
- private void setNewSource(GridGeometry domain, final List<SampleDimension>
ranges) {
+ private void setNewSource(GridGeometry domain, final List<SampleDimension>
ranges, final GridGeometry zoom) {
if (TRACE) {
trace("setNewSource(…): the new domain of data is:%n\t%s", domain);
}
@@ -679,6 +686,7 @@ public class CoverageCanvas extends MapCanvasAWT {
}
}
data.setImageSpace(domain, ranges, xyDimensions);
+ initialize(zoom);
setObjectiveBounds(bounds);
}
@@ -1134,7 +1142,14 @@ public class CoverageCanvas extends MapCanvasAWT {
* Invoked when an exception occurred while computing a transform but the
painting process can continue.
*/
private static void unexpectedException(final Exception e) {
- Logging.unexpectedException(getLogger(Modules.APPLICATION),
CoverageCanvas.class, "render", e);
+ unexpectedException("render", e);
+ }
+
+ /**
+ * Invoked when an exception occurred. The declared source method should
be a public or protected method.
+ */
+ static void unexpectedException(final String method, final Exception e) {
+ Logging.unexpectedException(getLogger(Modules.APPLICATION),
CoverageCanvas.class, method, e);
}
/**
@@ -1156,7 +1171,7 @@ public class CoverageCanvas extends MapCanvasAWT {
if (TRACE) {
trace("clear()");
}
- setNewSource(null, null);
+ setNewSource(null, null, null);
super.clear();
}
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java
index 43a9ff3752..ae6a3ba058 100644
---
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java
@@ -36,13 +36,13 @@ import org.apache.sis.coverage.grid.GridCoverage;
import org.apache.sis.internal.gui.Resources;
import org.apache.sis.internal.gui.ToolbarButton;
import org.apache.sis.internal.gui.NonNullObjectProperty;
+import org.apache.sis.internal.gui.PrivateAccess;
import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.portrayal.RenderException;
import org.apache.sis.gui.referencing.RecentReferenceSystems;
import org.apache.sis.gui.dataset.WindowHandler;
-import org.apache.sis.gui.dataset.WindowManager;
import org.apache.sis.gui.map.StatusBar;
import org.apache.sis.gui.Widget;
-import org.apache.sis.internal.gui.PrivateAccess;
/**
@@ -214,7 +214,7 @@ public class CoverageExplorer extends Widget {
* Handler of the window showing this coverage view. This is used for
creating new windows.
* Created when first needed for giving to subclasses a chance to complete
initialization.
*
- * @see #window()
+ * @see #getWindowHandler()
*/
private WindowHandler window;
@@ -264,58 +264,35 @@ public class CoverageExplorer extends Widget {
* @param source the source explorer from which to take the initial
coverage or resource.
*
* @since 1.2
- *
- * @deprecated Replaced by {@code
source.getImageRequest().ifPresent(newExplorer::setCoverage);}.
*/
- @Deprecated
public CoverageExplorer(final CoverageExplorer source) {
this(source.getViewType());
+ window = PrivateAccess.newWindowHandler.apply(source.window, this);
source.getImageRequest().ifPresent(this::setCoverage);
- }
-
- /*
- * Hack for giving access outside this package to a field that we do not
want to make public.
- * This is a way to simulate the "friend" keyword in C++.
- */
- static {
- PrivateAccess.initWindowHandler = CoverageExplorer::initWindowHandler;
- }
-
- /**
- * Initializes {@link #window} to the given value. This method should be
invoked soon after
- * construction and can be invoked only once.
- */
- private void initWindowHandler(final WindowHandler handler) {
- assert Platform.isFxApplicationThread() && window == null : window;
- window = handler;
- }
-
- /**
- * Returns the handler of the window showing this coverage view. Created
when first needed
- * for giving to subclass constructors a chance to complete their
initialization before the
- * {@code this} reference is passed to {@link WindowHandler} constructor.
- */
- private WindowHandler window() {
- assert Platform.isFxApplicationThread();
- if (window == null) {
- window = WindowHandler.create(this);
- }
- return window;
+ PrivateAccess.finishWindowHandler.accept(window);
}
/**
- * Returns a manager of windows showing different view of the coverage.
- * Those windows are created when the user click on the "New window"
button.
+ * Returns the handler of the window showing this coverage view.
+ * Those windows are created when the user clicks on the "New window"
button.
* Each window provides the area where data are shown and where the user
interacts.
* The window can be a JavaFX top-level window ({@link Stage}), but not
necessarily.
* It may also be a tile in a mosaic of windows.
*
- * @return the manager of windows created by the "New window" button.
+ * @return the handler of the window showing this coverage view.
*
* @since 1.3
*/
- public final WindowManager getWindowManager() {
- return window().manager;
+ public final WindowHandler getWindowHandler() {
+ assert Platform.isFxApplicationThread();
+ /*
+ * Created when first needed for giving to subclass constructors a
chance to complete
+ * their initialization before `this` reference is passed to
`WindowHandler` constructor.
+ */
+ if (window == null) {
+ window = WindowHandler.create(this);
+ }
+ return window;
}
/**
@@ -342,7 +319,7 @@ public class CoverageExplorer extends Widget {
if (c == null) {
switch (type) {
case TABLE: c = new GridControls(this); break;
- case IMAGE: c = new CoverageControls(this, window()); break;
+ case IMAGE: c = new CoverageControls(this,
getWindowHandler()); break;
default: throw new AssertionError(type);
}
views.put(type, c);
@@ -579,7 +556,6 @@ public class CoverageExplorer extends Widget {
*
* @param source the coverage or resource to load, or {@code null} if
none.
*
- * @see #getImageRequest()
* @see GridView#setImage(ImageRequest)
*/
public final void setCoverage(final ImageRequest source) {
@@ -655,14 +631,19 @@ public class CoverageExplorer extends Widget {
* @return the request to give to another explorer for showing the same
coverage.
*
* @see #setCoverage(ImageRequest)
- *
- * @since 1.3
*/
- public final Optional<ImageRequest> getImageRequest() {
+ private Optional<ImageRequest> getImageRequest() {
final GridCoverageResource resource = getResource();
final GridCoverage coverage = getCoverage();
if (resource != null || coverage != null) {
- return Optional.of(new ImageRequest(resource, coverage));
+ final ImageRequest request = new ImageRequest(resource, coverage);
+ final CoverageControls c = (CoverageControls)
views.get(View.IMAGE);
+ if (c != null) try {
+ request.zoom = c.view.getGridGeometry();
+ } catch (RenderException e) {
+ CoverageCanvas.unexpectedException("getGridGeometry", e);
+ }
+ return Optional.of(request);
} else {
return Optional.empty();
}
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageRequest.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageRequest.java
index d9f1c58e09..b0b620e6a4 100644
---
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageRequest.java
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImageRequest.java
@@ -17,12 +17,12 @@
package org.apache.sis.gui.coverage;
import java.util.Optional;
-import org.apache.sis.storage.GridCoverageResource;
import org.apache.sis.coverage.grid.GridCoverage;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.coverage.grid.GridExtent;
-import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.storage.GridCoverageResource;
import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.util.ArgumentChecks;
/**
@@ -78,6 +78,13 @@ public class ImageRequest {
*/
final GridExtent slice;
+ /**
+ * The initial objective CRS and zoom to use in a new {@link
CoverageCanvas}, or {@code null} if none.
+ * This is used only if we want to create a new canvas initialized to the
same viewing region and zoom
+ * level than an existing canvas.
+ */
+ GridGeometry zoom;
+
/**
* Creates a new request with both a resource and a coverage. At least one
argument shall be non-null.
* If both arguments are non-null, then {@code data} must be the result of
reading the given resource.
@@ -135,11 +142,11 @@ public class ImageRequest {
*/
public ImageRequest(final GridCoverage source, final GridExtent slice) {
ArgumentChecks.ensureNonNull("source", source);
+ this.coverage = source;
+ this.slice = slice;
this.resource = null;
this.domain = null;
this.range = null;
- this.coverage = source;
- this.slice = slice;
}
/**
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowHandler.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowHandler.java
index 68aa91620f..d3b1a46c66 100644
---
a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowHandler.java
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowHandler.java
@@ -336,6 +336,21 @@ public abstract class WindowHandler {
this.widget = widget;
}
+ /**
+ * Creates a new handler for duplicating an existing window.
+ * For indirect usage by {@link #duplicate()}.
+ *
+ * @param creator the handler which is duplicated.
+ * @param widget the widget providing the new view of the resource.
+ */
+ private ForCoverage(final WindowHandler creator, final
CoverageExplorer widget) {
+ this(creator, null, widget);
+ }
+ static {
+ PrivateAccess.newWindowHandler = ForCoverage::new;
+ PrivateAccess.finishWindowHandler = WindowHandler::finish;
+ }
+
/**
* The resource shown in the {@linkplain #window window}, or {@code
null} if unspecified.
*/
@@ -375,11 +390,7 @@ public abstract class WindowHandler {
*/
@Override
public WindowHandler duplicate() {
- final CoverageExplorer explorer = new
CoverageExplorer(widget.getViewType());
- final ForCoverage handler = new ForCoverage(this, null, explorer);
- PrivateAccess.initWindowHandler.accept(explorer, handler);
- widget.getImageRequest().ifPresent(explorer::setCoverage);
- return handler.finish();
+ return new CoverageExplorer(widget).getWindowHandler();
}
/**
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 a9411566a3..d911fa447a 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
@@ -64,9 +64,9 @@ import org.apache.sis.referencing.cs.CoordinateSystems;
import org.apache.sis.geometry.DirectPosition2D;
import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.geometry.AbstractEnvelope;
+import org.apache.sis.geometry.ImmutableEnvelope;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.coverage.grid.GridExtent;
-import org.apache.sis.coverage.grid.GridOrientation;
import org.apache.sis.gui.referencing.PositionableProjection;
import org.apache.sis.gui.referencing.RecentReferenceSystems;
import org.apache.sis.util.ArraysExt;
@@ -191,13 +191,22 @@ public abstract class MapCanvas extends PlanarCanvas {
*/
protected final StackPane fixedPane;
+ /**
+ * The data bounds to use for computing the initial value of {@link
#objectiveToDisplay}.
+ * We differ this recomputation until all parameters are known.
+ *
+ * @see #setObjectiveBounds(Envelope)
+ * @see #invalidObjectiveToDisplay
+ */
+ private Envelope objectiveBounds;
+
/**
* The data bounds to use for computing the initial value of {@link
#objectiveToDisplay}.
* Optionally contains the initial "objective to display" CRS to use if a
predetermined
* value is desired instead of an automatically computed one. The grid
extent is ignored,
* except for fetching the grid center if a non-linear transform needs to
be linearized.
*
- * @see #setInitialState(GridGeometry)
+ * @see #initialize(GridGeometry)
* @see #invalidObjectiveToDisplay
*/
private GridGeometry initialState;
@@ -247,7 +256,8 @@ public abstract class MapCanvas extends PlanarCanvas {
* Whether {@link #objectiveToDisplay} needs to be recomputed.
* We differ this recomputation until all parameters are known.
*
- * @see #initialState
+ * @see #objectiveBounds
+ * @see #objectiveToDisplay
*/
private boolean invalidObjectiveToDisplay;
@@ -369,6 +379,46 @@ public abstract class MapCanvas extends PlanarCanvas {
error = new ReadOnlyObjectWrapper<>(this, "error");
}
+ /**
+ * Sets the objective bounds and/or the zoom level and objective CRS to
use for the initial view of data.
+ * The {@code visibleArea} {@linkplain
GridGeometry#getCoordinateReferenceSystem() CRS} defines the initial
+ * {@linkplain #setObjectiveCRS(CoordinateReferenceSystem, DirectPosition)
objective CRS} of this canvas.
+ * The {@code visibleArea} {@linkplain GridGeometry#getEnvelope()
envelope} defines the (usually constant)
+ * {@linkplain #setObjectiveBounds(Envelope) objective bounds} of this
canvas.
+ * In addition if {@code visibleArea} contains a {@linkplain
GridGeometry#getGridToCRS grid to CRS} transform,
+ * its inverse will define the initial {@linkplain #setObjectiveToDisplay
objective to display} transform
+ * (which in turn defines the initial viewed area and zoom level).
+ *
+ * <p>This method should be invoked only when new data have been loaded,
or when the caller wants
+ * to discard any zoom or translation and reset the view to the given
bounds. This method does not
+ * cause new repaint event; {@link #requestRepaint()} must be invoked by
the caller if desired.</p>
+ *
+ * @param visibleArea bounding box, objective CRS and or initial zoom
level,
+ * or {@code null} if unknown (in which case an identity transform
will be set).
+ * @throws MismatchedDimensionException if the given grid geometry is not
two-dimensional.
+ *
+ * @see #setObjectiveBounds(Envelope)
+ * @see #getGridGeometry()
+ *
+ * @since 1.3
+ */
+ protected void initialize(final GridGeometry visibleArea) {
+ Envelope bounds = null;
+ if (visibleArea != null) {
+ if (visibleArea.isDefined(GridGeometry.ENVELOPE)) {
+ bounds = visibleArea.getEnvelope();
+ ArgumentChecks.ensureDimensionMatches("visibleArea",
BIDIMENSIONAL, bounds);
+ }
+ if (visibleArea.isDefined(GridGeometry.GRID_TO_CRS)) {
+ ArgumentChecks.ensureDimensionsMatch("visibleArea",
BIDIMENSIONAL, BIDIMENSIONAL,
+ visibleArea.getGridToCRS(PixelInCell.CELL_CENTER));
+ }
+ }
+ objectiveBounds = bounds;
+ initialState = visibleArea;
+ invalidObjectiveToDisplay = true;
+ }
+
/**
* Returns the bounds of the content in {@link #floatingPane} coordinates,
or {@code null} if unknown.
* Some subclasses may compute a larger image than the widget size for
better visual transition during
@@ -772,10 +822,25 @@ public abstract class MapCanvas extends PlanarCanvas {
}
}
+ /**
+ * Returns the data bounds to use for computing the initial "objective to
display" transform.
+ * This is the value specified by the last call to {@link
#setObjectiveBounds(Envelope)}.
+ * The coordinate reference system of the returned envelope defines also
the CRS which
+ * is restored when the {@link #reset()} method is invoked.
+ *
+ * @return the data bounds to use for computing the initial "objective to
display" transform,
+ * or {@code null} if unspecified.
+ *
+ * @since 1.3
+ */
+ public Envelope getObjectiveBounds() {
+ return objectiveBounds;
+ }
+
/**
* Sets the data bounds to use for computing the initial value of {@link
#objectiveToDisplay}.
- * Invoking this method also sets the {@link #getObjectiveCRS() objective
CRS} of this canvas
- * to the CRS of given envelope.
+ * Invoking this method also sets the initial {@linkplain
#getObjectiveCRS() objective CRS}
+ * of this canvas to the CRS of given envelope.
*
* <p>This method should be invoked only when new data have been loaded,
or when the caller wants
* to discard any zoom or translation and reset the view to the given
bounds. This method does not
@@ -787,39 +852,25 @@ public abstract class MapCanvas extends PlanarCanvas {
*
* @see #setObjectiveCRS(CoordinateReferenceSystem, DirectPosition)
*/
- protected void setObjectiveBounds(final Envelope visibleArea) {
+ public void setObjectiveBounds(final Envelope visibleArea) {
ArgumentChecks.ensureDimensionMatches("visibleArea", BIDIMENSIONAL,
visibleArea);
- if (visibleArea == null) {
- initialState = null;
- } else {
- setInitialState(new GridGeometry(null, visibleArea,
GridOrientation.HOMOTHETY));
- }
+ objectiveBounds = ImmutableEnvelope.castOrCopy(visibleArea);
+ invalidObjectiveToDisplay = true;
}
/**
- * Sets the data bounds and initial "objective to display" transform to
use. This method sets the same
- * information than {@link #setObjectiveBounds(Envelope)} using the
envelope of the given grid geometry.
- * But if in addition a "grid to CRS" transform is specified, it will be
used for defining the initial
- * value of the "objective to display" transform.
- *
- * <p>Note that the objective bounds (defined by grid geometry envelope)
are usually constant as long
- * as the data do not change, while the other properties may change under
user interactions.
- * This is the reason why the given argument is an <em>initial</em>
state.</p>
- *
- * <p>A typical use case for this method is to give in argument the {@link
#getGridGeometry()} value
- * of another canvas for initializing this canvas to the same geographic
region and zoom level,
- * assuming that the two canvas and rendering the same data.</p>
- *
- * @param visibleArea bounding box and initial zoom level to use for the
canvas.
- *
- * @see #setGridGeometry(GridGeometry)
+ * Sets the conversion from objective CRS to display coordinate system.
+ * Invoking this method has the effect of changing the viewed area, the
zoom level or the rotation of the map.
+ * Caller needs to invoke {@link #requestRepaint()} after this method call
(this is not done automatically).
*
- * @since 1.3
+ * @param newValue the new <cite>objective to display</cite> conversion.
+ * @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.
*/
- protected void setInitialState(final GridGeometry visibleArea) {
- ArgumentChecks.ensureNonNull("visibleArea", visibleArea);
- initialState = visibleArea;
- invalidObjectiveToDisplay = true;
+ @Override
+ public void setObjectiveToDisplay(final LinearTransform newValue) throws
RenderException {
+ super.setObjectiveToDisplay(newValue);
+ invalidObjectiveToDisplay = false;
}
/**
@@ -1068,28 +1119,28 @@ public abstract class MapCanvas extends PlanarCanvas {
new long[] {Math.round(target.getMinX()),
Math.round(target.getMinY())},
new long[] {Math.round(target.getMaxX()),
Math.round(target.getMaxY())}, false);
/*
- * If `setObjectiveBounds(…)` has been invoked (as it should
be), initialize the affine
- * transform to values which will allow this canvas to contain
fully the objective bounds.
- * Otherwise the transform is initialized to an identity
transform (should not happen often).
- * If a CRS is present, it is used for deciding if we need to
swap or flip axes.
+ * The main purpose of this block is to find the initial value
of the `objectiveToDisplay` transform
+ * (named `crsToDisplay` here). If that value was explicitly
specified by a call to `initialize(…)`,
+ * use it as-is. Otherwise we will compute it from the bounds
of data.
*/
- Envelope objectiveBounds = null;
- CoordinateReferenceSystem objectiveCRS = null;
+ CoordinateReferenceSystem objectiveCRS;
LinearTransform crsToDisplay = null;
- if (initialState != null) {
- if (initialState.isDefined(GridGeometry.GRID_TO_CRS)) {
- crsToDisplay =
initialState.getLinearGridToCRS(PixelInCell.CELL_CORNER).inverse();
- }
- if (initialState.isDefined(GridGeometry.ENVELOPE)) {
- objectiveBounds = initialState.getEnvelope();
- }
- if (initialState.isDefined(GridGeometry.CRS)) {
- objectiveCRS =
initialState.getCoordinateReferenceSystem();
- }
- }
- if (crsToDisplay == null) {
+ final GridGeometry init = initialState;
+ initialState = null; // For
using `objectiveBounds` next times.
+ if (init != null && init.isDefined(GridGeometry.GRID_TO_CRS)) {
+ crsToDisplay =
init.getLinearGridToCRS(PixelInCell.CELL_CORNER).inverse();
+ objectiveCRS = null; // Value will be fetched after the
`else` block.
+ } else {
+ /*
+ * If `setObjectiveBounds(…)` has been invoked (as it
should be), initialize the affine
+ * transform to values which will allow this canvas to
contain fully the objective bounds.
+ * Otherwise the transform is initialized to an identity
transform (should not happen often).
+ * If a CRS is present, it is used for deciding if we need
to swap or flip axes.
+ */
+ final Envelope objectiveBounds = getObjectiveBounds();
if (objectiveBounds != null) {
final MatrixSIS m;
+ objectiveCRS =
objectiveBounds.getCoordinateReferenceSystem();
if (objectiveCRS != null) {
AxisDirection[] srcAxes =
CoordinateSystems.getAxisDirections(objectiveCRS.getCoordinateSystem());
m = Matrices.createTransform(objectiveBounds,
srcAxes, target, toDisplayDirections(srcAxes));
@@ -1104,7 +1155,11 @@ public abstract class MapCanvas extends PlanarCanvas {
}
}
if (objectiveCRS == null) {
- objectiveCRS =
extent.toEnvelope(crsToDisplay.inverse()).getCoordinateReferenceSystem();
+ if (init.isDefined(GridGeometry.CRS)) {
+ objectiveCRS = init.getCoordinateReferenceSystem();
+ } else {
+ objectiveCRS =
extent.toEnvelope(crsToDisplay.inverse()).getCoordinateReferenceSystem();
+ }
/*
* Above code tried to provide a non-null CRS on a "best
effort" basis. The objective CRS
* may still be null, there is no obvious answer against
that. It is not the display CRS
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PrivateAccess.java
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PrivateAccess.java
index 68a15aaab3..5415c8a807 100644
---
a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PrivateAccess.java
+++
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PrivateAccess.java
@@ -16,7 +16,8 @@
*/
package org.apache.sis.internal.gui;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.BiFunction;
import org.apache.sis.gui.coverage.CoverageExplorer;
import org.apache.sis.gui.dataset.WindowHandler;
@@ -40,7 +41,14 @@ public final class PrivateAccess {
}
/**
- * A setter method for {@link CoverageExplorer#window}. Shall be invoked
in JavaFX thread.
+ * Accessor for {@link
org.apache.sis.gui.dataset.WindowHandler.ForCoverage} constructor.
+ * Used for assigning {@link CoverageExplorer#window} when duplicating an
existing window.
+ * Shall be invoked in JavaFX thread.
*/
- public static volatile BiConsumer<CoverageExplorer, WindowHandler>
initWindowHandler;
+ public static volatile BiFunction<WindowHandler, CoverageExplorer,
WindowHandler> newWindowHandler;
+
+ /**
+ * Accessor for {@link WindowHandler#finish()} method. Shall be invoked in
JavaFX thread.
+ */
+ public static volatile Consumer<WindowHandler> finishWindowHandler;
}