@orbat-mapper/tactical-draw-adapter-maplibre 0.2.0-alpha.16 → 0.2.0-alpha.18

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 (2) hide show
  1. package/dist/index.mjs +128 -39
  2. package/package.json +5 -5
package/dist/index.mjs CHANGED
@@ -1,8 +1,10 @@
1
1
  import { project, unproject } from "@orbat-mapper/control-measures";
2
- import { BaseMapAdapter, DEFAULT_TEXT_SIZE_MAX_PIXELS, DEFAULT_TEXT_SIZE_MIN_PIXELS, HATCH_PATTERNS, PointerCallbackHub, TOUCH_HIT_TOLERANCE_PX, bindAbortable, fillPatternImageId, makeFillPatternImage, nearestWorldCopyLng, pickHitTolerance, tryControlMeasureIdFromFeature } from "@orbat-mapper/tactical-draw";
2
+ import { BaseMapAdapter, DEFAULT_TEXT_SIZE_MAX_PIXELS, DEFAULT_TEXT_SIZE_MIN_PIXELS, HATCH_PATTERNS, PointerCallbackHub, TEXT_HALO_COLOR, TOUCH_HIT_TOLERANCE_PX, bindAbortable, evaluatePickCandidates, fillPatternImageId, makeFillPatternImage, mergeNativeAndCachedPickFeatures, nearestWorldCopyLng, pickHitTolerance, pickPointerType } from "@orbat-mapper/tactical-draw";
3
3
  //#region src/ml-adapter.ts
4
4
  /** Feature property key for the resolved hatch-pattern image id (see `promoteFeature`). */
5
5
  const FILL_PATTERN_IMAGE_ID_PROPERTY = "_fillPatternImageId";
6
+ const DEFAULT_LINE_COLOR = "#000";
7
+ const DEFAULT_LINE_WIDTH = 2;
6
8
  function isHatchPattern(value) {
7
9
  return HATCH_PATTERNS.includes(value);
8
10
  }
@@ -297,6 +299,74 @@ function buildTextFieldExpression(text) {
297
299
  rawTextField
298
300
  ] : rawTextField;
299
301
  }
