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

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
@@ -1,4 +1,4 @@
1
- import { BaseMapAdapter, EditPointerDriver, FeatureOps, LayerId, MapCursor, NativeEventEntry, PickHandler, PickOptions, PixelCoordinate, StyleOptions } from "@orbat-mapper/tactical-draw";
1
+ import { BaseMapAdapter, EditPointerDriver, FeatureOps, IconResource, LayerId, MapCursor, NativeEventEntry, PickHandler, PickOptions, PixelCoordinate, StyleOptions } from "@orbat-mapper/tactical-draw";
2
2
  import maplibregl from "maplibre-gl";
3
3
  import { Position } from "geojson";
4
4
 
@@ -15,11 +15,14 @@ interface MapLibreViewToken {
15
15
  }
16
16
  declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLibreEventToken, MapLibreViewToken> {
17
17
  private map;
18
+ private readonly pendingIconImages;
18
19
  constructor(map: maplibregl.Map);
19
20
  private toContinuousLng;
20
21
  protected createLayer(layerId: LayerId, style?: StyleOptions): MapLibreLayerHandle;
21
22
  protected destroyLayer(handle: MapLibreLayerHandle): void;
22
23
  protected applyFeatureOps(layerId: LayerId, ops: FeatureOps): void;
24
+ protected applyIconResources(resources: readonly IconResource[]): void;
25
+ protected removeIconResource(id: string): void;
23
26
  protected attachNativeEvent(type: "click" | "pointermove" | "dblclick", onEvent: (lonLat: Position, pixel: PixelCoordinate, originalEvent: unknown) => void): NativeEventEntry<MapLibreEventToken>;
24
27
  protected detachNativeEvent(entry: NativeEventEntry<MapLibreEventToken>): void;
25
28
  protected attachViewChange(handler: () => void): MapLibreViewToken;
package/dist/index.mjs CHANGED
@@ -52,13 +52,67 @@ function buildCirclePointFilter(style) {
52
52
  "$type",
53
53
  "Point"
54
54
  ],
55
+ ["!has", "iconId"],
55
56
  ["!has", style.text.labelProperty]
56
57
  ] : [
57
- "==",
58
- "$type",
59
- "Point"
58
+ "all",
59
+ [
60
+ "==",
61
+ "$type",
62
+ "Point"
63
+ ],
64
+ ["!has", "iconId"]
60
65
  ];
61
66
  }
