@orbat-mapper/tactical-draw-adapter-maplibre 0.2.0-alpha.7 → 0.2.0-alpha.9

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
@@ -13,10 +13,20 @@ type MapLibreEventToken = (e: any) => void;
13
13
  interface MapLibreViewToken {
14
14
  fn: () => void;
15
15
  }
16
+ interface MapLibreAdapterOptions {
17
+ /**
18
+ * How view changes propagate to view-state refits. "continuous" (default)
19
+ * refits on every frame of an animated zoom/rotate/pitch; "settle" defers
20
+ * the refit until the animation ends.
21
+ */
22
+ viewChangeMode?: "continuous" | "settle";
23
+ }
16
24
  declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLibreEventToken, MapLibreViewToken> {
17
25
  private map;
26
+ private readonly viewChangeMode;
18
27
  private readonly pendingIconImages;
19
- constructor(map: maplibregl.Map);
28
+ readonly scalesGroundPointSymbolsNatively = true;
29
+ constructor(map: maplibregl.Map, options?: MapLibreAdapterOptions);
20
30
  private toContinuousLng;
21
31
  protected createLayer(layerId: LayerId, style?: StyleOptions): MapLibreLayerHandle;
22
32
  protected destroyLayer(handle: MapLibreLayerHandle): void;
@@ -27,6 +37,7 @@ declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLib
27
37
  protected detachNativeEvent(entry: NativeEventEntry<MapLibreEventToken>): void;
28
38
  protected attachViewChange(handler: () => void): MapLibreViewToken;
29
39
  protected detachViewChange(token: MapLibreViewToken): void;
40
+ private viewChangeEvents;
30
41
  createEditPointerDriver(): EditPointerDriver;
31
42
  setLayerStyle(layerId: LayerId, style: StyleOptions): void;
32
43
  toLonLat(coordinate: number[]): Position;
@@ -48,4 +59,4 @@ declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLib
48
59
  setGlobeMode(enabled: boolean): void;
49
60
  }
50
61
  //#endregion
51
- export { MapLibreAdapter };
62
+ export { MapLibreAdapter, type MapLibreAdapterOptions };
package/dist/index.mjs CHANGED
@@ -80,11 +80,7 @@ function buildIconLayer(id, source) {
80
80
  ],
