import VectorLayer from 'ol/layer/Vector.js';
import GeoJSON from 'ol/format/GeoJSON.js';
import GPX from 'ol/format/GPX.js';
+import Select from 'ol/interaction/Select.js';
import MapPart from './MapPart.js';
import { SentinelCloudless } from './OpenLayerTileSources.js';
export default class OpenLayersMapPart extends MapPart {
/** The OpenLayers Map. */
#map;
+ callbacks = {};
+
// Constructor
constructor() {
super();
});
}
+ /* GEOGRAPHICAL METHODS */
+
setZoom(zoom) {
this.#map.getView().setZoom(zoom);
}
source: vectorSource,
}));
}
+
+ /* CALLBACKS */
+ enableFeatureSingleClick() {
+ // we cannot use 'this' in the function provided to OpenLayers
+ let mapPart = this;
+ this.#map.on('singleclick', function(e) {
+ let feature = null;
+ // we chose only one
+ e.map.forEachFeatureAtPixel(e.pixel, function(f) {
+ feature = f;
+ });
+ if (feature !== null)
+ mapPart.callbacks['onFeatureSingleClick'](feature.get('path'));
+ });
+ }
+
+ enableFeatureSelected() {
+ // we cannot use 'this' in the function provided to OpenLayers
+ let mapPart = this;
+ var select = new Select();
+ this.#map.addInteraction(select);
+ select.on('select', function(e) {
+ if (e.selected.length > 0) {
+ let feature = e.selected[0];
+ mapPart.callbacks['onFeatureSelected'](feature.get('path'));
+ }
+ });
+ }
}
void setZoom(int zoom);
void setCenter(double lng, double lat);
+
+ /** Event when a feature has been single-clicked. */
+ record FeatureSingleClickEvent(String path) {
+ };
+
+ /** Event when a feature has been selected. */
+ record FeatureSelectedEvent(String path) {
+ };
}
@Override
public Control createUiPart(Composite parent, Content context) {
- SwtJavaScriptMapPart map = new SwtJavaScriptMapPart(parent, 0);
+ SwtJSMapPart map = new SwtJSMapPart(parent, 0);
map.setCenter(13.404954, 52.520008); // Berlin
// map.setCenter(-74.00597, 40.71427); // NYC
// map.addPoint(-74.00597, 40.71427, null);
--- /dev/null
+package org.argeo.app.geo.swt;
+
+import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.function.Consumer;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.app.geo.ux.JsImplementation;
+import org.argeo.app.geo.ux.MapPart;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.BrowserFunction;
+import org.eclipse.swt.browser.ProgressEvent;
+import org.eclipse.swt.browser.ProgressListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * An SWT implementation of {@link MapPart} based on JavaScript execute in a
+ * {@link Browser} control.
+ */
+public class SwtJSMapPart extends Composite implements MapPart {
+ static final long serialVersionUID = 2713128477504858552L;
+
+ private final static CmsLog log = CmsLog.getLog(SwtJSMapPart.class);
+
+ private final static String GLOBAL_THIS_ = "globalThis.";
+
+ private final Browser browser;
+
+ private final CompletableFuture<Boolean> pageLoaded = new CompletableFuture<>();
+
+ private String jsImplementation = JsImplementation.OPENLAYERS_MAP_PART.getJsClass();
+ private String mapVar = "argeoMap";
+
+ public SwtJSMapPart(Composite parent, int style) {
+ super(parent, style);
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+ browser = new Browser(this, SWT.BORDER);
+ browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ browser.setUrl("/pkg/org.argeo.app.geo.js/index.html");
+ browser.addProgressListener(new ProgressListener() {
+ static final long serialVersionUID = 1L;
+
+ @Override
+ public void completed(ProgressEvent event) {
+ try {
+ // create map
+ browser.execute(getJsMapVar() + " = new " + jsImplementation + "();");
+ pageLoaded.complete(true);
+ } catch (Exception e) {
+ log.error("Cannot create map in browser", e);
+ pageLoaded.complete(false);
+ }
+ }
+
+ @Override
+ public void changed(ProgressEvent event) {
+ }
+ });
+ }
+
+ /*
+ * MapPart.js METHODS
+ */
+
+ @Override
+ public void addPoint(double lng, double lat, String style) {
+ callMapMethod("addPoint(%f, %f, %s)", lng, lat, style == null ? "'default'" : style);
+ }
+
+ @Override
+ public void addUrlLayer(String url, GeoFormat format) {
+ callMapMethod("addUrlLayer('%s', '%s')", url, format.name());
+ }
+
+ @Override
+ public void setZoom(int zoom) {
+ callMapMethod("setZoom(%d)", zoom);
+ }
+
+ @Override
+ public void setCenter(double lng, double lat) {
+ callMapMethod("setCenter(%f, %f)", lng, lat);
+ }
+
+ protected CompletionStage<Object> callMapMethod(String methodCall, Object... args) {
+ return callMethod(getJsMapVar(), methodCall, args);
+ }
+
+ protected CompletionStage<Object> callMethod(String jsObject, String methodCall, Object... args) {
+ return evaluate(jsObject + '.' + methodCall, args);
+ }
+
+ private String getJsMapVar() {
+ return GLOBAL_THIS_ + mapVar;
+ }
+
+ /**
+ * Execute this JavaScript on the client side after making sure that the page
+ * has been loaded and the map object has been created.
+ *
+ * @param js the JavaScript code, possibly formatted according to
+ * {@link String#format}, with {@link Locale#ROOT} as locale (for
+ * stability of decimal separator, as expected by JavaScript.
+ * @param args the optional arguments of
+ * {@link String#format(String, Object...)}
+ */
+ protected CompletionStage<Object> evaluate(String js, Object... args) {
+ CompletableFuture<Object> res = pageLoaded.thenApply((ready) -> {
+ if (!ready)
+ throw new IllegalStateException("Map " + mapVar + " is not initialised.");
+ Object result = browser.evaluate(String.format(Locale.ROOT, js, args));
+ return result;
+ });
+ return res.minimalCompletionStage();
+ }
+
+ /*
+ * CALLBACKS
+ */
+ public void onFeatureSelected(Consumer<FeatureSelectedEvent> toDo) {
+ addCallback("FeatureSelected", (arr) -> toDo.accept(new FeatureSelectedEvent((String) arr[0])));
+ }
+
+ public void onFeatureSingleClick(Consumer<FeatureSingleClickEvent> toDo) {
+ addCallback("FeatureSingleClick", (arr) -> toDo.accept(new FeatureSingleClickEvent((String) arr[0])));
+ }
+
+ protected void addCallback(String suffix, Consumer<Object[]> toDo) {
+ pageLoaded.thenAccept((ready) -> {
+ // browser functions must be directly on window (RAP specific)
+ new BrowserFunction(browser, mapVar + "__on" + suffix) {
+
+ @Override
+ public Object function(Object[] arguments) {
+ toDo.accept(arguments);
+ return null;
+ }
+
+ };
+ browser.execute(getJsMapVar() + ".callbacks['on" + suffix + "']=window." + mapVar + "__on" + suffix + ";");
+ callMethod(mapVar, "enable" + suffix + "()");
+ });
+ }
+}
+++ /dev/null
-package org.argeo.app.geo.swt;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.geo.ux.JsImplementation;
-import org.argeo.app.geo.ux.MapPart;
-import org.argeo.app.ux.SuiteUxEvent;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.browser.Browser;
-import org.eclipse.swt.browser.BrowserFunction;
-import org.eclipse.swt.browser.ProgressEvent;
-import org.eclipse.swt.browser.ProgressListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-
-/**
- * An SWT implementation of {@link MapPart} based on JavaScript execute in a
- * {@link Browser} control.
- */
-public class SwtJavaScriptMapPart extends Composite implements MapPart {
- static final long serialVersionUID = 2713128477504858552L;
-
- private final static CmsLog log = CmsLog.getLog(SwtJavaScriptMapPart.class);
-
- private Browser browser;
-
- private CompletableFuture<Boolean> pageLoaded = new CompletableFuture<>();
-
- private String jsImplementation = JsImplementation.OPENLAYERS_MAP_PART.getJsClass();
- private String mapVar = "globalThis.argeoMap";
-
- private final CmsView cmsView;
-
- public SwtJavaScriptMapPart(Composite parent, int style) {
- super(parent, style);
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
- setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- setLayout(CmsSwtUtils.noSpaceGridLayout());
-
- cmsView = CmsSwtUtils.getCmsView(parent);
-
- browser = new Browser(this, SWT.BORDER);
- browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
- // functions exposed to JavaScript
- new onFeatureSelect();
-
- browser.setUrl("/pkg/org.argeo.app.geo.js/index.html");
- browser.addProgressListener(new ProgressListener() {
- static final long serialVersionUID = 1L;
-
- @Override
- public void completed(ProgressEvent event) {
- try {
- // create map
- browser.execute(mapVar + " = new " + jsImplementation + "();");
- pageLoaded.complete(true);
- } catch (Exception e) {
- log.error("Cannot create map in browser", e);
- pageLoaded.complete(false);
- }
- }
-
- @Override
- public void changed(ProgressEvent event) {
- }
- });
- }
-
- @Override
- public void addPoint(double lng, double lat, String style) {
- callMethod(mapVar, "addPoint(%f, %f, %s)", lng, lat, style == null ? "'default'" : style);
- }
-
- @Override
- public void addUrlLayer(String url, GeoFormat format) {
- callMethod(mapVar, "addUrlLayer('%s', '%s')", url, format.name());
- }
-
- @Override
- public void setZoom(int zoom) {
- callMethod(mapVar, "setZoom(%d)", zoom);
- }
-
- @Override
- public void setCenter(double lng, double lat) {
- callMethod(mapVar, "setCenter(%f, %f)", lng, lat);
- }
-
- protected CompletionStage<Object> callMethod(String jsObject, String methodCall, Object... args) {
- return evaluate(jsObject + '.' + methodCall, args);
- }
-
- /**
- * Execute this JavaScript on the client side after making sure that the page
- * has been loaded and the map object has been created.
- *
- * @param js the JavaScript code, possibly formatted according to
- * {@link String#format}, with {@link Locale#ROOT} as locale (for
- * stability of decimal separator, as expected by JavaScript.
- * @param args the optional arguments of
- * {@link String#format(String, Object...)}
- */
- protected CompletionStage<Object> evaluate(String js, Object... args) {
- CompletableFuture<Object> res = pageLoaded.thenApply((ready) -> {
- if (!ready)
- throw new IllegalStateException("Map " + mapVar + " is not initialised.");
- Object result = browser.evaluate(String.format(Locale.ROOT, js, args));
- return result;
- });
- return res.minimalCompletionStage();
- }
-
- /** JavaScript function called when a feature is selected on the map. */
- private class onFeatureSelect extends BrowserFunction {
-
- onFeatureSelect() {
- super(browser, onFeatureSelect.class.getSimpleName());
- }
-
- @Override
- public Object function(Object[] arguments) {
- if (arguments.length == 0)
- return null;
- String path = arguments[0].toString();
- Map<String, Object> properties = new HashMap<>();
- properties.put(SuiteUxEvent.CONTENT_PATH, '/' + CmsConstants.SYS_WORKSPACE + path);
- cmsView.sendEvent(SuiteUxEvent.refreshPart.topic(), properties);
- return null;
- }
-
- }
-}