sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 01/02: More projections centered on cursor position.
Date Sat, 04 Jul 2020 14:38:29 GMT
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 8be56a77953bb0ec7146989f44ae9fe115146ed0
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Jul 3 12:12:19 2020 +0200

    More projections centered on cursor position.
---
 .../org/apache/sis/gui/referencing/MenuSync.java   | 64 +++++++++++++---------
 .../gui/referencing/PositionableProjection.java    | 44 +++++++++++++++
 .../gui/referencing/RecentReferenceSystems.java    | 36 ++++++++++--
 .../org/apache/sis/internal/gui/Resources.java     | 10 ++++
 .../apache/sis/internal/gui/Resources.properties   |  2 +
 .../sis/internal/gui/Resources_fr.properties       |  2 +
 6 files changed, 127 insertions(+), 31 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
index 3f44241..e841231 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/MenuSync.java
@@ -19,9 +19,9 @@ package org.apache.sis.gui.referencing;
 import java.util.Arrays;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
+import java.util.Locale;
 import java.util.Map;
 import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.value.ChangeListener;
 import javafx.collections.ObservableList;
 import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
@@ -60,11 +60,6 @@ final class MenuSync extends SimpleObjectProperty<ReferenceSystem>
implements Ev
     private static final String CHOOSER = "CHOOSER";
 
     /**
-     * The manager of reference systems to synchronize with.
-     */
-    private final RecentReferenceSystems owner;
-
-    /**
      * The list of menu items to keep up-to-date with an {@code ObservableList<ReferenceSystem>}.
      */
     private final ObservableList<MenuItem> menus;
@@ -75,23 +70,22 @@ final class MenuSync extends SimpleObjectProperty<ReferenceSystem>
implements Ev
     private final ToggleGroup group;
 
     /**
-     * The action to execute when a reference system is selected.
+     * The action to execute when a reference system is selected. This is not directly the
user-specified action, but
+     * rather an {@link org.apache.sis.gui.referencing.RecentReferenceSystems.Listener} instance
wrapping that action.
+     * This listener is invoked explicitly instead than using {@link SimpleObjectProperty}
listeners because we do not
+     * invoke it in all cases.
      */
-    private final ChangeListener<ReferenceSystem> action;
+    private final RecentReferenceSystems.Listener action;
 
     /**
      * Creates a new synchronization for the given list of menu items.
      *
-     * @param  owner    the manager of reference systems to synchronize with.
      * @param  systems  the reference systems for which to build menu items.
      * @param  bean     the menu to keep synchronized with the list of reference systems.
      * @param  action   the user-specified action to execute when a reference system is selected.
      */