81
81
  layout: {
82
82
  "icon-image": ["get", "iconId"],
83
- "icon-size": [
84
- "coalesce",
85
- ["get", "iconScale"],
86
- 1
87
- ],
83
+ "icon-size": buildIconSizeExpression(),
88
84
  "icon-rotate": [
89
85
  "*",
90
86
  [
@@ -162,6 +158,60 @@ function buildTextSizeExpression(text) {
162
158
  ];
163
159
  }
164
160
  /**
161
+ * Per-feature icon size, same zoom-stop scheme as `buildTextSizeExpression`:
162
+ * a ground-sized point symbol carries `iconSizeMeters`/`iconOctagonSize` (and
163
+ * optional `iconMinPixels`/`iconMaxPixels`), so the meters→pixels clamp is
164
+ * evaluated inside the style layer at every integer zoom stop and the symbol
165
+ * scales smoothly through animated zooms. Anything else falls back to the
166
+ * host-computed `iconScale` snapshot.
167
+ */
168
+ function buildIconSizeExpression() {
169
+ const minExpr = [
170
+ "coalesce",
171
+ ["get", "iconMinPixels"],
172
+ 0
173
+ ];
174
+ const maxExpr = [
175
+ "coalesce",
176
+ ["get", "iconMaxPixels"],
177
+ 1e9
178
+ ];
179
+ const scaleAtZoom = (zoom) => {
180
+ return [
181
+ "case",
182
+ ["has", "iconSizeMeters"],
183
+ [
184
+ "/",
185
+ [
186
+ "max",
187
+ minExpr,
188
+ [
189
+ "min",
190
+ maxExpr,
191
+ [
192
+ "*",
193
+ ["get", "iconSizeMeters"],
194
+ Math.pow(2, zoom + 9) / WEB_MERCATOR_CIRCUMFERENCE
195
+ ]
196
+ ]
197
+ ],
198
+ ["get", "iconOctagonSize"]
199
+ ],
200
+ [
201
+ "coalesce",
202
+ ["get", "iconScale"],
203
+ 1
204
+ ]
205
+ ];
206
+ };
207
+ return [
208
+ "interpolate",
209
+ ["linear"],
210
+ ["zoom"],
211
+ ...Array.from({ length: 25 }, (_, zoom) => [zoom, scaleAtZoom(zoom)]).flat()
212
+ ];
213
+ }
214
+ /**
165
215
  * Build the paint expression for a fill layer with per-feature
166
216
  * `properties.style.fillColor` overlay. Mirrors `buildCirclePaint`.
167
217
  */
@@ -287,10 +337,13 @@ function buildLinePaint(style) {
287
337
  }
288
338
  var MapLibreAdapter = class extends BaseMapAdapter {
289
339
  map;
340
+ viewChangeMode;
290
341
  pendingIconImages = /* @__PURE__ */ new Set();
291
- constructor(map) {
342
+ scalesGroundPointSymbolsNatively = true;
343
+ constructor(map, options = {}) {
292
344
  super();
293
345
  this.map = map;
346
+ this.viewChangeMode = options.viewChangeMode ?? "continuous";
294
347
  }
295
348
  toContinuousLng(lng) {
296
349
  return nearestWorldCopyLng(lng, this.map.getCenter().lng);
@@ -420,17 +473,19 @@ var MapLibreAdapter = class extends BaseMapAdapter {
420
473
  }
421
474
  attachViewChange(handler) {
422
475
  const fn = () => handler();
423
- this.map.on("zoom", fn);
424
- this.map.on("rotate", fn);
425
- this.map.on("pitch", fn);
426
- this.map.on("moveend", fn);
476
+ for (const event of this.viewChangeEvents()) this.map.on(event, fn);
427
477
  return { fn };
428
478
  }
429
479
  detachViewChange(token) {
430
- this.map.off("zoom", token.fn);
431
- this.map.off("rotate", token.fn);
432
- this.map.off("pitch", token.fn);
433
- this.map.off("moveend", token.fn);
480
+ for (const event of this.viewChangeEvents()) this.map.off(event, token.fn);
481
+ }
482
+ viewChangeEvents() {
483
+ return this.viewChangeMode === "settle" ? ["moveend"] : [
484
+ "zoom",
485
+ "rotate",
486
+ "pitch",
487
+ "moveend"
488
+ ];
434
489
  }
435
490
  createEditPointerDriver() {
436
491
  const map = this.map;
@@ -440,7 +495,10 @@ var MapLibreAdapter = class extends BaseMapAdapter {
440
495
  let disposed = false;
441
496
  let touchDragActive = false;
442
497
  let multiTouchCancelled = false;
443
- const TOUCH_INFO = { hitTolerance: TOUCH_HIT_TOLERANCE_PX };
498
+ const touchInfo = (e) => ({
499
+ hitTolerance: TOUCH_HIT_TOLERANCE_PX,
500
+ native: e.originalEvent
501
+ });
444
502
  const preventMapEventDefaults = (e) => {
445
503
  e.preventDefault();
446
504
  e.originalEvent.preventDefault?.();
@@ -450,23 +508,30 @@ var MapLibreAdapter = class extends BaseMapAdapter {
450
508
  const px = [e.point.x, e.point.y];
451
509
  const info = {
452
510
  altKey: e.originalEvent.altKey,
453
- shiftKey: e.originalEvent.shiftKey
511
+ shiftKey: e.originalEvent.shiftKey,
512
+ native: e.originalEvent
454
513
  };
455
514
  hub.fireDown(px, info);
456
515
  };
457
516
  const onMove = (e) => {
458
517
  const px = [e.point.x, e.point.y];
459
- hub.fireMove(px, { shiftKey: e.originalEvent.shiftKey });
518
+ hub.fireMove(px, {
519
+ shiftKey: e.originalEvent.shiftKey,
520
+ native: e.originalEvent
521
+ });
460
522
  };
461
523
  const onUp = (e) => {
462
524
  const px = [e.point.x, e.point.y];
463
- hub.fireUp(px, { shiftKey: e.originalEvent.shiftKey });
525
+ hub.fireUp(px, {
526
+ shiftKey: e.originalEvent.shiftKey,
527
+ native: e.originalEvent
528
+ });
464
529
  };
465
530
  const onTouchStart = (e) => {
466
531
  if (e.points.length !== 1) return;
467
532
  const before = dragPanWasEnabled;
468
533
  const px = [e.point.x, e.point.y];
469
- hub.fireDown(px, TOUCH_INFO);
534
+ hub.fireDown(px, touchInfo(e));
470
535
  touchDragActive = dragPanWasEnabled !== before;
471
536
  if (touchDragActive) preventMapEventDefaults(e);
472
537
  };
@@ -475,20 +540,20 @@ var MapLibreAdapter = class extends BaseMapAdapter {
475
540
  const px = [e.point.x, e.point.y];
476
541
  if (touchCount > 1 && touchDragActive && !multiTouchCancelled) {
477
542
  multiTouchCancelled = true;
478
- hub.fireUp(px, TOUCH_INFO);
543
+ hub.fireUp(px, touchInfo(e));
479
544
  touchDragActive = false;
480
545
  preventMapEventDefaults(e);
481
546
  return;
482
547
  }
483
548
  if (touchCount > 1) return;
484
549
  if (touchCount === 1) multiTouchCancelled = false;
485
- hub.fireMove(px, TOUCH_INFO);
550
+ hub.fireMove(px, touchInfo(e));
486
551
  if (touchDragActive) preventMapEventDefaults(e);
487
552
  };
488
553
  const onTouchEnd = (e) => {
489
554
  multiTouchCancelled = false;
490
555
  const px = [e.point.x, e.point.y];
491
- hub.fireUp(px, TOUCH_INFO);
556
+ hub.fireUp(px, touchInfo(e));
492
557
  touchDragActive = false;
493
558
  };
494
559
  map.on("mousedown", onDown);
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.7",
3
+ "version": "0.2.0-alpha.9",
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",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@orbat-mapper/control-measures": "^0.2.0-alpha.15",
41
- "@orbat-mapper/tactical-draw": "^0.2.0-alpha.22"
41
+ "@orbat-mapper/tactical-draw": "^0.2.0-alpha.24"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "maplibre-gl": ">=4.0.0 <6.0.0"