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