-    MenuSync(final RecentReferenceSystems owner, final ObservableList<ReferenceSystem>
systems,
-             final Menu bean, final ChangeListener<ReferenceSystem> action)
-    {
+    MenuSync(final ObservableList<ReferenceSystem> systems, final Menu bean, final
RecentReferenceSystems.Listener action) {
         super(bean, "value");
-        this.owner  = owner;
         this.menus  = bean.getItems();
         this.group  = new ToggleGroup();
         this.action = action;
@@ -126,14 +120,15 @@ final class MenuSync extends SimpleObjectProperty<ReferenceSystem>
implements Ev
      * Creates a new menu item for the given reference system.
      */
     private MenuItem createItem(final ReferenceSystem system) {
+        final Locale locale = action.owner().locale;
         if (system != RecentReferenceSystems.OTHER) {
-            final RadioMenuItem item = new RadioMenuItem(IdentifiedObjects.getDisplayName(system,
owner.locale));
+            final RadioMenuItem item = new RadioMenuItem(IdentifiedObjects.getDisplayName(system,
locale));
             item.getProperties().put(REFERENCE_SYSTEM_KEY, system);
             item.setToggleGroup(group);
             item.setOnAction(this);
             return item;
         } else {
-            final MenuItem item = new MenuItem(Vocabulary.getResources(owner.locale).getString(Vocabulary.Keys.Others)
+ '…');
+            final MenuItem item = new MenuItem(Vocabulary.getResources(locale).getString(Vocabulary.Keys.Others)
+ '…');
             item.getProperties().put(REFERENCE_SYSTEM_KEY, CHOOSER);
             item.setOnAction(this);
             return item;
@@ -156,6 +151,9 @@ final class MenuSync extends SimpleObjectProperty<ReferenceSystem>
implements Ev
      * scratch (with recycling of existing items) and inspect the differences.
      */
     final void notifyChanges(final ObservableList<? extends ReferenceSystem> systems)
{
+        /*
+         * Build a map of current menu items. Key are CRS objects.
+         */
         final Map<Object,MenuItem> mapping = new IdentityHashMap<>();
         for (final Iterator<MenuItem> it = menus.iterator(); it.hasNext();) {
             final MenuItem item = it.next();
@@ -164,30 +162,45 @@ final class MenuSync extends SimpleObjectProperty<ReferenceSystem>
implements Ev
                 dispose(item);
             }
         }
+        /*
+         * Prepare a list of menu items and assign a value to all elements where the menu
item can be reused as-is.
+         * Other menu items are left to null for now; those null values may appear anywhere
in the array. After this
+         * loop, the map will contain only menu items for CRS that are no longer in the list
of CRS to offer.
+         */
         final MenuItem[] items = new MenuItem[systems.size()];
         for (int i=0; i<items.length; i++) {
-            final Object key = systems.get(i);
-            items[i] = mapping.remove(key == RecentReferenceSystems.OTHER ? CHOOSER : key);
+            Object key = systems.get(i);
+            if (key == RecentReferenceSystems.OTHER) key = CHOOSER;
+            items[i] = mapping.remove(key);
         }
         /*
-         * Previous loop copied all items that could be reused as-is. Now search for all
items that are new.
-         * If there is some menu items available, recycle them.
+         * Previous loop took all items that could be reused as-is. Now search for all items
that are new.
+         * For each new item to create, recycle an arbitrary `mapping` element (in any order)
if some exist.
+         * When creating new items, it may happen that one of those items represent the currently
selected CRS.
          */
+        ReferenceSystem selected = get();
         final Iterator<MenuItem> recycle = mapping.values().iterator();
         for (int i=0; i<items.length; i++) {
             if (items[i] == null) {
+                MenuItem item;
                 final ReferenceSystem system = systems.get(i);
                 if (system != RecentReferenceSystems.OTHER && recycle.hasNext())
{
-                    final MenuItem item = recycle.next();
+                    item = recycle.next();
                     recycle.remove();
                     if (item instanceof RadioMenuItem) {
-                        item.setText(IdentifiedObjects.getDisplayName(system, owner.locale));
+                        item.setText(IdentifiedObjects.getDisplayName(system, action.owner().locale));
                         item.getProperties().put(REFERENCE_SYSTEM_KEY, system);
-                        items[i] = item;
-                        continue;
+                    } else {
+                        item = createItem(system);
                     }
+                } else {
+                    item = createItem(system);
+                }
+                if (selected != null && system == selected) {
+                    ((RadioMenuItem) item).setSelected(true);       // ClassCastException
should never occur here.
+                    selected = null;
                 }
-                items[i] = createItem(system);
+                items[i] = item;
             }
         }
         /*
@@ -235,7 +248,7 @@ final class MenuSync extends SimpleObjectProperty<ReferenceSystem>
implements Ev
     public void set(ReferenceSystem system) {
         final ReferenceSystem old = get();
         if (old != system) {
-            final ComparisonMode mode = owner.duplicationCriterion.get();
+            final ComparisonMode mode = action.owner().duplicationCriterion.get();
             for (final MenuItem item : menus) {
                 if (item instanceof RadioMenuItem) {
                     final Object current = item.getProperties().get(REFERENCE_SYSTEM_KEY);
@@ -250,6 +263,7 @@ final class MenuSync extends SimpleObjectProperty<ReferenceSystem>
implements Ev
             }
             super.set(system);
             group.selectToggle(null);
+            action.owner().addSelected(system);
             /*
              * Do not invoke action.changed(…) since we have no non-null value to provide.
              * Invoking that method with a null value would cause the CRSChooser to popup.
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/PositionableProjection.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/PositionableProjection.java
index 2f8e6a7..24ebda5 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/PositionableProjection.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/PositionableProjection.java
@@ -28,6 +28,7 @@ import org.opengis.referencing.crs.ProjectedCRS;
 import org.apache.sis.internal.gui.Resources;
 import org.apache.sis.internal.referencing.GeodeticObjectBuilder;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
+import org.apache.sis.internal.system.Modules;
 import org.apache.sis.measure.AngleFormat;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Longitude;
@@ -37,6 +38,7 @@ import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Utilities;
+import org.apache.sis.util.logging.Logging;
 
 
 /**
@@ -77,6 +79,48 @@ public abstract class PositionableProjection extends CodeList<PositionableProjec
     };
 
     /**
+     * Provides <cite>Universal Transverse Mercator</cite> projection for the
zone in the point of interest.
+     *
+     * @see org.apache.sis.referencing.operation.projection.Mercator
+     */
+    public static final PositionableProjection UTM =
+            new PositionableProjection("UTM", Resources.Keys.UTM)
+    {
+        @Override protected ProjectedCRS createProjectedCRS(final GeographicCRS baseCRS,
+                final double latitude, final double longitude) throws FactoryException
+        {
+            CommonCRS cd;
+            try {
+                cd = CommonCRS.forDatum(baseCRS);
+            } catch (IllegalArgumentException e) {
+                Logging.recoverableException(Logging.getLogger(Modules.APPLICATION),
+                            PositionableProjection.class, "createProjectedCRS", e);
+                cd = CommonCRS.WGS84;
+            }
+            return cd.universal(latitude, longitude);
+        }
+    };
+
+    /**
+     * Provides <cite>Mercator (variant C)</cite> projection centered on a point
of interest.
+     *
+     * @see org.apache.sis.referencing.operation.projection.Mercator
+     */
+    public static final PositionableProjection MERCATOR =
+            new PositionableProjection("MERCATOR", Resources.Keys.Mercator)
+    {
+        @Override protected ProjectedCRS createProjectedCRS(final GeographicCRS baseCRS,
+                final double latitude, final double longitude) throws FactoryException
+        {
+            return newBuilder(latitude, longitude)
+                    .setConversionMethod("Mercator (variant C)")
+                    .setParameter("Latitude of false origin",    latitude,  Units.DEGREE)
+                    .setParameter("Longitude of natural origin", longitude, Units.DEGREE)
+                    .createProjectedCRS(baseCRS, null);
+        }
+    };
+
+    /**
      * The projection name as a {@link Resources} keys.
      */
     private final short nameKey;
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
index abb3ccb..269cac7 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
@@ -287,6 +287,19 @@ public class RecentReferenceSystems {
     }
 
     /**
+     * Invoked when a new CRS is selected and that CRS has not been found in the list.
+     * The new CRS is added after the CRS and menu items will be added in background thread.
+     */
+    final void addSelected(final ReferenceSystem system) {
+        if (isAccepted(system)) {
+            synchronized (systemsOrCodes) {
+                systemsOrCodes.add(Math.min(systemsOrCodes.size(), NUM_CORE_ITEMS), system);
+                listModified();
+            }
+        }
+    }
+
+    /**
      * Adds the given reference systems to the list of alternative choices.
      * If there is duplicated values in the given list or with previously added systems,
      * then only the first occurrence of duplicated values is retained.
@@ -354,6 +367,14 @@ public class RecentReferenceSystems {
     }
 
     /**
+     * Returns whether the given object is accepted for inclusion in the list of CRS choice.
+     * In current implementation we accept a CRS if it has an authority code (typically an
EPSG code).
+     */
+    private static boolean isAccepted(final IdentifiedObject object) {
+        return IdentifiedObjects.getIdentifier(object, null) != null;
+    }
+
+    /**
      * Filters the {@link #systemsOrCodes} list by making sure that it contains only {@link
ReferenceSystem} instances.
      * Authority codes are resolved if possible or removed if they can not be resolved. Unverified
CRSs are compared
      * with authoritative definitions and replaced when a match is found. Duplications are
removed.
@@ -437,9 +458,7 @@ public class RecentReferenceSystems {
                 while (--j > i) {
                     if (Utilities.deepEquals(item, systemsOrCodes.get(j), mode)) {
                         final Object removed = systemsOrCodes.remove(j);
-                        if (IdentifiedObjects.getIdentifier((IdentifiedObject) removed, null)
!= null &&
-                            IdentifiedObjects.getIdentifier((IdentifiedObject) item,    null)
== null)
-                        {
+                        if (isAccepted((IdentifiedObject) removed) && !isAccepted((IdentifiedObject)
item)) {
                             /*
                              * Keep the instance which has an identifier. The instance without
identifier
                              * is typically a CRS with non-standard axis order. It happens
when it is the
@@ -605,15 +624,20 @@ public class RecentReferenceSystems {
      * and the selected reference system is added to the list of choices. If the selected
CRS is different than
      * the previous one, then {@link RecentChoices} is notified and the user-specified listener
is notified.
      */
-    private final class Listener implements ChangeListener<ReferenceSystem> {
+    final class Listener implements ChangeListener<ReferenceSystem> {
         /** The user-specified action to execute when a reference system is selected. */
         private final ChangeListener<ReferenceSystem> action;
 
         /** Creates a new listener of reference system selection. */
-        Listener(final ChangeListener<ReferenceSystem> action) {
+        private Listener(final ChangeListener<ReferenceSystem> action) {
             this.action = action;
         }
 
+        /** The manager of reference systems to synchronize with. */
+        final RecentReferenceSystems owner() {
+            return RecentReferenceSystems.this;
+        }
+
         /** Invoked when the user selects a reference system or the "Other…" item. */
         @SuppressWarnings("unchecked")
         @Override public void changed(final ObservableValue<? extends ReferenceSystem>
property,
@@ -830,7 +854,7 @@ next:       for (int i=0; i<count; i++) {
     public Menu createMenuItems(final ChangeListener<ReferenceSystem> action) {
         ArgumentChecks.ensureNonNull("action", action);
         final Menu menu = new Menu(Vocabulary.getResources(locale).getString(Vocabulary.Keys.ReferenceSystem));
-        final MenuSync property = new MenuSync(this, updateItems(), menu, new Listener(action));
+        final MenuSync property = new MenuSync(updateItems(), menu, new Listener(action));
         menu.getProperties().put(SELECTED_ITEM_KEY, property);
         controlValues.add(property);
         return menu;
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 eb04449..1d1da13 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
@@ -211,6 +211,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short MainWindow = 25;
 
         /**
+         * Mercator
+         */
+        public static final short Mercator = 44;
+
+        /**
          * New window
          */
         public static final short NewWindow = 26;
@@ -261,6 +266,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short TileIndexStart = 37;
 
         /**
+         * Universal Transverse Mercator
+         */
+        public static final short UTM = 45;
+
+        /**
          * Visualize
          */
         public static final short Visualize = 34;
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 12662a9..5a54e7c 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
@@ -50,6 +50,7 @@ GeospatialFiles        = Geospatial data files
 ImageStart             = Image start
 InconsistencyIn_2      = {0} \u2013 inconsistency in {1}
 Loading                = Loading\u2026
+Mercator               = Mercator
 MainWindow             = Main window
 NewWindow              = New window
 NoFeatureTypeInfo      = No feature type information.
@@ -61,5 +62,6 @@ SizeOrPosition         = Size or position
 StandardErrorStream    = Standard error stream
 TabularData            = Tabular data
 TileIndexStart         = Tile index start
+UTM                    = Universal Transverse Mercator
 Visualize              = Visualize
 Windows                = Windows
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 4125597..4a6add6 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
@@ -55,6 +55,7 @@ GeospatialFiles        = Fichiers de donn\u00e9es g\u00e9ospatiales
 ImageStart             = D\u00e9but de l\u2019image
 InconsistencyIn_2      = {0} \u2013 incoh\u00e9rence dans {1}
 Loading                = Chargement\u2026
+Mercator               = Mercator
 MainWindow             = Fen\u00eatre principale
 NewWindow              = Nouvelle fen\u00eatre
 NoFeatureTypeInfo      = Pas d\u2019information sur le type d\u2019entit\u00e9.
@@ -66,5 +67,6 @@ SizeOrPosition         = Taille ou position
 StandardErrorStream    = Flux d\u2019erreur standard
 TabularData            = Tableau de valeurs
 TileIndexStart         = D\u00e9but des indices de tuiles
+UTM                    = Transverse universelle de Mercator
 Visualize              = Visualiser
 Windows                = Fen\u00eatres


Mime
View raw message