@emuanalytics/flow-rdf-core 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +64 -0
  2. package/dist/flow-rdf-core.css +1 -0
  3. package/dist/index.cjs +3102 -0
  4. package/dist/index.d.cts +61 -0
  5. package/dist/index.d.mts +61 -0
  6. package/dist/index.d.ts +61 -0
  7. package/dist/index.mjs +3062 -0
  8. package/dist/lib/map/plugins/index.cjs +1652 -0
  9. package/dist/lib/map/plugins/index.d.cts +309 -0
  10. package/dist/lib/map/plugins/index.d.mts +309 -0
  11. package/dist/lib/map/plugins/index.d.ts +309 -0
  12. package/dist/lib/map/plugins/index.mjs +1614 -0
  13. package/dist/lib/utils/index.cjs +45 -0
  14. package/dist/lib/utils/index.d.cts +39 -0
  15. package/dist/lib/utils/index.d.mts +39 -0
  16. package/dist/lib/utils/index.d.ts +39 -0
  17. package/dist/lib/utils/index.mjs +5 -0
  18. package/dist/shared/flow-rdf-core.08975378.d.cts +68 -0
  19. package/dist/shared/flow-rdf-core.08975378.d.mts +68 -0
  20. package/dist/shared/flow-rdf-core.08975378.d.ts +68 -0
  21. package/dist/shared/flow-rdf-core.278a260a.cjs +1364 -0
  22. package/dist/shared/flow-rdf-core.2e456f93.mjs +83 -0
  23. package/dist/shared/flow-rdf-core.3042be4a.cjs +93 -0
  24. package/dist/shared/flow-rdf-core.84eded74.cjs +34 -0
  25. package/dist/shared/flow-rdf-core.b4b16e78.mjs +1329 -0
  26. package/dist/shared/flow-rdf-core.b80085b4.mjs +66 -0
  27. package/dist/shared/flow-rdf-core.bd95d618.mjs +29 -0
  28. package/dist/shared/flow-rdf-core.c44cf082.cjs +88 -0
  29. package/dist/shared/flow-rdf-core.c9d2f730.d.cts +1815 -0
  30. package/dist/shared/flow-rdf-core.c9d2f730.d.mts +1815 -0
  31. package/dist/shared/flow-rdf-core.c9d2f730.d.ts +1815 -0
  32. package/package.json +106 -0
  33. package/plugins/package.json +7 -0
  34. package/utils/package.json +7 -0
