@emuanalytics/flow-rdf-core 4.0.4 → 4.0.5

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 (89) hide show
  1. package/README.md +1 -64
  2. package/dist/index.d.mts +36 -31
  3. package/dist/index.d.ts +36 -31
  4. package/dist/index.mjs +20 -3249
  5. package/dist/lib/context/context-registry.mjs +63 -0
  6. package/dist/lib/context/debugger.mjs +240 -0
  7. package/dist/lib/context/flow-context.mjs +61 -0
  8. package/dist/lib/context/global-state.mjs +70 -0
  9. package/dist/lib/context/map-registry.mjs +59 -0
  10. package/dist/lib/context/query-registry.mjs +53 -0
  11. package/dist/lib/errors/flow-error.mjs +10 -0
  12. package/dist/lib/errors/flow-expression-error.mjs +14 -0
  13. package/dist/lib/errors/flow-map-error.mjs +9 -0
  14. package/dist/lib/expressions/expression-parser.mjs +198 -0
  15. package/dist/lib/expressions/functions/from-palette.mjs +75 -0
  16. package/dist/lib/expressions/functions/regex.mjs +25 -0
  17. package/dist/lib/expressions/functions/state.mjs +25 -0
  18. package/dist/lib/expressions/parser-helpers.mjs +37 -0
  19. package/dist/lib/expressions/reactive-expression.mjs +253 -0
  20. package/dist/lib/map/flow-core-map.mjs +749 -0
  21. package/dist/lib/map/plugins/annotations/annotation-layers.mjs +163 -0
  22. package/dist/lib/map/plugins/annotations/annotation.events.mjs +27 -0
  23. package/dist/lib/map/plugins/annotations/annotation.plugin.mjs +498 -0
  24. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/modes/DirectModeOverride.mjs +67 -0
  25. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/modes/DragCircleMode.mjs +65 -0
  26. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/utils/create_supplementary_points_circle.mjs +15 -0
  27. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/utils/drag_pan.mjs +20 -0
  28. package/dist/lib/map/plugins/data-join.plugin.mjs +173 -0
  29. package/dist/lib/map/plugins/export/export.plugin.mjs +21 -0
  30. package/dist/lib/map/plugins/export/map-exporter.mjs +177 -0
  31. package/dist/lib/map/plugins/hover-effect.plugin.mjs +58 -0
  32. package/dist/lib/map/plugins/index.d.mts +37 -72
  33. package/dist/lib/map/plugins/index.d.ts +37 -72
  34. package/dist/lib/map/plugins/index.mjs +11 -1810
  35. package/dist/lib/map/plugins/keyboard-shortcut-manager.mjs +120 -0
  36. package/dist/lib/map/plugins/map-plugin-base.mjs +25 -0
  37. package/dist/lib/map/plugins/plugin-manager.mjs +135 -0
  38. package/dist/lib/map/plugins/popup.plugin.mjs +55 -0
  39. package/dist/lib/map/plugins/selection/selection.events.mjs +21 -0
  40. package/dist/lib/map/plugins/selection/selection.plugin.mjs +136 -0
  41. package/dist/lib/map/plugins/selection/source-selection.mjs +156 -0
  42. package/dist/lib/map/plugins/url-sync.plugin.mjs +55 -0
  43. package/dist/lib/map/support/image-registry.mjs +82 -0
  44. package/dist/lib/map/support/map-events.mjs +71 -0
  45. package/dist/lib/map/support/map-feature-stream.mjs +158 -0
  46. package/dist/lib/map/support/map-popup.mjs +159 -0
  47. package/dist/lib/map/types.mjs +7 -0
  48. package/dist/lib/map-style/map-data-source.mjs +192 -0
  49. package/dist/lib/map-style/map-layer-style-builder-generated.mjs +1307 -0
  50. package/dist/lib/map-style/map-layer-style-builder.mjs +86 -0
  51. package/dist/lib/map-style/map-layer.mjs +223 -0
  52. package/dist/lib/map-style/map-style.mjs +178 -0
  53. package/dist/lib/queries/async-committer.mjs +30 -0
  54. package/dist/lib/queries/query.mjs +172 -0
  55. package/dist/lib/state-store/decorators.mjs +83 -0
  56. package/dist/lib/state-store/state-store.mjs +534 -0
  57. package/dist/lib/state-store/utils.mjs +7 -0
  58. package/dist/lib/utils/announce.mjs +12 -0
  59. package/dist/lib/utils/assert.mjs +12 -0
  60. package/dist/{shared/flow-rdf-core.187cb345.mjs → lib/utils/debounce.mjs} +3 -39
  61. package/dist/lib/utils/decorators.mjs +59 -0
  62. package/dist/lib/utils/deferred.mjs +11 -0
  63. package/dist/lib/utils/ensure-array.mjs +16 -0
  64. package/dist/{shared/flow-rdf-core.1f0901c5.mjs → lib/utils/feature-type-checks.mjs} +2 -2
  65. package/dist/lib/utils/functional.mjs +22 -0
  66. package/dist/lib/utils/global.mjs +18 -0
  67. package/dist/lib/utils/index.mjs +8 -5
  68. package/dist/lib/utils/random-id.mjs +9 -0
  69. package/dist/lib/utils/string-utils.mjs +3 -0
  70. package/dist/lib/utils/throttle.mjs +20 -0
  71. package/dist/{shared/flow-rdf-core.25e5f6d7.mjs → lib/utils/type-checks.mjs} +1 -26
  72. package/dist/lib/utils/watchable-promise.mjs +55 -0
  73. package/dist/package.json.mjs +3 -0
  74. package/dist/shared/{flow-rdf-core.4dde996e.d.cts → flow-rdf-core.8b5acf14.d.mts} +1315 -172
  75. package/dist/shared/{flow-rdf-core.4dde996e.d.ts → flow-rdf-core.8b5acf14.d.ts} +1315 -172
  76. package/package.json +4 -8
  77. package/dist/index.cjs +0 -3316
  78. package/dist/index.d.cts +0 -90
  79. package/dist/lib/map/plugins/index.cjs +0 -1850
  80. package/dist/lib/map/plugins/index.d.cts +0 -345
  81. package/dist/lib/utils/index.cjs +0 -48
  82. package/dist/lib/utils/index.d.cts +0 -160
  83. package/dist/shared/flow-rdf-core.06e85a6c.cjs +0 -1394
  84. package/dist/shared/flow-rdf-core.4dde996e.d.mts +0 -1916
  85. package/dist/shared/flow-rdf-core.55c169ca.cjs +0 -150
  86. package/dist/shared/flow-rdf-core.63162669.d.cts +0 -10
  87. package/dist/shared/flow-rdf-core.65045caf.mjs +0 -1360
  88. package/dist/shared/flow-rdf-core.a82b9be4.cjs +0 -34
  89. package/dist/shared/flow-rdf-core.f50908f0.cjs +0 -102
