@orbat-mapper/tactical-draw-adapter-maplibre 0.8.1 → 0.8.2

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
@@ -315,6 +315,12 @@ declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLib
315
315
  * (ADR-0057).
316
316
  */
317
317
  private insertionAnchor;
318
+ private unvalidatedStyle;
319
+ private addStyleSource;
320
+ private addStyleLayer;
321
+ private setStyleFilter;
322
+ private setStylePaint;
323
+ private setStyleLayout;
318
324
  private toContinuousLng;
319
325
  protected createLayer(layerId: LayerId, style?: StyleOptions): MapLibreLayerHandle;
320
326
  protected destroyLayer(handle: MapLibreLayerHandle): void;
package/dist/index.mjs CHANGED
@@ -1486,6 +1486,12 @@ function buildIconSizing(props) {
1486
1486
  const FILL_PATTERN_IMAGE_ID_PROPERTY = "_fillPatternImageId";
1487
1487
  const DEFAULT_LINE_COLOR = "#000";
1488
1488
  const DEFAULT_LINE_WIDTH = 2;
1489
+ /**
1490
+ * MapLibre validates each style mutation against a fresh serialization of the
1491
+ * whole style, making bulk adds O(N²) (#569). The adapter builds every source,
1492
+ * layer, and property value itself, so it skips that validation.
1493
+ */
1494
+ const UNVALIDATED = { validate: false };
1489
1495
  function isHatchPattern(value) {
1490
1496
  return HATCH_PATTERNS.includes(value);
1491
1497
  }
@@ -2194,19 +2200,50 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2194
2200
  insertionAnchor(handle, draped) {
2195
2201
  return this.terrainActive && !draped ? handle.overlayAnchorId : handle.mlLayerIds[0];
2196
2202
  }
2203
+ unvalidatedStyle() {
2204
+ const style = this.map.style;
2205
+ return typeof style?.addSource === "function" && typeof style.addLayer === "function" && typeof this.map._update === "function" ? style : void 0;
2206
+ }
2207
+ addStyleSource(id, source) {
2208
+ const style = this.unvalidatedStyle();
2209
+ if (!style) {
2210
+ this.map.addSource(id, source);
2211
+ return;
2212
+ }
2213
+ style.addSource(id, source, UNVALIDATED);
2214
+ this.map._update(true);
2215
+ }
2216
+ addStyleLayer(layer, beforeId) {
2217
+ const style = this.unvalidatedStyle();
2218
+ if (!style) {
2219
+ this.map.addLayer(layer, beforeId);
2220
+ return;
2221
+ }
2222
+ style.addLayer(layer, beforeId, UNVALIDATED);
2223
+ this.map._update(true);
2224
+ }
2225
+ setStyleFilter(layerId, filter) {
2226
+ this.map.setFilter(layerId, filter, UNVALIDATED);
2227
+ }
2228
+ setStylePaint(layerId, name, value) {
2229
+ this.map.setPaintProperty(layerId, name, value, UNVALIDATED);
2230
+ }
2231
+ setStyleLayout(layerId, name, value) {
2232
+ this.map.setLayoutProperty(layerId, name, value, UNVALIDATED);
2233
+ }
2197
2234
  toContinuousLng(lng) {
2198
2235
  return nearestWorldCopyLng(lng, this.map.getCenter().lng);
2199
2236
  }
2200
2237
  createLayer(layerId, style) {
2201
2238
  const sourceId = `${layerId}-src`;
2202
- this.map.addSource(sourceId, {
2239
+ this.addStyleSource(sourceId, {
2203
2240
  type: "geojson",
2204
2241
  data: emptyFC(),
2205
2242
  promoteId: "_fid"
2206
2243
  });
2207
2244
  const mlLayerIds = [];
2208
2245
  const fillId = `${layerId}-fill`;
2209
- this.map.addLayer(hiddenLayer({
2246
+ this.addStyleLayer(hiddenLayer({
2210
2247
  id: fillId,
2211
2248
  type: "fill",
2212
2249
  source: sourceId,
@@ -2215,7 +2252,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2215
2252
  }));
2216
2253
  mlLayerIds.push(fillId);
2217
2254
  const fillPatternId = `${layerId}-fill-pattern`;
2218
- this.map.addLayer(hiddenLayer({
2255
+ this.addStyleLayer(hiddenLayer({
2219
2256
  id: fillPatternId,
2220
2257
  type: "fill",
2221
2258
  source: sourceId,
@@ -2224,7 +2261,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2224
2261
  }));
2225
2262
  mlLayerIds.push(fillPatternId);
2226
2263
  const lineId = `${layerId}-line`;
2227
- this.map.addLayer(hiddenLayer({
2264
+ this.addStyleLayer(hiddenLayer({
2228
2265
  id: lineId,
2229
2266
  type: "line",
2230
2267
  source: sourceId,
@@ -2249,7 +2286,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2249
2286
  }));
2250
2287
  mlLayerIds.push(lineId);
2251
2288
  const circleId = `${layerId}-circle`;
2252
- this.map.addLayer(hiddenLayer({
2289
+ this.addStyleLayer(hiddenLayer({
2253
2290
  id: circleId,
2254
2291
  type: "circle",
2255
2292
  source: sourceId,
@@ -2258,11 +2295,11 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2258
2295
  }));
2259
2296
  mlLayerIds.push(circleId);
2260
2297
  const iconId = `${layerId}-icon`;
2261
- this.map.addLayer(hiddenLayer(buildIconLayer(iconId, sourceId)));
2298
+ this.addStyleLayer(hiddenLayer(buildIconLayer(iconId, sourceId)));
2262
2299
  mlLayerIds.push(iconId);
2263
2300
  if (style?.text) {
2264
2301
  const symbolId = `${layerId}-symbol`;
2265
- this.map.addLayer(hiddenLayer(buildSymbolLayer(symbolId, sourceId, style.text)));
2302
+ this.addStyleLayer(hiddenLayer(buildSymbolLayer(symbolId, sourceId, style.text)));
2266
2303
  mlLayerIds.push(symbolId);
2267
2304
  }
2268
2305
  return {
@@ -2384,7 +2421,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2384
2421
  }
2385
2422
  createGroupBundle(layerId, handle) {
2386
2423
  const sourceId = `${layerId}-group-${++handle.nextGroupBundleId}-src`;
2387
- this.map.addSource(sourceId, {
2424
+ this.addStyleSource(sourceId, {
2388
2425
  type: "geojson",
2389
2426
  data: emptyFC(),
2390
2427
  promoteId: "_fid"
@@ -2428,12 +2465,12 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2428
2465
  const suffix = `${bundle.sourceId}-${encodeURIComponent(overlay.id)}`;
2429
2466
  const sourceId = `${suffix}-image-src`;
2430
2467
  const layerId = `${suffix}-image`;
2431
- this.map.addSource(sourceId, {
2468
+ this.addStyleSource(sourceId, {
2432
2469
  type: "image",
2433
2470
  url,
2434
2471
  coordinates: overlay.coordinates
2435
2472
  });
2436
- this.map.addLayer({
2473
+ this.addStyleLayer({
2437
2474
  id: layerId,
2438
2475
  type: "raster",
2439
2476
  source: sourceId,
@@ -2459,7 +2496,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2459
2496
  coordinates: overlay.coordinates.map((point) => [...point])
2460
2497
  });
2461
2498
  else if (coordinatesChanged) source?.setCoordinates?.(overlay.coordinates.map((point) => [...point]));
2462
- if (opacityChanged) this.map.setPaintProperty(existing.layerId, "raster-opacity", overlay.opacity);
2499
+ if (opacityChanged) this.setStylePaint(existing.layerId, "raster-opacity", overlay.opacity);
2463
2500
  existing.url = url;
2464
2501
  existing.opacity = overlay.opacity;
2465
2502
  existing.coordinates = overlay.coordinates;
@@ -2513,7 +2550,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2513
2550
  featureId
2514
2551
  ]
2515
2552
  };
2516
- this.map.addLayer(layer, this.insertionAnchor(handle, isDrapedChannel(channel)));
2553
+ this.addStyleLayer(layer, this.insertionAnchor(handle, isDrapedChannel(channel)));
2517
2554
  bundle.paintSurfaces.set(key, {
2518
2555
  layerId,
2519
2556
  channel,
@@ -2532,17 +2569,17 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2532
2569
  surface.sizingKey = sizingKey;
2533
2570
  if (channel === "icon") {
2534
2571
  const size = buildIconSizing(props);
2535
- if (size !== void 0) this.map.setLayoutProperty(surface.layerId, "icon-size", size);
2572
+ if (size !== void 0) this.setStyleLayout(surface.layerId, "icon-size", size);
2536
2573
  return;
2537
2574
  }
2538
2575
  const next = buildTextSizing(style.text, props);
2539
2576
  const previous = surface.textSizing;
2540
- if (JSON.stringify(previous?.size) !== JSON.stringify(next.size)) this.map.setLayoutProperty(surface.layerId, "text-size", next.size);
2577
+ if (JSON.stringify(previous?.size) !== JSON.stringify(next.size)) this.setStyleLayout(surface.layerId, "text-size", next.size);
2541
2578
  for (const [key, property] of [
2542
2579
  ["haloWidth", "text-halo-width"],
2543
2580
  ["haloBlur", "text-halo-blur"],
2544
2581
  ["opacity", "text-opacity"]
2545
- ]) if (JSON.stringify(previous?.[key]) !== JSON.stringify(next[key])) this.map.setPaintProperty(surface.layerId, property, next[key]);
2582
+ ]) if (JSON.stringify(previous?.[key]) !== JSON.stringify(next[key])) this.setStylePaint(surface.layerId, property, next[key]);
2546
2583
  surface.textSizing = next;
2547
2584
  }
2548
2585
  buildGroupLayer(id, sourceId, channel, style, textSizing, iconSizing) {
@@ -2887,7 +2924,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2887
2924
  for (const bundle of handle.groupBundles.values()) for (const surface of bundle.paintSurfaces.values()) this.applyChannelStyle(surface.layerId, surface.channel, style, resolvedStyle, surface.featureId);
2888
2925
  if (style.text && !handle.mlLayerIds.some((id) => id.endsWith("-symbol"))) {
2889
2926
  const symbolId = `${layerId}-symbol`;
2890
- this.map.addLayer(hiddenLayer(buildSymbolLayer(symbolId, handle.sourceId, resolvedStyle.text)));
2927
+ this.addStyleLayer(hiddenLayer(buildSymbolLayer(symbolId, handle.sourceId, resolvedStyle.text)));
2891
2928
  handle.mlLayerIds.push(symbolId);
2892
2929
  }
2893
2930
  }
@@ -2904,24 +2941,24 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2904
2941
  switch (channel) {
2905
2942
  case "fill":
2906
2943
  if (styleUpdate.fillColor === void 0) break;
2907
- this.map.setPaintProperty(layerId, "fill-color", buildFillPaint(resolvedStyle)["fill-color"]);
2944
+ this.setStylePaint(layerId, "fill-color", buildFillPaint(resolvedStyle)["fill-color"]);
2908
2945
  break;
2909
2946
  case "fill-pattern": break;
2910
2947
  case "line": {
2911
2948
  const paint = buildLinePaint(resolvedStyle);
2912
- if (styleUpdate.strokeColor !== void 0) this.map.setPaintProperty(layerId, "line-color", paint["line-color"]);
2913
- if (styleUpdate.strokeWidth !== void 0) this.map.setPaintProperty(layerId, "line-width", paint["line-width"]);
2914
- if (styleUpdate.strokeDash !== void 0) this.map.setPaintProperty(layerId, "line-dasharray", paint["line-dasharray"]);
2949
+ if (styleUpdate.strokeColor !== void 0) this.setStylePaint(layerId, "line-color", paint["line-color"]);
2950
+ if (styleUpdate.strokeWidth !== void 0) this.setStylePaint(layerId, "line-width", paint["line-width"]);
2951
+ if (styleUpdate.strokeDash !== void 0) this.setStylePaint(layerId, "line-dasharray", paint["line-dasharray"]);
2915
2952
  break;
2916
2953
  }
2917
2954
  case "circle": {
2918
2955
  if (!(styleUpdate.pointRadius !== void 0 || styleUpdate.pointFillColor !== void 0 || styleUpdate.fillColor !== void 0 || styleUpdate.strokeColor !== void 0 || styleUpdate.strokeWidth !== void 0 || styleUpdate.text !== void 0)) break;
2919
2956
  const paint = buildCirclePaint(resolvedStyle);
2920
- this.map.setPaintProperty(layerId, "circle-radius", paint["circle-radius"]);
2921
- this.map.setPaintProperty(layerId, "circle-color", paint["circle-color"]);
2922
- this.map.setPaintProperty(layerId, "circle-stroke-color", paint["circle-stroke-color"]);
2923
- this.map.setPaintProperty(layerId, "circle-stroke-width", paint["circle-stroke-width"]);
2924
- this.map.setFilter(layerId, featureId === void 0 ? buildCirclePointFilter(resolvedStyle) : [
2957
+ this.setStylePaint(layerId, "circle-radius", paint["circle-radius"]);
2958
+ this.setStylePaint(layerId, "circle-color", paint["circle-color"]);
2959
+ this.setStylePaint(layerId, "circle-stroke-color", paint["circle-stroke-color"]);
2960
+ this.setStylePaint(layerId, "circle-stroke-width", paint["circle-stroke-width"]);
2961
+ this.setStyleFilter(layerId, featureId === void 0 ? buildCirclePointFilter(resolvedStyle) : [
2925
2962
  "==",
2926
2963
  "_fid",
2927
2964
  featureId
@@ -2931,13 +2968,13 @@ var MapLibreAdapter = class extends BaseMapAdapter {
2931
2968
  case "icon": break;
2932
2969
  case "text":
2933
2970
  if (!styleUpdate.text || !resolvedStyle.text) break;
2934
- if (styleUpdate.text.text || styleUpdate.text.labelProperty) this.map.setLayoutProperty(layerId, "text-field", buildTextFieldExpression(resolvedStyle.text));
2935
- if (styleUpdate.text.fillColor) this.map.setPaintProperty(layerId, "text-color", resolvedStyle.text.fillColor);
2936
- if (styleUpdate.text.strokeColor) this.map.setPaintProperty(layerId, "text-halo-color", buildTextHaloColorExpression(resolvedStyle.text));
2971
+ if (styleUpdate.text.text || styleUpdate.text.labelProperty) this.setStyleLayout(layerId, "text-field", buildTextFieldExpression(resolvedStyle.text));
2972
+ if (styleUpdate.text.fillColor) this.setStylePaint(layerId, "text-color", resolvedStyle.text.fillColor);
2973
+ if (styleUpdate.text.strokeColor) this.setStylePaint(layerId, "text-halo-color", buildTextHaloColorExpression(resolvedStyle.text));
2937
2974
  if (featureId === void 0) {
2938
2975
  const sizing = buildTextSizing(resolvedStyle.text, null);
2939
- this.map.setPaintProperty(layerId, "text-halo-width", sizing.haloWidth);
2940
- this.map.setPaintProperty(layerId, "text-halo-blur", sizing.haloBlur);
2976
+ this.setStylePaint(layerId, "text-halo-width", sizing.haloWidth);
2977
+ this.setStylePaint(layerId, "text-halo-blur", sizing.haloBlur);
2941
2978
  }
2942
2979
  break;
2943
2980
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbat-mapper/tactical-draw-adapter-maplibre",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
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.31.0",
41
- "@orbat-mapper/tactical-draw": "^0.13.0"
40
+ "@orbat-mapper/control-measures": "^0.32.0",
41
+ "@orbat-mapper/tactical-draw": "^0.13.2"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "maplibre-gl": ">=5.8.0 <7.0.0"