From 6e13b9416a5fd1f5477eb7233f86d3eacbb88c55 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Wed, 18 Oct 2023 10:14:22 +0200 Subject: [PATCH] Improve open layers --- js/src/geo/OpenLayersMapPart.js | 30 ++++++++++++-- .../org/argeo/app/ux/js/AbstractJsObject.java | 2 +- .../src/org/argeo/app/ux/js/JsClient.java | 4 +- .../org/argeo/app/geo/acr/GeoEntityUtils.java | 18 ++++++++- .../argeo/app/geo/http/WfsHttpHandler.java | 3 +- .../argeo/app/geo/ux/OpenLayersMapPart.java | 40 +++++++++++++++++++ .../src/org/argeo/app/ol/Layer.java | 5 +++ .../src/org/argeo/app/ol/Source.java | 4 ++ 8 files changed, 97 insertions(+), 9 deletions(-) diff --git a/js/src/geo/OpenLayersMapPart.js b/js/src/geo/OpenLayersMapPart.js index 160930d..6fedd7a 100644 --- a/js/src/geo/OpenLayersMapPart.js +++ b/js/src/geo/OpenLayersMapPart.js @@ -34,6 +34,8 @@ export default class OpenLayersMapPart extends MapPart { #overviewMap; + select; + /** Styled layer descriptor */ #sld; @@ -50,6 +52,7 @@ export default class OpenLayersMapPart extends MapPart { }), ], }); + this.select = new Select(); this.#map = new Map({ controls: defaultControls({ attribution: false, @@ -70,6 +73,7 @@ export default class OpenLayersMapPart extends MapPart { // }), target: this.getMapName(), }); + this.#map.addInteraction(this.select); //this.#map.getView().set('projection', 'EPSG:4326', true); } @@ -160,7 +164,7 @@ export default class OpenLayersMapPart extends MapPart { return true; }); if (feature !== null) - mapPart.callbacks['onFeatureSingleClick'](feature.get('path')); + mapPart.callbacks['onFeatureSingleClick'](feature.get('cr:path')); }); } @@ -172,7 +176,7 @@ export default class OpenLayersMapPart extends MapPart { select.on('select', function(e) { if (e.selected.length > 0) { let feature = e.selected[0]; - mapPart.callbacks['onFeatureSelected'](feature.get('path')); + mapPart.callbacks['onFeatureSelected'](feature.get('cr:path')); } }); } @@ -216,7 +220,7 @@ export default class OpenLayersMapPart extends MapPart { return; } const coordinate = e.coordinate; - const path = selected.get('path'); + const path = selected.get('cr:path'); if (path === null) return true; const res = mapPart.callbacks['onFeaturePopup'](path); @@ -236,6 +240,26 @@ export default class OpenLayersMapPart extends MapPart { return 'map'; } + selectFeatures(layerName, featureIds) { + // we cannot use 'this' in the function provided to OpenLayers + let mapPart = this; + this.select.getFeatures().clear(); + const layer = this.getLayerByName(layerName); + const source = layer.getSource(); + for (const featureId of featureIds) { + let feature = source.getFeatureById(featureId); + if (feature === null) { + source.on('featuresloadend', function(e) { + feature = source.getFeatureById(featureId); + if (feature !== null) + mapPart.select.getFeatures().push(feature); + }); + } else { + this.select.getFeatures().push(feature); + } + } + } + // // STATIC FOR EXTENSION // diff --git a/org.argeo.app.core/src/org/argeo/app/ux/js/AbstractJsObject.java b/org.argeo.app.core/src/org/argeo/app/ux/js/AbstractJsObject.java index 8645a77..3e13b68 100644 --- a/org.argeo.app.core/src/org/argeo/app/ux/js/AbstractJsObject.java +++ b/org.argeo.app.core/src/org/argeo/app/ux/js/AbstractJsObject.java @@ -65,7 +65,7 @@ public abstract class AbstractJsObject { return reference; } - String getJsReference() { + protected String getJsReference() { return jsClient.getJsVarName(reference); } diff --git a/org.argeo.app.core/src/org/argeo/app/ux/js/JsClient.java b/org.argeo.app.core/src/org/argeo/app/ux/js/JsClient.java index 060f120..2503cf0 100644 --- a/org.argeo.app.core/src/org/argeo/app/ux/js/JsClient.java +++ b/org.argeo.app.core/src/org/argeo/app/ux/js/JsClient.java @@ -44,7 +44,7 @@ public interface JsClient { String createJsFunction(String name, Function toDo); /** Get a global variable name. */ - public String getJsVarName(String name); + String getJsVarName(String name); /** * Completion stage when the client is ready (typically the page has loaded in @@ -65,7 +65,7 @@ public interface JsClient { } default boolean isInstanceOf(String reference, String jsClass) { - return (Boolean) evaluate(getJsVarName(reference) + " instanceof " + jsClass); + return (Boolean) evaluate("return "+getJsVarName(reference) + " instanceof " + jsClass); } /* diff --git a/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java b/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java index 43f0a02..62b432e 100644 --- a/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java +++ b/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java @@ -42,7 +42,7 @@ public class GeoEntityUtils { } public static void putGeometry(Content c, QName name, Geometry geometry) { - QName jsonFileName = new ContentName(name.getNamespaceURI(), name.getLocalPart() + _GEOM_JSON); + QName jsonFileName = getJsonFileName(name); Content geom = c.soleChild(jsonFileName).orElseGet( () -> c.add(jsonFileName, Collections.singletonMap(DName.getcontenttype.qName(), "application/json"))); try (OutputStream out = geom.open(OutputStream.class)) { @@ -57,12 +57,21 @@ public class GeoEntityUtils { updateBoundingBox(c); } + public static boolean hasGeometry(Content c, QNamed name) { + return hasGeometry(c, name.qName()); + } + + public static boolean hasGeometry(Content c, QName name) { + QName jsonFileName = getJsonFileName(name); + return c.hasChild(jsonFileName); + } + public static T getGeometry(Content c, QNamed name, Class clss) { return getGeometry(c, name.qName(), clss); } public static T getGeometry(Content c, QName name, Class clss) { - QName jsonFileName = new ContentName(name.getNamespaceURI(), name.getLocalPart() + _GEOM_JSON); + QName jsonFileName = getJsonFileName(name); Content geom = c.soleChild(jsonFileName).orElse(null); if (geom == null) return null; @@ -76,6 +85,11 @@ public class GeoEntityUtils { } } + private static QName getJsonFileName(QName name) { + QName jsonFileName = new ContentName(name.getNamespaceURI(), name.getLocalPart() + _GEOM_JSON); + return jsonFileName; + } + public static Point toPoint(Content c) { if (c.hasContentClass(EntityType.geopoint)) { Double lat = c.get(WGS84PosName.lat, Double.class).orElseThrow(); diff --git a/org.argeo.app.geo/src/org/argeo/app/geo/http/WfsHttpHandler.java b/org.argeo.app.geo/src/org/argeo/app/geo/http/WfsHttpHandler.java index 1509773..c9a1e85 100644 --- a/org.argeo.app.geo/src/org/argeo/app/geo/http/WfsHttpHandler.java +++ b/org.argeo.app.geo/src/org/argeo/app/geo/http/WfsHttpHandler.java @@ -112,7 +112,8 @@ public class WfsHttpHandler implements HttpHandler { outputFormat = "application/json"; } String bboxStr = getKvpParameter(parameters, BBOX); - log.debug(bboxStr); + if (log.isTraceEnabled()) + log.trace(bboxStr); final Envelope bbox; if (bboxStr != null) { String srs; diff --git a/org.argeo.app.geo/src/org/argeo/app/geo/ux/OpenLayersMapPart.java b/org.argeo.app.geo/src/org/argeo/app/geo/ux/OpenLayersMapPart.java index a856bcc..512a473 100644 --- a/org.argeo.app.geo/src/org/argeo/app/geo/ux/OpenLayersMapPart.java +++ b/org.argeo.app.geo/src/org/argeo/app/geo/ux/OpenLayersMapPart.java @@ -1,7 +1,12 @@ package org.argeo.app.geo.ux; import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import org.argeo.app.geo.ux.MapPart.FeaturePopupEvent; +import org.argeo.app.geo.ux.MapPart.FeatureSelectedEvent; +import org.argeo.app.geo.ux.MapPart.FeatureSingleClickEvent; import org.argeo.app.ol.AbstractOlObject; import org.argeo.app.ol.Layer; import org.argeo.app.ol.OlMap; @@ -63,4 +68,39 @@ public class OpenLayersMapPart extends AbstractGeoJsObject { return mapPartName; } + public void selectFeatures(String layerName, Object... ids) { + executeMethod(getMethodName(), layerName, (Object[]) ids); + } + + /* + * CALLBACKS + */ + public void onFeatureSelected(Consumer toDo) { + addCallback("FeatureSelected", (arr) -> { + toDo.accept(new FeatureSelectedEvent((String) arr[0])); + return null; + }); + } + + public void onFeatureSingleClick(Consumer toDo) { + addCallback("FeatureSingleClick", (arr) -> { + toDo.accept(new FeatureSingleClickEvent((String) arr[0])); + return null; + }); + } + + public void onFeaturePopup(Function toDo) { + addCallback("FeaturePopup", (arr) -> { + return toDo.apply(new FeaturePopupEvent((String) arr[0])); + }); + } + + protected void addCallback(String suffix, Function toDo) { + getJsClient().getReadyStage().thenAccept((ready) -> { + String functionName = getJsClient().createJsFunction(getMapPartName() + "__on" + suffix, toDo); + getJsClient().execute(getJsReference() + ".callbacks['on" + suffix + "']=" + functionName + ";"); + executeMethod("enable" + suffix); + }); + } + } diff --git a/org.argeo.app.geo/src/org/argeo/app/ol/Layer.java b/org.argeo.app.geo/src/org/argeo/app/ol/Layer.java index f6ccd1b..feb0700 100644 --- a/org.argeo.app.geo/src/org/argeo/app/ol/Layer.java +++ b/org.argeo.app.geo/src/org/argeo/app/ol/Layer.java @@ -30,6 +30,11 @@ public class Layer extends AbstractOlObject { executeMethod(getMethodName(), source); } + public Source getSource() { + String reference = getReference() + ".getSource()"; + return new Source(getJsClient(), reference); + } + public void setMinResolution(double minResolution) { if (isNew()) getNewOptions().put("minResolution", minResolution); diff --git a/org.argeo.app.geo/src/org/argeo/app/ol/Source.java b/org.argeo.app.geo/src/org/argeo/app/ol/Source.java index 50e25da..541353e 100644 --- a/org.argeo.app.geo/src/org/argeo/app/ol/Source.java +++ b/org.argeo.app.geo/src/org/argeo/app/ol/Source.java @@ -5,4 +5,8 @@ public class Source extends AbstractOlObject { public Source(Object... args) { super(args); } + + public void refresh() { + executeMethod(getMethodName()); + } } -- 2.30.2