@orbat-mapper/tactical-draw-adapter-maplibre 0.2.0-alpha.10 → 0.2.0-alpha.12

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.
package/dist/index.d.mts CHANGED
@@ -25,12 +25,17 @@ declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLib
25
25
  private map;
26
26
  private readonly viewChangeMode;
27
27
  private readonly pendingIconImages;
28
+ private readonly pendingLayerUpdates;
29
+ private readonly deferredLayerRemovals;
28
30
  readonly scalesGroundPointSymbolsNatively = true;
29
31
  constructor(map: maplibregl.Map, options?: MapLibreAdapterOptions);
30
32
  private toContinuousLng;
31
33
  protected createLayer(layerId: LayerId, style?: StyleOptions): MapLibreLayerHandle;
32
34
  protected destroyLayer(handle: MapLibreLayerHandle): void;
33
35
  protected applyFeatureOps(layerId: LayerId, ops: FeatureOps): void;
36
+ private updateSourceData;
37
+ private flushDeferredLayerRemoval;
38
+ deferLayerRemovalUntilLayerUpdate(layerId: LayerId, updatedLayerId: LayerId): void;
34
39
  protected applyIconResources(resources: readonly IconResource[]): void;
35
40
  protected removeIconResource(id: string): void;
36
41
  protected attachNativeEvent(type: "click" | "pointermove" | "dblclick", onEvent: (lonLat: Position, pixel: PixelCoordinate, originalEvent: unknown) => void): NativeEventEntry<MapLibreEventToken>;
package/dist/index.mjs CHANGED
@@ -1,6 +1,9 @@
1
1
  import { project, unproject } from "@orbat-mapper/control-measures";
2
2
  import { BaseMapAdapter, DEFAULT_TEXT_SIZE_MAX_PIXELS, DEFAULT_TEXT_SIZE_MIN_PIXELS, PointerCallbackHub, TOUCH_HIT_TOLERANCE_PX, bindAbortable, nearestWorldCopyLng, pickHitTolerance, tryControlMeasureIdFromFeature } from "@orbat-mapper/tactical-draw";
3
3
  //#region src/ml-adapter.ts