@@ -0,0 +1,1614 @@
1
+ import { M as MapEvent, a as MapMouseEvent, b as assert, c as MapFeatureStream, F as FlowError, d as FlowMapError, e as MapDataSource, f as MapLayer, r as randomId, R as ReactiveExpression } from '../../../shared/flow-rdf-core.b4b16e78.mjs';
2
+ export { P as PluginManager } from '../../../shared/flow-rdf-core.b4b16e78.mjs';
3
+ import MapboxDraw from '@emuanalytics/mapbox-gl-draw';
4
+ import turfArea from '@turf/area';
5
+ import turfBBox from '@turf/bbox';
6
+ import turfBooleanEqual from '@turf/boolean-equal';
7
+ import turfBuffer from '@turf/buffer';
8
+ import turfLength from '@turf/length';
9
+ import Debug from 'debug';
10
+ import { c as isPolygon, i as isLineString, b as isPoint } from '../../../shared/flow-rdf-core.bd95d618.mjs';
11
+ import EventEmitter$1, { EventEmitter } from 'eventemitter3';
12
+ import { d as isDefined, g as isStringFull, x as isFunction, f as isString, e as ensureArray, o as isNumber, z as isURL, k as isArrayFull, b as isNil, a as isNull, m as isObject } from '../../../shared/flow-rdf-core.b80085b4.mjs';
13
+ import * as lit from 'lit-html';
14
+ import maplibre__default from '@emuanalytics/maplibre-gl';
15
+ import turfCircle from '@turf/circle';
16
+ import turfDistance from '@turf/distance';
17
+ import { point } from '@turf/helpers';
18
+ import { autorun, toJS } from 'mobx';
19
+ import '@emuanalytics/maplibre-gl-style-spec';
20
+ import 'mobx-utils';
21
+ import 'parsimmon';
22
+ import 'colorbrewer';
23
+ import 'nanoid';
24
+ import '@turf/invariant';
25
+
26
+ var __defProp$a = Object.defineProperty;
27
+ var __defNormalProp$a = (obj, key, value) => key in obj ? __defProp$a(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
28
+ var __publicField$a = (obj, key, value) => {
29
+ __defNormalProp$a(obj, typeof key !== "symbol" ? key + "" : key, value);
30
+ return value;
31
+ };
32
+ class AnnotationEvent extends MapEvent {
33
+ constructor(type, map, data) {
34
+ super(type, map);
35
+ __publicField$a(this, "mode");
36
+ __publicField$a(this, "canUndo");
37
+ __publicField$a(this, "canRedo");
38
+ __publicField$a(this, "activeFeatureId");
39
+ __publicField$a(this, "activeFeatureType");
40
+ __publicField$a(this, "features");
41
+ this.mode = data.mode;
42
+ this.canUndo = data.canUndo;
43
+ this.canRedo = data.canRedo;
44
+ this.activeFeatureId = data.activeFeatureId;
45
+ this.activeFeatureType = data.activeFeatureType;
46
+ this.features = data.features;
47
+ }
48
+ }
49
+
50
+ var __defProp$9 = Object.defineProperty;
51
+ var __defNormalProp$9 = (obj, key, value) => key in obj ? __defProp$9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
52
+ var __publicField$9 = (obj, key, value) => {
53
+ __defNormalProp$9(obj, typeof key !== "symbol" ? key + "" : key, value);
54
+ return value;
55
+ };
56
+ class MapPluginBase extends EventEmitter {
57
+ constructor(pluginManager, options) {
58
+ super();
59
+ __publicField$9(this, "_pluginManager");
60
+ __publicField$9(this, "options");
61
+ this._pluginManager = pluginManager;
62
+ this.options = options ?? {};
63
+ }
64
+ get map() {
65
+ return this._pluginManager.map;
66
+ }
67
+ get pluginManager() {
68
+ return this._pluginManager;
69
+ }
70
+ }
71
+
72
+ function isFeatureWithId(f) {
73
+ return isDefined(f) && isDefined(f.source) && isDefined(f.id);
74
+ }
75
+
76
+ var __defProp$8 = Object.defineProperty;
77
+ var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
78
+ var __publicField$8 = (obj, key, value) => {
79
+ __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value);
80
+ return value;
81
+ };
82
+ Debug("flow:map-popup");
83
+ class MapPopup extends EventEmitter$1 {
84
+ /**
85
+ * Constructs a new `MapPopup` instance.
86
+ *
87
+ * This is called by the `FlowMap::openPopup` method.
88
+ *
89
+ * @param map Parent Flo.w map.
90
+ * @param options Popup options.
91
+ */
92
+ constructor(map, options) {
93
+ super();
94
+ this.map = map;
95
+ this.options = options;
96
+ __publicField$8(this, "_feature");
97
+ __publicField$8(this, "_contentContainer");
98
+ __publicField$8(this, "_featureStream", null);
99
+ //private _featureStreamSubscription: ISubscription | null = null;
100
+ __publicField$8(this, "_maplibrePopup", null);
101
+ __publicField$8(this, "_featureStreamDisposer");
102
+ this.options = {
103
+ // Defaults
104
+ closeButton: true,
105
+ closeOnClick: false,
106
+ closeOnMove: false,
107
+ maxWidth: "300px",
108
+ trackFeature: true,
109
+ //
110
+ ...this.options
111
+ };
112
+ }
113
+ get feature() {
114
+ return this._feature;
115
+ }
116
+ /**
117
+ * Close the popup.
118
+ */
119
+ close() {
120
+ if (this._maplibrePopup) {
121
+ this._maplibrePopup.remove();
122
+ this._maplibrePopup = null;
123
+ }
124
+ }
125
+ /**
126
+ * Move the popup to a new location.
127
+ *
128
+ * @param lngLat New location.
129
+ */
130
+ moveTo(lngLat) {
131
+ this._maplibrePopup.setLngLat(lngLat);
132
+ }
133
+ open(eventOrLngLat, feature) {
134
+ if (this._maplibrePopup) {
135
+ return;
136
+ }
137
+ if (eventOrLngLat instanceof MapMouseEvent) {
138
+ if (isFeatureWithId(eventOrLngLat.features[0])) {
139
+ feature = eventOrLngLat.features[0];
140
+ }
141
+ eventOrLngLat = eventOrLngLat.lngLat;
142
+ }
143
+ if (feature) {
144
+ assert(isFeatureWithId(feature), "MapPopup.open(): feature must have unique ID");
145
+ }
146
+ this._maplibrePopup = new maplibre__default.Popup({
147
+ className: this.cssClasses().join(" "),
148
+ closeButton: this.options.closeButton,
149
+ closeOnClick: this.options.closeOnClick,
150
+ closeOnMove: this.options.closeOnMove,
151
+ maxWidth: this.options.maxWidth
152
+ });
153
+ this._maplibrePopup.once("close", this.popupCloseHandler.bind(this));
154
+ this._contentContainer = document.createElement("div");
155
+ this._contentContainer.classList.add("flow-map-popup-content");
156
+ this._maplibrePopup.setDOMContent(this._contentContainer);
157
+ this._maplibrePopup.addTo(this.map.maplibreMap);
158
+ if (feature) {
159
+ this._feature = feature;
160
+ this._featureStream = new MapFeatureStream({
161
+ map: this.map,
162
+ source: feature.source,
163
+ sourceLayer: feature.sourceLayer,
164
+ featureIds: [feature.id]
165
+ });
166
+ this._featureStreamDisposer = this.map.context.state.autorun(() => {
167
+ const data = this._featureStream?.featureData;
168
+ if (data) {
169
+ this.handleFeatureUpdate(data[feature.id]);
170
+ }
171
+ });
172
+ } else {
173
+ this.moveTo(eventOrLngLat);
174
+ this.renderContent(null);
175
+ }
176
+ }
177
+ cssClasses() {
178
+ const classes = ["flow-map-popup"];
179
+ if (isStringFull(this.options.class)) {
180
+ classes.push(this.options.class);
181
+ }
182
+ if (this.options.closeButton) {
183
+ classes.push("flow-map-popup--with-close");
184
+ }
185
+ return classes;
186
+ }
187
+ handleFeatureUpdate(feature) {
188
+ if (!feature) {
189
+ this.close();
190
+ return;
191
+ }
192
+ if (this.options.trackFeature) {
193
+ switch (feature.geometry.type) {
194
+ case "Point":
195
+ this.moveTo(feature.geometry.coordinates);
196
+ break;
197
+ case "MultiPoint":
198
+ this.moveTo(feature.geometry.coordinates[0]);
199
+ break;
200
+ }
201
+ }
202
+ this.renderContent(feature);
203
+ }
204
+ popupCloseHandler() {
205
+ if (this._featureStreamDisposer) {
206
+ this._featureStreamDisposer();
207
+ this._featureStreamDisposer = void 0;
208
+ }
209
+ this.emit("closed", this);
210
+ this.removeAllListeners();
211
+ }
212
+ renderContent(feature) {
213
+ const state = this.map.context.state;
214
+ const content = isFunction(this.options.content) ? this.options.content(feature, state) : this.options.content;
215
+ if (isString(content)) {
216
+ this._contentContainer.innerHTML = content;
217
+ } else if (content instanceof Node) {
218
+ this._contentContainer.innerHTML = "";
219
+ this._contentContainer.appendChild(content);
220
+ } else {
221
+ lit.render(content, this._contentContainer);
222
+ }
223
+ }
224
+ }
225
+
226
+ var __defProp$7 = Object.defineProperty;
227
+ var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
228
+ var __publicField$7 = (obj, key, value) => {
229
+ __defNormalProp$7(obj, typeof key !== "symbol" ? key + "" : key, value);
230
+ return value;
231
+ };
232
+ class PopupPlugin extends MapPluginBase {
233
+ constructor(pluginManager) {
234
+ super(pluginManager);
235
+ __publicField$7(this, "_popupTemplates", /* @__PURE__ */ new Map());
236
+ __publicField$7(this, "_popups", /* @__PURE__ */ new Set());
237
+ __publicField$7(this, "name", "PopupPlugin");
238
+ }
239
+ registerPopup(id, options) {
240
+ this._popupTemplates.set(id, options);
241
+ }
242
+ openPopup(id, eventOrLngLat, feature) {
243
+ const popupOptions = this._popupTemplates.get(id);
244
+ if (!popupOptions) {
245
+ throw new FlowError(`MapPopup: no popup with id '${id}'`);
246
+ }
247
+ const popup = new MapPopup(this.map, popupOptions);
248
+ this._popups.add(popup);
249
+ popup.once("closed", () => {
250
+ this._popups.delete(popup);
251
+ });
252
+ if (eventOrLngLat instanceof MapMouseEvent) {
253
+ popup.open(eventOrLngLat);
254
+ } else {
255
+ popup.open(eventOrLngLat, feature);
256
+ }
257
+ return popup;
258
+ }
259
+ closeAllPopups() {
260
+ this._popups.forEach((popup) => popup.close());
261
+ this._popups.clear();
262
+ }
263
+ closePopupsForSources(sources) {
264
+ sources = ensureArray(sources);
265
+ this._popups.forEach((popup) => {
266
+ if (popup.feature?.source && sources.indexOf(popup.feature?.source) >= 0) {
267
+ popup.close();
268
+ this._popups.delete(popup);
269
+ }
270
+ });
271
+ }
272
+ }
273
+
274
+ var __defProp$6 = Object.defineProperty;
275
+ var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
276
+ var __publicField$6 = (obj, key, value) => {
277
+ __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
278
+ return value;
279
+ };
280
+ class SelectionEvent extends MapEvent {
281
+ constructor(type, map, sourceIdentifier, features) {
282
+ super(type, map);
283
+ __publicField$6(this, "source");
284
+ __publicField$6(this, "sourceLayer");
285
+ __publicField$6(this, "features");
286
+ this.source = sourceIdentifier.source;
287
+ this.sourceLayer = sourceIdentifier.sourceLayer;
288
+ this.features = features;
289
+ }
290
+ }
291
+
292
+ var __defProp$5 = Object.defineProperty;
293
+ var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
294
+ var __publicField$5 = (obj, key, value) => {
295
+ __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
296
+ return value;
297
+ };
298
+ class SourceSelection {
299
+ constructor(plugin, source, options) {
300
+ __publicField$5(this, "_featureStream");
301
+ __publicField$5(this, "_map");
302
+ __publicField$5(this, "_options");
303
+ __publicField$5(this, "_plugin");
304
+ __publicField$5(this, "_popups", /* @__PURE__ */ new Map());
305
+ __publicField$5(this, "_selectedFeatureIds", /* @__PURE__ */ new Set());
306
+ __publicField$5(this, "_sourceId");
307
+ __publicField$5(this, "_stateSubscription", null);
308
+ this._plugin = plugin;
309
+ this._map = plugin.map;
310
+ this._options = options;
311
+ const sourceType = plugin.map.getSource(source).type;
312
+ this._sourceId = {
313
+ source,
314
+ sourceLayer: sourceType === "vector" ? options.sourceLayer ?? "geometry" : void 0
315
+ };
316
+ this._featureStream = this._map.featureStream({
317
+ source,
318
+ sourceLayer: options.sourceLayer,
319
+ featureIds: []
320
+ });
321
+ if (options.selectionState) {
322
+ this._stateSubscription = this._map.context.state.toStream(options.selectionState, true).subscribe((val) => {
323
+ val = new Set(ensureArray(val));
324
+ this.updateSelection(val);
325
+ });
326
+ }
327
+ }
328
+ get options() {
329
+ return this._options;
330
+ }
331
+ get selectedFeatureIds() {
332
+ return this._selectedFeatureIds;
333
+ }
334
+ get selectedFeatureStream() {
335
+ return this._featureStream;
336
+ }
337
+ destroy() {
338
+ this._popups.forEach((popup) => popup.close());
339
+ this._stateSubscription?.unsubscribe();
340
+ this._selectedFeatureIds = /* @__PURE__ */ new Set();
341
+ this.updateMapSelectionState();
342
+ }
343
+ handleClick(lngLat, feature, selectionOptions, multiSelect, enablePopups) {
344
+ if (this._options.mode === "none") {
345
+ return;
346
+ }
347
+ const selectedFeatureIDs = new Set(this._selectedFeatureIds);
348
+ let clearSelection = false;
349
+ let toggleFeature = false;
350
+ let showPopup = false;
351
+ if (feature && feature.source === this._sourceId.source) {
352
+ if (this._options.mode === "single") {
353
+ if (selectedFeatureIDs.has(feature.id)) {
354
+ toggleFeature = true;
355
+ } else {
356
+ clearSelection = true;
357
+ toggleFeature = true;
358
+ }
359
+ } else {
360
+ if (!multiSelect) {
361
+ clearSelection = true;
362
+ }
363
+ toggleFeature = true;
364
+ }
365
+ } else if (feature && feature.source !== this._sourceId.source) {
366
+ clearSelection = this._options.clear === "group" ? selectionOptions?.group === this._options.group : !!this._options.clear;
367
+ } else {
368
+ clearSelection = !multiSelect;
369
+ }
370
+ if (clearSelection) {
371
+ selectedFeatureIDs.clear();
372
+ }
373
+ if (toggleFeature) {
374
+ assert(isFeatureWithId(feature));
375
+ if (selectedFeatureIDs.has(feature.id) && multiSelect) {
376
+ selectedFeatureIDs.delete(feature.id);
377
+ if (this._popups.has(feature.id))
378
+ this._popups.get(feature.id).close();
379
+ } else {
380
+ selectedFeatureIDs.add(feature.id);
381
+ showPopup = true;
382
+ }
383
+ }
384
+ this.updateSelection(selectedFeatureIDs);
385
+ if (enablePopups && showPopup && this._options.popup) {
386
+ assert(isFeatureWithId(feature));
387
+ if (!this._popups.has(feature.id)) {
388
+ const popup = this._map.getPlugin(PopupPlugin).openPopup(this._options.popup, lngLat, feature);
389
+ if (popup) {
390
+ popup.on("closed", () => {
391
+ this._popups.delete(feature.id);
392
+ });
393
+ this._popups.set(feature.id, popup);
394
+ }
395
+ }
396
+ }
397
+ }
398
+ updateSelection(featureIds) {
399
+ if (!this.areSetsEqual(featureIds, this._selectedFeatureIds)) {
400
+ this._selectedFeatureIds = featureIds;
401
+ this.updateMapSelectionState();
402
+ this.updateStateStore();
403
+ this._featureStream.featureIds = Array.from(featureIds);
404
+ this._popups.forEach((popup) => {
405
+ assert(isFeatureWithId(popup.feature));
406
+ if (popup.feature.id && !featureIds.has(popup.feature.id)) {
407
+ popup.close();
408
+ }
409
+ });
410
+ const event = new SelectionEvent(
411
+ "selectionChanged",
412
+ this._map,
413
+ this._sourceId,
414
+ Array.from(this._selectedFeatureIds)
415
+ );
416
+ this._plugin.emit(event.type, event);
417
+ }
418
+ }
419
+ areSetsEqual(a, b) {
420
+ return a.size === b.size && [...a].every((value) => b.has(value));
421
+ }
422
+ updateMapSelectionState() {
423
+ this._map.removeFeatureState(this._sourceId, "selected", false);
424
+ this._selectedFeatureIds.forEach(
425
+ (id) => this._map.setFeatureState({ ...this._sourceId, id }, { selected: true }, false)
426
+ );
427
+ }
428
+ updateStateStore() {
429
+ if (isStringFull(this._options.selectionState)) {
430
+ const selectedIds = Array.from(this._selectedFeatureIds);
431
+ if (this._options.mode === "single") {
432
+ this._map.context.state.set(this._options.selectionState, selectedIds[0]);
433
+ } else if (this._options.mode === "multi") {
434
+ this._map.context.state.set(this._options.selectionState, selectedIds);
435
+ }
436
+ }
437
+ }
438
+ }
439
+
440
+ var __defProp$4 = Object.defineProperty;
441
+ var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
442
+ var __publicField$4 = (obj, key, value) => {
443
+ __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
444
+ return value;
445
+ };
446
+ class SelectionPlugin extends MapPluginBase {
447
+ constructor(pluginManager) {
448
+ super(pluginManager);
449
+ __publicField$4(this, "_sourceSelections", /* @__PURE__ */ new Map());
450
+ __publicField$4(this, "_enablePopups", true);
451
+ __publicField$4(this, "name", "SelectionPlugin");
452
+ }
453
+ clearSelection(source) {
454
+ const sourceSelection = this._sourceSelections.get(source);
455
+ if (!sourceSelection) {
456
+ return;
457
+ }
458
+ switch (sourceSelection.options.mode) {
459
+ case "single":
460
+ case "multi":
461
+ sourceSelection.updateSelection(/* @__PURE__ */ new Set());
462
+ }
463
+ }
464
+ deselectFeatures(source, features) {
465
+ const sourceSelection = this._sourceSelections.get(source);
466
+ if (!sourceSelection) {
467
+ return;
468
+ }
469
+ features = ensureArray(features);
470
+ const currentSelection = new Set(sourceSelection.selectedFeatureIds);
471
+ switch (sourceSelection.options.mode) {
472
+ case "single":
473
+ case "multi":
474
+ features.forEach((f) => currentSelection.delete(f));
475
+ sourceSelection.updateSelection(currentSelection);
476
+ }
477
+ }
478
+ destroy() {
479
+ this._sourceSelections.forEach((sel) => sel.destroy());
480
+ this._sourceSelections.clear();
481
+ }
482
+ disableSelection(source) {
483
+ if (this._sourceSelections.has(source)) {
484
+ this._sourceSelections.get(source).destroy();
485
+ this._sourceSelections.delete(source);
486
+ }
487
+ }
488
+ enableSelection(source, options) {
489
+ if (options.popup) {
490
+ try {
491
+ this.map.getPlugin(PopupPlugin);
492
+ } catch (e) {
493
+ throw new FlowMapError(`enableSelection(): Popup plugin is required for automatic selection popups`);
494
+ }
495
+ }
496
+ this.disableSelection(source);
497
+ this._sourceSelections.set(source, new SourceSelection(this, source, options));
498
+ }
499
+ handleBeforeSourceRemoved(event) {
500
+ if (this._sourceSelections.has(event.source)) {
501
+ this._sourceSelections.get(event.source)?.destroy();
502
+ this._sourceSelections.delete(event.source);
503
+ }
504
+ }
505
+ handleClick(event) {
506
+ const multiSelect = event.originalEvent.shiftKey || event.originalEvent.altKey || event.originalEvent.metaKey;
507
+ const clickedFeature = event.features[0];
508
+ const clickedSource = clickedFeature?.source;
509
+ const selectionOptions = this._sourceSelections.get(clickedSource)?.options;
510
+ this._sourceSelections.forEach((sel) => sel.handleClick(event.lngLat, clickedFeature, selectionOptions, multiSelect, this._enablePopups));
511
+ }
512
+ selectFeatures(source, features) {
513
+ const sourceSelection = this._sourceSelections.get(source);
514
+ if (!sourceSelection) {
515
+ return;
516
+ }
517
+ features = ensureArray(features);
518
+ let currentSelection = new Set(sourceSelection.selectedFeatureIds);
519
+ switch (sourceSelection.options.mode) {
520
+ case "single":
521
+ currentSelection = /* @__PURE__ */ new Set([features[0]]);
522
+ sourceSelection.updateSelection(currentSelection);
523
+ break;
524
+ case "multi":
525
+ features.forEach((f) => currentSelection.add(f));
526
+ sourceSelection.updateSelection(currentSelection);
527
+ break;
528
+ }
529
+ if (sourceSelection.selectedFeatureIds.size && isStringFull(sourceSelection.options.group)) {
530
+ this._sourceSelections.forEach((s) => {
531
+ if (s != sourceSelection && s.options.clear === "group" && s.options.group == sourceSelection.options.group) {
532
+ s.updateSelection(/* @__PURE__ */ new Set());
533
+ }
534
+ });
535
+ }
536
+ }
537
+ selectedFeatureIds(source) {
538
+ const sourceSelection = this._sourceSelections.get(source);
539
+ if (!sourceSelection) {
540
+ return [];
541
+ }
542
+ return Array.from(sourceSelection.selectedFeatureIds);
543
+ }
544
+ selectedFeatureStream(source) {
545
+ const sourceSelection = this._sourceSelections.get(source);
546
+ if (!sourceSelection) {
547
+ throw new FlowMapError("no source selection");
548
+ }
549
+ return sourceSelection.selectedFeatureStream;
550
+ }
551
+ enablePopups(enabled = true) {
552
+ this._enablePopups = true;
553
+ }
554
+ disablePopups() {
555
+ if (this._enablePopups) {
556
+ this.map.getPlugin(PopupPlugin).closeAllPopups();
557
+ this._enablePopups = false;
558
+ }
559
+ }
560
+ }
561
+
562
+ function addAnnotationLayers(map) {
563
+ map.transaction(() => {
564
+ const annotationSource = new MapDataSource({
565
+ id: "map-annotations",
566
+ type: "geojson",
567
+ src: { type: "FeatureCollection", features: [] },
568
+ featureId: "id"
569
+ });
570
+ map.addSource(annotationSource);
571
+ const polygonFillLayer = new MapLayer({
572
+ id: "map-annotations-polygon-fill",
573
+ type: "fill",
574
+ source: annotationSource.id,
575
+ filter: `geometry-type() == 'Polygon' && coalesce(@visible, true)`,
576
+ style: `
577
+ fill-color: @fillColor;
578
+ `
579
+ });
580
+ map.addLayer(polygonFillLayer);
581
+ const polygonStrokeLayer = new MapLayer({
582
+ id: "map-annotations-polygon-stroke",
583
+ type: "line",
584
+ source: annotationSource.id,
585
+ filter: `geometry-type() == 'Polygon' && coalesce(@visible, true)`,
586
+ style: `
587
+ line-color: @strokeColor;
588
+ line-width: @strokeWidth;
589
+ `
590
+ });
591
+ map.addLayer(polygonStrokeLayer);
592
+ const lineStrokeLayer = new MapLayer({
593
+ id: "map-annotations-line-stroke",
594
+ type: "line",
595
+ source: annotationSource.id,
596
+ filter: `geometry-type() == 'LineString' && coalesce(@visible, true)`,
597
+ style: `
598
+ line-color: @strokeColor;
599
+ line-width: @strokeWidth;
600
+ `
601
+ });
602
+ map.addLayer(lineStrokeLayer);
603
+ const pointLayer = new MapLayer({
604
+ id: "map-annotations-point",
605
+ type: "circle",
606
+ source: annotationSource.id,
607
+ filter: `geometry-type() == 'Point' && coalesce(@visible, true)`,
608
+ style: `
609
+ circle-radius: @radius;
610
+ circle-color: @fillColor;
611
+ circle-stroke-color: @strokeColor;
612
+ circle-stroke-width: @strokeWidth;
613
+ `
614
+ });
615
+ map.addLayer(pointLayer);
616
+ const polygonLabelsLayer = new MapLayer({
617
+ id: "map-annotations-polygon-labels",
618
+ type: "symbol",
619
+ source: annotationSource.id,
620
+ filter: `geometry-type() == 'Polygon' && to-boolean(@labelText) && coalesce(@visible, true)`,
621
+ interactive: false,
622
+ style: `
623
+ symbol-placement: 'point';
624
+ text-field: @labelText;
625
+ text-color: @labelColor;
626
+ text-size: @labelSize;
627
+ text-anchor: @polygonLabelAnchor;
628
+ /* text-offset: @labelOffset; */
629
+ text-justify: @labelJustify;
630
+ text-max-width: @labelMaxWidth;
631
+ text-allow-overlap: true;
632
+ `
633
+ });
634
+ map.addLayer(polygonLabelsLayer);
635
+ const pointLabelsLayer = new MapLayer({
636
+ id: "map-annotations-point-labels",
637
+ type: "symbol",
638
+ source: annotationSource.id,
639
+ filter: `geometry-type() == 'Point' && to-boolean(@labelText) && coalesce(@visible, true)`,
640
+ interactive: false,
641
+ style: `
642
+ symbol-placement: 'point';
643
+ text-field: @labelText;
644
+ text-color: @labelColor;
645
+ text-size: @labelSize;
646
+ text-anchor: @labelAnchor;
647
+ text-offset: @labelOffset;
648
+ /* text-radial-offset: (@radius + 8) / coalesce(@labelSize, {{annotationsStyleDefaults.labelSize}}); */
649
+ text-justify: @labelJustify;
650
+ text-max-width: @labelMaxWidth;
651
+ text-allow-overlap: true;
652
+ `
653
+ });
654
+ map.addLayer(pointLabelsLayer);
655
+ const lineLabelsLayer = new MapLayer({
656
+ id: "map-annotations-line-labels",
657
+ type: "symbol",
658
+ source: annotationSource.id,
659
+ filter: `geometry-type() == 'LineString' && to-boolean(@labelText) && coalesce(@visible, true)`,
660
+ interactive: false,
661
+ style: `
662
+ symbol-placement: 'line-center';
663
+ text-field: @labelText;
664
+ text-color: @labelColor;
665
+ text-anchor: @labelAnchor;
666
+ text-size: @labelSize;
667
+ /* text-offset: @labelOffset; */
668
+ text-justify: @labelJustify;
669
+ text-max-width: @labelMaxWidth;
670
+ text-allow-overlap: true;
671
+ text-ignore-placement: true;
672
+ `
673
+ });
674
+ map.addLayer(lineLabelsLayer);
675
+ const polygonSelectionLayer = new MapLayer({
676
+ id: "map-annotations-polygon-selection",
677
+ type: "line",
678
+ source: annotationSource.id,
679
+ filter: `geometry-type() == 'Polygon' && coalesce(@visible, true)`,
680
+ interactive: false,
681
+ style: `
682
+ line-color: @@selected ? @selectionStrokeColor : @@hover ? @hoverStrokeColor : transparent;
683
+ line-width: @selectionWidth;
684
+ line-opacity: @selectionStrokeOpacity;
685
+ `
686
+ });
687
+ map.addLayer(polygonSelectionLayer);
688
+ const pointSelectionLayer = new MapLayer({
689
+ id: "map-annotations-point-selection",
690
+ type: "circle",
691
+ source: annotationSource.id,
692
+ filter: `geometry-type() == 'Point' && coalesce(@visible, true)`,
693
+ interactive: false,
694
+ style: `
695
+ circle-radius: @radius + 1;
696
+ circle-color: transparent;
697
+ circle-stroke-color: @@selected ? @selectionStrokeColor : @@hover ? @hoverStrokeColor : transparent;
698
+ circle-stroke-width: @selectionWidth;
699
+ circle-stroke-opacity: @selectionStrokeOpacity;
700
+ `
701
+ });
702
+ map.addLayer(pointSelectionLayer);
703
+ const lineSelectionLayer = new MapLayer({
704
+ id: "map-annotations-line-selection",
705
+ type: "line",
706
+ source: annotationSource.id,
707
+ filter: `geometry-type() == 'LineString' && coalesce(@visible, true)`,
708
+ interactive: false,
709
+ style: `
710
+ line-color: @@selected ? @selectionStrokeColor : @@hover ? @hoverStrokeColor : transparent;
711
+ line-opacity: @selectionStrokeOpacity;
712
+ line-width: @selectionWidth/2;
713
+ line-gap-width: @strokeWidth;
714
+ `
715
+ });
716
+ map.addLayer(lineSelectionLayer);
717
+ });
718
+ return map.getSource("map-annotations");
719
+ }
720
+
721
+ function createSupplementaryPointsForCircle(geojson) {
722
+ const { properties, geometry } = geojson;
723
+ if (properties.user_type !== "Circle")
724
+ return null;
725
+ const supplementaryPoints = [];
726
+ const vertices = geometry.coordinates[0].slice(0, -1);
727
+ for (let index = 0; index < vertices.length; index += Math.round(vertices.length / 4)) {
728
+ supplementaryPoints.push(MapboxDraw.lib.createVertex(properties.id, vertices[index], `0.${index}`, false));
729
+ }
730
+ return supplementaryPoints;
731
+ }
732
+
733
+ const DirectModeOverride = MapboxDraw.modes.direct_select;
734
+ const moveFeatures = MapboxDraw.lib.moveFeatures;
735
+ const constrainFeatureMovement = MapboxDraw.lib.constrainFeatureMovement;
736
+ const createSupplementaryPoints = MapboxDraw.lib.createSupplementaryPoints;
737
+ const Constants$1 = MapboxDraw.constants;
738
+ DirectModeOverride.dragFeature = function(state, e, delta) {
739
+ moveFeatures(this.getSelected(), delta);
740
+ this.getSelected().filter((feature) => feature.properties.type === "Circle").map((circle) => circle.properties.circleCenter).forEach((center) => {
741
+ center[0] += delta.lng;
742
+ center[1] += delta.lat;
743
+ });
744
+ state.dragMoveLocation = e.lngLat;
745
+ };
746
+ DirectModeOverride.dragVertex = function(state, e, delta) {
747
+ if (state.feature.properties.type === "Circle") {
748
+ const center = state.feature.properties.circleCenter;
749
+ const movedVertex = [e.lngLat.lng, e.lngLat.lat];
750
+ const radius = turfDistance(point(center), point(movedVertex), { units: "kilometers" });
751
+ const circleFeature = turfCircle(center, radius);
752
+ state.feature.incomingCoords(circleFeature.geometry.coordinates);
753
+ state.feature.properties.circleRadius = radius;
754
+ } else {
755
+ const selectedCoords = state.selectedCoordPaths.map((coord_path) => state.feature.getCoordinate(coord_path));
756
+ const selectedCoordPoints = selectedCoords.map((coords) => ({
757
+ type: Constants$1.geojsonTypes.FEATURE,
758
+ properties: {},
759
+ geometry: {
760
+ type: Constants$1.geojsonTypes.POINT,
761
+ coordinates: coords
762
+ }
763
+ }));
764
+ const constrainedDelta = constrainFeatureMovement(selectedCoordPoints, delta);
765
+ for (let i = 0; i < selectedCoords.length; i++) {
766
+ const coord = selectedCoords[i];
767
+ state.feature.updateCoordinate(state.selectedCoordPaths[i], coord[0] + constrainedDelta.lng, coord[1] + constrainedDelta.lat);
768
+ }
769
+ }
770
+ };
771
+ DirectModeOverride.toDisplayFeatures = function(state, geojson, push) {
772
+ if (state.featureId === geojson.properties.id) {
773
+ geojson.properties.active = Constants$1.activeStates.ACTIVE;
774
+ push(geojson);
775
+ const supplementaryPoints = geojson.properties.user_type === "Circle" ? createSupplementaryPointsForCircle(geojson) : createSupplementaryPoints(geojson, {
776
+ map: this.map,
777
+ midpoints: true,
778
+ selectedPaths: state.selectedCoordPaths
779
+ });
780
+ supplementaryPoints.forEach(push);
781
+ } else {
782
+ geojson.properties.active = Constants$1.activeStates.INACTIVE;
783
+ push(geojson);
784
+ }
785
+ this.fireActionable(state);
786
+ };
787
+ const DirectMode = DirectModeOverride;
788
+
789
+ const dragPan = {
790
+ enable(ctx) {
791
+ setTimeout(() => {
792
+ if (!ctx.map || !ctx.map.dragPan || !ctx._ctx || !ctx._ctx.store || !ctx._ctx.store.getInitialConfigValue)
793
+ return;
794
+ if (!ctx._ctx.store.getInitialConfigValue("dragPan"))
795
+ return;
796
+ ctx.map.dragPan.enable();
797
+ }, 0);
798
+ },
799
+ disable(ctx) {
800
+ setTimeout(() => {
801
+ if (!ctx.map || !ctx.map.doubleClickZoom)
802
+ return;
803
+ ctx.map.dragPan.disable();
804
+ }, 0);
805
+ }
806
+ };
807
+
808
+ const DragCircleMode = { ...MapboxDraw.modes.draw_polygon };
809
+ const Constants = MapboxDraw.constants;
810
+ DragCircleMode.onSetup = function(opts) {
811
+ const polygon = this.newFeature({
812
+ type: Constants.geojsonTypes.FEATURE,
813
+ properties: {
814
+ type: "Circle",
815
+ circleCenter: []
816
+ },
817
+ geometry: {
818
+ type: Constants.geojsonTypes.POLYGON,
819
+ coordinates: []
820
+ }
821
+ });
822
+ this.addFeature(polygon);
823
+ this.clearSelectedFeatures();
824
+ dragPan.disable(this);
825
+ this.updateUIClasses({ mouse: Constants.cursors.ADD });
826
+ this.activateUIButton(Constants.types.POLYGON);
827
+ this.setActionableState({
828
+ trash: true
829
+ });
830
+ return {
831
+ polygon,
832
+ currentVertexPosition: 0
833
+ };
834
+ };
835
+ DragCircleMode.onMouseDown = DragCircleMode.onTouchStart = function(state, e) {
836
+ const currentCenter = state.polygon.properties.circleCenter;
837
+ if (currentCenter.length === 0) {
838
+ state.polygon.properties.circleCenter = [e.lngLat.lng, e.lngLat.lat];
839
+ }
840
+ };
841
+ DragCircleMode.onDrag = DragCircleMode.onMouseMove = function(state, e) {
842
+ const center = state.polygon.properties.circleCenter;
843
+ if (center.length > 0) {
844
+ const distanceInKm = turfDistance(
845
+ point(center),
846
+ point([e.lngLat.lng, e.lngLat.lat]),
847
+ { units: "kilometers" }
848
+ );
849
+ const circleFeature = turfCircle(center, distanceInKm);
850
+ state.polygon.incomingCoords(circleFeature.geometry.coordinates);
851
+ state.polygon.properties.circleRadius = distanceInKm;
852
+ }
853
+ };
854
+ DragCircleMode.onMouseUp = DragCircleMode.onTouchEnd = function(state) {
855
+ dragPan.enable(this);
856
+ return this.changeMode(Constants.modes.SIMPLE_SELECT, { featureIds: [state.polygon.id] });
857
+ };
858
+ DragCircleMode.onClick = DragCircleMode.onTap = function(state) {
859
+ state.polygon.properties.circleCenter = [];
860
+ };
861
+ DragCircleMode.toDisplayFeatures = function(state, geojson, display) {
862
+ const isActivePolygon = geojson.properties.id === state.polygon.id;
863
+ geojson.properties.active = isActivePolygon ? Constants.activeStates.ACTIVE : Constants.activeStates.INACTIVE;
864
+ return display(geojson);
865
+ };
866
+ const DragCircleMode$1 = DragCircleMode;
867
+
868
+ var __defProp$3 = Object.defineProperty;
869
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
870
+ var __publicField$3 = (obj, key, value) => {
871
+ __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
872
+ return value;
873
+ };
874
+ Debug("flow:annotation-plugin");
875
+ const _AnnotationPlugin = class _AnnotationPlugin extends MapPluginBase {
876
+ constructor(pluginManager, options) {
877
+ super(pluginManager, options);
878
+ __publicField$3(this, "name", "AnnotationPlugin");
879
+ // Feature data model
880
+ __publicField$3(this, "_features", /* @__PURE__ */ new Map());
881
+ __publicField$3(this, "_featureIds", []);
882
+ // Undo/redo stacks
883
+ __publicField$3(this, "_undoStack", []);
884
+ __publicField$3(this, "_redoStack", []);
885
+ // Editing state
886
+ __publicField$3(this, "_mode", "idle");
887
+ __publicField$3(this, "_activeFeatureId", null);
888
+ __publicField$3(this, "_activeFeatureType", null);
889
+ __publicField$3(this, "_onCommitCleanup", []);
890
+ __publicField$3(this, "_annotationDataSource");
891
+ __publicField$3(this, "_drawControl", null);
892
+ __publicField$3(this, "_removeKeyboardShortcuts", null);
893
+ this.options = { ..._AnnotationPlugin._defaultOptions, ...options };
894
+ this.options.defaultStyle = { ..._AnnotationPlugin._defaultOptions.defaultStyle, ...options?.defaultStyle };
895
+ }
896
+ initialize() {
897
+ this._annotationDataSource = addAnnotationLayers(this.map);
898
+ if (this.options.selectionOptions) {
899
+ this.map.getPlugin(SelectionPlugin).enableSelection(this._annotationDataSource.id, this.options.selectionOptions);
900
+ }
901
+ if (this.options.interactive) {
902
+ this._removeKeyboardShortcuts = this.pluginManager.addKeyboardShortcuts({
903
+ "Enter,Escape": () => {
904
+ this.commit();
905
+ },
906
+ "Delete,Backspace": () => this.deleteSelectedFeature(),
907
+ "Ctrl+KeyZ": () => {
908
+ this.undo();
909
+ },
910
+ "Ctrl+Shift+KeyZ": () => {
911
+ this.redo();
912
+ },
913
+ KeyP: () => this.createFeature("Polygon"),
914
+ KeyL: () => this.createFeature("LineString"),
915
+ KeyM: () => this.createFeature("Point"),
916
+ KeyC: () => this.createFeature("Circle")
917
+ });
918
+ }
919
+ }
920
+ destroy() {
921
+ if (this._removeKeyboardShortcuts)
922
+ this._removeKeyboardShortcuts();
923
+ }
924
+ clear() {
925
+ this.commit();
926
+ this._featureIds = [];
927
+ this._features = /* @__PURE__ */ new Map();
928
+ this._undoStack = [];
929
+ this._redoStack = [];
930
+ this._mode = "idle";
931
+ this._activeFeatureId = null;
932
+ this._activeFeatureType = null;
933
+ this.render();
934
+ return this;
935
+ }
936
+ undo() {
937
+ const operation = this._undoStack.pop();
938
+ if (operation) {
939
+ operation.undo();
940
+ this._redoStack.push(operation);
941
+ this.render();
942
+ }
943
+ return this;
944
+ }
945
+ /**
946
+ * Redo the last undone operation.
947
+ */
948
+ redo() {
949
+ const operation = this._redoStack.pop();
950
+ if (operation) {
951
+ operation.do();
952
+ this._undoStack.push(operation);
953
+ this.render();
954
+ }
955
+ return this;
956
+ }
957
+ getFeature(featureId) {
958
+ const feature = this._features.get(featureId);
959
+ if (!feature) {
960
+ throw new FlowError(`<AnnotationPlugin>: No feature with id '${featureId}'`);
961
+ }
962
+ return feature;
963
+ }
964
+ getFeatures() {
965
+ return this._featureIds.map((id) => this._features.get(id));
966
+ }
967
+ setFeatures(features) {
968
+ this.commit();
969
+ const normalizedFeatures = features.map((f) => this.applyDefaults(f));
970
+ const oldFeatures = this._features;
971
+ const oldFeatureIds = this._featureIds;
972
+ const operation = {
973
+ do: () => {
974
+ this._featureIds = normalizedFeatures.map((f) => f.id);
975
+ this._features = new Map(normalizedFeatures.map((f) => [f.id, f]));
976
+ },
977
+ undo: () => {
978
+ this._featureIds = oldFeatureIds;
979
+ this._features = oldFeatures;
980
+ }
981
+ };
982
+ this.performOperation(operation);
983
+ return this;
984
+ }
985
+ addFeature(feature) {
986
+ this.commit();
987
+ const fullFeature = this.applyDefaults(feature);
988
+ const operation = {
989
+ do: () => {
990
+ this._features.set(fullFeature.id, fullFeature);
991
+ this._featureIds.push(fullFeature.id);
992
+ },
993
+ undo: () => {
994
+ this._features.delete(fullFeature.id);
995
+ const index = this._featureIds.indexOf(fullFeature.id);
996
+ if (index !== -1) {
997
+ this._featureIds.splice(index, 1);
998
+ }
999
+ }
1000
+ };
1001
+ this.performOperation(operation);
1002
+ return fullFeature;
1003
+ }
1004
+ deleteFeature(featureId) {
1005
+ this.commit();
1006
+ if (!featureId) {
1007
+ return this;
1008
+ }
1009
+ this.deselectFeature(featureId);
1010
+ const feature = this.getFeature(featureId);
1011
+ const operation = {
1012
+ do: () => {
1013
+ this._features.delete(featureId);
1014
+ const index = this._featureIds.indexOf(featureId);
1015
+ if (index !== -1) {
1016
+ this._featureIds.splice(index, 1);
1017
+ }
1018
+ },
1019
+ undo: () => {
1020
+ this._features.set(featureId, feature);
1021
+ this._featureIds.push(featureId);
1022
+ }
1023
+ };
1024
+ this.performOperation(operation);
1025
+ return this;
1026
+ }
1027
+ createFeature(featureType) {
1028
+ this.commit();
1029
+ this.startDrawing();
1030
+ this._drawControl.changeMode(this.drawModeForFeatureType(featureType));
1031
+ this._mode = "creating";
1032
+ this._activeFeatureId = null;
1033
+ this._activeFeatureType = featureType;
1034
+ this.emitStateChange();
1035
+ const commitChanges = () => {
1036
+ setTimeout(() => this.commit(), 0);
1037
+ };
1038
+ this.map.maplibreMap.once("draw.create", commitChanges);
1039
+ this.map.maplibreMap.once("draw.modechange", commitChanges);
1040
+ this._onCommitCleanup.push(() => {
1041
+ this.map.maplibreMap.off("draw.create", commitChanges);
1042
+ this.map.maplibreMap.off("draw.modechange", commitChanges);
1043
+ });
1044
+ }
1045
+ editFeature(featureId) {
1046
+ const feature = this.getFeature(featureId);
1047
+ this._mode = "editing";
1048
+ this._activeFeatureId = featureId;
1049
+ this._activeFeatureType = feature.properties.type;
1050
+ const commitChanges = () => {
1051
+ setTimeout(() => this.commit(), 0);
1052
+ };
1053
+ this.startDrawing();
1054
+ this._drawControl.add(feature);
1055
+ this.setFeatureProperty(featureId, "visible", false);
1056
+ this.render();
1057
+ switch (this.editModeForFeature(feature)) {
1058
+ case "direct_select":
1059
+ this._drawControl.changeMode("direct_select", {
1060
+ featureId
1061
+ });
1062
+ this.map.maplibreMap.once("draw.modechange", commitChanges);
1063
+ this._onCommitCleanup.push(() => this.map.maplibreMap.off("draw.modechange", commitChanges));
1064
+ break;
1065
+ case "simple_select":
1066
+ this._drawControl.changeMode("simple_select", {
1067
+ featureIds: [featureId]
1068
+ });
1069
+ this.map.maplibreMap.once("draw.selectionchange", commitChanges);
1070
+ this._onCommitCleanup.push(() => this.map.maplibreMap.off("draw.selectionchange", commitChanges));
1071
+ break;
1072
+ }
1073
+ }
1074
+ zoomToFeature(featureId, animationOptions) {
1075
+ const feature = this.getFeature(featureId);
1076
+ if (!feature)
1077
+ return this;
1078
+ const bbox = feature.properties.type === "Point" ? turfBBox(turfBuffer(feature, 50)) : turfBBox(feature);
1079
+ this.map.setBounds(bbox, animationOptions);
1080
+ return this;
1081
+ }
1082
+ setFeatureProperty(featureId, property, value) {
1083
+ const feature = this.getFeature(featureId);
1084
+ const featureCopy = {
1085
+ ...feature,
1086
+ properties: { ...feature.properties, [property]: value }
1087
+ };
1088
+ this._features.set(featureId, featureCopy);
1089
+ return this;
1090
+ }
1091
+ setFeatureProperties(featureId, properties) {
1092
+ const feature = this.getFeature(featureId);
1093
+ const featureCopy = {
1094
+ ...feature,
1095
+ properties: { ...feature.properties, ...properties }
1096
+ };
1097
+ this._features.set(featureId, featureCopy);
1098
+ return this;
1099
+ }
1100
+ commit() {
1101
+ this._onCommitCleanup.forEach((f) => f());
1102
+ this._onCommitCleanup = [];
1103
+ if (this._mode === "idle")
1104
+ return this;
1105
+ const drawControlFeature = this._drawControl.getAll().features[0];
1106
+ let operation = null;
1107
+ if (this._mode === "editing") {
1108
+ const mapFeature = this.getFeature(this._activeFeatureId);
1109
+ this.setFeatureProperty(mapFeature.id, "visible", true);
1110
+ if (!turfBooleanEqual(mapFeature.geometry, drawControlFeature.geometry)) {
1111
+ operation = {
1112
+ do: () => {
1113
+ this.setFeatureGeometry(mapFeature.id, drawControlFeature.geometry);
1114
+ if (drawControlFeature.properties.type === "Circle") {
1115
+ this.setFeatureProperty(mapFeature.id, "center", drawControlFeature.properties.circleCenter);
1116
+ this.setFeatureProperty(mapFeature.id, "radiusInKm", drawControlFeature.properties.circleRadius);
1117
+ }
1118
+ },
1119
+ undo: () => {
1120
+ this.setFeatureGeometry(mapFeature.id, mapFeature.geometry);
1121
+ if (mapFeature.properties.type === "Circle") {
1122
+ this.setFeatureProperty(mapFeature.id, "center", mapFeature.properties.circleCenter);
1123
+ this.setFeatureProperty(mapFeature.id, "radiusInKm", mapFeature.properties.circleRadius);
1124
+ }
1125
+ }
1126
+ };
1127
+ }
1128
+ } else if (this._mode === "creating") {
1129
+ if (this.isValidGeometry(drawControlFeature)) {
1130
+ let createdFeature = drawControlFeature;
1131
+ createdFeature.id = "";
1132
+ this.applyDefaults(createdFeature);
1133
+ operation = {
1134
+ do: () => {
1135
+ this._features.set(drawControlFeature.id, drawControlFeature);
1136
+ this._featureIds.push(drawControlFeature.id);
1137
+ if (this.options.selectionOptions?.mode === "single" || this.options.selectionOptions?.mode === "multi") {
1138
+ this.map.getPlugin(SelectionPlugin).selectFeatures(this._annotationDataSource.id, drawControlFeature.id);
1139
+ }
1140
+ },
1141
+ undo: () => {
1142
+ createdFeature = this._features.get(createdFeature.id);
1143
+ this._features.delete(createdFeature.id);
1144
+ const index = this._featureIds.indexOf(createdFeature.id);
1145
+ if (index !== -1) {
1146
+ this._featureIds.splice(index, 1);
1147
+ }
1148
+ }
1149
+ };
1150
+ }
1151
+ }
1152
+ if (operation) {
1153
+ this.performOperation(operation);
1154
+ }
1155
+ this._mode = "idle";
1156
+ this._activeFeatureId = null;
1157
+ this._activeFeatureType = null;
1158
+ this.endDrawing();
1159
+ this.render();
1160
+ return this;
1161
+ }
1162
+ cancel() {
1163
+ this._onCommitCleanup.forEach((f) => f());
1164
+ this._onCommitCleanup = [];
1165
+ if (this._mode === "idle")
1166
+ return this;
1167
+ if (this._mode === "editing") {
1168
+ this.setFeatureProperty(this._activeFeatureId, "visible", true);
1169
+ }
1170
+ this._activeFeatureId = null;
1171
+ this._activeFeatureType = null;
1172
+ this._mode = "idle";
1173
+ this.endDrawing();
1174
+ this.render();
1175
+ return this;
1176
+ }
1177
+ render() {
1178
+ const renderedFeatures = {
1179
+ type: "FeatureCollection",
1180
+ features: this._featureIds.map((id) => this._features.get(id))
1181
+ };
1182
+ this._annotationDataSource.setSrc(renderedFeatures);
1183
+ this.emitStateChange();
1184
+ return this;
1185
+ }
1186
+ handleDblClick(event) {
1187
+ if (!this.options.interactive) {
1188
+ return;
1189
+ }
1190
+ const clickedFeature = event.features[0];
1191
+ if (clickedFeature && clickedFeature.source === this._annotationDataSource.id) {
1192
+ this.editFeature(clickedFeature.id.toString());
1193
+ }
1194
+ }
1195
+ handleBeforeStyleChanged(event) {
1196
+ const { oldStyle, newStyle } = event;
1197
+ if (this._drawControl) {
1198
+ if (oldStyle.sources["mapbox-gl-draw"]) {
1199
+ newStyle.sources["mapbox-gl-draw"] = oldStyle.sources["mapbox-gl-draw"];
1200
+ }
1201
+ oldStyle.layers.filter((l) => l.id.startsWith("gl-draw")).forEach((l) => newStyle.layers.push(l));
1202
+ }
1203
+ return newStyle;
1204
+ }
1205
+ startDrawing() {
1206
+ if (this._drawControl) {
1207
+ return this._drawControl;
1208
+ }
1209
+ this.pluginManager.setExclusivePlugin(this);
1210
+ this._drawControl = new MapboxDraw({
1211
+ displayControlsDefault: false,
1212
+ keybindings: false,
1213
+ modes: {
1214
+ ...MapboxDraw.modes,
1215
+ drag_circle: DragCircleMode$1,
1216
+ direct_select: DirectMode
1217
+ //...options.modes
1218
+ },
1219
+ userProperties: true
1220
+ });
1221
+ this.map.maplibreMap.addControl(this._drawControl);
1222
+ return this._drawControl;
1223
+ }
1224
+ endDrawing() {
1225
+ this.pluginManager.removeExclusivePlugin(this);
1226
+ if (this._drawControl) {
1227
+ this.map.maplibreMap.removeControl(this._drawControl);
1228
+ this._drawControl = null;
1229
+ }
1230
+ }
1231
+ applyDefaults(feature) {
1232
+ feature.id = feature.properties.id = this.generateFeatureId(feature);
1233
+ feature.properties = {
1234
+ id: this.generateFeatureId(feature),
1235
+ name: this.generateFeatureName(feature),
1236
+ type: feature.properties.type ?? feature.geometry.type,
1237
+ selected: false,
1238
+ ...this.options.defaultStyle,
1239
+ ...feature.properties
1240
+ };
1241
+ feature.id = feature.properties.id;
1242
+ return feature;
1243
+ }
1244
+ generateFeatureId(feature) {
1245
+ if (isNumber(feature.id) || isStringFull(feature.id)) {
1246
+ return feature.id.toString();
1247
+ } else {
1248
+ return randomId("feature");
1249
+ }
1250
+ }
1251
+ generateFeatureName(feature) {
1252
+ const type = feature.properties.type ?? feature.geometry.type;
1253
+ return `${type} ${this._featureIds.length}`;
1254
+ }
1255
+ performOperation(operation) {
1256
+ this._undoStack.push(operation);
1257
+ this._redoStack.length = 0;
1258
+ operation.do();
1259
+ }
1260
+ deselectFeature(featureId) {
1261
+ if (this.options.selectionOptions?.mode === "single" || this.options.selectionOptions?.mode === "multi") {
1262
+ this.map.getPlugin(SelectionPlugin).deselectFeatures(this._annotationDataSource.id, featureId);
1263
+ }
1264
+ }
1265
+ deleteSelectedFeature() {
1266
+ if (this.options.selectionOptions?.mode === "single" || this.options.selectionOptions?.mode === "multi") {
1267
+ const selected = this.map.getPlugin(SelectionPlugin).selectedFeatureIds(this._annotationDataSource.id);
1268
+ selected.forEach((id) => this.deleteFeature(id));
1269
+ this.render();
1270
+ }
1271
+ }
1272
+ editModeForFeature(feature) {
1273
+ switch (feature.geometry.type) {
1274
+ case "Point":
1275
+ return "simple_select";
1276
+ default:
1277
+ return "direct_select";
1278
+ }
1279
+ }
1280
+ drawModeForFeatureType(featureType) {
1281
+ switch (featureType) {
1282
+ case "Polygon":
1283
+ return "draw_polygon";
1284
+ case "LineString":
1285
+ return "draw_line_string";
1286
+ case "Point":
1287
+ return "draw_point";
1288
+ case "Circle":
1289
+ return "drag_circle";
1290
+ default:
1291
+ throw new Error(`Unrecognized feature type: ${featureType}`);
1292
+ }
1293
+ }
1294
+ setFeatureGeometry(featureId, geometry) {
1295
+ const feature = this.getFeature(featureId);
1296
+ const featureCopy = {
1297
+ ...feature,
1298
+ geometry
1299
+ };
1300
+ this._features.set(featureId, featureCopy);
1301
+ return featureCopy;
1302
+ }
1303
+ isValidGeometry(feature) {
1304
+ if (isPolygon(feature)) {
1305
+ return feature.geometry.coordinates[0][0] !== null && feature.geometry.coordinates[0].length > 3 && turfArea(feature) > 0;
1306
+ } else if (isLineString(feature)) {
1307
+ return feature.geometry.coordinates.length > 1 && turfLength(feature) > 0;
1308
+ } else if (isPoint(feature)) {
1309
+ return feature.geometry.coordinates.length > 0;
1310
+ }
1311
+ return false;
1312
+ }
1313
+ emitStateChange() {
1314
+ const eventData = {
1315
+ mode: this._mode,
1316
+ canUndo: this._undoStack.length > 0,
1317
+ canRedo: this._redoStack.length > 0,
1318
+ activeFeatureId: this._activeFeatureId,
1319
+ activeFeatureType: this._activeFeatureType,
1320
+ features: this._featureIds.map((id) => this._features.get(id))
1321
+ };
1322
+ this.emit("annotationStateChanged", new AnnotationEvent("annotationStateChanged", this.map, eventData));
1323
+ }
1324
+ };
1325
+ __publicField$3(_AnnotationPlugin, "_defaultOptions", {
1326
+ interactive: true,
1327
+ defaultStyle: {
1328
+ labelSize: 10,
1329
+ labelColor: "#ffffff",
1330
+ labelMaxWidth: 10,
1331
+ labelOffset: [0, 0.5],
1332
+ labelJustify: "auto",
1333
+ labelAnchor: "top",
1334
+ polygonLabelAnchor: "center",
1335
+ radius: 5,
1336
+ fillColor: "rgba(204,204,204,0.65)",
1337
+ strokeWidth: 1,
1338
+ strokeColor: "#ffffff",
1339
+ selectionStrokeColor: "orange",
1340
+ selectionStrokeOpacity: 0.5,
1341
+ selectionWidth: 3,
1342
+ hoverStrokeColor: "transparent"
1343
+ }
1344
+ });
1345
+ let AnnotationPlugin = _AnnotationPlugin;
1346
+
1347
+ var __defProp$2 = Object.defineProperty;
1348
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1349
+ var __publicField$2 = (obj, key, value) => {
1350
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1351
+ return value;
1352
+ };
1353
+ const debug$1 = Debug("flow:data-join-map-plugin");
1354
+ const _DataJoin = class _DataJoin {
1355
+ constructor(id, map, options) {
1356
+ this.id = id;
1357
+ this.map = map;
1358
+ __publicField$2(this, "autorunDisposer", null);
1359
+ __publicField$2(this, "dataExpression", null);
1360
+ __publicField$2(this, "options");
1361
+ __publicField$2(this, "parameterExpressions", {});
1362
+ this.options = {
1363
+ ..._DataJoin.defaultOptions,
1364
+ ...options
1365
+ };
1366
+ }
1367
+ get source() {
1368
+ return this.options.source;
1369
+ }
1370
+ start() {
1371
+ this.stop();
1372
+ if (isString(this.options.data)) {
1373
+ if (isURL(this.options.data))
1374
+ this.options.data = `'${this.options.data}'`;
1375
+ this.dataExpression = new ReactiveExpression(this.options.data);
1376
+ this.dataExpression.bindState(this.map.context.state);
1377
+ } else {
1378
+ this.dataExpression = null;
1379
+ }
1380
+ if (isArrayFull(this.options.parameters)) {
1381
+ this.parameterExpressions = this.options.parameters.reduce((accum, param) => {
1382
+ const expr = new ReactiveExpression(param.value.toString());
1383
+ expr.bindState(this.map.context.state);
1384
+ accum[param.name] = expr;
1385
+ return accum;
1386
+ }, {});
1387
+ }
1388
+ this.autorunDisposer = autorun(
1389
+ async () => {
1390
+ let data = this.dataExpression ? toJS(this.dataExpression.value) : this.options.data;
1391
+ const missingParams = this.options.parameters.map(
1392
+ (p) => p.required && isNil(this.parameterExpressions[p.name].value) ? p.name : void 0
1393
+ ).filter((p) => !!p);
1394
+ if (isNull(data)) {
1395
+ debug$1(`FlowMap<${this.map.id}>: Skipping data join '${this.id}' (null data)`);
1396
+ return;
1397
+ }
1398
+ if (missingParams.length) {
1399
+ debug$1(`FlowMap<${this.map.id}>: Skipping data join '${this.id}' (null required parameters: ${missingParams.join(", ")})`);
1400
+ return;
1401
+ }
1402
+ debug$1(`FlowMap<${this.map.id}>: Performing data join '${this.id}'`);
1403
+ if (isString(data)) {
1404
+ try {
1405
+ data = await this.loadData(data);
1406
+ } catch (e) {
1407
+ this.map.emitError(`Failed to load data for data join '${this.id}' - ${e.message}`, e);
1408
+ data = [];
1409
+ }
1410
+ }
1411
+ this.doDataJoin(data);
1412
+ },
1413
+ { name: `FlowDataJoin.${this.map.id}.${this.id})` }
1414
+ );
1415
+ }
1416
+ stop() {
1417
+ if (this.autorunDisposer) {
1418
+ this.autorunDisposer();
1419
+ this.autorunDisposer = null;
1420
+ }
1421
+ }
1422
+ doDataJoin(data) {
1423
+ const source = this.map.getSource(this.options.source);
1424
+ const sourceLayer = source.type === "vector" ? this.options.sourceLayer || "geometry" : void 0;
1425
+ if (this.options.clear) {
1426
+ this.map.removeFeatureState({ source: source.id, sourceLayer }, this.id);
1427
+ }
1428
+ const featureData = data.reduce((accum, d) => {
1429
+ accum[d[this.options.foreignKey]] = { [this.id]: d };
1430
+ return accum;
1431
+ }, {});
1432
+ this.map.setFeatureStateBulk({ source: source.id, sourceLayer }, featureData);
1433
+ }
1434
+ loadData(datasetOrUrl) {
1435
+ if (isURL(datasetOrUrl)) {
1436
+ const url = new URL(datasetOrUrl);
1437
+ const params = new URLSearchParams(url.searchParams);
1438
+ if (isArrayFull(this.options.parameters)) {
1439
+ this.options.parameters.forEach((p) => {
1440
+ params.append(p.name, this.parameterExpressions[p.name].value);
1441
+ });
1442
+ }
1443
+ url.search = params.toString();
1444
+ const headers = new Headers({ Accept: "application/json" });
1445
+ const request = new Request(url.toString(), {
1446
+ method: "GET",
1447
+ mode: "cors",
1448
+ headers,
1449
+ cache: "default"
1450
+ });
1451
+ return fetch(request).then((response) => {
1452
+ if (!response.ok) {
1453
+ throw new Error(`status=${response.status} ${response.statusText}`);
1454
+ }
1455
+ return response.json();
1456
+ });
1457
+ } else {
1458
+ const params = {};
1459
+ if (isArrayFull(this.options.parameters)) {
1460
+ this.options.parameters.forEach((p) => {
1461
+ params[p.name] = this.parameterExpressions[p.name].value;
1462
+ });
1463
+ }
1464
+ return this.map.context.flowClient.datasets.query(datasetOrUrl, {}, params);
1465
+ }
1466
+ }
1467
+ };
1468
+ __publicField$2(_DataJoin, "defaultOptions", {
1469
+ clear: false,
1470
+ parameters: []
1471
+ });
1472
+ let DataJoin = _DataJoin;
1473
+ class DataJoinPlugin extends MapPluginBase {
1474
+ constructor(pluginManager) {
1475
+ super(pluginManager);
1476
+ __publicField$2(this, "_joins", /* @__PURE__ */ new Map());
1477
+ __publicField$2(this, "name", "DataJoinPlugin");
1478
+ }
1479
+ addDataJoin(id, options) {
1480
+ const dataJoin = new DataJoin(id, this.map, options);
1481
+ this._joins.set(id, dataJoin);
1482
+ dataJoin.start();
1483
+ }
1484
+ bindState(state) {
1485
+ this._joins.forEach((join) => join.start());
1486
+ }
1487
+ destroy() {
1488
+ this._joins.forEach((join) => join.stop());
1489
+ this._joins.clear();
1490
+ }
1491
+ handleSourceRemoved(event) {
1492
+ this._joins.forEach((join) => {
1493
+ if (join.source === event.source) {
1494
+ join.stop();
1495
+ this._joins.delete(join.id);
1496
+ }
1497
+ });
1498
+ }
1499
+ removeDataJoin(id) {
1500
+ const dataJoin = this._joins.get(id);
1501
+ if (dataJoin) {
1502
+ dataJoin.stop();
1503
+ this._joins.delete(id);
1504
+ } else {
1505
+ throw new FlowError(`Cannot remove unrecognized data join '${id}'`);
1506
+ }
1507
+ }
1508
+ }
1509
+
1510
+ var __defProp$1 = Object.defineProperty;
1511
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1512
+ var __publicField$1 = (obj, key, value) => {
1513
+ __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1514
+ return value;
1515
+ };
1516
+ class HoverEffectPlugin extends MapPluginBase {
1517
+ constructor(pluginManager) {
1518
+ super(pluginManager);
1519
+ __publicField$1(this, "hoveredFeature", null);
1520
+ __publicField$1(this, "name", "HoverEffectPlugin");
1521
+ }
1522
+ handleMouseMove(event) {
1523
+ const hoveredFeature = event.features[0];
1524
+ if (hoveredFeature) {
1525
+ this.map.maplibreMap._container.classList.add("feature-hovered");
1526
+ } else {
1527
+ this.map.maplibreMap._container.classList.remove("feature-hovered");
1528
+ }
1529
+ if (!this.compareFeatures(hoveredFeature, this.hoveredFeature)) {
1530
+ this.clearHover();
1531
+ if (isFeatureWithId(hoveredFeature) && isDefined(this.map.getSource(hoveredFeature.source)?.featureId)) {
1532
+ this.map.setFeatureState(hoveredFeature, { hover: true }, false);
1533
+ this.hoveredFeature = hoveredFeature;
1534
+ }
1535
+ }
1536
+ }
1537
+ handleMouseOut(event) {
1538
+ this.clearHover();
1539
+ }
1540
+ handleSetExclusivePlugin(exclusivePlugins) {
1541
+ if (exclusivePlugins.length) {
1542
+ this.clearHover();
1543
+ this.map.maplibreMap._container.classList.remove("feature-hovered");
1544
+ }
1545
+ }
1546
+ compareFeatures(f1, f2) {
1547
+ if (f1 === f2) {
1548
+ return true;
1549
+ }
1550
+ if (isObject(f1) && isObject(f2)) {
1551
+ return f1.id == f2.id && f1.source === f2.source && f1.sourceLayer == f2.sourceLayer;
1552
+ }
1553
+ return false;
1554
+ }
1555
+ clearHover() {
1556
+ if (this.hoveredFeature && this.map.getSource(this.hoveredFeature.source)) {
1557
+ this.map.removeFeatureState(this.hoveredFeature, "hover", false);
1558
+ this.hoveredFeature = null;
1559
+ }
1560
+ }
1561
+ }
1562
+
1563
+ var __defProp = Object.defineProperty;
1564
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1565
+ var __publicField = (obj, key, value) => {
1566
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1567
+ return value;
1568
+ };
1569
+ const debug = Debug("flow:url-sync-plugin");
1570
+ class UrlSyncPlugin extends MapPluginBase {
1571
+ constructor(pluginManager) {
1572
+ super(pluginManager);
1573
+ __publicField(this, "name", "UrlSyncPlugin");
1574
+ }
1575
+ initialize() {
1576
+ const searchParams = new URLSearchParams(window.location.search);
1577
+ const hash = searchParams.get(this.map.id);
1578
+ if (hash) {
1579
+ const center = this.hashToMapState(hash);
1580
+ debug("Setting map position from URL", center);
1581
+ this.map.setCenter(center);
1582
+ }
1583
+ }
1584
+ handleMoveEnd(event) {
1585
+ const center = this.mapStateToHash(this.map.getCenter());
1586
+ const searchParams = new URLSearchParams(window.location.search);
1587
+ searchParams.set(this.map.id, center);
1588
+ const newUrl = `${location.pathname}?${searchParams.toString()}`;
1589
+ debug("Updating URL with map position", center);
1590
+ window.history.replaceState({}, "", newUrl);
1591
+ }
1592
+ mapStateToHash(mapCenter) {
1593
+ const state = {
1594
+ z: mapCenter.zoom.toFixed(3),
1595
+ x: mapCenter.lng.toFixed(3),
1596
+ y: mapCenter.lat.toFixed(3),
1597
+ pitch: mapCenter.pitch.toFixed(1),
1598
+ bearing: mapCenter.bearing.toFixed(1)
1599
+ };
1600
+ return `${state.z}_${state.x}_${state.y}_${state.pitch}_${state.bearing}`;
1601
+ }
1602
+ hashToMapState(hash) {
1603
+ const [zoom, x, y, pitch, bearing] = hash.split("_");
1604
+ return {
1605
+ lng: +x,
1606
+ lat: +y,
1607
+ zoom: +zoom,
1608
+ pitch: +pitch,
1609
+ bearing: +bearing
1610
+ };
1611
+ }
1612
+ }
1613
+
1614
+ export { AnnotationEvent, AnnotationPlugin, DataJoin, DataJoinPlugin, HoverEffectPlugin, MapPluginBase, PopupPlugin, SelectionEvent, SelectionPlugin, UrlSyncPlugin };