@orbat-mapper/tactical-draw-adapter-maplibre 0.2.0-alpha.12 → 0.2.0-alpha.13
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 +17 -0
- package/dist/index.mjs +117 -15
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -36,6 +36,23 @@ declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLib
|
|
|
36
36
|
private updateSourceData;
|
|
37
37
|
private flushDeferredLayerRemoval;
|
|
38
38
|
deferLayerRemovalUntilLayerUpdate(layerId: LayerId, updatedLayerId: LayerId): void;
|
|
39
|
+
/**
|
|
40
|
+
* Promote a source feature for MapLibre: stamp `_fid` (so data-driven paint
|
|
41
|
+
* and picks can key on the caller's feature id) and, for a hatch-patterned
|
|
42
|
+
* Polygon, `_fillPatternImageId` (registering the tile image on demand). At
|
|
43
|
+
* most one object spread — a feature needing neither passes through
|
|
44
|
+
* untouched.
|
|
45
|
+
*/
|
|
46
|
+
private promoteFeature;
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a hatch-patterned Polygon's `style.fillPattern`/`fillColor` to its
|
|
49
|
+
* hatch-tile image id, registering the image on demand. Returns `undefined`
|
|
50
|
+
* for features without a hatch pattern, so `fillPatternImageId` stays the
|
|
51
|
+
* single source of truth for the id baked into `_fillPatternImageId`.
|
|
52
|
+
*/
|
|
53
|
+
private resolveFillPatternImage;
|
|
54
|
+
/** Register a hatch-tile image once per (color, pattern) pair, keyed by `fillPatternImageId`. */
|
|
55
|
+
private ensureFillPatternImage;
|
|
39
56
|
protected applyIconResources(resources: readonly IconResource[]): void;
|
|
40
57
|
protected removeIconResource(id: string): void;
|
|
41
58
|
protected attachNativeEvent(type: "click" | "pointermove" | "dblclick", onEvent: (lonLat: Position, pixel: PixelCoordinate, originalEvent: unknown) => void): NativeEventEntry<MapLibreEventToken>;
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { project, unproject } from "@orbat-mapper/control-measures";
|
|
2
|
-
import { BaseMapAdapter, DEFAULT_TEXT_SIZE_MAX_PIXELS, DEFAULT_TEXT_SIZE_MIN_PIXELS, PointerCallbackHub, TOUCH_HIT_TOLERANCE_PX, bindAbortable, nearestWorldCopyLng, pickHitTolerance, tryControlMeasureIdFromFeature } from "@orbat-mapper/tactical-draw";
|
|
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";
|
|
3
3
|
//#region src/ml-adapter.ts
|
|
4
|
+
/** Feature property key for the resolved hatch-pattern image id (see `promoteFeature`). */
|
|
5
|
+
const FILL_PATTERN_IMAGE_ID_PROPERTY = "_fillPatternImageId";
|
|
6
|
+
function isHatchPattern(value) {
|
|
7
|
+
return HATCH_PATTERNS.includes(value);
|
|
8
|
+
}
|
|
4
9
|
function isPromiseLike(value) {
|
|
5
10
|
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
6
11
|
}
|
|
@@ -309,6 +314,60 @@ function buildFillPaint(style) {
|
|
|
309
314
|
"fill-opacity": 1
|
|
310
315
|
};
|
|
311
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* `properties.style.fillPattern` read as a GL expression, defaulting to
|
|
319
|
+
* `"solid"`. `fillPattern` is a per-feature-only field (ADR-0007) — unlike
|
|
320
|
+
* `fillColor`/`strokeColor` it has no layer-level `StyleOptions` default, so
|
|
321
|
+
* this expression takes no `style` argument.
|
|
322
|
+
*/
|
|
323
|
+
function fillPatternField() {
|
|
324
|
+
return [
|
|
325
|
+
"coalesce",
|
|
326
|
+
[
|
|
327
|
+
"get",
|
|
328
|
+
"fillPattern",
|
|
329
|
+
[
|
|
330
|
+
"coalesce",
|
|
331
|
+
["get", "style"],
|
|
332
|
+
["literal", {}]
|
|
333
|
+
]
|
|
334
|
+
],
|
|
335
|
+
"solid"
|
|
336
|
+
];
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Filter for one of the two mutually-exclusive Polygon fill layers, keyed on
|
|
340
|
+
* whether the feature's resolved `fillPattern` is a hatch pattern. The solid
|
|
341
|
+
* `-fill` layer takes `hatched: false` (every Polygon except hatched ones);
|
|
342
|
+
* the `-fill-pattern` layer takes `hatched: true`. Split because MapLibre's
|
|
343
|
+
* `fill-color` and `fill-pattern` paints cannot coexist on one fill layer.
|
|
344
|
+
*/
|
|
345
|
+
function buildFillFilter(hatched) {
|
|
346
|
+
const isHatched = [
|
|
347
|
+
"in",
|
|
348
|
+
fillPatternField(),
|
|
349
|
+
["literal", [...HATCH_PATTERNS]]
|
|
350
|
+
];
|
|
351
|
+
return [
|
|
352
|
+
"all",
|
|
353
|
+
[
|
|
354
|
+
"==",
|
|
355
|
+
["geometry-type"],
|
|
356
|
+
"Polygon"
|
|
357
|
+
],
|
|
358
|
+
hatched ? isHatched : ["!", isHatched]
|
|
359
|
+
];
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Paint for the hatch-pattern fill layer. The image id is baked per-feature
|
|
363
|
+
* into `properties._fillPatternImageId` (see `promoteFeature`), so the paint
|
|
364
|
+
* is a plain `["get", ...]` rather than re-deriving the id as a GL expression,
|
|
365
|
+
* keeping `fillPatternImageId` the single source of truth for the id string.
|
|
366
|
+
*/
|
|
367
|
+
const FILL_PATTERN_PAINT = {
|
|
368
|
+
"fill-pattern": ["get", FILL_PATTERN_IMAGE_ID_PROPERTY],
|
|
369
|
+
"fill-opacity": 1
|
|
370
|
+
};
|
|
312
371
|
function buildLinePaint(style) {
|
|
313
372
|
const colorDefault = style?.strokeColor ?? "#000";
|
|
314
373
|
const widthDefault = style?.strokeWidth ?? 2;
|
|
@@ -367,14 +426,19 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
367
426
|
id: fillId,
|
|
368
427
|
type: "fill",
|
|
369
428
|
source: sourceId,
|
|
370
|
-
filter:
|
|
371
|
-
"==",
|
|
372
|
-
"$type",
|
|
373
|
-
"Polygon"
|
|
374
|
-
],
|
|
429
|
+
filter: buildFillFilter(false),
|
|
375
430
|
paint: buildFillPaint(style)
|
|
376
431
|
});
|
|
377
432
|
mlLayerIds.push(fillId);
|
|
433
|
+
const fillPatternId = `${layerId}-fill-pattern`;
|
|
434
|
+
this.map.addLayer({
|
|
435
|
+
id: fillPatternId,
|
|
436
|
+
type: "fill",
|
|
437
|
+
source: sourceId,
|
|
438
|
+
filter: buildFillFilter(true),
|
|
439
|
+
paint: FILL_PATTERN_PAINT
|
|
440
|
+
});
|
|
441
|
+
mlLayerIds.push(fillPatternId);
|
|
378
442
|
const lineId = `${layerId}-line`;
|
|
379
443
|
this.map.addLayer({
|
|
380
444
|
id: lineId,
|
|
@@ -449,13 +513,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
449
513
|
if (!handle) return;
|
|
450
514
|
const source = this.map.getSource(handle.sourceId);
|
|
451
515
|
if (!source) return;
|
|
452
|
-
const promotedFeatures = features.map((f) => f
|
|
453
|
-
...f,
|
|
454
|
-
properties: {
|
|
455
|
-
...f.properties ?? {},
|
|
456
|
-
_fid: f.id
|
|
457
|
-
}
|
|
458
|
-
} : f);
|
|
516
|
+
const promotedFeatures = features.map((f) => this.promoteFeature(f));
|
|
459
517
|
let resolveSourceData;
|
|
460
518
|
const sourceDataCompletion = new Promise((resolve) => {
|
|
461
519
|
resolveSourceData = resolve;
|
|
@@ -492,6 +550,48 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
492
550
|
if (!pending || !this.layers.has(layerId)) return;
|
|
493
551
|
this.deferredLayerRemovals.set(layerId, { after: pending.completion });
|
|
494
552
|
}
|
|
553
|
+
/**
|
|
554
|
+
* Promote a source feature for MapLibre: stamp `_fid` (so data-driven paint
|
|
555
|
+
* and picks can key on the caller's feature id) and, for a hatch-patterned
|
|
556
|
+
* Polygon, `_fillPatternImageId` (registering the tile image on demand). At
|
|
557
|
+
* most one object spread — a feature needing neither passes through
|
|
558
|
+
* untouched.
|
|
559
|
+
*/
|
|
560
|
+
promoteFeature(feature) {
|
|
561
|
+
const extra = {};
|
|
562
|
+
if (feature.id !== void 0) extra._fid = feature.id;
|
|
563
|
+
const imageId = this.resolveFillPatternImage(feature);
|
|
564
|
+
if (imageId !== void 0) extra[FILL_PATTERN_IMAGE_ID_PROPERTY] = imageId;
|
|
565
|
+
if (Object.keys(extra).length === 0) return feature;
|
|
566
|
+
return {
|
|
567
|
+
...feature,
|
|
568
|
+
properties: {
|
|
569
|
+
...feature.properties ?? {},
|
|
570
|
+
...extra
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Resolve a hatch-patterned Polygon's `style.fillPattern`/`fillColor` to its
|
|
576
|
+
* hatch-tile image id, registering the image on demand. Returns `undefined`
|
|
577
|
+
* for features without a hatch pattern, so `fillPatternImageId` stays the
|
|
578
|
+
* single source of truth for the id baked into `_fillPatternImageId`.
|
|
579
|
+
*/
|
|
580
|
+
resolveFillPatternImage(feature) {
|
|
581
|
+
const props = feature.properties;
|
|
582
|
+
const pattern = props?.style?.fillPattern;
|
|
583
|
+
if (!isHatchPattern(pattern)) return void 0;
|
|
584
|
+
const color = props?.fillColor ?? props?.style?.fillColor ?? "rgba(0,0,0,0)";
|
|
585
|
+
const imageId = fillPatternImageId(color, pattern);
|
|
586
|
+
this.ensureFillPatternImage(imageId, color, pattern);
|
|
587
|
+
return imageId;
|
|
588
|
+
}
|
|
589
|
+
/** Register a hatch-tile image once per (color, pattern) pair, keyed by `fillPatternImageId`. */
|
|
590
|
+
ensureFillPatternImage(imageId, color, pattern) {
|
|
591
|
+
if (this.map.hasImage(imageId)) return;
|
|
592
|
+
const image = makeFillPatternImage(color, pattern, 2);
|
|
593
|
+
this.map.addImage(imageId, image, { pixelRatio: 2 });
|
|
594
|
+
}
|
|
495
595
|
applyIconResources(resources) {
|
|
496
596
|
if (typeof Image === "undefined") return;
|
|
497
597
|
for (const resource of resources) {
|
|
@@ -664,8 +764,10 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
664
764
|
const layer = this.map.getLayer(mlId);
|
|
665
765
|
if (!layer) continue;
|
|
666
766
|
const type = layer.type;
|
|
667
|
-
if (type === "fill")
|
|
668
|
-
|
|
767
|
+
if (type === "fill") {
|
|
768
|
+
if (mlId.endsWith("-fill-pattern")) continue;
|
|
769
|
+
this.map.setPaintProperty(mlId, "fill-color", buildFillPaint(style)["fill-color"]);
|
|
770
|
+
} else if (type === "line") {
|
|
669
771
|
if (style.strokeColor !== void 0 || style.strokeWidth !== void 0) {
|
|
670
772
|
const paint = buildLinePaint(style);
|
|
671
773
|
if (style.strokeColor !== void 0) this.map.setPaintProperty(mlId, "line-color", paint["line-color"]);
|
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.13",
|
|
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/
|
|
41
|
-
"@orbat-mapper/
|
|
40
|
+
"@orbat-mapper/control-measures": "^0.2.0-alpha.20",
|
|
41
|
+
"@orbat-mapper/tactical-draw": "^0.2.0-alpha.28"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"maplibre-gl": ">=4.0.0 <6.0.0"
|