You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2019/11/12 14:24:28 UTC
[sis] branch geoapi-4.0 updated (6b11704 -> 5f84c5b)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git.
from 6b11704 CRSChooser builds its list of CRS in a background thread. Filtering delegated to JavaFX FilteredList class.
new b0b38a0 Refactor CRSChooser as a Dialog subclass.
new 5f84c5b Fix an IndexOutOfBoundsException when showing an empty list of features. Avoid drawing a line on image border when rectangle cross anti-meridian.
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../org/apache/sis/gui/dataset/ExpandableList.java | 2 +-
.../sis/gui/metadata/IdentificationInfo.java | 17 +-
.../apache/sis/gui/referencing/AuthorityCodes.java | 93 +++++++---
.../org/apache/sis/gui/referencing/CRSButton.java | 75 --------
.../org/apache/sis/gui/referencing/CRSChooser.java | 169 ++++++++++++++----
.../org/apache/sis/gui/referencing/WKTPane.java | 188 ++++++++++++++++++---
.../apache/sis/internal/gui/ExceptionReporter.java | 11 ++
.../org/apache/sis/internal/gui/Resources.java | 20 +++
.../apache/sis/internal/gui/Resources.properties | 4 +
.../sis/internal/gui/Resources_fr.properties | 6 +-
.../org/apache/sis/util/resources/Vocabulary.java | 5 +
.../sis/util/resources/Vocabulary.properties | 1 +
.../sis/util/resources/Vocabulary_fr.properties | 1 +
13 files changed, 424 insertions(+), 168 deletions(-)
delete mode 100644 application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSButton.java
[sis] 02/02: Fix an IndexOutOfBoundsException when showing an empty
list of features. Avoid drawing a line on image border when rectangle cross
anti-meridian.
Posted by de...@apache.org.
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
commit 5f84c5bf4c1c69ae3c6d47c0b0728c26ff10d7a6
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Nov 12 11:41:46 2019 +0100
Fix an IndexOutOfBoundsException when showing an empty list of features.
Avoid drawing a line on image border when rectangle cross anti-meridian.
---
.../java/org/apache/sis/gui/dataset/ExpandableList.java | 2 +-
.../org/apache/sis/gui/metadata/IdentificationInfo.java | 17 ++++++++++++-----
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ExpandableList.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ExpandableList.java
index 3d07ca8..1ecabce 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ExpandableList.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ExpandableList.java
@@ -236,7 +236,7 @@ final class ExpandableList extends TransformationList<Feature,Feature>
@Override
public int size() {
int size = getSource().size();
- if (expansion != null) {
+ if (size != 0 && expansion != null) {
size += expansion.length - 1;
}
return size;
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/IdentificationInfo.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/IdentificationInfo.java
index e70019c..eecfca6 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/IdentificationInfo.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/IdentificationInfo.java
@@ -307,6 +307,7 @@ final class IdentificationInfo extends Section<Identification> {
}
final double wi = Math.min(w, MAP_WIDTH - x); // Width of part inside [-180 … +180]°.
w -= wi; // Width of part not drawn by `wi`.
+ final boolean crossAntimeridian = (w > 0);
/*
* At this point we got the coordinates of the rectangle to draw, adjusted for making sure
* that they are inside valid ranges. The `w` variable is usually 0, unless we had to cut
@@ -325,13 +326,19 @@ final class IdentificationInfo extends Section<Identification> {
gc.setStroke(Color.DARKBLUE);
gc.setGlobalAlpha(0.1);
gc.fillRect(x, y, wi, h);
- if (w > 0) {
- gc.fillRect(0, y, w, h);
+ if (crossAntimeridian) {
+ gc.fillRect(0, y, w, h); // Second half of rectangle crossing anti-meridian.
}
gc.setGlobalAlpha(1.0);
- gc.strokeRect(x, y, wi, h);
- if (w > 0) {
- gc.strokeRect(0, y, w, h);
+ if (!crossAntimeridian) {
+ gc.strokeRect(x, y, wi, h);
+ } else {
+ double xw = x + wi;
+ double yh = y + h;
+ gc.strokePolyline(new double[] {xw, x, x, xw},
+ new double[] {y, y, yh, yh}, 4);
+ gc.strokePolyline(new double[] {0, w, w, 0},
+ new double[] {y, y, yh, yh}, 4);
}
}
return false;
[sis] 01/02: Refactor CRSChooser as a Dialog subclass.
Posted by de...@apache.org.
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
commit b0b38a05fd8e7c39db82f067a0f582facfcbadbc
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Nov 12 11:21:08 2019 +0100
Refactor CRSChooser as a Dialog subclass.
---
.../apache/sis/gui/referencing/AuthorityCodes.java | 93 +++++++---
.../org/apache/sis/gui/referencing/CRSButton.java | 75 --------
.../org/apache/sis/gui/referencing/CRSChooser.java | 169 ++++++++++++++----
.../org/apache/sis/gui/referencing/WKTPane.java | 188 ++++++++++++++++++---
.../apache/sis/internal/gui/ExceptionReporter.java | 11 ++
.../org/apache/sis/internal/gui/Resources.java | 20 +++
.../apache/sis/internal/gui/Resources.properties | 4 +
.../sis/internal/gui/Resources_fr.properties | 6 +-
.../org/apache/sis/util/resources/Vocabulary.java | 5 +
.../sis/util/resources/Vocabulary.properties | 1 +
.../sis/util/resources/Vocabulary_fr.properties | 1 +
11 files changed, 411 insertions(+), 162 deletions(-)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/AuthorityCodes.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/AuthorityCodes.java
index 60e6e8d..0f05141 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/AuthorityCodes.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/AuthorityCodes.java
@@ -41,6 +41,7 @@ import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.internal.util.StandardDateFormat;
import org.apache.sis.internal.gui.BackgroundThreads;
+import org.apache.sis.internal.gui.ExceptionReporter;
import org.apache.sis.internal.util.Constants;
@@ -87,13 +88,22 @@ final class AuthorityCodes extends ObservableListBase<Code>
final Locale locale;
/**
- * The task where to send request for CRS descriptions, or {@code null} if an error occurred.
- * In later case, no more background tasks will be scheduled.
+ * The task where to send requests for CRS descriptions (never {@code null}).
*/
private Loader loader;
/**
+ * Non-null if an error occurred while fetching CRS codes.
+ *
+ * @todo Provide a button for showing this error in an {@link ExceptionReporter}.
+ */
+ private Throwable error;
+
+ /**
* Creates a new deferred list and starts a background process for loading CRS codes.
+ *
+ * @param factory the authority factory, or {@code null} for default factory.
+ * @param locale the preferred locale of CRS descriptions.
*/
AuthorityCodes(final CRSAuthorityFactory factory, final Locale locale) {
this.locale = locale;
@@ -103,6 +113,14 @@ final class AuthorityCodes extends ObservableListBase<Code>
}
/**
+ * Returns the authority factory. If no explicit factory has been given at construction time,
+ * the {@linkplain CRS#getAuthorityFactory(String) Apache SIS default factory} is returned.
+ */
+ final CRSAuthorityFactory getFactory() throws FactoryException {
+ return loader.getFactory();
+ }
+
+ /**
* Returns the number of elements in this list. This method initially returns only the number of
* cached elements. This number may increase progressively as the background loading progresses.
*/
@@ -159,7 +177,7 @@ final class AuthorityCodes extends ObservableListBase<Code>
final ReadOnlyStringWrapper getName(final Code code) {
final ReadOnlyStringWrapper p = code.name();
final String name = p.getValue();
- if (name == null && loader != null) {
+ if (name == null) {
loader.requestName(code);
}
return p;
@@ -245,6 +263,18 @@ final class AuthorityCodes extends ObservableListBase<Code>
}
/**
+ * Returns the authority factory. This method is normally invoked from the background thread,
+ * but we nevertheless synchronize it in case {@link AuthorityCodes#getFactory()} is invoked
+ * concurrently.
+ */
+ final synchronized CRSAuthorityFactory getFactory() throws FactoryException {
+ if (factory == null) {
+ factory = CRS.getAuthorityFactory(Constants.EPSG);
+ }
+ return factory;
+ }
+
+ /**
* Sends to this background thread a request for fetching the name (description) of given code.
* The {@link AuthorityCodes} list will receive an update event after the name has been fetched.
* This method is invoked from JavaFX thread.
@@ -271,6 +301,7 @@ final class AuthorityCodes extends ObservableListBase<Code>
snapshot = toDescribe.toArray(new Code[size]);
toDescribe.clear();
}
+ final CRSAuthorityFactory factory = getFactory();
final Map<Code,String> updated = new IdentityHashMap<>(snapshot.length);
for (final Code code : snapshot) {
// Do not update code in this thread; it will be updated in JavaFX thread.
@@ -298,10 +329,8 @@ final class AuthorityCodes extends ObservableListBase<Code>
protected Object call() throws Exception {
long lastTime = System.nanoTime();
List<String> codes = Collections.emptyList();
+ final CRSAuthorityFactory factory = getFactory();
try {
- if (factory == null) {
- factory = CRS.getAuthorityFactory(Constants.EPSG);
- }
if (loadCodes) {
codes = new ArrayList<>(100);
final Iterator<String> it = factory.getAuthorityCodes(type).iterator();
@@ -353,37 +382,45 @@ final class AuthorityCodes extends ObservableListBase<Code>
updated = (Map<Code,String>) result;
}
update(newCodes, updated);
- /*
- * Prepare the next task for loading description. If new description requests were posted
- * between the end of `call()` execution and the start of this `succeeded()` execution,
- * starts the new task immediately.
- */
- loader = new Loader(this);
- final boolean isEmpty;
- synchronized (toDescribe) {
- isEmpty = toDescribe.isEmpty();
- }
- if (!isEmpty) {
- BackgroundThreads.execute(loader);
- }
+ scheduleNewLoader();
}
/**
- * Invoked if an error occurred while loading the codes. A pseudo-code is added with error message
- * and no more background tasks will be scheduled.
+ * Invoked if an error occurred while loading the codes. A pseudo-code is added with error message.
+ * A background task is still scheduled for allowing {@link AuthorityCodes} to get descriptions of
+ * codes obtained so far.
*/
@Override
protected void failed() {
super.failed();
- loader = null;
final Throwable e = getException();
- final Code code = new Code(Vocabulary.getResources(locale).getString(Vocabulary.Keys.Errors));
- String message = Exceptions.getLocalizedMessage(e, locale);
- if (message == null) {
- message = e.toString();
+ if (error == null) {
+ final Code code = new Code(Vocabulary.getResources(locale).getString(Vocabulary.Keys.Errors));
+ String message = Exceptions.getLocalizedMessage(e, locale);
+ if (message == null) {
+ message = e.getClass().getSimpleName();
+ }
+ code.name().set(message);
+ add(code);
+ }
+ error = e;
+ scheduleNewLoader();
+ }
+
+ /**
+ * Prepares the next task for loading descriptions. If new description requests were posted
+ * between the end of {@link #call()} execution and the start of the {@link #succeeded()} or
+ * {@link #failed()} execution, starts the new task immediately.
+ */
+ private void scheduleNewLoader() {
+ loader = new Loader(this);
+ final boolean isEmpty;
+ synchronized (toDescribe) {
+ isEmpty = toDescribe.isEmpty();
+ }
+ if (!isEmpty) {
+ BackgroundThreads.execute(loader);
}
- code.name().set(message);
- add(code);
}
}
}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSButton.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSButton.java
deleted file mode 100644
index 5c871e5..0000000
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/CRSButton.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 javafx.beans.property.ObjectProperty;
-import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.value.ObservableValue;
-import javafx.event.ActionEvent;
-import javafx.event.EventHandler;
-import javafx.scene.control.Button;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-
-/**
- * Widget button used to select a {@link CoordinateReferenceSystem}.
- *
- * @author Johann Sorel (Geomatys)
- * @version 1.1
- * @since 1.1
- * @module
- */
-public class CRSButton extends Button{
-
- private final ObjectProperty<CoordinateReferenceSystem> crsProperty = new SimpleObjectProperty<>();
-
- /**
- * Create a new CRSButton with no {@link CoordinateReferenceSystem} defined.
- */
- public CRSButton() {
- setText("-");
-
- setOnAction(new EventHandler<ActionEvent>() {
- @Override
- public void handle(ActionEvent event) {
- final CoordinateReferenceSystem crs = CRSChooser.showDialog(CRSButton.this, crsProperty.get());
- crsProperty.set(crs);
- }
- });
-
- //update button text when needed
- crsProperty.addListener((ObservableValue<? extends CoordinateReferenceSystem> observable,
- CoordinateReferenceSystem oldValue, CoordinateReferenceSystem newValue) -> {
- if (newValue!=null) {
- setText(newValue.getName().toString());
- } else {
- setText(" - ");
- }
- });
- }
-
- /**
- * Returns the property containing the edited {@link CoordinateReferenceSystem}.
- * This property can be modified and will send events.
- * It can be used with JavaFx binding operations.
- *
- * @return Property containing the edited {@link CoordinateReferenceSystem}
- */
- public ObjectProperty<CoordinateReferenceSystem> crsProperty() {
- return crsProperty;
- }
-
-}
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 73fd239..52ee27d 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
@@ -17,23 +17,29 @@
package org.apache.sis.gui.referencing;
import java.util.Locale;
+import java.util.Optional;
import java.util.function.Predicate;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
-import javafx.scene.control.Alert;
-import javafx.scene.control.Button;
+import javafx.scene.Node;
import javafx.scene.control.ButtonType;
+import javafx.scene.control.Control;
+import javafx.scene.control.Dialog;
import javafx.scene.control.DialogPane;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
+import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
+import javafx.stage.Window;
+import org.apache.sis.internal.gui.ExceptionReporter;
+import org.opengis.util.FactoryException;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.internal.gui.IdentityValueFactory;
@@ -53,11 +59,23 @@ import org.apache.sis.referencing.CRS;
* @since 1.1
* @module
*/
-public class CRSChooser {
+public class CRSChooser extends Dialog<CoordinateReferenceSystem> {
/**
* The pane where the controls for this CRS chooser will be put.
+ * The top part contains the tools bar. The center part contains
+ * the table or the WKT and change depending on user actions.
*/
- final BorderPane content;
+ private final BorderPane content;
+
+ /**
+ * The tools bar for this pane. Children are in this order:
+ * <ul>
+ * <li>A {@link Label} for the second child.</li>
+ * <li>A text field, combo box or other control.</li>
+ * <li>An arbitrary number of buttons.</li>
+ * </ul>
+ */
+ private final HBox tools;
/**
* The text field where user can enter a fragment of the name of the CRS (s)he is looking for.
@@ -72,17 +90,27 @@ public class CRSChooser {
private final TableView<Code> table;
/**
+ * 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 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}.
*/
public CRSChooser(final CRSAuthorityFactory factory) {
- final Locale locale = Locale.getDefault();
- final AuthorityCodes codeList = new AuthorityCodes(factory, locale);
+ final Locale locale = Locale.getDefault();
+ final Resources i18n = Resources.forLocale(locale);
+ final Vocabulary vocabulary = Vocabulary.getResources(locale);
+ final AuthorityCodes codeList = new AuthorityCodes(factory, locale);
table = new TableView<>(codeList);
-
- final Vocabulary vocabulary = Vocabulary.getResources(locale);
+ /*
+ * Columns to show in CRS table. First column is typically EPSG codes and second
+ * column is the CRS descriptions. The content is loaded in a background thread.
+ */
final TableColumn<Code,Code> codes = new TableColumn<>(vocabulary.getString(Vocabulary.Keys.Code));
final TableColumn<Code,String> names = new TableColumn<>(vocabulary.getString(Vocabulary.Keys.Name));
names.setCellValueFactory(codeList);
@@ -93,24 +121,92 @@ public class CRSChooser {
table.setPrefWidth(500);
table.getColumns().setAll(codes, names);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
-
+ /*
+ * 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) -> {
filter(searchField.getText());
});
- final Resources i18n = Resources.forLocale(locale);
+ HBox.setHgrow(searchField, Priority.ALWAYS);
final Label label = new Label(i18n.getString(Resources.Keys.Filter));
- final Button info = new Button("\uD83D\uDEC8"); // Unicode U+1F6C8: Circled Information Source
label.setLabelFor(searchField);
- HBox.setHgrow(searchField, Priority.ALWAYS);
- final HBox bar = new HBox(label, searchField, info);
- bar.setSpacing(9);
- bar.setAlignment(Pos.BASELINE_LEFT);
- BorderPane.setMargin(bar, new Insets(0, 0, 9, 0));
-
+ /*
+ * 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 info = new ToggleButton("ℹ"); // Unicode U+2139: Information Source.
+ table.getSelectionModel().selectedItemProperty().addListener((e,o,n) -> info.setDisable(n == null));
+ info.setOnAction((ActionEvent event) -> {
+ setTools(info.isSelected());
+ });
+ info.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, info);
+ tools.setSpacing(9);
+ tools.setAlignment(Pos.BASELINE_LEFT);
+ BorderPane.setMargin(tools, new Insets(0, 0, 9, 0));
+ /*
+ * Layout table and tools bar inside the dialog content.
+ * Configure the dialog buttons.
+ */
+ final DialogPane pane = getDialogPane();
content = new BorderPane();
content.setCenter(table);
- content.setTop(bar);
+ content.setTop(tools);
+ pane.setContent(content);
+ pane.getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
+ setTitle(i18n.getString(Resources.Keys.SelectCRS));
+ setResultConverter(this::getSelectedCRS);
+ setResizable(true);
+ }
+
+ /**
+ * Sets the tools bar and 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;
+ if (wkt) {
+ if (wktPane == null) {
+ wktPane = new WKTPane(locale);
+ }
+ wktPane.setContent(getAuthorityCodes(), table.getSelectionModel().getSelectedItem().code);
+ labelText = Resources.Keys.Format;
+ control = wktPane.convention;
+ main = wktPane.text;
+ } else {
+ labelText = Resources.Keys.Filter;
+ control = searchField;
+ main = table;
+ }
+ final ObservableList<Node> children = tools.getChildren();
+ final Label label = (Label) children.get(0);
+ final Resources i18n = Resources.forLocale(locale);
+ label.setText(i18n.getString(labelText));
+ label.setLabelFor(control);
+ children.set(1, control);
+ content.setCenter(main);
+ }
+
+ /**
+ * Returns the list of all authority codes. The list may not be complete at the
+ * time this method returns because codes are loaded in a background thread.
+ */
+ private AuthorityCodes getAuthorityCodes() {
+ ObservableList<?> items = table.getItems();
+ if (items instanceof FilteredList<?>) {
+ items = ((FilteredList<?>) items).getSource();
+ }
+ return (AuthorityCodes) items;
}
/**
@@ -161,21 +257,30 @@ public class CRSChooser {
}
/**
- * Show a modal dialog to select a {@link CoordinateReferenceSystem}.
+ * Returns the currently selected CRS, or {@code null} if none.
+ *
+ * @return the currently selected CRS, or {@code null}.
+ */
+ private CoordinateReferenceSystem getSelectedCRS(final ButtonType button) {
+ if (ButtonType.OK.equals(button)) {
+ final Code code = table.getSelectionModel().getSelectedItem();
+ if (code != null) try {
+ return getAuthorityCodes().getFactory().createCoordinateReferenceSystem(code.code);
+ } catch (FactoryException e) {
+ ExceptionReporter.canNotCreateCRS(code.code, e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Shows a dialog to select a {@link CoordinateReferenceSystem}.
*
- * @param parent parent frame of widget.
- * @param crs {@link CoordinateReferenceSystem} to edit.
- * @return modified {@link CoordinateReferenceSystem}.
+ * @param parent parent frame of dialog.
+ * @return the selected {@link CoordinateReferenceSystem}, or empty if none.
*/
- public static CoordinateReferenceSystem showDialog(Object parent, CoordinateReferenceSystem crs) {
- final CRSChooser chooser = new CRSChooser(null);
-// chooser.crsProperty.set(crs);
- final Alert alert = new Alert(Alert.AlertType.NONE);
- final DialogPane pane = alert.getDialogPane();
- pane.setContent(chooser.content);
- alert.getButtonTypes().setAll(ButtonType.OK,ButtonType.CANCEL);
- alert.setResizable(true);
- final ButtonType res = alert.showAndWait().orElse(ButtonType.CANCEL);
- return null;//res == ButtonType.CANCEL ? null : chooser.crsProperty.get();
+ public Optional<CoordinateReferenceSystem> showDialog(final Window parent) {
+ initOwner(parent);
+ return showAndWait();
}
}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/WKTPane.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/WKTPane.java
index b984d4c..c39051f 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/WKTPane.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/WKTPane.java
@@ -16,53 +16,189 @@
*/
package org.apache.sis.gui.referencing;
+import java.util.EnumMap;
+import java.util.Locale;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
-import javafx.scene.control.Alert;
-import javafx.scene.control.ButtonType;
+import javafx.concurrent.Task;
import javafx.scene.control.ChoiceBox;
-import javafx.scene.control.DialogPane;
import javafx.scene.control.TextArea;
-import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
+import javafx.util.StringConverter;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.io.wkt.Convention;
-import org.apache.sis.io.wkt.FormattableObject;
+import org.apache.sis.io.wkt.WKTFormat;
+import org.apache.sis.internal.gui.BackgroundThreads;
+import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.Exceptions;
/**
* Small panel to display an object as WKT in various conventions.
*
* @author Johann Sorel (Geomatys)
- * @version 1.0
- * @since 1.0
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
* @module
*/
-final class WKTPane extends BorderPane {
+final class WKTPane extends StringConverter<Convention> implements ChangeListener<Convention> {
+ /**
+ * The object to use for formatting and CRS.
+ */
+ private final WKTFormat format;
- private final ChoiceBox<Convention> choice = new ChoiceBox<>(FXCollections.observableArrayList(Convention.values()));
- private final TextArea text = new TextArea();
+ /**
+ * A choice box for choosing the WKT conventions.
+ */
+ final ChoiceBox<Convention> convention;
- public WKTPane(final FormattableObject obj) {
- setTop(choice);
- setCenter(text);
+ /**
+ * Localized string representations of {@link #convention}.
+ */
+ private final EnumMap<Convention,String> conventionTexts;
- choice.valueProperty().addListener(new ChangeListener<Convention>() {
- @Override
- public void changed(ObservableValue<? extends Convention> observable, Convention oldValue, Convention newValue) {
- text.setText(obj.toString(newValue));
+ /**
+ * The pane where to show the Well Known Text.
+ */
+ final TextArea text = new TextArea();
+
+ /**
+ * The object to format.
+ */
+ private CoordinateReferenceSystem crs;
+
+ /**
+ * Creates a new pane for showing CRS Well Known Text.
+ */
+ @SuppressWarnings("ThisEscapedInObjectConstruction")
+ WKTPane(final Locale locale) {
+ final Convention[] sc = { // Selected conventions in the order we want them to appear.
+ Convention.WKT2_SIMPLIFIED,
+ Convention.WKT2,
+ Convention.WKT1,
+ Convention.WKT1_COMMON_UNITS
+ };
+ conventionTexts = new EnumMap<>(Convention.class);
+ final Vocabulary vocabulary = Vocabulary.getResources(locale);
+ for (final Convention c : sc) {
+ conventionTexts.put(c, toString(c, vocabulary));
+ }
+ format = new WKTFormat(locale, null);
+ format.setConvention(Convention.WKT2_SIMPLIFIED);
+ convention = new ChoiceBox<>(FXCollections.observableArrayList(sc));
+ convention.setConverter(this);
+ convention.getSelectionModel().select(format.getConvention());
+ convention.valueProperty().addListener(this);
+ convention.setMaxWidth(Double.MAX_VALUE);
+ HBox.setHgrow(convention, Priority.ALWAYS);
+ }
+
+ /**
+ * Returns the text to write in {@link #convention} choice box for the given convention.
+ */
+ @SuppressWarnings("fallthrough")
+ private String toString(final Convention c, final Vocabulary vocabulary) {
+ final Object version;
+ boolean simplified = false;
+ switch (c) {
+ case WKT2_SIMPLIFIED: simplified = true; // Fall through.
+ case WKT2: version = 2; break;
+ case WKT1: version = 1; break;
+ case WKT1_COMMON_UNITS: version = "GDAL 1-2"; break;
+ default: return c.name();
+ }
+ String text = vocabulary.getString(Vocabulary.Keys.Version_2, "WKT (Well Known Text)", version);
+ if (simplified) {
+ text += " — " + vocabulary.getString(Vocabulary.Keys.Simplified);
+ }
+ return text;
+ }
+
+ /**
+ * Returns the text to write in {@link #convention} choice box for the given convention.
+ */
+ @Override
+ public String toString(final Convention c) {
+ return conventionTexts.get(c);
+ }
+
+ /**
+ * Returns the convention from the given string.
+ * This is the reverse of {@link #toString(Convention)}.
+ */
+ @Override
+ public Convention fromString(final String text) {
+ for (final EnumMap.Entry<Convention,String> e : conventionTexts.entrySet()) {
+ if (e.getValue().equals(text)) return e.getKey();
+ }
+ return null;
+ }
+
+ /**
+ * Invoked when the user select a new format. This method is public as an implementation side-effect;
+ * it should not be invoked explicitly.
+ */
+ @Override
+ public void changed(ObservableValue<? extends Convention> observable, Convention oldValue, Convention newValue) {
+ format.setConvention(newValue);
+ refresh();
+ }
+
+ /**
+ * Sets the CRS to show in this pane. The CRS is constructed in a background thread.
+ */
+ final void setContent(final AuthorityCodes source, final String code) {
+ text.setDisable(true);
+ 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() {
+ super.succeeded();
+ setContent(getValue());
+ }
+
+ /** Invoked in JavaFX thread on cancellation. */
+ @Override protected void cancelled() {
+ super.cancelled();
+ text.setText(null);
+ }
+
+ /** Invoked in JavaFX thread on failure. */
+ @Override protected void failed() {
+ super.failed();
+ text.setDisable(false);
+ text.setEditable(false);
+ text.setText(Exceptions.getLocalizedMessage(getException(), source.locale));
}
});
- choice.getSelectionModel().select(Convention.WKT1);
}
- public static void showDialog(Object parent, FormattableObject candidate){
- final WKTPane chooser = new WKTPane(candidate);
+ /**
+ * Sets the content to the given coordianate reference system.
+ */
+ private void setContent(final CoordinateReferenceSystem newCRS) {
+ text.setEditable(false); // TODO: make editable if we allow WKT parsing in a future version.
+ text.setDisable(false);
+ if (newCRS != crs) {
+ crs = newCRS;
+ refresh();
+ }
+ }
- final Alert alert = new Alert(Alert.AlertType.NONE);
- final DialogPane pane = alert.getDialogPane();
- pane.setContent(chooser);
- alert.getButtonTypes().setAll(ButtonType.OK);
- alert.setResizable(true);
- alert.showAndWait();
+ /**
+ * Rewrites the WKT using current conventions.
+ */
+ private void refresh() {
+ if (crs != null) {
+ text.setText(format.format(crs));
+ }
}
}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java
index d48274f..458b549 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java
@@ -140,6 +140,17 @@ public final class ExceptionReporter implements EventHandler<ActionEvent> {
}
/**
+ * Shows the reporter for a failure to create a CRS.
+ * This method does nothing if the exception is null.
+ *
+ * @param code code of the CRS that can not be created.
+ * @param exception the error that occurred.
+ */
+ public static void canNotCreateCRS(final String code, final Throwable exception) {
+ show(Resources.Keys.ErrorCreatingCRS, Resources.Keys.CanNotCreateCRS_1, new Object[] {code}, exception);
+ }
+
+ /**
* Constructs and shows the exception reporter. The title and text are keys from the {@link Resources}.
* If the title and/or text are 0, then the {@link Alert} default title and text will be used.
*
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 6076310..35c08eb 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
@@ -76,6 +76,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short CanNotClose_1 = 12;
/**
+ * Can not create reference system “{0}”.
+ */
+ public static final short CanNotCreateCRS_1 = 35;
+
+ /**
* Can not open “{0}”.
*/
public static final short CanNotReadFile_1 = 5;
@@ -126,6 +131,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short ErrorClosingFile = 13;
/**
+ * Error creating reference system
+ */
+ public static final short ErrorCreatingCRS = 36;
+
+ /**
* Error opening file
*/
public static final short ErrorOpeningFile = 6;
@@ -151,6 +161,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short Filter = 34;
/**
+ * Format:
+ */
+ public static final short Format = 38;
+
+ /**
* Geospatial data files
*/
public static final short GeospatialFiles = 4;
@@ -206,6 +221,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short ResourceIdentification = 23;
/**
+ * Select a coordinate reference system
+ */
+ public static final short SelectCRS = 37;
+
+ /**
* Spatial representation
*/
public static final short SpatialRepresentation = 24;
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 9824bb6..4c13c70 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
@@ -24,6 +24,7 @@ Abstract = Abstract:
AllFiles = All files
CanNotReadFile_1 = Can not open \u201c{0}\u201d.
CanNotClose_1 = Can not close \u201c{0}\u201d. Data may be lost.
+CanNotCreateCRS_1 = Can not create reference system \u201c{0}\u201d.
CellGeometry = Cell geometry:
Close = Close
Copy = Copy
@@ -35,10 +36,12 @@ Date = Date:
Dimensions = Dimensions:
ErrorClosingFile = Error closing file
ErrorOpeningFile = Error opening file
+ErrorCreatingCRS = Error creating reference system
Exit = Exit
Extent = Extent:
File = File
Filter = Filter:
+Format = Format:
GeospatialFiles = Geospatial data files
Loading = Loading\u2026
Metadata = Metadata
@@ -51,6 +54,7 @@ Purpose = Purpose:
ReferenceSystem = Reference system:
ResourceIdentification = Resource identification
SpatialRepresentation = Spatial representation
+SelectCRS = Select a coordinate reference system
Summary = Summary
TopicCategory = Topic category:
TypeOfResource = Type of resource:
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 bbce0ce..bc42c59 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
@@ -29,6 +29,7 @@ Abstract = R\u00e9sum\u00e9\u00a0:
AllFiles = Tous les fichiers
CanNotReadFile_1 = Ne peut pas ouvrir \u00ab\u202f{0}\u202f\u00bb.
CanNotClose_1 = Ne peut pas fermer \u00ab\u202f{0}\u202f\u00bb. Il pourrait y avoir une perte de donn\u00e9es.
+CanNotCreateCRS_1 = Ne peut pas cr\u00e9er le syst\u00e8me de r\u00e9f\u00e9rence \u00ab\u202f{0}\u202f\u00bb.
CellGeometry = G\u00e9om\u00e9trie des cellules\u00a0:
Close = Fermer
Copy = Copier
@@ -40,10 +41,12 @@ Date = Date\u00a0:
Dimensions = Dimensions\u00a0:
ErrorClosingFile = Erreur \u00e0 la fermeture du fichier
ErrorOpeningFile = Erreur \u00e0 l\u2019ouverture du fichier
+ErrorCreatingCRS = Erreur \u00e0 la cr\u00e9ation du syst\u00e8me de r\u00e9f\u00e9rence.
Exit = Quitter
Extent = \u00c9tendue\u00a0:
File = Fichier
-Filter = Filtrer\u00a0:
+Filter = Filtre\u00a0:
+Format = Format\u00a0:
GeospatialFiles = Fichiers de donn\u00e9es g\u00e9ospatiales
Loading = Chargement\u2026
Metadata = Metadonn\u00e9es
@@ -56,6 +59,7 @@ Purpose = Objectif\u00a0:
ReferenceSystem = Syst\u00e8me de r\u00e9f\u00e9rence\u00a0:
ResourceIdentification = Identification de la ressource
SpatialRepresentation = Repr\u00e9sentation spatiale
+SelectCRS = Choisir un syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es
Summary = R\u00e9sum\u00e9
TopicCategory = Cat\u00e9gorie th\u00e9matique\u00a0:
TypeOfResource = Type de ressource\u00a0:
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 92c9568..23aeb70 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
@@ -737,6 +737,11 @@ public final class Vocabulary extends IndexedResourceBundle {
public static final short Scale = 136;
/**
+ * Simplified
+ */
+ public static final short Simplified = 174;
+
+ /**
* {0}/{1}
*/
public static final short SlashSeparatedList_2 = 137;
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 90f4c3b..56682fe 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
@@ -150,6 +150,7 @@ Root = Root
RootMeanSquare = Root Mean Square
SampleDimensions = Sample dimensions
Scale = Scale
+Simplified = Simplified
SlashSeparatedList_2 = {0}/{1}
Source = Source
SouthBound = South bound
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 aecb2c5..0e80ee4 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
@@ -157,6 +157,7 @@ Root = Racine
RootMeanSquare = Moyenne quadratique
SampleDimensions = Dimensions d\u2019\u00e9chantillonnage
Scale = \u00c9chelle
+Simplified = Simplifi\u00e9
SlashSeparatedList_2 = {0}/{1}
Source = Source
SouthBound = Limite sud