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 3ff7c26 Resolve numerous problems with the display of geographic/projected coordinates
under change of CRS.
3ff7c26 is described below
commit 3ff7c264a1066b8c6a0455f960b96bc296f121a0
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Apr 23 19:54:23 2020 +0200
Resolve numerous problems with the display of geographic/projected coordinates under change
of CRS.
---
.../apache/sis/gui/coverage/CoverageExplorer.java | 2 +-
.../java/org/apache/sis/gui/map/StatusBar.java | 353 +++++++++++++++------
.../org/apache/sis/internal/gui/Resources.java | 5 +
.../apache/sis/internal/gui/Resources.properties | 1 +
.../sis/internal/gui/Resources_fr.properties | 1 +
.../org/apache/sis/geometry/CoordinateFormat.java | 1 +
.../referencing/provider/PolarStereographicA.java | 2 +-
7 files changed, 259 insertions(+), 106 deletions(-)
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 23686e8..8712a48 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
@@ -152,7 +152,7 @@ public class CoverageExplorer extends Widget {
viewTypeProperty.addListener(this::onViewTypeSpecified);
referenceSystems = new RecentReferenceSystems();
referenceSystems.addUserPreferences();
- referenceSystems.addAlternatives("EPSG:3395"); // WGS 84 / World Mercator
+ referenceSystems.addAlternatives("EPSG:4326", "EPSG:3395"); // WGS 84 / World
Mercator
/*
* Prepare buttons to add on the toolbar. Those buttons are not managed by this class;
* they are managed by org.apache.sis.gui.dataset.DataWindow. We only declare here
the
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 5a16356..2de5596 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
@@ -19,6 +19,7 @@ package org.apache.sis.gui.map;
import java.util.Locale;
import java.util.Optional;
import javax.measure.Unit;
+import javafx.geometry.Pos;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.paint.Color;
@@ -27,9 +28,9 @@ import javafx.scene.layout.Region;
import javafx.scene.layout.Priority;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
-import javafx.scene.control.ProgressBar;
import javafx.scene.control.ContextMenu;
import javafx.scene.input.MouseEvent;
+import javafx.scene.text.TextAlignment;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.beans.value.ObservableValue;
@@ -59,24 +60,27 @@ import org.apache.sis.internal.util.Strings;
import org.apache.sis.measure.Quantities;
import org.apache.sis.measure.Units;
import org.apache.sis.util.Classes;
+import org.apache.sis.util.Utilities;
import org.apache.sis.util.Exceptions;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.gui.Widget;
import org.apache.sis.gui.referencing.RecentReferenceSystems;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
import org.apache.sis.internal.gui.BackgroundThreads;
import org.apache.sis.internal.gui.ExceptionReporter;
import org.apache.sis.internal.gui.Resources;
import org.apache.sis.internal.gui.Styles;
import org.apache.sis.internal.system.Modules;
import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.IdentifiedObjects;
/**
* A status bar showing geographic or projected coordinates under mouse cursor.
* The number of fraction digits is adjusted according pixel resolution for each coordinate
to format.
- * Other components such as progress bar or error message may also be shown.
+ * Other components such as error message may also be shown.
*
* <p>Since the main {@code StatusBar} job is to listen to mouse events for updating
coordinates,
* this class implements {@link EventHandler} directly. {@code StatusBar} can be registered
as a listener
@@ -108,7 +112,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
/**
* Some spaces to add around the status bar.
*/
- private static final Insets PADDING = new Insets(5, Styles.SCROLLBAR_WIDTH, 6, 0);
+ private static final Insets PADDING = new Insets(5, Styles.SCROLLBAR_WIDTH, 6, 9);
/**
* The container of controls making the status bar.
@@ -116,28 +120,23 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
private final HBox view;
/**
- * The progress bar, hidden by default. This bar is initialized to undetermined state.
- */
- private final ProgressBar progress;
-
- /**
* Message to write in the middle of the status bar.
* This component usually has nothing to show; it is used mostly for error messages.
- * It takes all the space between {@link #progress} and {@link #coordinates}.
+ * It takes all the space before {@link #position}.
*/
private final Label message;
/**
- * Local coordinates currently formatted in the {@link #coordinates} field.
+ * Local coordinates currently formatted in the {@link #position} field.
* This is used for detecting if coordinate values changed since last formatting.
* Those coordinates are often integer values.
*/
private double lastX, lastY;
/**
- * The area of interest, or {@code null} if none. This is a reference to the
- * {@link RecentReferenceSystems#areaOfInterest} property. We do not make this
- * property public because it does not belong to this object.
+ * The area of interest, or {@code null} if none. Used for computing {@link #objectiveToFormatCRS}.
+ * This field is a reference to the {@link RecentReferenceSystems#areaOfInterest} property.
+ * We do not make this property public because it does not belong to this object.
*/
private final ObjectProperty<Envelope> areaOfInterest;
@@ -147,11 +146,22 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
* data CRS. It may not be the same than the CRS of coordinates actually shown in the
status bar.
*
* @see #getObjectiveCRS()
+ * @see #getFormatReferenceSystem()
* @see MapCanvas#getObjectiveCRS()
*/
private CoordinateReferenceSystem objectiveCRS;
/**
+ * Hold the transform from <cite>objective CRS</cite> to the CRS of coordinates
shown in this status bar.
+ * The {@linkplain CoordinateOperation#getSourceCRS() source CRS} is {@link #objectiveCRS}
and
+ * the {@linkplain CoordinateOperation#getTargetCRS() target CRS} is {@link CoordinateFormat#getDefaultCRS()}.
+ * This coordinate operation may be null if there is no CRS change to apply
+ * (in which case {@link #localToFormatCRS} is the same instance than {@link #localToObjectiveCRS})
+ * or if the target is not a CRS (for example it may be a Military Grid Reference System
(MGRS) code).
+ */
+ private CoordinateOperation objectiveToFormatCRS;
+
+ /**
* Conversion from local coordinates to geographic or projected coordinates of rendered
data.
* This is not necessarily the conversion to the coordinates shown in this status bar.
* This conversion shall never be null but may be the identity transform.
@@ -165,19 +175,24 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
/**
* Conversion from local coordinates to geographic or projected coordinates shown in
this status bar.
- * This is the concatenation of {@link #localToObjectiveCRS} with the transform from
{@link #objectiveCRS}
- * to the user-selected CRS for displaying in the status bar. This conversion shall never
be null but may be
- * the identity transform. It is usually non-affine if the display CRS is not the same
than the objective CRS.
+ * This is the concatenation of {@link #localToObjectiveCRS} with {@link #objectiveToFormatCRS}
transform.
+ * The result is a transform to the user-selected CRS for coordinates shown in the status
bar.
+ * This conversion shall never be null but may be the identity transform.
+ * It is usually non-affine if the display CRS is not the same than the objective CRS.
* This transform may have a {@linkplain CoordinateOperation#getCoordinateOperationAccuracy()
limited accuracy}.
*
- * <p>The target CRS can be obtained by {@link CoordinateFormat#getDefaultCRS()}.</p>
+ * <p>The target CRS can be obtained by {@link CoordinateOperation#getTargetCRS()}
on
+ * {@link #objectiveToFormatCRS} or by {@link CoordinateFormat#getDefaultCRS()}.</p>
*/
- private MathTransform localToTargetCRS;
+ private MathTransform localToFormatCRS;
/**
* The source local indices before conversion to geospatial coordinates.
* The number of dimensions is often {@value #BIDIMENSIONAL}.
* Shall never be {@code null}.
+ *
+ * @see #targetCoordinates
+ * @see #position
*/
private double[] sourceCoordinates;
@@ -185,12 +200,15 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
* Coordinates after conversion to the CRS. The number of dimensions depends on
* the target CRS. This object is reused during each coordinate transformation.
* Shall never be {@code null}.
+ *
+ * @see #sourceCoordinates
+ * @see #position
*/
private GeneralDirectPosition targetCoordinates;
/**
* The desired precisions for each dimension in the {@link #targetCoordinates} to format.
- * It may vary for each position if the {@link #localToTargetCRS} transform is non-linear.
+ * It may vary for each position if the {@link #localToFormatCRS} transform is non-linear.
* This array is initially {@code null} and created when first needed.
*/
private double[] precisions;
@@ -212,9 +230,11 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
private final CoordinateFormat format;
/**
- * The labels where to format the coordinates.
+ * The labels where to format the cursor position, either as coordinate values or other
representations.
+ * The text is usually the result of formatting {@link #targetCoordinates} as numerical
values,
+ * but may also be other representations such as Military Grid Reference System (MGRS)
codes.
*/
- private final Label coordinates;
+ private final Label position;
/**
* The canvas that this status bar is tracking.
@@ -226,42 +246,52 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
public final ObjectProperty<MapCanvas> canvasProperty;
/**
- * The listener registered on {@link MapCanvas#renderingProperty()}.
- * This reference is stored for allowed removal.
+ * The listener registered on {@link MapCanvas#renderingProperty()}, or {@code null}
if the
+ * listener has not yet been registered. This listener is remembered for allowing removal.
*
* @see #setCanvas(MapCanvas)
+ * @see #onCanvasSpecified(ObservableValue, MapCanvas, MapCanvas)
*/
private ChangeListener<Boolean> renderingListener;
/**
- * Creates a new status bar.
+ * 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.
+ */
+ private boolean isMouseListenerRegistered;
+
+ /**
+ * Creates a new status bar for showing coordinates of mouse cursor position in a canvas.
+ * If the {@code choices} argument is non-null, user will be able to select different
CRS
+ * using the contextual menu on the status bar.
*
- * @param referenceSystems the manager of reference systems chosen by the user, or
{@code null} if none.
+ * @param choices the manager of reference systems chosen by user, or {@code null}
if none.
*/
- public StatusBar(final RecentReferenceSystems referenceSystems) {
+ public StatusBar(final RecentReferenceSystems choices) {
localToObjectiveCRS = MathTransforms.identity(BIDIMENSIONAL);
- localToTargetCRS = localToObjectiveCRS;
+ localToFormatCRS = localToObjectiveCRS;
targetCoordinates = new GeneralDirectPosition(BIDIMENSIONAL);
sourceCoordinates = targetCoordinates.coordinates;
lastX = lastY = Double.NaN;
format = new CoordinateFormat();
- coordinates = new Label();
+ position = new Label();
message = new Label();
- progress = new ProgressBar();
- progress.setVisible(false);
+ message.setVisible(false); // Waiting for getting a message
to display.
message.setTextFill(Styles.ERROR_TEXT);
message.setMaxWidth(Double.POSITIVE_INFINITY);
HBox.setHgrow(message, Priority.ALWAYS);
- coordinates.minWidthProperty().bind(coordinates.widthProperty());
- view = new HBox(12, progress, message, coordinates);
+ view = new HBox(18, message, position);
view.setPadding(PADDING);
+ view.setAlignment(Pos.CENTER_RIGHT);
+ position.setAlignment(Pos.CENTER_RIGHT);
+ position.setTextAlignment(TextAlignment.RIGHT);
canvasProperty = new SimpleObjectProperty<>(this, "canvas");
canvasProperty.addListener(this::onCanvasSpecified);
- if (referenceSystems == null) {
+ if (choices == null) {
areaOfInterest = null;
} else {
- areaOfInterest = referenceSystems.areaOfInterest;
- final ContextMenu menu = new ContextMenu(referenceSystems.createMenuItems(this::onSelectCRS));
+ areaOfInterest = choices.areaOfInterest;
+ final ContextMenu menu = new ContextMenu(choices.createMenuItems(this::onSelectCRS));
view.setOnMousePressed((MouseEvent event) -> {
if (event.isSecondaryButtonDown()) {
menu.show((HBox) event.getSource(), event.getScreenX(), event.getScreenY());
@@ -292,9 +322,9 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
}
/**
- * Sets the canvas that this status bar is tracking.
- * This method registers all necessary listeners.
- * A value of {@code null} unregisters all listeners.
+ * Sets the canvas that this status bar is tracking. After this method has been invoked,
+ * this {@code StatusBar} will show coordinates (usually geographic or projected) below
+ * mouse cursor when the mouse is over that canvas.
*
* @param canvas the canvas to track, or {@code null} if none.
*
@@ -305,7 +335,10 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
}
/**
- * Invoked when a new value is set on {@link #canvasProperty}.
+ * Invoked when a new value is set on {@link #canvasProperty}. Previous listeners (if
any) are removed
+ * but new mouse listeners may not be added immediately. Instead if the canvas seems
uninitialized, we
+ * will wait for the first call to {@link #apply(GridGeometry)} before to add the listener.
We do that
+ * for avoiding to show irrelevant coordinate values.
*/
private void onCanvasSpecified(final ObservableValue<? extends MapCanvas> property,
final MapCanvas previous, final MapCanvas value)
@@ -316,26 +349,55 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
previous.floatingPane.removeEventHandler(MouseEvent.MOUSE_MOVED, this);
previous.renderingProperty().removeListener(renderingListener);
renderingListener = null;
+ isMouseListenerRegistered = false;
}
if (value != null) {
+ value.renderingProperty().addListener(renderingListener = new RenderingListener());
+ }
+ position.setVisible(false);
+ registerMouseListeners(value);
+ try {
+ apply(value != null ? value.getGridGeometry() : null);
+ } catch (RenderException e) {
+ setErrorMessage(null, e);
+ }
+ }
+
+ /**
+ * Registers mouse listeners for the given canvas if not null. It is caller responsibility
to invoke
+ * this method only if {@link #isMouseListenerRegistered} is {@code false} (this method
does not verify).
+ *
+ * @see #apply(GridGeometry)
+ */
+ private void registerMouseListeners(final MapCanvas value) {
+ /*
+ * The canvas "objective to CRS" is null only for unitialized canvas.
+ * After the canvas has been initialized, it can not be null anymore.
+ * We use that for deciding if listener registration should be delayed.
+ */
+ if (value != null && value.getObjectiveCRS() != null) {
+ // Set first for avoiding duplicated registrations if an exception happen.
+ isMouseListenerRegistered = true;
value.floatingPane.addEventHandler(MouseEvent.MOUSE_ENTERED, this);
value.floatingPane.addEventHandler(MouseEvent.MOUSE_EXITED, this);
value.floatingPane.addEventHandler(MouseEvent.MOUSE_MOVED, this);
- value.renderingProperty().addListener(renderingListener = new RenderingListener());
}
}
/**
* Listener notified when {@link MapCanvas} completed its rendering. This listener sets
* {@link StatusBar#localToObjectiveCRS} to the inverse of {@link MapCanvas#objectiveToDisplay}.
+ * It assumes that even if the JavaFX local coordinates and {@link #localToFormatCRS}
transform
+ * changed, the "real world" coordinates under the mouse cursor is still the same. This
assumption
+ * should be true if this listener is notified as a result of zoom, translation or rotation
events.
*/
private final class RenderingListener implements ChangeListener<Boolean> {
@Override public void changed(final ObservableValue<? extends Boolean> property,
final Boolean previous, final Boolean value)
{
- progress.setVisible(value);
if (!value) try {
- applyCanvasGeometry(getCanvas().getGridGeometry());
+ apply(getCanvas().getGridGeometry());
+ reformat();
} catch (RenderException e) {
setErrorMessage(null, e);
}
@@ -343,13 +405,13 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
}
/**
- * Configures this status bar for showing coordinates in the CRS and resolution given
by the specified
- * grid geometry. The geometry properties are applied as below:
+ * Configures this status bar for showing coordinates in the CRS and with the resolution
given
+ * by the specified grid geometry. The geometry properties are applied as below:
*
* <ul>
* <li>{@link GridGeometry#getCoordinateReferenceSystem()} defines the CRS of
the coordinates to format.</li>
* <li>{@link GridGeometry#getGridToCRS(PixelInCell) GridGeometry.getGridToCRS(PixelInCell.CELL_CENTER)}
- * defines the conversion from coordinate values locale to the canvas to coordinate
values in the CRS
+ * defines the conversion from coordinate values local to the canvas to coordinate
values in the CRS
* (the {@linkplain #getLocalToObjectiveCRS() local to objective CRS} conversion).</li>
* <li>{@link GridGeometry#getExtent()} provides the view size in pixels, used
for estimating a resolution.</li>
* <li>{@link GridGeometry#getResolution(boolean)} is also used for estimating
a resolution.</li>
@@ -359,8 +421,25 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
* after this method call with {@link #setLocalToObjectiveCRS(MathTransform)}.
*
* @param geometry geometry of the coverage shown in {@link MapCanvas}, or {@code null}.
+ *
+ * @see MapCanvas#getGridGeometry()
*/
public void applyCanvasGeometry(final GridGeometry geometry) {
+ position.setVisible(false);
+ apply(geometry);
+ }
+
+ /**
+ * Implementation of {@link #applyCanvasGeometry(GridGeometry)} without changing {@link
#position} visibility state.
+ * Invoking this method usually invalidate the coordinates shown in this status bar.
The new coordinates can not be
+ * easily recomputed because the {@link #lastX} and {@link #lastY} values may not be
valid anymore, as a result of
+ * possible changes in JavaFX local coordinate system. Consequently the coordinates should
be temporarily hidden
+ * until a new {@link MouseEvent} gives us the new local coordinates, unless this method
is invoked in a context
+ * where we know that the "real world" coordinates should be the same even if local coordinates
changed.
+ *
+ * @param geometry geometry of the coverage shown in {@link MapCanvas}, or {@code null}.
+ */
+ private void apply(final GridGeometry geometry) {
/*
* Compute values in local variables without modifying `StatusBar` fields for now.
* The fields will be updated only after we know that this operation is successful.
@@ -409,10 +488,15 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
}
}
}
+ final boolean sameCRS = Utilities.equalsIgnoreMetadata(objectiveCRS, crs);
/*
- * Remaining code should not fail, so we can modify `StatusBar` fields.
- * Prepare objects to be reused for each coordinate transformation.
- * Configure the `CoordinateFormat` with the CRS.
+ * Remaining code should not fail, so we can start modifying the `StatusBar` fields.
+ * The buffers for source and target coordinates are recreated because the number
of
+ * dimensions may have changed. The `lastX` and `lastY` coordinates are local to
the
+ * JavaFX view and considered invalid because they depend on the transforms applied
+ * on JavaFX node, which may have changed together with `localToObjectiveCRS` change.
+ * So we can not use those values for updating the coordinates shown in status bar.
+ * Instead we will wait for the next mouse event to provide new local coordinates.
*/
if (localToCRS != null) {
sourceCoordinates = new double[Math.max(localToCRS.getSourceDimensions(), BIDIMENSIONAL)];
@@ -423,40 +507,52 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
sourceCoordinates = targetCoordinates.coordinates; // Okay to share array
if same dimension.
}
objectiveCRS = crs;
- localToObjectiveCRS = localToTargetCRS = localToCRS;
+ localToObjectiveCRS = localToCRS;
+ localToFormatCRS = localToCRS; // May be updated again
below.
inflatePrecisions = inflate;
precisions = null;
- format.setDefaultCRS(crs);
+ lastX = lastY = Double.NaN; // Not valid anymove
— see above block comment.
+ CoordinateReferenceSystem restore = null;
+ if (sameCRS) {
+ if (objectiveToFormatCRS != null) {
+ localToFormatCRS = MathTransforms.concatenate(localToCRS, objectiveToFormatCRS.getMathTransform());
+ }
+ // Keep the format CRS unchanged since we made `localToFormatCRS` consistent
with its value.
+ } else {
+ objectiveToFormatCRS = null;
+ restore = format.getDefaultCRS(); // CRS to restore in a background
thread.
+ format.setDefaultCRS(crs); // Should be invoked before to set
precision.
+ }
format.setGroundPrecision(Quantities.create(resolution, unit));
- refresh();
- }
-
- /**
- * Invoked when the user selects a new reference system for the coordinates to show in
status bar.
- *
- * @param property the {@link org.apache.sis.gui.referencing.MenuSync} property.
- * @param oldValue the old reference system, or {@code null} if none.
- * @param newValue the CRS to use for formatting coordinates in this status bar.
- */
- private void onSelectCRS(ObservableValue<? extends ReferenceSystem> property,
- ReferenceSystem oldValue, ReferenceSystem newValue)
- {
- setTargetCRS(newValue instanceof CoordinateReferenceSystem ? (CoordinateReferenceSystem)
newValue : null);
+ if (ReferencingUtilities.getDimension(restore) == localToFormatCRS.getTargetDimensions())
{
+ setFormatCRS(restore);
+ }
+ /*
+ * If this is the first time that this method is invoked after `setCanvas(MapCanvas)`,
+ * the listeners are not yet registered and should be added now. Listeners registration
+ * was delayed because if they were added on uninitialized canvas, they would have
show
+ * irrelevant coordinates.
+ */
+ if (geometry != null && !isMouseListenerRegistered) {
+ registerMouseListeners(canvasProperty.getValue());
+ }
}
/**
* Sets the coordinate reference system of the coordinates shown in this status bar.
* The change may not appear immediately after method return; this method may use a
- * background thread for computing the coordinate operation.
+ * background thread for computing the coordinate operation. That task may be long
+ * the first time that it is executed, but should be fast on subsequent invocations.
+ *
+ * @param crs the new CRS, or {@code null} for {@link #objectiveCRS}.
*/
- private void setTargetCRS(final CoordinateReferenceSystem crs) {
- if (objectiveCRS != null && objectiveCRS != crs) {
- coordinates.setTextFill(Styles.OUTDATED_TEXT);
+ private void setFormatCRS(final CoordinateReferenceSystem crs) {
+ if (crs != null && objectiveCRS != null && objectiveCRS != crs) {
+ position.setTextFill(Styles.OUTDATED_TEXT);
final Envelope aoi = (areaOfInterest != null) ? areaOfInterest.get() : null;
BackgroundThreads.execute(new Task<MathTransform>() {
/**
- * The operation used for computing the transform to target CRS.
- * This is used for configuring format with positional accuracy.
+ * The new {@link StatusBar#objectiveToFormatCRS} value if successful.
*/
private CoordinateOperation operation;
@@ -473,48 +569,88 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
} catch (TransformException e) {
bbox = null;
Logging.recoverableException(Logging.getLogger(Modules.APPLICATION),
- StatusBar.class, "setTargetCRS", e);
+ StatusBar.class, "setFormatCRS", e);
}
operation = CRS.findOperation(objectiveCRS, crs, bbox);
return MathTransforms.concatenate(localToObjectiveCRS, operation.getMathTransform());
}
/**
- * Invoked in JavaFX thread on success. The {@link StatusBar#localToTargetCRS}
transform
+ * Invoked in JavaFX thread on success. The {@link StatusBar#localToFormatCRS}
transform
* is set to the transform that we computed in background and the {@link
CoordinateFormat}
* is configured with auxiliary information such as positional accuracy.
*/
@Override protected void succeeded() {
final CoordinateReferenceSystem targetCRS = operation.getTargetCRS();
- format.setDefaultCRS(targetCRS != null ? targetCRS : crs);
- localToTargetCRS = getValue();
-// TODO: CRS.getLinearAccuracy(op);
- coordinates.setTextFill(Styles.NORMAL_TEXT);
- refresh();
+ applyFormatCRS(targetCRS != null ? targetCRS : crs, operation, getValue());
}
/**
- * Invoked in JavaFX thread on failure. The previous CRS is keep unchanged
but
+ * Invoked in JavaFX thread on failure. The previous CRS is kept unchanged
but
* the coordinates will appear in red for telling user that there is a problem.
*/
@Override protected void failed() {
- setErrorMessage(null, getException());
- resetTargetCRS(Styles.ERROR_TEXT);
+ final Locale locale = format.getLocale(Locale.Category.DISPLAY);
+ setErrorMessage(Resources.forLocale(locale).getString(Resources.Keys.CanNotUseRefSys_1,
+ IdentifiedObjects.getDisplayName(crs, locale)), getException());
+ resetFormatCRS(Styles.ERROR_TEXT);
}
});
} else {
- resetTargetCRS(Styles.NORMAL_TEXT);
+ position.setMinWidth(0);
+ resetFormatCRS(Styles.NORMAL_TEXT);
+ }
+ }
+
+ /**
+ * Invoked after the background thread computed the new coordinate operation.
+ * This method rewrites the coordinates on the assumption that {@link #lastX}
+ * and {@link #lastY} are still valid. This assumption should be correct when
+ * only the format CRS has been updated and not {@link #localToObjectiveCRS}.
+ *
+ * @param crs the new CRS. Should not be {@code null}.
+ * @param operation the new value to assign to {@link #objectiveToFormatCRS}
+ * @param complete the concatenation of {@link #localToObjectiveCRS} with {@code operation}.
+ */
+ private void applyFormatCRS(final CoordinateReferenceSystem crs,
+ final CoordinateOperation operation, final MathTransform complete)
+ {
+ format.setDefaultCRS(crs);
+ objectiveToFormatCRS = operation;
+ localToFormatCRS = complete;
+// TODO: CRS.getLinearAccuracy(op);
+ position.setTextFill(Styles.NORMAL_TEXT);
+ position.setMinWidth(0);
+ setErrorMessage(null, null);
+ reformat();
+ }
+
+ /**
+ * Reformats the coordinates shown in {@link #position} using current {@link #lastX}
and {@link #lastY} values.
+ * This method should be invoked only when the caller knows that those values are still
valid. Note that those
+ * values may be invalid if {@link javafx.scene.Node#getTransforms()} changed even if
{@link #objectiveCRS} is
+ * the same.
+ */
+ private void reformat() {
+ if (position.isVisible()) {
+ final double x = lastX;
+ final double y = lastY;
+ lastX = lastY = Double.NaN;
+ if (!Double.isNaN(x) && !Double.isNaN(y)) {
+ setLocalCoordinates(x, y);
+ }
}
}
/**
- * Resets {@link #localToTargetCRS} to its default value. This is invoked either when
the specified
+ * Resets {@link #localToFormatCRS} to its default value. This is invoked either when
the
* target CRS is {@link #objectiveCRS}, or when an attempt to use another CRS failed.
*/
- private void resetTargetCRS(final Color textFill) {
- localToTargetCRS = localToObjectiveCRS;
+ private void resetFormatCRS(final Color textFill) {
+ objectiveToFormatCRS = null;
+ localToFormatCRS = localToObjectiveCRS;
format.setDefaultCRS(objectiveCRS);
- coordinates.setTextFill(textFill);
+ position.setTextFill(textFill);
}
/**
@@ -578,7 +714,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
actual = conversion.getTargetDimensions();
if (expected == actual) {
localToObjectiveCRS = conversion;
- setTargetCRS(format.getDefaultCRS()); // Recompute
`localToTargetCRS`.
+ setFormatCRS(format.getDefaultCRS()); // Recompute
`localToFormatCRS`.
return;
}
}
@@ -593,25 +729,13 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
* @return the local coordinates currently shown in the status bar.
*/
public Optional<Point2D> getLocalCoordinates() {
- if (coordinates.isVisible() && !Double.isNaN(lastX) && !Double.isNaN(lastY))
{
+ if (position.isVisible() && !Double.isNaN(lastX) && !Double.isNaN(lastY))
{
return Optional.of(new Point2D(lastX, lastY));
}
return Optional.empty();
}
/**
- * Rewrites the coordinates. This method is invoked after a change of coordinate reference
system.
- */
- private void refresh() {
- final double x = lastX;
- final double y = lastY;
- lastX = lastY = Double.NaN;
- if (!Double.isNaN(x) && !Double.isNaN(y)) {
- setLocalCoordinates(x, y);
- }
- }
-
- /**
* Converts and formats the given pixel coordinates. Those coordinates will be automatically
* converted to geographic or projected coordinates if a "local to CRS" conversion is
available.
*
@@ -628,7 +752,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
try {
Matrix derivative;
try {
- derivative = MathTransforms.derivativeAndTransform(localToTargetCRS,
+ derivative = MathTransforms.derivativeAndTransform(localToFormatCRS,
sourceCoordinates, 0, targetCoordinates.coordinates, 0);
} catch (TransformException ignore) {
/*
@@ -636,7 +760,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
* derivative calculation. Try again without derivative (the precision
will be set
* to the default resolution computed in `setCanvasGeometry(…)`).
*/
- localToTargetCRS.transform(sourceCoordinates, 0, targetCoordinates.coordinates,
0, 1);
+ localToFormatCRS.transform(sourceCoordinates, 0, targetCoordinates.coordinates,
0, 1);
derivative = null;
}
if (derivative == null) {
@@ -676,8 +800,16 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
text = Classes.getShortClassName(cause);
}
}
- coordinates.setText(text);
- coordinates.setVisible(true);
+ position.setText(text);
+ position.setVisible(true);
+ /*
+ * Make sure that there is enough space for keeping the coordinates always visible.
+ * This is the needed if there is an error message on the left which may be long.
+ */
+ final double width = Math.min(view.getWidth() / 2, Math.ceil(position.prefWidth(position.getHeight())));
+ if (width > position.getMinWidth()) {
+ position.setMinWidth(width);
+ }
}
}
@@ -706,7 +838,20 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
return;
}
}
- coordinates.setVisible(false);
+ position.setVisible(false);
+ }
+
+ /**
+ * Invoked when the user selects a new reference system for the coordinates to show in
status bar.
+ *
+ * @param property the {@link org.apache.sis.gui.referencing.MenuSync} property.
+ * @param oldValue the old reference system, or {@code null} if none.
+ * @param newValue the CRS to use for formatting coordinates in this status bar.
+ */
+ private void onSelectCRS(ObservableValue<? extends ReferenceSystem> property,
+ ReferenceSystem oldValue, ReferenceSystem newValue)
+ {
+ setFormatCRS(newValue instanceof CoordinateReferenceSystem ? (CoordinateReferenceSystem)
newValue : null);
}
/**
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 bb0c425..ec356e9 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;
/**
+ * Can not use the “{0}” reference system.
+ */
+ public static final short CanNotUseRefSys_1 = 58;
+
+ /**
* Cell geometry
*/
public static final short CellGeometry = 15;
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 4ead37e..9761bf2 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.
+CanNotUseRefSys_1 = Can not use the \u201c{0}\u201d reference system.
CellGeometry = Cell geometry
Close = Close
Copy = Copy
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 9659427..f73612f 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.
+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
Copy = Copier
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
b/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
index db94513..e3ae13d 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
@@ -681,6 +681,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
*
* @see DecimalFormat#setMaximumFractionDigits(int)
* @see AngleFormat#setPrecision(double, boolean)
+ * @see Quantities#create(double, Unit)
*
* @since 1.1
*/
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
index 15f023c..6a65c50 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
@@ -149,7 +149,7 @@ public final class PolarStereographicA extends AbstractStereographic {
/**
* False Easting and false Northing value used in Universal Polar Stereographic (UPS)
projections.
- * Represented as an integer for the convenience of Military Reference Grid System (MGRS)
or other
+ * Represented as an integer for the convenience of Military Grid Reference System (MGRS)
or other
* grid systems.
*/
public static final int UPS_SHIFT = 2000000;
|