sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 04/04: If geographic bounding box is not found directly on the root data element, search in aggregate elements.
Date Tue, 07 Jul 2020 17:47:49 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 785f1d58ade007d603751384acd1cc262e2337e1
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Jul 7 19:46:30 2020 +0200

    If geographic bounding box is not found directly on the root data element, search in aggregate
elements.
---
 .../sis/gui/metadata/IdentificationInfo.java       | 128 +++++++++++++++++++--
 .../apache/sis/gui/metadata/MetadataSummary.java   |  17 ++-
 2 files changed, 134 insertions(+), 11 deletions(-)

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 2fbaad2..2f08f61 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
@@ -17,7 +17,10 @@
 package org.apache.sis.gui.metadata;
 
 import java.util.Date;
+import java.util.Set;
+import java.util.LinkedHashSet;
 import java.util.StringJoiner;
+import javafx.concurrent.Task;
 import javafx.geometry.HPos;
 import javafx.scene.canvas.Canvas;
 import javafx.scene.canvas.GraphicsContext;
@@ -38,10 +41,15 @@ import org.opengis.metadata.extent.GeographicExtent;
 import org.opengis.metadata.identification.Identification;
 import org.opengis.util.InternationalString;
 import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.metadata.iso.extent.Extents;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.internal.gui.BackgroundThreads;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Longitude;
+import org.apache.sis.storage.Aggregate;
+import org.apache.sis.storage.Resource;
+import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Workaround;
 import org.apache.sis.util.resources.Vocabulary;
