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 da1e14d Provides more guidance to user when selecting a CRS by warning when the
CRS domain of validity does not intersect the area of interest.
da1e14d is described below
commit da1e14d8bea23e3ef16cc7cf49e4a970d0c415a7
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Apr 15 18:40:45 2020 +0200
Provides more guidance to user when selecting a CRS by warning when the CRS domain of
validity does not intersect the area of interest.
---
.../java/org/apache/sis/gui/coverage/Controls.java | 30 +--
.../apache/sis/gui/coverage/CoverageControls.java | 6 +-
.../apache/sis/gui/coverage/CoverageExplorer.java | 8 +-
.../org/apache/sis/gui/coverage/GridControls.java | 4 +-
.../org/apache/sis/gui/dataset/ResourceTree.java | 2 +-
.../java/org/apache/sis/gui/map/StatusBar.java | 2 +-
.../org/apache/sis/gui/referencing/CRSChooser.java | 260 ++++++++++++++++++---
.../org/apache/sis/internal/gui/Resources.java | 5 +
.../apache/sis/internal/gui/Resources.properties | 1 +
.../sis/internal/gui/Resources_fr.properties | 1 +
.../java/org/apache/sis/internal/gui/Styles.java | 85 ++++++-
.../apache/sis/gui/referencing/CRSChooserApp.java | 92 ++++++++
.../apache/sis/metadata/iso/extent/Extents.java | 17 +-
.../org/apache/sis/util/resources/Vocabulary.java | 20 ++
.../sis/util/resources/Vocabulary.properties | 4 +
.../sis/util/resources/Vocabulary_fr.properties | 4 +
16 files changed, 460 insertions(+), 81 deletions(-)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
index 7636376..39e6ee3 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java
@@ -17,16 +17,13 @@
package org.apache.sis.gui.coverage;
import javafx.geometry.Insets;
-import javafx.scene.Node;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
-import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
-import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import org.apache.sis.coverage.grid.GridCoverage;
import org.apache.sis.internal.gui.Styles;
@@ -49,11 +46,6 @@ abstract class Controls {
static final Insets CAPTION_MARGIN = new Insets(12, 0, 9, 0);
/**
- * Space between a group of controls and the border encompassing the group.
- */
- private static final Insets GROUP_INSETS = new Insets(12);
-
- /**
* The border to use for grouping some controls together.
*/
private static final Border GROUP_BORDER = new Border(new BorderStroke(
@@ -80,25 +72,9 @@ abstract class Controls {
* @return a pane with each (label, control) pair on a row.
*/
static GridPane createControlGrid(final Label... controls) {
- final GridPane gp = new GridPane();
- final ColumnConstraints controlColumn = new ColumnConstraints();
- controlColumn.setHgrow(Priority.ALWAYS);
- gp.getColumnConstraints().setAll(new ColumnConstraints(), controlColumn);
- gp.setPadding(GROUP_INSETS);
- gp.setBorder(GROUP_BORDER);
- gp.setVgap(9);
- gp.setHgap(9);
- int row = 0;
- for (final Label label : controls) {
- if (label != null) {
- final Node control = label.getLabelFor();
- GridPane.setConstraints(label, 0, row);
- GridPane.setConstraints(control, 1, row);
- gp.getChildren().addAll(label, control);
- row++;
- }
- }
+ final GridPane gp = Styles.createControlGrid(controls);
Styles.setAllRowToSameHeight(gp);
+ gp.setBorder(GROUP_BORDER);
return gp;
}
@@ -135,7 +111,7 @@ abstract class Controls {
/**
* Invoked after {@link CoverageExplorer#setCoverage(ImageRequest)} for updating the
table of
- * sample dimensions with information become available. This method is invoked in JavaFX
thread.
+ * sample dimensions when information become available. This method is invoked in JavaFX
thread.
*
* @param data the new coverage, or {@code null} if none.
*/
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
index e824a83..f38ddc7 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageControls.java
@@ -59,7 +59,7 @@ final class CoverageControls extends Controls {
/**
* Creates a new set of coverage controls.
*
- * @param vocabulary localized set of words, provided in argument before often known
by the caller.
+ * @param vocabulary localized set of words, provided in argument because often known
by the caller.
* @param coverage property containing the coverage to show.
*/
CoverageControls(final Vocabulary vocabulary, final ObjectProperty<GridCoverage>
coverage) {
@@ -89,7 +89,7 @@ final class CoverageControls extends Controls {
* Put all sections together and have the first one expanded by default.
*/
controls = new Accordion(
- new TitledPane(vocabulary.getString(Vocabulary.Keys.Display), displayPane)
+ new TitledPane(vocabulary.getString(Vocabulary.Keys.Display), displayPane)
// TODO: more controls to be added in a future version.
);
controls.setExpandedPane(controls.getPanes().get(0));
@@ -109,7 +109,7 @@ final class CoverageControls extends Controls {
/**
* Invoked after {@link CoverageExplorer#setCoverage(ImageRequest)} for updating the
table of
- * sample dimensions with information become available. This method is invoked in JavaFX
thread.
+ * sample dimensions when information become available. This method is invoked in JavaFX
thread.
*
* @param data the new coverage, or {@code null} if none.
*/
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 b57e342..194eab8 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
@@ -28,7 +28,6 @@ import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import org.apache.sis.coverage.grid.GridCoverage;
import org.apache.sis.internal.gui.Resources;
-import org.apache.sis.internal.gui.Styles;
import org.apache.sis.internal.gui.ToolbarButton;
import org.apache.sis.internal.gui.NonNullObjectProperty;
import org.apache.sis.util.resources.Vocabulary;
@@ -50,6 +49,11 @@ import org.apache.sis.gui.Widget;
*/
public class CoverageExplorer extends Widget {
/**
+ * Initial position of divider in split panes.
+ */
+ private static final double INITIAL_SPLIT = 200;
+
+ /**
* Type of view shown in the explorer.
* It may be either an image or a table of numerical values.
*/
@@ -170,7 +174,7 @@ public class CoverageExplorer extends Widget {
final Controls c = views[0]; // First View enumeration
is default value.
group.selectToggle(group.getToggles().get(0));
content = new SplitPane(c.controls(), c.view());
- content.setDividerPosition(0, Styles.INITIAL_SPLIT);
+ content.setDividerPosition(0, INITIAL_SPLIT);
ToolbarButton.insert(content, buttons);
}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridControls.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridControls.java
index 75e8dda..a4a477b 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridControls.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridControls.java
@@ -60,7 +60,7 @@ final class GridControls extends Controls {
/**
* Creates a new set of grid controls.
*
- * @param vocabulary localized set of words, provided in argument before often known
by the caller.
+ * @param vocabulary localized set of words, provided in argument because often known
by the caller.
*/
GridControls(final Vocabulary vocabulary) {
view = new GridView();
@@ -130,7 +130,7 @@ final class GridControls extends Controls {
/**
* Invoked after {@link CoverageExplorer#setCoverage(ImageRequest)} for updating the
table of
- * sample dimensions with information become available. This method is invoked in JavaFX
thread.
+ * sample dimensions when information become available. This method is invoked in JavaFX
thread.
*
* @param data the new coverage, or {@code null} if none.
*/
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
index 8be8853..a32df07 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
@@ -454,7 +454,7 @@ public class ResourceTree extends TreeView<Resource> {
if (tree != null) {
final Throwable failure = ((Unloadable) resource).failure;
text = tree.string(failure);
- more = new Button(Styles.ERROR_DETAILS);
+ more = new Button(Styles.ERROR_DETAILS_ICON);
more.setOnAction((e) -> ExceptionReporter.show(
tree.localized.getString(Resources.Keys.ErrorDetails),
tree.localized.getString(Resources.Keys.CanNotReadResource),
failure));
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 baf82c0..166ee20 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
@@ -550,7 +550,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
}
}
final String alert = text;
- more = new Button(Styles.ERROR_DETAILS);
+ more = new Button(Styles.ERROR_DETAILS_ICON);
more.setOnAction((e) -> ExceptionReporter.show(
Resources.forLocale(locale).getString(Resources.Keys.ErrorDetails), alert,
details));
}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
index c21c3c4..e606fc7 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSChooser.java
@@ -20,6 +20,7 @@ import java.util.Locale;
import java.util.Optional;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
+import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@@ -33,23 +34,51 @@ import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
+import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
+import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Window;
-import org.apache.sis.internal.gui.ExceptionReporter;
+import javafx.util.Duration;
+import org.opengis.geometry.Envelope;
import org.opengis.util.FactoryException;
+import org.opengis.metadata.extent.Extent;
+import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.opengis.referencing.crs.GeodeticCRS;
+import org.opengis.referencing.crs.GeographicCRS;
+import org.opengis.referencing.crs.GeocentricCRS;
+import org.opengis.referencing.crs.ProjectedCRS;
+import org.opengis.referencing.crs.VerticalCRS;
+import org.opengis.referencing.crs.TemporalCRS;
+import org.opengis.referencing.crs.CompoundCRS;
+import org.opengis.referencing.crs.EngineeringCRS;
+import org.opengis.referencing.crs.GeneralDerivedCRS;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.Conversion;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.TransformException;
+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.IdentityValueFactory;
import org.apache.sis.internal.gui.Resources;
+import org.apache.sis.internal.gui.Styles;
+import org.apache.sis.geometry.ImmutableEnvelope;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.metadata.iso.extent.Extents;
import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.Exceptions;
import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.IdentifiedObjects;
/**
* A list of Coordinate Reference Systems (CRS) from which the user can select.
+ * The CRS choices is built in a background thread from a specified {@link CRSAuthorityFactory}.
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
@@ -88,18 +117,51 @@ public class CRSChooser extends Dialog<CoordinateReferenceSystem>
{
private final TableView<Code> table;
/**
+ * A panel showing the type and domain of validity of selected CRS.
+ */
+ private final GridPane summary;
+
+ /**
+ * The label where to write the CRS type and domain of validity.
+ */
+ private final Label type, domain;
+
+ /**
+ * The area of interest, or {@code null} if none.
+ * Axis order is (<var>longitude</var>, <var>latitude</var>).
+ */
+ private final ImmutableEnvelope areaOfInterest;
+
+ /**
* The pane showing the CRS in Well Known Text format.
* Created when first needed.
*/
private WKTPane wktPane;
/**
+ * Creates a chooser proposing all coordinate reference systems from the default factory.
+ */
+ public CRSChooser() {
+ this(null, null);
+ }
+
+ /**
* Creates a chooser proposing all coordinate reference systems from the given factory.
*
- * @param factory the factory to use for creating coordinate reference systems, or
{@code null}
- * for the {@linkplain CRS#getAuthorityFactory(String) Apache SIS default
factory}.
+ * @param factory the factory to use for creating coordinate reference systems,
or {@code null}
+ * for the {@linkplain CRS#getAuthorityFactory(String) Apache
SIS default factory}.
+ * @param areaOfInterest geographic area for which to choose a CRS, or {@code null}
if no restriction.
*/
- public CRSChooser(final CRSAuthorityFactory factory) {
+ public CRSChooser(final CRSAuthorityFactory factory, Envelope areaOfInterest) {
+ if (areaOfInterest == null) {
+ this.areaOfInterest = null;
+ } else try {
+ final DefaultGeographicBoundingBox bbox = new DefaultGeographicBoundingBox();
+ bbox.setBounds(areaOfInterest);
+ this.areaOfInterest = new ImmutableEnvelope(bbox);
+ } catch (TransformException e) {
+ throw new IllegalArgumentException(e);
+ }
final Locale locale = Locale.getDefault();
final Resources i18n = Resources.forLocale(locale);
final Vocabulary vocabulary = Vocabulary.getResources(locale);
@@ -124,34 +186,61 @@ public class CRSChooser extends Dialog<CoordinateReferenceSystem>
{
clock.setFont(Font.font(30));
table.setPlaceholder(clock);
/*
- * Text field for filtering the list of CRS codes using keywords.
- * The filtering is applied when the "Enter" key is pressed in that field.
- */
- searchField = new TextField();
- searchField.setOnAction((ActionEvent event) -> {
- CodeFilter.apply(table, searchField.getText());
- });
- HBox.setHgrow(searchField, Priority.ALWAYS);
- final Label label = new Label(i18n.getString(Resources.Keys.Filter));
- label.setLabelFor(searchField);
- /*
- * Button for showing the CRS description in Well Known Text (WKT) format.
- * The button is enabled only if a row in the table is selected.
+ * Controls on the top of CRS list. This is either a filter or a combox box
+ * giving WKT format choices, depending on what is currently shown.
*/
- final ToggleButton info = new ToggleButton("\uD83D\uDDB9"); // Unicode U+1F5B9: Document
With Text.
- table.getSelectionModel().selectedItemProperty().addListener((e,o,n) -> info.setDisable(n
== null));
- info.setOnAction((ActionEvent event) -> {
- setTools(info.isSelected());
- });
- info.setDisable(true);
+ {// block for keeping variable locales.
+ /*
+ * Text field for filtering the list of CRS codes using keywords.
+ * The filtering is applied when the "Enter" key is pressed in that field.
+ */
+ searchField = new TextField();
+ searchField.setOnAction((ActionEvent event) -> {
+ CodeFilter.apply(table, searchField.getText());
+ });
+ HBox.setHgrow(searchField, Priority.ALWAYS);
+ final Label label = new Label(i18n.getString(Resources.Keys.Filter));
+ label.setLabelFor(searchField);
+ /*
+ * Button for showing the CRS description in Well Known Text (WKT) format.
+ * The button is enabled only if a row in the table is selected.
+ */
+ final ToggleButton infoButton = new ToggleButton("\uD83D\uDDB9"); // Unicode
U+1F5B9: Document With Text.
+ table.getSelectionModel().selectedItemProperty().addListener((e,o,n) -> {
+ infoButton.setDisable(n == null);
+ updateSummary(n);
+ });
+ infoButton.setOnAction((ActionEvent event) -> {
+ setTools(infoButton.isSelected());
+ });
+ infoButton.setDisable(true);
+ /*
+ * Creates the tools bar to show above the table of codes.
+ * The tools bar contains the search field and the button for showing the WKT.
+ */
+ tools = new HBox(label, searchField, infoButton);
+ tools.setSpacing(9);
+ tools.setAlignment(Pos.BASELINE_LEFT);
+ BorderPane.setMargin(tools, new Insets(0, 0, 9, 0));
+ }
/*
- * Creates the tools bar to show above the table of codes.
- * The tools bar contains the search field and the button for showing the WKT.
+ * Details about the selected items. This is a form with the following lines:
+ * - Type (e.g. "Projected — Transverse Mercator").
+ * - Domain of validity.
*/
- tools = new HBox(label, searchField, info);
- tools.setSpacing(9);
- tools.setAlignment(Pos.BASELINE_LEFT);
- BorderPane.setMargin(tools, new Insets(0, 0, 9, 0));
+ {// block for keeping variable locales.
+ final Label lt = new Label(vocabulary.getLabel(Vocabulary.Keys.Type));
+ final Label ld = new Label(vocabulary.getLabel(Vocabulary.Keys.Domain));
+ lt.setLabelFor(type = new Label());
+ ld.setLabelFor(domain = new Label());
+ summary = Styles.createControlGrid(lt, ld);
+ final Tooltip tp = new Tooltip();
+ tp.setShowDelay(Duration.seconds(0.5));
+ tp.setShowDuration(Duration.minutes(1));
+ tp.maxWidthProperty().bind(summary.widthProperty());
+ tp.setWrapText(true);
+ domain.setTooltip(tp);
+ }
/*
* Layout table and tools bar inside the dialog content.
* Configure the dialog buttons.
@@ -160,6 +249,7 @@ public class CRSChooser extends Dialog<CoordinateReferenceSystem>
{
content = new BorderPane();
content.setCenter(table);
content.setTop(tools);
+ content.setBottom(summary);
pane.setContent(content);
pane.getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
setTitle(i18n.getString(Resources.Keys.SelectCRS));
@@ -168,15 +258,16 @@ public class CRSChooser extends Dialog<CoordinateReferenceSystem>
{
}
/**
- * Sets the tools bar and content to controls for the given mode.
+ * Sets the tools bar and its content to controls for the given mode.
* If {@code wkt} is {@code true}, then this method set the controls for showing the
WKT.
* If {@code wkt} is {@code false} (the default), then this method set the controls to
the table of CRS codes.
*/
private void setTools(final boolean wkt) {
final Locale locale = getAuthorityCodes().locale;
final short labelText;
- final Control control;
- final Control main;
+ final Control control;
+ final Control main;
+ final GridPane info;
if (wkt) {
if (wktPane == null) {
wktPane = new WKTPane(locale);
@@ -185,10 +276,12 @@ public class CRSChooser extends Dialog<CoordinateReferenceSystem>
{
labelText = Resources.Keys.Format;
control = wktPane.convention;
main = wktPane.text;
+ info = null;
} else {
labelText = Resources.Keys.Filter;
control = searchField;
main = table;
+ info = summary;
}
final ObservableList<Node> children = tools.getChildren();
final Label label = (Label) children.get(0);
@@ -197,6 +290,7 @@ public class CRSChooser extends Dialog<CoordinateReferenceSystem>
{
label.setLabelFor(control);
children.set(1, control);
content.setCenter(main);
+ content.setBottom(info);
}
/**
@@ -212,6 +306,108 @@ public class CRSChooser extends Dialog<CoordinateReferenceSystem>
{
}
/**
+ * Invoked when a new CRS is selected in the table. This method updates
+ * the {@link #type} and {@link #domain} fields with CRS information.
+ */
+ private void updateSummary(final Code selected) {
+ final AuthorityCodes source = getAuthorityCodes();
+ final String code = selected.code;
+ BackgroundThreads.execute(new Task<CoordinateReferenceSystem>() {
+ /** Invoked in background thread for fetching the CRS from an authority code.
*/
+ @Override protected CoordinateReferenceSystem call() throws FactoryException
{
+ return source.getFactory().createCoordinateReferenceSystem(code);
+ }
+
+ /** Invoked in JavaFX thread on success. */
+ @Override protected void succeeded() {
+ final CoordinateReferenceSystem crs = getValue();
+ type.setTextFill(Styles.NORMAL_TEXT);
+ type.setText(typeOf(crs, source.locale));
+ setDomainOfValidity(crs.getDomainOfValidity(), source.locale);
+ }
+
+ /** Invoked in JavaFX thread on cancellation. */
+ @Override protected void cancelled() {
+ type.setText(null);
+ domain.setText(null);
+ }
+
+ /** Invoked in JavaFX thread on failure. */
+ @Override protected void failed() {
+ cancelled();
+ type.setTextFill(Styles.ERROR_TEXT);
+ type.setText(Exceptions.getLocalizedMessage(getException(), source.locale));
+ }
+ });
+ }
+
+ /**
+ * Sets the text that describes the domain of validity.
+ */
+ private void setDomainOfValidity(final Extent domainOfValidity, final Locale locale)
{
+ String text = Extents.getDescription(domainOfValidity, locale);
+ String tip = text;
+ Color color = Styles.NORMAL_TEXT;
+ if (areaOfInterest != null) {
+ final GeographicBoundingBox bbox = Extents.getGeographicBoundingBox(domainOfValidity);
+ if (bbox != null && !areaOfInterest.intersects(new ImmutableEnvelope(bbox)))
{
+ tip = Resources.forLocale(locale).getString(Resources.Keys.DoesNotCoverAOI);
+ text = Styles.WARNING_ICON + " " + (text != null ? text : tip);
+ color = Styles.ERROR_TEXT;
+ }
+ }
+ domain.setTextFill(color);
+ domain.setText(text);
+ domain.getTooltip().setText(tip);
+ }
+
+ /**
+ * Returns the text to show of right of the "type" label.
+ */
+ private static String typeOf(CoordinateReferenceSystem crs, final Locale locale) {
+ while (crs instanceof CompoundCRS) {
+ crs = ((CompoundCRS) crs).getComponents().get(0);
+ }
+ final short key;
+ final int expected;
+ if (crs instanceof GeographicCRS) {key = Vocabulary.Keys.Geographic; expected
= 2;}
+ else if (crs instanceof GeocentricCRS) {key = Vocabulary.Keys.Geocentric; expected
= 3;}
+ else if (crs instanceof GeodeticCRS) {key = Vocabulary.Keys.Geodetic; expected
= 0;}
+ else if (crs instanceof VerticalCRS) {key = Vocabulary.Keys.Vertical; expected
= 1;}
+ else if (crs instanceof TemporalCRS) {key = Vocabulary.Keys.Temporal; expected
= 1;}
+ else if (crs instanceof ProjectedCRS) {key = Vocabulary.Keys.Projected; expected
= 2;}
+ else if (crs instanceof EngineeringCRS) {key = Vocabulary.Keys.Engineering; expected
= 0;}
+ else {
+ key = Vocabulary.Keys.Unknown;
+ expected = 0;
+ }
+ String text = Vocabulary.getResources(locale).getString(key);
+ final int dimension = ReferencingUtilities.getDimension(crs);
+ final boolean addDimension = (dimension != expected && expected != 0);
+ final boolean isProjection = (crs instanceof GeneralDerivedCRS);
+ if (addDimension | isProjection) {
+ final StringBuilder buffer = new StringBuilder(text);
+ if (addDimension) {
+ buffer.append(" (").append(dimension).append("D)");
+ }
+ if (isProjection) {
+ final Conversion conversion = ((GeneralDerivedCRS) crs).getConversionFromBase();
+ if (conversion != null) {
+ final OperationMethod method = conversion.getMethod();
+ if (method != null) {
+ final String name = IdentifiedObjects.getDisplayName(method, locale);
+ if (name != null) {
+ buffer.append(" — ").append(name);
+ }
+ }
+ }
+ }
+ text = buffer.toString();
+ }
+ return text;
+ }
+
+ /**
* Returns the currently selected CRS, or {@code null} if none.
*
* @return the currently selected CRS, or {@code 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 1356c30..27817d3 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
@@ -151,6 +151,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short Display = 41;
/**
+ * Does not cover the area of interest.
+ */
+ public static final short DoesNotCoverAOI = 57;
+
+ /**
* Error closing file
*/
public static final short ErrorClosingFile = 13;
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 dd5714f..bcac615e 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
@@ -39,6 +39,7 @@ Data = Data
Date = Date:
Dimensions = Dimensions:
Display = Display
+DoesNotCoverAOI = Does not cover the area of interest.
ErrorDetails = Details about error
ErrorExportingData = Error exporting data
ErrorOpeningFile = Error opening file
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 c8cd341..37b1099 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
@@ -44,6 +44,7 @@ Data = Donn\u00e9es
Date = Date\u00a0:
Dimensions = Dimensions\u00a0:
Display = Affichage
+DoesNotCoverAOI = Ne couvre pas la r\u00e9gion d\u2019int\u00e9r\u00eat.
ErrorDetails = D\u00e9tails \u00e0 propos de l\u2019erreur
ErrorExportingData = Erreur \u00e0 l\u2019exportation de donn\u00e9es
ErrorOpeningFile = Erreur \u00e0 l\u2019ouverture du fichier
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
index 0cc704a..ab4a0f5 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Styles.java
@@ -19,23 +19,29 @@ package org.apache.sis.internal.gui;
import java.util.Arrays;
import java.io.IOException;
import java.io.InputStream;
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
+import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
+import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.internal.system.Modules;
/**
- * A central place where to store all colors used by SIS application.
- * This provides a single place to revisit if we learn more about how
- * to make those color more dynamic with JavaFX styling.
+ * A central place where to store some appearance choices such as colors used by SIS application.
+ * This provides a single place to revisit if we learn more about how to make those choices
more
+ * configurable with JavaFX styling.
*
- * <p>This class also opportunistically provides a few utility methods
- * related to appearance.</p>
+ * <p>This class also opportunistically provides a few utility methods related to appearance.</p>
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.1
@@ -44,12 +50,6 @@ import org.apache.sis.internal.system.Modules;
*/
public final class Styles {
/**
- * Initial position of divider in split panes. This is used in data windows,
- * but not necessarily in the main window (the resource explorer).
- */
- public static final double INITIAL_SPLIT = 200;
-
- /**
* Approximate size of vertical scroll bar.
*/
public static final int SCROLLBAR_WIDTH = 20;
@@ -107,8 +107,15 @@ public final class Styles {
/**
* The Unicode character to put in a button for requesting more information about an
error.
+ * The symbol is {@value}.
*/
- public static final String ERROR_DETAILS = "ℹ";
+ public static final String ERROR_DETAILS_ICON = "\u2139\uFE0F"; // ℹ
+
+ /**
+ * The Unicode character to put in a label for representing a warning.
+ * The symbol is {@value}.
+ */
+ public static final String WARNING_ICON = "\u26A0\uFE0F"; // ⚠
/**
* Do not allow instantiation of this class.
@@ -142,6 +149,60 @@ public final class Styles {
}
/**
+ * Space between a group of controls and the border encompassing the group.
+ */
+ private static final Insets FORM_INSETS = new Insets(12);
+
+ /**
+ * Creates a grid pane of two columns and an arbitrary number of rows.
+ * Each row contains a (label, control) pair, with all growths and shrinks applied on
the second column.
+ * The controls must be associated to the given labels by {@link Label#getLabelFor()}.
+ * If a label is {@code null}, then no row is created for that label.
+ *
+ * @param controls (label, control) pairs to layout in rows.
+ * @return a pane with each (label, control) pair on a row.
+ */
+ public static GridPane createControlGrid(final Label... controls) {
+ final GridPane gp = new GridPane();
+ final ColumnConstraints labelColumn = new ColumnConstraints();
+ final ColumnConstraints controlColumn = new ColumnConstraints();
+ labelColumn .setHgrow(Priority.NEVER);
+ controlColumn.setHgrow(Priority.ALWAYS);
+ /*
+ * I'm not aware of a way to specify that the first column should be always wide
enough
+ * for showing fully the labels. As a workaround, we set the minimum width to the
width
+ * of the widest label.
+ */
+ final ChangeListener<Number> widthFixer = new ChangeListener<>() {
+ @Override public void changed(final ObservableValue<? extends Number> e,
final Number o, final Number n) {
+ final double v = n.doubleValue();
+ if (v > o.doubleValue()) {
+ if (v > labelColumn.getMinWidth()) {
+ labelColumn.setMinWidth(v);
+ }
+ e.removeListener(this);
+ }
+ }
+ };
+ gp.getColumnConstraints().setAll(labelColumn, controlColumn);
+ gp.setPadding(FORM_INSETS);
+ gp.setVgap(9);
+ gp.setHgap(9);
+ int row = 0;
+ for (final Label label : controls) {
+ if (label != null) {
+ final Node control = label.getLabelFor();
+ GridPane.setConstraints(label, 0, row);
+ GridPane.setConstraints(control, 1, row);
+ gp.getChildren().addAll(label, control);
+ label.widthProperty().addListener(widthFixer);
+ row++;
+ }
+ }
+ return gp;
+ }
+
+ /**
* Sets all rows in the given grid pane to the same height.
*
* @param gp the grid pane in which to set row constraints.
diff --git a/application/sis-javafx/src/test/java/org/apache/sis/gui/referencing/CRSChooserApp.java
b/application/sis-javafx/src/test/java/org/apache/sis/gui/referencing/CRSChooserApp.java
new file mode 100644
index 0000000..648ace5
--- /dev/null
+++ b/application/sis-javafx/src/test/java/org/apache/sis/gui/referencing/CRSChooserApp.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.gui.referencing;
+
+import java.util.Optional;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+import javafx.scene.control.Label;
+import javafx.application.Application;
+import javafx.geometry.Insets;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.internal.gui.BackgroundThreads;
+import org.apache.sis.referencing.CommonCRS;
+
+
+/**
+ * Shows {@link CRSChooser}. The area of interest is set to Canada
+ * for allowing to test the CRS domain of validity checks.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public final strictfp class CRSChooserApp extends Application {
+ /**
+ * Starts the test application.
+ *
+ * @param args ignored.
+ */
+ public static void main(final String[] args) {
+ launch(args);
+ }
+
+ /**
+ * Sets a dummy scene for the given window.
+ * The main purpose is to provide a scene for testing dialog boxes
+ * that user can close after the test is finished.
+ */
+ private static void setDummyScene(final Stage window) {
+ Label note = new Label("Close this window for stopping the application.");
+ note.setPadding(new Insets(15));
+ Scene scene = new Scene(note);
+ window.setTitle("CRSChooserApp");
+ window.setScene(scene);
+ }
+
+ /**
+ * Creates and starts the test application.
+ *
+ * @param window where to show the application.
+ */
+ @Override
+ public void start(final Stage window) {
+ setDummyScene(window);
+ window.show();
+
+ final GeneralEnvelope bbox = new GeneralEnvelope(CommonCRS.defaultGeographic());
+ bbox.setRange(0, -140.99778, -52.6480987209);
+ bbox.setRange(1, 41.6751050889, 83.23324); // Canada
+ final CRSChooser chooser = new CRSChooser(null, bbox);
+ final Optional<CoordinateReferenceSystem> crs = chooser.showDialog(window);
+
+ System.out.println("The selected CRS is: " + crs);
+ }
+
+ /**
+ * Stops the test application.
+ *
+ * @throws Exception if an error occurred while stopping the application.
+ */
+ @Override
+ public void stop() throws Exception {
+ BackgroundThreads.stop();
+ super.stop();
+ }
+}
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
index 3ac8015..4b3f623 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
@@ -19,6 +19,7 @@ package org.apache.sis.metadata.iso.extent;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
+import java.util.Locale;
import javax.measure.Unit;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.DirectPosition;
@@ -44,6 +45,7 @@ import org.apache.sis.metadata.InvalidMetadataException;
import org.apache.sis.measure.Longitude;
import org.apache.sis.measure.MeasurementRange;
import org.apache.sis.measure.Range;
+import org.apache.sis.util.iso.Types;
import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.ArgumentChecks;
@@ -70,7 +72,7 @@ import org.opengis.geometry.Geometry;
* </ul>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
*
* @see org.apache.sis.geometry.Envelopes
*
@@ -415,6 +417,19 @@ public final class Extents extends Static {
}
/**
+ * Returns the description of the given extent, or {@code null} if none.
+ *
+ * @param extent the extent from which to get a description, or {@code null}.
+ * @param locale desired locale, or {@code null} for default.
+ * @return description of the given extent, or {@code null} if none.
+ *
+ * @since 1.1
+ */
+ public static String getDescription(final Extent extent, final Locale locale) {
+ return (extent != null) ? Types.toString(extent.getDescription(), locale) : null;
+ }
+
+ /**
* Returns the position at the median longitude and latitude values of the given bounding
box.
* This method does not check the {@linkplain DefaultGeographicBoundingBox#getInclusion()
inclusion} status.
* This method takes in account bounding boxes that cross the anti-meridian.
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
index e91fcb2..982319a 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
@@ -400,6 +400,11 @@ public final class Vocabulary extends IndexedResourceBundle {
public static final short EndPoint = 59;
/**
+ * Engineering
+ */
+ public static final short Engineering = 205;
+
+ /**
* {0} entr{0,choice,0#y|2#ies}
*/
public static final short EntryCount_1 = 60;
@@ -445,11 +450,21 @@ public final class Vocabulary extends IndexedResourceBundle {
public static final short GeodesicDistance = 67;
/**
+ * Geodetic
+ */
+ public static final short Geodetic = 202;
+
+ /**
* Geodetic dataset
*/
public static final short GeodeticDataset = 68;
/**
+ * Geographic
+ */
+ public static final short Geographic = 203;
+
+ /**
* Geographic extent
*/
public static final short GeographicExtent = 69;
@@ -780,6 +795,11 @@ public final class Vocabulary extends IndexedResourceBundle {
public static final short Preprocessing = 125;
/**
+ * Projected
+ */
+ public static final short Projected = 204;
+
+ /**
* “{0}”
*/
public static final short Quoted_1 = 126;
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
index 8f5c86e..f68e1bd 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
@@ -83,6 +83,7 @@ EllipsoidChange = Ellipsoid change
EllipsoidalHeight = Ellipsoidal height
EndDate = End date
EndPoint = End point
+Engineering = Engineering
EntryCount_1 = {0} entr{0,choice,0#y|2#ies}
Envelope = Envelope
Errors = Errors
@@ -92,7 +93,9 @@ Geocentric = Geocentric
GeocentricRadius = Geocentric radius
GeocentricConversion = Geocentric conversion
GeodesicDistance = Geodesic distance
+Geodetic = Geodetic
GeodeticDataset = Geodetic dataset
+Geographic = Geographic
GeographicExtent = Geographic extent
GeographicIdentifier = Geographic identifier
Gray = Gray
@@ -159,6 +162,7 @@ Parenthesis_2 = {0} ({1})
Paths = Paths
Plugins = Plug-ins
Preprocessing = Preprocessing
+Projected = Projected
Quoted_1 = \u201c{0}\u201d
Read = Read
Red = Red
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
index 93d9fa9..d70b496 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
@@ -91,6 +91,7 @@ EllipsoidalHeight = Hauteur ellipso\u00efdale
EntryCount_1 = {0} entr\u00e9e{0,choice,0#|2#s}
EndDate = Date de fin
EndPoint = Point d\u2019arriv\u00e9
+Engineering = Ing\u00e9nierie
Envelope = Enveloppe
Errors = Erreurs
FillValue = Valeur de remplissage
@@ -99,7 +100,9 @@ Geocentric = G\u00e9ocentrique
GeocentricRadius = Rayon g\u00e9ocentrique
GeocentricConversion = Conversion g\u00e9ocentrique
GeodesicDistance = Distance g\u00e9od\u00e9sique
+Geodetic = G\u00e9od\u00e9sique
GeodeticDataset = Base de donn\u00e9es g\u00e9od\u00e9sique
+Geographic = G\u00e9ographique
GeographicExtent = \u00c9tendue g\u00e9ographique
GeographicIdentifier = Identifiant g\u00e9ographique
Gray = Gris
@@ -166,6 +169,7 @@ Parenthesis_2 = {0} ({1})
Paths = Chemins
Plugins = Modules d\u2019extension
Preprocessing = Pr\u00e9traitement
+Projected = Projet\u00e9
Quoted_1 = \u00ab\u202f{0}\u202f\u00bb
Read = Lecture
Red = Rouge
|