67
+ function buildIconLayer(id, source) {
68
+ return {
69
+ id,
70
+ type: "symbol",
71
+ source,
72
+ filter: [
73
+ "all",
74
+ [
75
+ "==",
76
+ "$type",
77
+ "Point"
78
+ ],
79
+ ["has", "iconId"]
80
+ ],
81
+ layout: {
82
+ "icon-image": ["get", "iconId"],
83
+ "icon-size": [
84
+ "coalesce",
85
+ ["get", "iconScale"],
86
+ 1
87
+ ],
88
+ "icon-rotate": [
89
+ "*",
90
+ [
91
+ "coalesce",
92
+ ["get", "iconRotation"],
93
+ 0
94
+ ],
95
+ 180 / Math.PI
96
+ ],
97
+ "icon-rotation-alignment": "viewport",
98
+ "icon-pitch-alignment": "viewport",
99
+ "icon-allow-overlap": true,
100
+ "icon-ignore-placement": true
101
+ },
102
+ paint: { "icon-opacity": [
103
+ "coalesce",
104
+ ["get", "iconOpacity"],
105
+ 1
106
+ ] }
107
+ };
108
+ }
109
+ function centeredSvg(resource) {
110
+ const halfWidth = Math.max(resource.anchor[0], resource.width - resource.anchor[0]);
111
+ const halfHeight = Math.max(resource.anchor[1], resource.height - resource.anchor[1]);
112
+ const width = halfWidth * 2;
113
+ const height = halfHeight * 2;
114
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><g transform="translate(${halfWidth - resource.anchor[0]} ${halfHeight - resource.anchor[1]})">${resource.svg}</g></svg>`;
115
+ }
62
116
  /** Web Mercator world circumference in meters (2π × 6378137). */
63
117
  const WEB_MERCATOR_CIRCUMFERENCE = 40075016.686;
64
118
  /**
@@ -233,6 +287,7 @@ function buildLinePaint(style) {
233
287
  }
234
288
  var MapLibreAdapter = class extends BaseMapAdapter {
235
289
  map;
290
+ pendingIconImages = /* @__PURE__ */ new Set();
236
291
  constructor(map) {
237
292
  super();
238
293
  this.map = map;
@@ -295,6 +350,9 @@ var MapLibreAdapter = class extends BaseMapAdapter {
295
350
  paint: buildCirclePaint(style)
296
351
  });
297
352
  mlLayerIds.push(circleId);
353
+ const iconId = `${layerId}-icon`;
354
+ this.map.addLayer(buildIconLayer(iconId, sourceId));
355
+ mlLayerIds.push(iconId);
298
356
  if (style?.text) {
299
357
  const symbolId = `${layerId}-symbol`;
300
358
  this.map.addLayer(buildSymbolLayer(symbolId, sourceId, style.text));
@@ -327,6 +385,25 @@ var MapLibreAdapter = class extends BaseMapAdapter {
327
385
  features
328
386
  });
329
387
  }
388
+ applyIconResources(resources) {
389
+ if (typeof Image === "undefined") return;
390
+ for (const resource of resources) {
391
+ if (this.map.hasImage(resource.id) || this.pendingIconImages.has(resource.id)) continue;
392
+ this.pendingIconImages.add(resource.id);
393
+ const image = new Image();
394
+ image.onload = () => {
395
+ if (!this.pendingIconImages.has(resource.id)) return;
396
+ this.pendingIconImages.delete(resource.id);
397
+ if (!this.map.hasImage(resource.id)) this.map.addImage(resource.id, image);
398
+ };
399
+ image.onerror = () => this.pendingIconImages.delete(resource.id);
400
+ image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(centeredSvg(resource))}`;
401
+ }
402
+ }
403
+ removeIconResource(id) {
404
+ this.pendingIconImages.delete(id);
405
+ if (this.map.hasImage(id)) this.map.removeImage(id);
406
+ }
330
407
  attachNativeEvent(type, onEvent) {
331
408
  const mlEvent = type === "pointermove" ? "mousemove" : type === "dblclick" ? "dblclick" : "click";
332
409
  const fn = (e) => {
@@ -371,16 +448,19 @@ var MapLibreAdapter = class extends BaseMapAdapter {
371
448
  };
372
449
  const onDown = (e) => {
373
450
  const px = [e.point.x, e.point.y];
374
- const info = { altKey: e.originalEvent.altKey };
451
+ const info = {
452
+ altKey: e.originalEvent.altKey,
453
+ shiftKey: e.originalEvent.shiftKey
454
+ };
375
455
  hub.fireDown(px, info);
376
456
  };
377
457
  const onMove = (e) => {
378
458
  const px = [e.point.x, e.point.y];
379
- hub.fireMove(px);
459
+ hub.fireMove(px, { shiftKey: e.originalEvent.shiftKey });
380
460
  };
381
461
  const onUp = (e) => {
382
462
  const px = [e.point.x, e.point.y];
383
- hub.fireUp(px);
463
+ hub.fireUp(px, { shiftKey: e.originalEvent.shiftKey });
384
464
  };
385
465
  const onTouchStart = (e) => {
386
466
  if (e.points.length !== 1) 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.5",
3
+ "version": "0.2.0-alpha.7",
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.14",
41
- "@orbat-mapper/tactical-draw": "^0.2.0-alpha.18"
40
+ "@orbat-mapper/control-measures": "^0.2.0-alpha.15",
41
+ "@orbat-mapper/tactical-draw": "^0.2.0-alpha.22"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "maplibre-gl": ">=4.0.0 <6.0.0"