@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.
- package/dist/index.mjs +128 -39
- 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,
|
|
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
|
|
404
|
-
|
|
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 ??
|
|
493
|
-
const widthDefault = style?.strokeWidth ??
|
|
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
|
-
|
|
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
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
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
|
|
908
|
-
if (style.text.strokeWidth !== void 0) this.map.setPaintProperty(mlId, "text-halo-width", style.text
|
|
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
|
|
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:
|
|
982
|
-
geometry:
|
|
983
|
-
properties:
|
|
984
|
-
};
|
|
985
|
-
const
|
|
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:
|
|
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.
|
|
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.
|
|
41
|
-
"@orbat-mapper/tactical-draw": "^0.2.0-alpha.
|
|
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": ">=
|
|
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.
|
|
50
|
+
"maplibre-gl": "^5.24.0",
|
|
51
51
|
"publint": "^0.3.21",
|
|
52
52
|
"tsdown": "^0.22.2",
|
|
53
53
|
"typescript": "~5.9.3",
|