4
+ function isPromiseLike(value) {
5
+ return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
6
+ }
4
7
  function emptyFC() {
5
8
  return {
6
9
  type: "FeatureCollection",
@@ -274,6 +277,7 @@ function buildSymbolLayer(symbolId, sourceId, text) {
274
277
  "text-size": buildTextSizeExpression(text),
275
278
  "text-rotate": textRotate,
276
279
  "text-anchor": textAnchor,
280
+ "text-max-width": 9999,
277
281
  "text-rotation-alignment": "map",
278
282
  "text-pitch-alignment": "map",
279
283
  "text-allow-overlap": true
@@ -339,6 +343,8 @@ var MapLibreAdapter = class extends BaseMapAdapter {
339
343
  map;
340
344
  viewChangeMode;
341
345
  pendingIconImages = /* @__PURE__ */ new Set();
346
+ pendingLayerUpdates = /* @__PURE__ */ new Map();
347
+ deferredLayerRemovals = /* @__PURE__ */ new Map();
342
348
  scalesGroundPointSymbolsNatively = true;
343
349
  constructor(map, options = {}) {
344
350
  super();
@@ -418,25 +424,73 @@ var MapLibreAdapter = class extends BaseMapAdapter {
418
424
  };
419
425
  }
420
426
  destroyLayer(handle) {
427
+ for (const [layerId, layerHandle] of this.layers) {
428
+ if (layerHandle !== handle) continue;
429
+ this.pendingLayerUpdates.get(layerId)?.cancel();
430
+ this.pendingLayerUpdates.delete(layerId);
431
+ this.deferredLayerRemovals.delete(layerId);
432
+ break;
433
+ }
421
434
  for (const mlId of handle.mlLayerIds) if (this.map.getLayer(mlId)) this.map.removeLayer(mlId);
422
435
  if (this.map.getSource(handle.sourceId)) this.map.removeSource(handle.sourceId);
423
436
  }
424
437
  applyFeatureOps(layerId, ops) {
438
+ const deferred = this.deferredLayerRemovals.get(layerId);
439
+ const isPureRemoval = ops.adds.length === 0 && ops.updates.length === 0 && (ops.removes?.length ?? 0) > 0;
440
+ if (deferred && isPureRemoval) {
441
+ deferred.after.then(() => this.flushDeferredLayerRemoval(layerId, deferred), () => this.flushDeferredLayerRemoval(layerId, deferred));
442
+ return;
443
+ }
444
+ if (deferred) this.deferredLayerRemovals.delete(layerId);
445
+ this.updateSourceData(layerId, ops.full);
446
+ }
447
+ updateSourceData(layerId, features) {
425
448
  const handle = this.layers.get(layerId);
426
449
  if (!handle) return;
427
450
  const source = this.map.getSource(handle.sourceId);
428
451
  if (!source) return;
429
- const features = ops.full.map((f) => f.id !== void 0 ? {
452
+ const promotedFeatures = features.map((f) => f.id !== void 0 ? {
430
453
  ...f,
431
454
  properties: {
432
455
  ...f.properties ?? {},
433
456
  _fid: f.id
434
457
  }
435
458
  } : f);
436
- source.setData({
437
- type: "FeatureCollection",
438
- features
459
+ let resolveSourceData;
460
+ const sourceDataCompletion = new Promise((resolve) => {
461
+ resolveSourceData = resolve;
439
462
  });
463
+ const onSourceData = (event) => {
464
+ if (event.sourceDataType !== "content") return;
465
+ source.off("data", onSourceData);
466
+ resolveSourceData();
467
+ };
468
+ source.on("data", onSourceData);
469
+ const result = source.setData({
470
+ type: "FeatureCollection",
471
+ features: promotedFeatures
472
+ }, true);
473
+ const supportsCompletionPromise = isPromiseLike(result);
474
+ if (supportsCompletionPromise) source.off("data", onSourceData);
475
+ const completion = supportsCompletionPromise ? Promise.resolve(result) : sourceDataCompletion;
476
+ const pending = {
477
+ completion,
478
+ cancel() {
479
+ source.off("data", onSourceData);
480
+ }
481
+ };
482
+ this.pendingLayerUpdates.set(layerId, pending);
483
+ completion.catch(() => {});
484
+ }
485
+ flushDeferredLayerRemoval(layerId, deferred) {
486
+ if (this.deferredLayerRemovals.get(layerId) !== deferred) return;
487
+ this.deferredLayerRemovals.delete(layerId);
488
+ this.updateSourceData(layerId, this.getLayerFeatures(layerId));
489
+ }
490
+ deferLayerRemovalUntilLayerUpdate(layerId, updatedLayerId) {
491
+ const pending = this.pendingLayerUpdates.get(updatedLayerId);
492
+ if (!pending || !this.layers.has(layerId)) return;
493
+ this.deferredLayerRemovals.set(layerId, { after: pending.completion });
440
494
  }
441
495
  applyIconResources(resources) {
442
496
  if (typeof Image === "undefined") return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbat-mapper/tactical-draw-adapter-maplibre",
3
- "version": "0.2.0-alpha.10",
3
+ "version": "0.2.0-alpha.12",
4
4
  "description": "MapLibre GL JS engine adapter for @orbat-mapper/tactical-draw. Draw and edit tactical control measures (MIL-STD-2525 / APP-6) on a MapLibre map.",
5
5
  "license": "MIT",
6
6
  "author": "Orbat Mapper",
@@ -37,8 +37,8 @@
37
37
  "access": "public"
38
38
  },
39
39
  "dependencies": {
40
- "@orbat-mapper/control-measures": "^0.2.0-alpha.16",
41
- "@orbat-mapper/tactical-draw": "^0.2.0-alpha.25"
40
+ "@orbat-mapper/tactical-draw": "^0.2.0-alpha.27",
41
+ "@orbat-mapper/control-measures": "^0.2.0-alpha.16"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "maplibre-gl": ">=4.0.0 <6.0.0"