@@ -0,0 +1,749 @@
1
+ import maplibre from '@emuanalytics/maplibre-gl';
2
+ import Debug from 'debug';
3
+ import EventEmitter from 'eventemitter3';
4
+ import { runInAction } from 'mobx';
5
+ import { toStream } from 'mobx-utils';
6
+ import { FlowMapError } from '../errors/flow-map-error.mjs';
7
+ import { assert } from '../utils/assert.mjs';
8
+ import { debounceWithArgs } from '../utils/debounce.mjs';
9
+ import { randomId } from '../utils/random-id.mjs';
10
+ import { throttle } from '../utils/throttle.mjs';
11
+ import { isPlainObject, isNil, isDefined, isStringFull, isUndefined, isURL, isString } from '../utils/type-checks.mjs';
12
+ import { PluginManager } from './plugins/plugin-manager.mjs';
13
+ import { ImageRegistry } from './support/image-registry.mjs';
14
+ import { MapStyleChangeEvent, MapSourceEvent, MapEvent, MapLoadingEvent, MapMouseEvent } from './support/map-events.mjs';
15
+ import { MapFeatureStream } from './support/map-feature-stream.mjs';
16
+ import { MapStyle } from '../map-style/map-style.mjs';
17
+ import { MapLayer } from '../map-style/map-layer.mjs';
18
+ import { MapDataSource } from '../map-style/map-data-source.mjs';
19
+ import { getGlobalState } from '../context/global-state.mjs';
20
+ import { FlowCoreError } from '../errors/flow-error.mjs';
21
+
22
+ var __defProp = Object.defineProperty;
23
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
24
+ var __publicField = (obj, key, value) => {
25
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
26
+ return value;
27
+ };
28
+ const debug = Debug("flow:map");
29
+ const MOUSE_MOVE_THROTTLE_DURATION = 100;
30
+ const _FlowCoreMap = class _FlowCoreMap extends EventEmitter {
31
+ /**
32
+ * Constructs a new `FlowCoreMap` instance.
33
+ *
34
+ * The map will be initialized with the specified options and base style.
35
+ * When the map API is ready for use, the {@link initialized} promise will resolve.
36
+ *
37
+ * @example
38
+ * ```js
39
+ * const map = new FlowCoreMap({
40
+ * id: 'my-map',
41
+ * container: '#map-container',
42
+ * baseStyle: 'fjord',
43
+ * });
44
+ *
45
+ * map.initialized.then(() => {
46
+ * console.log('Map initialized');
47
+ * // Configure map data sources and layers
48
+ * ...
49
+ * });
50
+ * ```
51
+ *
52
+ */
53
+ constructor(options) {
54
+ super();
55
+ __publicField(this, "_context");
56
+ __publicField(this, "_initialized");
57
+ __publicField(this, "_loading", true);
58
+ __publicField(this, "_debouncedEmitSourceEvent");
59
+ __publicField(this, "_imageRegistry", new ImageRegistry());
60
+ __publicField(this, "_options");
61
+ __publicField(this, "_pluginManager");
62
+ __publicField(this, "_resolveInitialized", () => {
63
+ });
64
+ __publicField(this, "_style");
65
+ __publicField(this, "_styleUpdateSubscription", null);
66
+ __publicField(this, "_throttledMouseMove", throttle(this.handleMouseMove, MOUSE_MOVE_THROTTLE_DURATION, { context: this }));
67
+ __publicField(this, "_maplibreMap");
68
+ __publicField(this, "updateMapStyle", (style) => {
69
+ const oldStyle = this._maplibreMap.getStyle();
70
+ const beforeEvent = new MapStyleChangeEvent("beforeStyleChanged", this, oldStyle, style);
71
+ style = this._pluginManager.forwardStyleChangeEvent(
72
+ new MapStyleChangeEvent("beforeStyleChanged", this, oldStyle, style)
73
+ );
74
+ this.emit(beforeEvent.type, beforeEvent);
75
+ debug(`${this.formattedName}: Updating style`, style);
76
+ this._maplibreMap.setStyle(style);
77
+ this.emitEvent(new MapStyleChangeEvent("styleChanged", this, oldStyle, style));
78
+ });
79
+ this._options = {
80
+ id: randomId("map"),
81
+ ..._FlowCoreMap._defaultOptions,
82
+ ...options
83
+ };
84
+ this._style = new MapStyle();
85
+ this._debouncedEmitSourceEvent = debounceWithArgs(this.emitEvent, 20, {
86
+ hash: (event) => event.source,
87
+ context: this
88
+ });
89
+ this._initialized = new Promise((resolve) => {
90
+ this._resolveInitialized = resolve;
91
+ });
92
+ this._pluginManager = new PluginManager(this);
93
+ this.initialize();
94
+ }
95
+ /**
96
+ * Return the underlying [Maplibre GL JS](https://maplibre.org/maplibre-gl-js/docs/) map instance. This can be used to
97
+ * access the full Maplibre API for advanced use cases.
98
+ *
99
+ * :::danger
100
+ * Use with caution. Directly modifying the Maplibre map instance can cause unexpected behavior particularly if
101
+ * the changes conflict with the {@link FlowCoreMap} state. Do not call any Maplibre methods that add or
102
+ * remove map layers or data sources, or modify the map style directly.
103
+ * :::
104
+ */
105
+ get maplibreMap() {
106
+ return this._maplibreMap;
107
+ }
108
+ /**
109
+ * Returns the {@link FlowContext} associated with this map.
110
+ */
111
+ get context() {
112
+ return this._context;
113
+ }
114
+ /**
115
+ * Returns the map's container element.
116
+ */
117
+ get container() {
118
+ return this._maplibreMap.getContainer();
119
+ }
120
+ /**
121
+ * Returns the map identifier.
122
+ */
123
+ get id() {
124
+ return this._options.id;
125
+ }
126
+ /**
127
+ * Returns a promise that resolves when the map is fully initialized and ready for use.
128
+ *
129
+ * @example
130
+ * ```js
131
+ * map.initialized.then(() => {
132
+ * console.log('Map ready fo use');
133
+ * // Configure map data sources and layers
134
+ * ...
135
+ * });
136
+ * ```
137
+ */
138
+ get initialized() {
139
+ return this._initialized;
140
+ }
141
+ /**
142
+ * Returns the full Maplibre style specification for this map. This is a `computed` property and will trigger
143
+ * reactive updates when accessed in a reactive context.
144
+ *
145
+ * @example
146
+ * ```js
147
+ * stateStore.autorun(() => {
148
+ * const style = map.styleSpecification;
149
+ * console.log('Map style was updated', style);
150
+ * });
151
+ * ```
152
+ */
153
+ get styleSpecification() {
154
+ return this._style.styleSpecification;
155
+ }
156
+ /**
157
+ * @internal
158
+ *
159
+ * Used by map export to get images added to map.
160
+ */
161
+ get imageRegistry() {
162
+ return this._imageRegistry;
163
+ }
164
+ addLayer(layerOrOptions) {
165
+ const layer = isPlainObject(layerOrOptions) ? new MapLayer(layerOrOptions) : layerOrOptions;
166
+ this._style.addLayer(layer);
167
+ return layer;
168
+ }
169
+ addSource(sourceOrOptions) {
170
+ const source = isPlainObject(sourceOrOptions) ? new MapDataSource(sourceOrOptions) : sourceOrOptions;
171
+ this.emitEvent(new MapSourceEvent("beforeSourceAdded", this, source.id));
172
+ source.setFlowEndpoint({
173
+ host: this.context.flowClient.baseUrl,
174
+ apiKey: this.context.flowClient.apiKey || void 0
175
+ });
176
+ this._style.addSource(source);
177
+ this.emitEvent(new MapSourceEvent("sourceAdded", this, source.id));
178
+ return source;
179
+ }
180
+ /**
181
+ * Destroys the map instance and removes it from the DOM.
182
+ */
183
+ destroy() {
184
+ this._pluginManager.removeAllPlugins();
185
+ this._styleUpdateSubscription?.unsubscribe();
186
+ this._maplibreMap?.remove();
187
+ if (this.context) {
188
+ this.context.maps.unregisterMap(this);
189
+ }
190
+ this.emit("destroyed", new MapEvent("destroyed", this));
191
+ }
192
+ /**
193
+ * Called internally and by map plugins to notify an error.
194
+ *
195
+ * To handle errors, attach an error event listener to the map instance as follows:
196
+ *
197
+ * ```js
198
+ * map.on('error', (error) => {
199
+ * // Handle error
200
+ * console.error(`A map error occurred: ${error.message}`);
201
+ * });
202
+ * ```
203
+ *
204
+ * If no error event listener is attached, the error will be logged to the console.
205
+ */
206
+ emitError(message, originalError) {
207
+ const error = new FlowMapError(`${this.formattedName}: ${message}`, originalError);
208
+ this._pluginManager.forwardEvent("error", error);
209
+ if (this.listenerCount("error")) {
210
+ this.emit("error", error);
211
+ } else {
212
+ console.error(error);
213
+ }
214
+ }
215
+ /** Used internally and by plugins to emit a map event. */
216
+ emitEvent(event) {
217
+ this._pluginManager.forwardEvent(event.type, event);
218
+ this.emit(event.type, event);
219
+ }
220
+ /**
221
+ * Returns an observable {@link MapFeatureStream} for the specified feature identifiers. The feature stream
222
+ * will emit feature data for the specified features as their properties updated.
223
+ * Call the {@link MapFeatureStream.subscribe} method to receive feature data updates.
224
+ *
225
+ * :::warning
226
+ * All subscriptions should be disposed of when no longer needed.
227
+ * :::
228
+ *
229
+ * This method is used internally by the map popup plugin to receive feature properties for rendering
230
+ * popup content but can be used whenever you want to track feature data changes for a set of features.
231
+ *
232
+ * :::warning
233
+ * The feature stream will only emit feature data for features that are currently visible on the map. If the
234
+ * user pans the map away from the requested features, the map will eventually unload the features and will
235
+ * stop providing updates.
236
+ * :::
237
+ *
238
+ * @example
239
+ * ```js
240
+ * const subscription = map.featureStream({
241
+ * source: 'my-data-source',
242
+ * featureIds: ['feature-1', 'feature-2']
243
+ * }).subscribe((featureData) => {
244
+ * console.log('Feature data updated', featureData);
245
+ * });
246
+ * ...
247
+ * subscription.unsubscribe();
248
+ * ```
249
+ *
250
+ * @param options
251
+ * @returns
252
+ */
253
+ featureStream(options) {
254
+ return new MapFeatureStream({ map: this, ...options });
255
+ }
256
+ /**
257
+ * Returns the feature state for the specified map feature.
258
+ *
259
+ * See ##TODO for more information about map feature state.
260
+ *
261
+ * @param feature - Map feature identifier
262
+ */
263
+ getFeatureState(feature) {
264
+ return this._maplibreMap.getFeatureState(feature);
265
+ }
266
+ /**
267
+ * Returns the {@link MapLayer} with the specified ID.
268
+ *
269
+ * @param id - Map layer ID
270
+ */
271
+ getLayer(id) {
272
+ return this._style.getLayer(id);
273
+ }
274
+ /**
275
+ * Returns all map layers that match the specified filter function.
276
+ * If no filter is specified, all layers are returned.
277
+ *
278
+ * @param filter - Filter function. Return `true` to include the map layer in the result.
279
+ */
280
+ getLayers(filter) {
281
+ return this._style.getLayers(filter);
282
+ }
283
+ /**
284
+ * Returns a registered map plugin.
285
+ *
286
+ * @example
287
+ * Retrieve the popup plugin from a map:
288
+ * ```js
289
+ * const popupPlugin = map.getPlugin(PopupPlugin);
290
+ * popupPlugin.registerPopup('my-popup', {...});
291
+ * ```
292
+ *
293
+ * @param pluginOrName - Plugin constructor or name as a string.
294
+ */
295
+ getPlugin(pluginOrName) {
296
+ return this._pluginManager.getPlugin(pluginOrName);
297
+ }
298
+ /**
299
+ * Returns the {@link MapDataSource} with the specified ID.
300
+ *
301
+ * @param id - Data source ID
302
+ */
303
+ getSource(id) {
304
+ return this._style.getSource(id);
305
+ }
306
+ /**
307
+ * Returns all {@link MapDataSource} instance that match the specified filter function.
308
+ * If no filter is specified, all data sources are returned.
309
+ *
310
+ * @param filter - Filter function. Return `true` to include the map data source in the result.
311
+ */
312
+ getSources(filter) {
313
+ return this._style.getSources(filter);
314
+ }
315
+ /**
316
+ * Register a map icon image.
317
+ *
318
+ * The image can be used on [symbol-type map layers](/flow-rdf-core/layer-reference/symbol) by specifying the image ID
319
+ * as the [`icon-image`](/flow-rdf-core/layer-reference/symbol#icon-image) property.
320
+ *
321
+ * @param id Image ID.
322
+ * @param src Image source URL.
323
+ * @param width Image display width.
324
+ * @param height Image display height.
325
+ * @param pixelDensity Image pixel density. Defaults to `2` - the source image should be @2x resolution to enable better quality rendering on high-resolution displays.
326
+ * @param recolor Set to `true` to allow recoloring of map icon image using map layer style properties.
327
+ */
328
+ registerImage(id, src, width, height, pixelDensity = 2, recolor = false) {
329
+ debug(`${this.formattedName}: Registering image '${id}'`);
330
+ this._imageRegistry.registerImage(id, src, width, height, pixelDensity, recolor);
331
+ }
332
+ /**
333
+ * Remove feature state properties from a map feature.
334
+ *
335
+ * @param feature Feature to modify. Specify `null` to modify all map features.
336
+ * @param key Feature state key to remove. Specify `null` to remove all feature state for specified feature(s).
337
+ */
338
+ removeFeatureState(feature, key, fireEvent = true) {
339
+ if (isNil(feature.id) && isDefined(key)) {
340
+ const style = this._maplibreMap.style;
341
+ style._checkLoaded();
342
+ const sourceId = feature.source;
343
+ const sourceCache = style.sourceCaches[sourceId];
344
+ if (sourceCache === void 0) {
345
+ throw new FlowCoreError(`${this.formattedName}: the source '${sourceId}' does not exist in the map's style.`);
346
+ }
347
+ const sourceState = sourceCache._state;
348
+ const sourceType = sourceCache.getSource().type;
349
+ const sourceLayer = sourceType === "vector" ? feature.sourceLayer ?? "geometry" : "_geojsonTileLayer";
350
+ sourceState.deletedStates[sourceLayer] = sourceState.deletedStates[sourceLayer] || {};
351
+ const sourceLayerDeleted = sourceState.deletedStates[sourceLayer] === null;
352
+ if (sourceLayerDeleted) {
353
+ return;
354
+ }
355
+ if (sourceState.state[sourceLayer]) {
356
+ Object.keys(sourceState.state[sourceLayer]).forEach((feature2) => {
357
+ if (sourceState.deletedStates[sourceLayer][feature2] !== null) {
358
+ sourceState.deletedStates[sourceLayer][feature2] = sourceState.deletedStates[sourceLayer][feature2] || {};
359
+ sourceState.deletedStates[sourceLayer][feature2][key] = null;
360
+ }
361
+ });
362
+ }
363
+ this._maplibreMap._update();
364
+ } else {
365
+ this._maplibreMap.removeFeatureState(feature, key);
366
+ }
367
+ if (fireEvent) {
368
+ const sourceEvent = new MapSourceEvent("sourceFeatureState", this, feature.source);
369
+ this._debouncedEmitSourceEvent(sourceEvent);
370
+ }
371
+ }
372
+ /**
373
+ * Remove the specified {@link MapLayer} from the map.
374
+ * @param id - Map layer ID
375
+ */
376
+ removeLayer(id) {
377
+ this._style.removeLayer(id);
378
+ return this;
379
+ }
380
+ /**
381
+ * Remove the specified {@link MapDataSource} from the map.
382
+ * @param id - Map data source ID
383
+ */
384
+ removeSource(id, removeLayers = false) {
385
+ this.emitEvent(new MapSourceEvent("beforeSourceRemoved", this, id));
386
+ this._style.removeSource(id, removeLayers);
387
+ this.emitEvent(new MapSourceEvent("sourceRemoved", this, id));
388
+ return this;
389
+ }
390
+ /**
391
+ * Set the map's base style.
392
+ *
393
+ * ##TODO list of available base styles
394
+ *
395
+ * @param style - A style identifier or a Maplibre style specification.
396
+ */
397
+ async setBaseStyle(style) {
398
+ const baseStyle = await this.loadBaseStyle(style);
399
+ this._style.setBaseStyle(baseStyle);
400
+ }
401
+ /**
402
+ * Returns the map's current center, bearing, pitch, and zoom level.
403
+ */
404
+ getCenter() {
405
+ const lngLat = this._maplibreMap.getCenter();
406
+ const zoom = this._maplibreMap.getZoom();
407
+ const bearing = this._maplibreMap.getBearing();
408
+ const pitch = this._maplibreMap.getPitch();
409
+ return {
410
+ lng: lngLat.lng,
411
+ lat: lngLat.lat,
412
+ zoom,
413
+ bearing,
414
+ pitch
415
+ };
416
+ }
417
+ /**
418
+ * Sets one or more of the map's center, bearing, pitch, and/or zoom level.
419
+ * Animation options can be specified to animate the transition. If animation options are not specified,
420
+ * the map will jump to the new view immediately.
421
+ *
422
+ * A promise is returned that resolves when the map has finished moving.
423
+ *
424
+ * @example
425
+ * Zoom the map and log to console when finished:
426
+ * ```js
427
+ * const { zoom } = map.getCenter();
428
+ * map.setCenter({zoom: zoom + 2}, {mode: 'ease', duration: 2000}).then(() => {
429
+ * console.log('Map zoomed ');
430
+ * });
431
+ * ```
432
+ *
433
+ * @param center
434
+ * @param animationOptions
435
+ * @returns A promise that resolves when the map has finished moving.
436
+ */
437
+ setCenter(center, animationOptions) {
438
+ const lngLat = isDefined(center.lng) && isDefined(center.lat) ? new maplibre.LngLat(center.lng, center.lat) : void 0;
439
+ let method = this._maplibreMap.jumpTo;
440
+ if (animationOptions) {
441
+ switch (animationOptions.mode) {
442
+ case "ease":
443
+ method = this._maplibreMap.easeTo;
444
+ break;
445
+ case "fly":
446
+ method = this._maplibreMap.flyTo;
447
+ break;
448
+ default:
449
+ method = this._maplibreMap.jumpTo;
450
+ }
451
+ }
452
+ const options = {
453
+ center: lngLat,
454
+ ...isDefined(center.zoom) && { zoom: center.zoom },
455
+ ...isDefined(center.bearing) && { bearing: center.bearing },
456
+ ...isDefined(center.pitch) && { pitch: center.pitch },
457
+ ...animationOptions
458
+ };
459
+ const promise = new Promise((resolve) => {
460
+ this.once("moveEnd", () => resolve());
461
+ });
462
+ method.call(this._maplibreMap, options);
463
+ return promise;
464
+ }
465
+ /**
466
+ * Returns the map's current bounds in degrees as an array of `[west, south, east, north]`.
467
+ *
468
+ * See also {@link getVisibleBounds}.
469
+ */
470
+ getBounds() {
471
+ const bounds = this._maplibreMap.getBounds();
472
+ return [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()];
473
+ }
474
+ /**
475
+ * Set the map's bounds to the specified bounding box.
476
+ *
477
+ * Animation options can be specified to animate the transition. If animation options are not specified,
478
+ * the map will jump to the new view immediately.
479
+ *
480
+ * Specify the `padding` option to add padding around the bounding box in pixels.
481
+ *
482
+ * @param bounds - Bounding box in degrees as an array of `[west, south, east, north]`.
483
+ * @param options - Animation and padding options.
484
+ */
485
+ setBounds(bounds, options) {
486
+ const fitBoundsOptions = {
487
+ linear: !options || options.mode === "ease" || options.mode === "jump",
488
+ animate: !!options && options.mode !== "jump",
489
+ ...options
490
+ };
491
+ this._maplibreMap.fitBounds(bounds, fitBoundsOptions);
492
+ }
493
+ /**
494
+ * Returns the map's current visible bounds in degrees as an array of `[west, south, east, north]`.
495
+ *
496
+ * The visible bounds may differ from the map's full bounds if fixed positioning is used. With fixed positioning,
497
+ * the map's canvas extends outside its container underneath other elements like side-bars. This allows side-bars
498
+ * to be opened and closed without re-rendering the map. The visible bounds represent the region of the map that
499
+ * is currently visible within the map's container.
500
+ *
501
+ * See also {@link getBounds}.
502
+ */
503
+ getVisibleBounds() {
504
+ const parentEl = this.container.parentElement;
505
+ if (!parentEl) {
506
+ return this.getBounds();
507
+ }
508
+ const mapRect = parentEl.getBoundingClientRect();
509
+ const topLeft = this._maplibreMap.unproject([mapRect.x, mapRect.y]);
510
+ const bottomRight = this._maplibreMap.unproject([mapRect.x + mapRect.width, mapRect.y + mapRect.height]);
511
+ const bounds = [topLeft.lng, bottomRight.lat, bottomRight.lng, topLeft.lat];
512
+ return bounds;
513
+ }
514
+ /**
515
+ * Set feature state properties for a map feature.
516
+ *
517
+ * ##TODO more info on feature state
518
+ *
519
+ * @param feature Feature to modify.
520
+ * @param state Feature state properties to set.
521
+ */
522
+ setFeatureState(feature, state, fireEvent = true) {
523
+ const source = this.getSource(feature.source);
524
+ assert(
525
+ isDefined(source.featureId),
526
+ `${this.formattedName}: cannot set feature state (source '${source.id}' has not featureId specified)`
527
+ );
528
+ if (source?.type === "vector") {
529
+ feature.sourceLayer = feature.sourceLayer || "geometry";
530
+ }
531
+ this._maplibreMap.setFeatureState(feature, state);
532
+ if (fireEvent) {
533
+ const sourceEvent = new MapSourceEvent("sourceFeatureState", this, feature.source);
534
+ this._debouncedEmitSourceEvent(sourceEvent);
535
+ }
536
+ }
537
+ /**
538
+ * Set feature state properties in bulk for multiple map features.
539
+ *
540
+ * ##TODO more info on feature state
541
+ *
542
+ * @param feature Feature to modify.
543
+ * @param state Feature state properties to set.
544
+ */
545
+ setFeatureStateBulk(sourceId, featureState) {
546
+ const source = this.getSource(sourceId.source);
547
+ assert(isStringFull(source.featureId));
548
+ this._maplibreMap.style._checkLoaded();
549
+ const sourceCache = this._maplibreMap.style.sourceCaches[sourceId.source];
550
+ if (isUndefined(sourceCache)) {
551
+ throw new FlowCoreError(
552
+ `${this.formattedName}: the source '${sourceId.source}' does not exist in the map's style.`
553
+ );
554
+ }
555
+ const sourceLayer = sourceCache.getSource().type === "vector" ? sourceId.sourceLayer ?? "geometry" : "";
556
+ Object.keys(featureState).forEach((featureId) => {
557
+ sourceCache.setFeatureState(sourceLayer, featureId, featureState[featureId]);
558
+ });
559
+ this._maplibreMap._update();
560
+ const sourceEvent = new MapSourceEvent("sourceFeatureState", this, sourceId.source);
561
+ this._debouncedEmitSourceEvent(sourceEvent);
562
+ }
563
+ /**
564
+ * Run a series of map updates as a single transaction. This can be used to batch multiple updates
565
+ * while only triggering a single re-render of the map.
566
+ *
567
+ * @example
568
+ * ```js
569
+ * map.transaction(() => {
570
+ * map.addSource({ ... });
571
+ * map.addLayer({ ... });
572
+ * map.addLayer({ ... });
573
+ * });
574
+ * ```
575
+ *
576
+ * @param fn - Function containing map updates to run as a transaction.
577
+ */
578
+ transaction(fn) {
579
+ runInAction(() => fn());
580
+ }
581
+ /**
582
+ * Register a map plugin with the map instance.
583
+ *
584
+ * Some plugins may require additional options to be passed when registering the plugin.
585
+ *
586
+ * @example
587
+ * Register the HoverEffectPlugin with the map:
588
+ * ```js
589
+ * map.usePlugin(HoverEffectPlugin);
590
+ * ```
591
+ *
592
+ * @param plugin - Plugin constructor or name
593
+ * @param options - Plugin-specific options
594
+ * @returns The plugin instance
595
+ */
596
+ usePlugin(plugin, options) {
597
+ return this._pluginManager.usePlugin(plugin, options);
598
+ }
599
+ /**
600
+ * Register multiple map plugins with the map instance.
601
+ *
602
+ * @example
603
+ * Register multiple plugins with the map:
604
+ * ```js
605
+ * map.usePlugins([HoverEffectPlugin, PopupPlugin]);
606
+ * ```
607
+ *
608
+ * @param plugins - Array of plugin constructors to register
609
+ */
610
+ usePlugins(plugins) {
611
+ plugins.forEach((plugin) => this.usePlugin(plugin));
612
+ }
613
+ get formattedName() {
614
+ return `FlowMap<${this._options.id}>`;
615
+ }
616
+ bindMapEvents() {
617
+ this._maplibreMap.on("error", (e) => this.emitError(e.error.message, e.error));
618
+ this._maplibreMap.on("mousemove", this._throttledMouseMove);
619
+ this._maplibreMap.on("mouseout", this._throttledMouseMove);
620
+ this._maplibreMap.on("click", this.handleMouseClick("click").bind(this));
621
+ this._maplibreMap.on("dblclick", this.handleMouseClick("dblClick").bind(this));
622
+ this._maplibreMap.on("styleimagemissing", this.handleMissingImage.bind(this));
623
+ this._maplibreMap.on("sourcedata", this.handleLoading.bind(this));
624
+ this._maplibreMap.on("idle", this.handleLoading.bind(this));
625
+ const eventsToForward = ["moveEnd"];
626
+ eventsToForward.forEach((eventName) => {
627
+ this._maplibreMap.on(eventName.toLowerCase(), (event) => {
628
+ const flowEvent = MapEvent.fromMaplibreEvent(eventName, this, event);
629
+ this.emitEvent(flowEvent);
630
+ });
631
+ });
632
+ }
633
+ featureFromMaplibreFeature(feature) {
634
+ return {
635
+ id: feature.id,
636
+ geometry: feature.geometry,
637
+ source: feature.source,
638
+ sourceLayer: feature.sourceLayer,
639
+ layer: feature.layer.id,
640
+ properties: feature.properties,
641
+ state: feature.state
642
+ };
643
+ }
644
+ featureQueryBox(event) {
645
+ const queryBox = this._options.featureClickTolerance ? [
646
+ [event.point.x - this._options.featureClickTolerance, event.point.y - this._options.featureClickTolerance],
647
+ [event.point.x + this._options.featureClickTolerance, event.point.y + this._options.featureClickTolerance]
648
+ ] : event.point;
649
+ return queryBox;
650
+ }
651
+ async handleLoading(event) {
652
+ let loading = false;
653
+ if (event.type === "sourcedata" && event.isSourceLoaded === false) {
654
+ loading = true;
655
+ } else if (event.type === "idle") {
656
+ loading = false;
657
+ } else {
658
+ loading = !!Object.values(this._maplibreMap.style.sourceCaches).find((cache) => !cache.loaded());
659
+ }
660
+ if (this._loading !== loading) {
661
+ const loadingEvent = new MapLoadingEvent(this, loading);
662
+ this.emitEvent(loadingEvent);
663
+ this._loading = loading;
664
+ }
665
+ }
666
+ async handleMissingImage(event) {
667
+ const imageInfo = this._imageRegistry.getImage(event.id);
668
+ if (imageInfo === this._imageRegistry.unknownImage) {
669
+ console.error(`${this.formattedName}: Image '${event.id}' not registered`);
670
+ }
671
+ if (!imageInfo.image.complete) {
672
+ imageInfo.image.onload = () => {
673
+ this._maplibreMap.updateImage(event.id, imageInfo.image);
674
+ this._maplibreMap.redraw();
675
+ };
676
+ }
677
+ this._maplibreMap.addImage(event.id, imageInfo.image, {
678
+ pixelRatio: imageInfo.pixelDensity,
679
+ sdf: imageInfo.recolor
680
+ });
681
+ }
682
+ handleMouseClick(type) {
683
+ return (event) => {
684
+ const layerIds = this._style.getLayers((layer) => layer.interactive).map((layer) => layer.id);
685
+ const features = this._maplibreMap.queryRenderedFeatures(this.featureQueryBox(event), { layers: layerIds }).map(this.featureFromMaplibreFeature.bind(this));
686
+ if (features.length) {
687
+ debug(`${this.formattedName} Feature ${type}ed`, features[0]);
688
+ }
689
+ const flowEvent = MapMouseEvent.fromMaplibreMouseEvent(type, event, this, features);
690
+ this.emitEvent(flowEvent);
691
+ };
692
+ }
693
+ handleMouseMove(event) {
694
+ if (event.type === "mousemove") {
695
+ const layerIds = this._style.getLayers((layer) => layer.interactive).map((layer) => layer.id);
696
+ const features = this._maplibreMap.queryRenderedFeatures(this.featureQueryBox(event), { layers: layerIds }).map(this.featureFromMaplibreFeature.bind(this));
697
+ const flowEvent = MapMouseEvent.fromMaplibreMouseEvent("mouseMove", event, this, features);
698
+ this.emitEvent(flowEvent);
699
+ } else if (event.type === "mouseout") {
700
+ const flowEvent = MapMouseEvent.fromMaplibreMouseEvent("mouseOut", event, this, []);
701
+ this.emitEvent(flowEvent);
702
+ }
703
+ }
704
+ async initialize() {
705
+ const globalContextRegistry = getGlobalState().globalContextRegistry;
706
+ this._context = await globalContextRegistry.getContext(this._options.contextId);
707
+ this._style.bindState(this._context.state);
708
+ const baseStyle = await this.loadBaseStyle(this._options.baseStyle);
709
+ this._style.setBaseStyle(baseStyle);
710
+ this._maplibreMap = new maplibre.Map({
711
+ ...this._options,
712
+ transformRequest: this.transformMapRequest.bind(this),
713
+ style: baseStyle
714
+ });
715
+ this._maplibreMap.once("load", () => {
716
+ this._styleUpdateSubscription = toStream(() => this._style.styleSpecification, false).subscribe(
717
+ this.updateMapStyle
718
+ );
719
+ this.bindMapEvents();
720
+ this._resolveInitialized();
721
+ debug(`${this.formattedName}: initialized with options`, this._options);
722
+ this.context.maps.registerMap(this);
723
+ });
724
+ }
725
+ async loadBaseStyle(style) {
726
+ if (isURL(style)) {
727
+ return fetch(style).then((resp) => resp.json());
728
+ } else if (isString(style)) {
729
+ return this.context.flowClient.maps.get(style);
730
+ } else {
731
+ return style;
732
+ }
733
+ }
734
+ transformMapRequest(url, resourceType) {
735
+ const requestParameters = { url };
736
+ if (this.context.flowClient.accessToken && url.startsWith(this.context.flowClient.baseUrl)) {
737
+ requestParameters.headers = { Authorization: `Bearer ${this.context.flowClient.accessToken}` };
738
+ }
739
+ return requestParameters;
740
+ }
741
+ };
742
+ __publicField(_FlowCoreMap, "_defaultOptions", {
743
+ boxZoom: false,
744
+ doubleClickZoom: false,
745
+ featureClickTolerance: 0
746
+ });
747
+ let FlowCoreMap = _FlowCoreMap;
748
+
749
+ export { FlowCoreMap };