]> git.argeo.org Git - gpl/argeo-suite.git/blob - swt/org.argeo.app.geo.swt/src/org/argeo/app/geo/swt/SwtJSMapPart.java
Introduce WFS HTTP handler
[gpl/argeo-suite.git] / swt / org.argeo.app.geo.swt / src / org / argeo / app / geo / swt / SwtJSMapPart.java
1 package org.argeo.app.geo.swt;
2
3 import java.util.Locale;
4 import java.util.concurrent.CompletableFuture;
5 import java.util.concurrent.CompletionStage;
6 import java.util.function.Consumer;
7 import java.util.function.Function;
8
9 import org.argeo.api.cms.CmsLog;
10 import org.argeo.app.geo.ux.JsImplementation;
11 import org.argeo.app.geo.ux.MapPart;
12 import org.eclipse.swt.SWT;
13 import org.eclipse.swt.browser.Browser;
14 import org.eclipse.swt.browser.BrowserFunction;
15 import org.eclipse.swt.browser.ProgressEvent;
16 import org.eclipse.swt.browser.ProgressListener;
17 import org.eclipse.swt.layout.GridData;
18 import org.eclipse.swt.widgets.Composite;
19 import org.eclipse.swt.widgets.Control;
20
21 /**
22 * An SWT implementation of {@link MapPart} based on JavaScript execute in a
23 * {@link Browser} control.
24 */
25 public class SwtJSMapPart implements MapPart {
26 static final long serialVersionUID = 2713128477504858552L;
27
28 private final static CmsLog log = CmsLog.getLog(SwtJSMapPart.class);
29
30 private final static String GLOBAL_THIS_ = "globalThis.";
31
32 private final Browser browser;
33
34 private final CompletableFuture<Boolean> pageLoaded = new CompletableFuture<>();
35
36 private String jsImplementation = JsImplementation.OPENLAYERS_MAP_PART.getJsClass();
37 private final String mapName;// = "argeoMap";
38
39 public SwtJSMapPart(String mapName, Composite parent, int style) {
40 this.mapName = mapName;
41 browser = new Browser(parent, 0);
42 browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
43
44 browser.setUrl("/pkg/org.argeo.app.geo.js/index.html");
45 browser.addProgressListener(new ProgressListener() {
46 static final long serialVersionUID = 1L;
47
48 @Override
49 public void completed(ProgressEvent event) {
50 try {
51 // create map
52 browser.execute(getJsMapVar() + " = new " + jsImplementation + "('" + mapName + "');");
53 loadExtensions();
54 pageLoaded.complete(true);
55 } catch (Exception e) {
56 log.error("Cannot create map in browser", e);
57 pageLoaded.complete(false);
58 }
59 }
60
61 @Override
62 public void changed(ProgressEvent event) {
63 }
64 });
65 }
66
67 public Control getControl() {
68 return browser;
69 }
70
71 /*
72 * MapPart.js METHODS
73 */
74
75 @Override
76 public void addPoint(double lng, double lat, String style) {
77 callMapMethod("addPoint(%f, %f, %s)", lng, lat, style == null ? "'default'" : style);
78 }
79
80 @Override
81 public void addUrlLayer(String url, GeoFormat format, String style) {
82 callMapMethod("addUrlLayer('%s', '%s', %s)", url, format.name(), style);
83 }
84
85 @Override
86 public void setZoom(int zoom) {
87 callMapMethod("setZoom(%d)", zoom);
88 }
89
90 @Override
91 public void setCenter(double lng, double lat) {
92 callMapMethod("setCenter(%f, %f)", lng, lat);
93 }
94
95 protected CompletionStage<Object> callMapMethod(String methodCall, Object... args) {
96 return callMethod(getJsMapVar(), methodCall, args);
97 }
98
99 protected CompletionStage<Object> callMethod(String jsObject, String methodCall, Object... args) {
100 return evaluate(jsObject + '.' + methodCall, args);
101 }
102
103 private String getJsMapVar() {
104 return GLOBAL_THIS_ + mapName;
105 }
106
107 /**
108 * Execute this JavaScript on the client side after making sure that the page
109 * has been loaded and the map object has been created.
110 *
111 * @param js the JavaScript code, possibly formatted according to
112 * {@link String#format}, with {@link Locale#ROOT} as locale (for
113 * stability of decimal separator, as expected by JavaScript.
114 * @param args the optional arguments of
115 * {@link String#format(String, Object...)}
116 */
117 protected CompletionStage<Object> evaluate(String js, Object... args) {
118 CompletableFuture<Object> res = pageLoaded.thenApply((ready) -> {
119 if (!ready)
120 throw new IllegalStateException("Map " + mapName + " is not initialised.");
121 Object result = browser.evaluate(String.format(Locale.ROOT, js, args));
122 return result;
123 });
124 return res.minimalCompletionStage();
125 }
126
127 protected void loadExtension(String url) {
128 // String js = """
129 // var script = document.createElement("script");
130 // script.src = '%s';
131 // document.head.appendChild(script);
132 // """;
133 // browser.evaluate(String.format(Locale.ROOT, js, url));
134 browser.evaluate(String.format(Locale.ROOT, "import('%s')", url));
135 }
136
137 /** To be overridden with calls to {@link #loadExtension(String)}. */
138 protected void loadExtensions() {
139
140 }
141
142 /*
143 * CALLBACKS
144 */
145 public void onFeatureSelected(Consumer<FeatureSelectedEvent> toDo) {
146 addCallback("FeatureSelected", (arr) -> {
147 toDo.accept(new FeatureSelectedEvent((String) arr[0]));
148 return null;
149 });
150 }
151
152 public void onFeatureSingleClick(Consumer<FeatureSingleClickEvent> toDo) {
153 addCallback("FeatureSingleClick", (arr) -> {
154 toDo.accept(new FeatureSingleClickEvent((String) arr[0]));
155 return null;
156 });
157 }
158
159 public void onFeaturePopup(Function<FeaturePopupEvent, String> toDo) {
160 addCallback("FeaturePopup", (arr) -> {
161 return toDo.apply(new FeaturePopupEvent((String) arr[0]));
162 });
163 }
164
165 protected void addCallback(String suffix, Function<Object[], Object> toDo) {
166 pageLoaded.thenAccept((ready) -> {
167 // browser functions must be directly on window (RAP specific)
168 new BrowserFunction(browser, mapName + "__on" + suffix) {
169
170 @Override
171 public Object function(Object[] arguments) {
172 Object result = toDo.apply(arguments);
173 return result;
174 }
175
176 };
177 browser.execute(getJsMapVar() + ".callbacks['on" + suffix + "']=window." + mapName + "__on" + suffix + ";");
178 callMethod(mapName, "enable" + suffix + "()");
179 });
180 }
181 }