@@ -96,7 +104,7 @@ final class IdentificationInfo extends Section<Identification> {
     /**
      * The canvas where to draw geographic bounding boxes over a world map.
      * Shall never be null, but need to be recreated for each new map.
-     * A canvas of size (0,0) is available for drawing a new map.
+     * A canvas of size (0,0) is available at initialization time for drawing a new map.
      *
      * @see #isWorldMapEmpty()
      * @see #drawOnMap(GeographicBoundingBox)
@@ -104,6 +112,24 @@ final class IdentificationInfo extends Section<Identification>
{
     private Canvas extentOnMap;
 
     /**
+     * Whether the geographic bounding box covers the world.
+     */
+    private boolean isWorld;
+
+    /**
+     * Whether the map was visible with previous data, before {@link #buildContent(Identification)}
call.
+     * We use this information for avoiding flicker effect when a map is removed, then added
back after a
+     * slight delay by {@link #completeMissingGeographicBounds(Aggregate)}.
+     */
+    private boolean mapWasVisible;
+
+    /**
+     * The task which is running in background thread for searching bounding boxes in {@link
Aggregate} children.
+     * We use this reference for cancelling the task if a new resource is selected before
the previous task finished.
+     */
+    private Task<?> aggregateWalker;
+
+    /**
      * Creates an initially empty view for identification information.
      */
     IdentificationInfo(final MetadataSummary owner) {
@@ -124,6 +150,7 @@ final class IdentificationInfo extends Section<Identification> {
      */
     @Workaround(library = "JavaFX", version = "13")
     private void clearWorldMap() {
+        // Do not clear `isWorld` because caller may want the clear the map because it is
world map.
         if (!isWorldMapEmpty()) {
             final int p = linesStartIndex() - 1;
             assert getChildren().get(p) == extentOnMap;
@@ -149,10 +176,67 @@ final class IdentificationInfo extends Section<Identification>
{
     }
 
     /**
+     * If this pane has no geographic bounds information, search for geographic bounds in
the child resources.
+     * This method is used as a fallback when {@link #buildContent(Identification)} did not
find bounding box
+     * in the metadata directly provided. If bounds has been found, then this method does
nothing.
+     */
+    final void completeMissingGeographicBounds(final Aggregate resource) {
+        if (!isWorld && isWorldMapEmpty() && !super.isEmpty()) {
+            /*
+             * If a map was visible previously, add back an empty map for avoiding flicking
effect.
+             * If it appears that the map has no bounding box to show, it will be removed
after the
+             * background thread finished its work.
+             */
+            if (mapWasVisible) {
+                drawMapBackground();
+            }
+            BackgroundThreads.execute(aggregateWalker = new Task<Set<GeographicBoundingBox>>()
{
+                /** Invoked in a background thread for fetching bounding boxes. */
+                @Override protected Set<GeographicBoundingBox> call() throws DataStoreException
{
+                    final Set<GeographicBoundingBox> boxes = new LinkedHashSet<>();
+search:             for (final Resource child : resource.components()) {
+                        final Metadata metadata = child.getMetadata();
+                        if (metadata != null) {
+                            for (final Identification id : nonNull(metadata.getIdentificationInfo()))
{
+                                if (id != null) {
+                                    for (final Extent extent : id.getExtents()) {
+                                        if (isCancelled()) break search;
+                                        final GeographicBoundingBox b = Extents.getGeographicBoundingBox(extent);
+                                        if (b != null) boxes.add(b);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    return boxes;
+                }
+
+                /** Shows the result in JavaFX thread. */
+                @Override protected void succeeded() {
+                    aggregateWalker = null;
+                    if (!isCancelled()) {
+                        drawOnMap(getValue());
+                    }
+                }
+
+                /** Invoked in JavaFX thread if metadata loading failed. */
+                @Override protected void failed() {
+                    aggregateWalker = null;
+                    owner.setError(getException());
+                }
+            });
+        }
+    }
+
+    /**
      * Sets the identification information from the given metadata.
      */
     @Override
     void setInformation(final Metadata metadata) {
+        if (aggregateWalker != null) {
+            aggregateWalker.cancel();
+            aggregateWalker = null;
+        }
         setInformation(nonNull(metadata == null ? null : metadata.getIdentificationInfo()),
Identification[]::new);
     }
 
@@ -163,6 +247,8 @@ final class IdentificationInfo extends Section<Identification> {
      */
     @Override
     void buildContent(final Identification info) {
+        mapWasVisible = !isWorldMapEmpty();
+        isWorld = false;
         clearWorldMap();
         String text = null;
         final Citation citation = info.getCitation();
@@ -258,7 +344,6 @@ final class IdentificationInfo extends Section<Identification> {
          */
         text = null;
         Identifier identifier = null;
-        boolean isWorld = false;
         for (final Extent extent : nonNull(info.getExtents())) {
             if (extent != null) {
                 if (text == null) {
@@ -288,6 +373,23 @@ final class IdentificationInfo extends Section<Identification>
{
     }
 
     /**
+     * Draws all given geographic bounding boxes on the map.
+     */
+    private void drawOnMap(final Set<GeographicBoundingBox> boxes) {
+        if (boxes.isEmpty()) {
+            clearWorldMap();
+            return;
+        }
+        for (final GeographicBoundingBox box : boxes) {
+            isWorld = drawOnMap(box);
+            if (isWorld) {
+                clearWorldMap();
+                return;
+            }
+        }
+    }
+
+    /**
      * Draws the given geographic bounding box on the map. This method can be invoked many
times
      * if there is many bounding boxes on the same map.
      *
@@ -336,13 +438,7 @@ final class IdentificationInfo extends Section<Identification>
{
              * the rectangle in two parts because of anti-meridian crossing.
              */
             if (isWorldMapEmpty()) {
-                final Image image = MetadataSummary.getWorldMap();
-                if (image == null) {
-                    return false;                   // Failed to load the image.
-                }
-                extentOnMap.setWidth (image.getWidth());
-                extentOnMap.setHeight(image.getHeight());
-                extentOnMap.getGraphicsContext2D().drawImage(image, 0, 0);
+                drawMapBackground();
             }
             final GraphicsContext gc = extentOnMap.getGraphicsContext2D();
             gc.setStroke(Color.DARKBLUE);
@@ -365,4 +461,18 @@ final class IdentificationInfo extends Section<Identification>
{
         }
         return false;
     }
+
+    /**
+     * Draws the map where bounding boxes will be overlay.
+     */
+    private boolean drawMapBackground() {
+        final Image image = MetadataSummary.getWorldMap();
+        if (image == null) {
+            return false;
+        }
+        extentOnMap.setWidth (image.getWidth());
+        extentOnMap.setHeight(image.getHeight());
+        extentOnMap.getGraphicsContext2D().drawImage(image, 0, 0);
+        return true;
+    }
 }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
index ba28dbb..ef94a91 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
@@ -46,6 +46,7 @@ import org.apache.sis.internal.util.Strings;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.Resource;
+import org.apache.sis.storage.Aggregate;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.collection.TreeTable;
@@ -145,6 +146,7 @@ public class MetadataSummary extends Widget {
     public MetadataSummary() {
         vocabulary  = Vocabulary.getResources((Locale) null);
         information = new TitledPane[] {
+            // If order is modified, revisit `getIdentificationInfo()`.
             new TitledPane(vocabulary.getString(Vocabulary.Keys.ResourceIdentification),
new IdentificationInfo(this)),
             new TitledPane(vocabulary.getString(Vocabulary.Keys.SpatialRepresentation), 
new RepresentationInfo(this))
         };
@@ -156,6 +158,14 @@ public class MetadataSummary extends Widget {
     }
 
     /**
+     * Returns the identification information pane. This method is defined close to the constructor
+     * so we can verify that the array index matches the expected position of that pane.
+     */
+    private IdentificationInfo getIdentificationInfo() {
+        return (IdentificationInfo) information[0].getContent();
+    }
+
+    /**
      * Returns the children inside the view.
      */
     private ObservableList<Node> getChildren() {
@@ -226,6 +236,9 @@ public class MetadataSummary extends Widget {
                     for (final MetadataTree view : nativeMetadataViews) {
                         view.setContent(nativeMetadata);
                     }
+                    if (resource instanceof Aggregate) {
+                        getIdentificationInfo().completeMissingGeographicBounds((Aggregate)
resource);
+                    }
                 }
 
                 /** Invoked in JavaFX thread if metadata loading failed. */
@@ -248,7 +261,7 @@ public class MetadataSummary extends Widget {
      */
     public final void setMetadata(final Metadata metadata) {
         assert Platform.isFxApplicationThread();
-        metadataProperty.setValue(metadata);
+        metadataProperty.set(metadata);
     }
 
     /**
@@ -261,7 +274,7 @@ public class MetadataSummary extends Widget {
      * @see #setMetadata(Metadata)
      */
     public final Metadata getMetadata() {
-        return metadataProperty.getValue();
+        return metadataProperty.get();
     }
 
     /**


Mime
View raw message