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 <[email protected]>
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;