302
+ const HAS_TEXT_HALO = [
303
+ "boolean",
304
+ ["get", "textHalo"],
305
+ false
306
+ ];
307
+ const HAS_TEXT_BACKGROUND = [
308
+ "boolean",
309
+ ["get", "textBackground"],
310
+ false
311
+ ];
312
+ /**
313
+ * Per-feature `text-halo-color` value: `textHalo` wins over both the layer's
314
+ * `text.strokeColor` and the `textBackground` halo, matching the OL/Leaflet
315
+ * adapters' precedence. Factored out so `setLayerStyle`'s incremental
316
+ * `text-halo-color` update (below) can reuse it instead of dropping the
317
+ * `textHalo` case.
318
+ */
319
+ function buildTextHaloColorExpression(text) {
320
+ const fallback = text.strokeColor ? text.strokeColor : [
321
+ "case",
322
+ HAS_TEXT_BACKGROUND,
323
+ "#ffffff",
324
+ "rgba(255,255,255,0)"
325
+ ];
326
+ return [
327
+ "case",
328
+ HAS_TEXT_HALO,
329
+ [
330
+ "coalesce",
331
+ ["get", "textHaloColor"],
332
+ TEXT_HALO_COLOR
333
+ ],
334
+ fallback
335
+ ];
336
+ }
337
+ /**
338
+ * Per-feature `text-halo-width` value, mirroring
339
+ * `buildTextHaloColorExpression`'s precedence. MapLibre's `text-halo-width`
340
+ * already extends outward from the glyph edge, matching
341
+ * `resolveTextHaloWidth`'s own outward-depth semantics, so the `textHalo`
342
+ * case uses its formula directly (unlike OpenLayers's straddling canvas
343
+ * stroke, which must double it).
344
+ */
345
+ function buildTextHaloWidthExpression(text) {
346
+ return [
347
+ "case",
348
+ HAS_TEXT_HALO,
349
+ [
350
+ "max",
351
+ 1.5,
352
+ [
353
+ "/",
354
+ [
355
+ "coalesce",
356
+ ["get", "textSizePixels"],
357
+ 24
358
+ ],
359
+ 8
360
+ ]
361
+ ],
362
+ text.strokeColor ? text.strokeWidth ?? 1 : [
363
+ "case",
364
+ HAS_TEXT_BACKGROUND,
365
+ 3,
366
+ 0
367
+ ]
368
+ ];
369
+ }
300
370
  /**
301
371
  * Build the layout and paint objects for a symbol (text) layer.
302
372
  * Centralises the rotation convention so `createLayer` and `setLayerStyle`
@@ -330,11 +400,6 @@ function buildSymbolLayer(symbolId, sourceId, text) {
330
400
  ],
331
401
  "#000"
332
402
  ];
333
- const hasTextBackground = [
334
- "boolean",
335
- ["get", "textBackground"],
336
- false
337
- ];
338
403
  const textAnchor = text.labelProperty ? [
339
404
  "case",
340
405
  [
@@ -400,18 +465,8 @@ function buildSymbolLayer(symbolId, sourceId, text) {
400
465
  },
401
466
  paint: {
402
467
  "text-color": textColor,
403
- "text-halo-color": text.strokeColor ? text.strokeColor : [
404
- "case",
405
- hasTextBackground,
406
- "#ffffff",
407
- "rgba(255,255,255,0)"
408
- ],
409
- "text-halo-width": text.strokeColor ? text.strokeWidth ?? 1 : [
410
- "case",
411
- hasTextBackground,
412
- 3,
413
- 0
414
- ],
468
+ "text-halo-color": buildTextHaloColorExpression(text),
469
+ "text-halo-width": buildTextHaloWidthExpression(text),
415
470
  "text-opacity": TEXT_OPACITY_EXPRESSION
416
471
  }
417
472
  };
@@ -488,9 +543,11 @@ const FILL_PATTERN_PAINT = {
488
543
  "fill-pattern": ["get", FILL_PATTERN_IMAGE_ID_PROPERTY],
489
544
  "fill-opacity": 1
490
545
  };
546
+ const SOLID_LINE_DASH = [1, 0];
491
547
  function buildLinePaint(style) {
492
- const colorDefault = style?.strokeColor ?? "#000";
493
- const widthDefault = style?.strokeWidth ?? 2;
548
+ const colorDefault = style?.strokeColor ?? DEFAULT_LINE_COLOR;
549
+ const widthDefault = style?.strokeWidth ?? DEFAULT_LINE_WIDTH;
550
+ const dashDefault = style?.strokeDash?.length ? [...style.strokeDash] : SOLID_LINE_DASH;
494
551
  const styleObj = [
495
552
  "coalesce",
496
553
  ["get", "style"],
@@ -515,7 +572,15 @@ function buildLinePaint(style) {
515
572
  ],
516
573
  widthDefault
517
574
  ],
518
- ...style?.strokeDash ? { "line-dasharray": style.strokeDash } : {}
575
+ "line-dasharray": [
576
+ "coalesce",
577
+ [
578
+ "get",
579
+ "strokeDash",
580
+ styleObj
581
+ ],
582
+ ["literal", dashDefault]
583
+ ]
519
584
  };
520
585
  }
521
586
  var MapLibreAdapter = class extends BaseMapAdapter {
@@ -888,12 +953,10 @@ var MapLibreAdapter = class extends BaseMapAdapter {
888
953
  if (mlId.endsWith("-fill-pattern")) continue;
889
954
  this.map.setPaintProperty(mlId, "fill-color", buildFillPaint(style)["fill-color"]);
890
955
  } else if (type === "line") {
891
- if (style.strokeColor !== void 0 || style.strokeWidth !== void 0) {
892
- const paint = buildLinePaint(style);
893
- if (style.strokeColor !== void 0) this.map.setPaintProperty(mlId, "line-color", paint["line-color"]);
894
- if (style.strokeWidth !== void 0) this.map.setPaintProperty(mlId, "line-width", paint["line-width"]);
895
- }
896
- if (style.strokeDash) this.map.setPaintProperty(mlId, "line-dasharray", style.strokeDash);
956
+ const paint = buildLinePaint(style);
957
+ if (style.strokeColor !== void 0) this.map.setPaintProperty(mlId, "line-color", paint["line-color"]);
958
+ if (style.strokeWidth !== void 0) this.map.setPaintProperty(mlId, "line-width", paint["line-width"]);
959
+ if (style.strokeDash !== void 0) this.map.setPaintProperty(mlId, "line-dasharray", paint["line-dasharray"]);
897
960
  } else if (type === "circle") {
898
961
  const paint = buildCirclePaint(style);
899
962
  this.map.setPaintProperty(mlId, "circle-radius", paint["circle-radius"]);
@@ -904,8 +967,8 @@ var MapLibreAdapter = class extends BaseMapAdapter {
904
967
  } else if (type === "symbol" && style.text) {
905
968
  if (style.text.text || style.text.labelProperty) this.map.setLayoutProperty(mlId, "text-field", buildTextFieldExpression(style.text));
906
969
  if (style.text.fillColor) this.map.setPaintProperty(mlId, "text-color", style.text.fillColor);
907
- if (style.text.strokeColor) this.map.setPaintProperty(mlId, "text-halo-color", style.text.strokeColor);
908
- if (style.text.strokeWidth !== void 0) this.map.setPaintProperty(mlId, "text-halo-width", style.text.strokeWidth);
970
+ if (style.text.strokeColor) this.map.setPaintProperty(mlId, "text-halo-color", buildTextHaloColorExpression(style.text));
971
+ if (style.text.strokeWidth !== void 0) this.map.setPaintProperty(mlId, "text-halo-width", buildTextHaloWidthExpression(style.text));
909
972
  }
910
973
  }
911
974
  if (style.text && !handle.mlLayerIds.some((id) => id.endsWith("-symbol"))) {
@@ -973,20 +1036,46 @@ var MapLibreAdapter = class extends BaseMapAdapter {
973
1036
  const tol = pickHitTolerance(originalEvent);
974
1037
  const { x, y } = e.point;
975
1038
  const bbox = [[x - tol, y - tol], [x + tol, y + tol]];
976
- const features = this.map.queryRenderedFeatures(bbox, { layers: handle.mlLayerIds });
977
- if (features.length === 0) return;
978
- const f = features[0];
979
- const geojson = {
1039
+ const relevantFeatures = mergeNativeAndCachedPickFeatures(this.map.queryRenderedFeatures(bbox, { layers: handle.mlLayerIds }).map((feature) => ({
980
1040
  type: "Feature",
981
- id: f.id,
982
- geometry: f.geometry,
983
- properties: f.properties
984
- };
985
- const cmId = tryControlMeasureIdFromFeature(geojson);
1041
+ id: feature.id,
1042
+ geometry: feature.geometry,
1043
+ properties: feature.properties
1044
+ })), this.featureCache.get(layerId) ?? []);
1045
+ const size = this.getViewportSize();
1046
+ const candidate = evaluatePickCandidates(relevantFeatures.map(({ feature, nativeHit, visualOrder }) => {
1047
+ const iconId = feature.properties?.iconId;
1048
+ const icon = typeof iconId === "string" ? this.getIconResource(iconId) : void 0;
1049
+ return {
1050
+ feature,
1051
+ visualOrder,
1052
+ nativeHit,
1053
+ ...icon ? { icon } : {},
1054
+ layerStyle: {
1055
+ ...handle.style,
1056
+ strokeColor: handle.style?.strokeColor ?? DEFAULT_LINE_COLOR,
1057
+ strokeWidth: handle.style?.strokeWidth ?? DEFAULT_LINE_WIDTH
1058
+ }
1059
+ };
1060
+ }), {
1061
+ pixel: [x, y],
1062
+ pointerType: pickPointerType(originalEvent),
1063
+ tolerance: tol,
1064
+ toPixel: (position) => this.getPixelFromCoordinate(position),
1065
+ viewport: {
1066
+ minX: 0,
1067
+ minY: 0,
1068
+ maxX: size.width,
1069
+ maxY: size.height
1070
+ },
1071
+ bearing: this.getBearing()
1072
+ });
1073
+ if (!candidate) return;
1074
+ const cmId = candidate.graphicId;
986
1075
  if (cmId == null) return;
987
1076
  handler({
988
1077
  id: cmId,
989
- feature: geojson,
1078
+ feature: candidate.feature,
990
1079
  pixel: [x, y],
991
1080
  coordinate: [this.toContinuousLng(e.lngLat.lng), e.lngLat.lat],
992
1081
  originalEvent
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.16",
3
+ "version": "0.2.0-alpha.18",
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,17 +37,17 @@
37
37
  "access": "public"
38
38
  },
39
39
  "dependencies": {
40
- "@orbat-mapper/control-measures": "^0.2.0-alpha.24",
41
- "@orbat-mapper/tactical-draw": "^0.2.0-alpha.31"
40
+ "@orbat-mapper/control-measures": "^0.2.0-alpha.25",
41
+ "@orbat-mapper/tactical-draw": "^0.2.0-alpha.34"
42
42
  },
43
43
  "peerDependencies": {
44
- "maplibre-gl": ">=4.0.0 <6.0.0"
44
+ "maplibre-gl": ">=5.8.0 <6.0.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@arethetypeswrong/core": "^0.18.3",
48
48
  "@types/geojson": "^7946.0.16",
49
49
  "jsdom": "^27.4.0",
50
- "maplibre-gl": "^5.18.0",
50
+ "maplibre-gl": "^5.24.0",
51
51
  "publint": "^0.3.21",
52
52
  "tsdown": "^0.22.2",
53
53
  "typescript": "~5.9.3",