@orbat-mapper/tactical-draw-adapter-maplibre 0.3.1 → 0.3.3
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/README.md +7 -0
- package/dist/index.d.mts +9 -2
- package/dist/index.mjs +69 -52
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
contract, so the draw/edit sessions, draw rules, gestures, and handle
|
|
6
6
|
rendering from `tactical-draw` run unchanged on a MapLibre map.
|
|
7
7
|
|
|
8
|
+
Each Graphic keeps one stable GeoJSON source and an ordered bundle of native
|
|
9
|
+
paint surfaces. A surface represents one feature's fill, outline, point, icon,
|
|
10
|
+
or text operation, which lets feature-array order remain authoritative across
|
|
11
|
+
MapLibre's separate layer types. This costs up to one native layer per paint
|
|
12
|
+
operation, while content updates reuse the source and surfaces and pure
|
|
13
|
+
reorders move those layers without recreating unrelated Graphic bundles.
|
|
14
|
+
|
|
8
15
|
`maplibre-gl` 5.8 or 6.x is a peer dependency — your app supplies the version. MapLibre 6
|
|
9
16
|
applications must also follow MapLibre's
|
|
10
17
|
[v5-to-v6 setup guidance](https://maplibre.org/maplibre-gl-js/docs/guides/v5-to-v6-migration-guide/),
|
package/dist/index.d.mts
CHANGED
|
@@ -15,7 +15,9 @@ interface MapLibreLayerHandle {
|
|
|
15
15
|
type PaintChannel = "fill" | "fill-pattern" | "line" | "circle" | "icon" | "text";
|
|
16
16
|
interface MapLibreGroupBundle {
|
|
17
17
|
readonly sourceId: string;
|
|
18
|
-
readonly
|
|
18
|
+
readonly paintSurfaces: Map<string, MapLibrePaintSurface>;
|
|
19
|
+
paintOrder: string[];
|
|
20
|
+
nextPaintSurfaceId: number;
|
|
19
21
|
features: readonly Feature[];
|
|
20
22
|
/** Features most recently submitted to the native source, after MapLibre-specific promotion. */
|
|
21
23
|
sourceFeatures: readonly Feature[];
|
|
@@ -34,6 +36,11 @@ interface MapLibreGroupBundle {
|
|
|
34
36
|
requiredIconIds: ReadonlySet<string>;
|
|
35
37
|
overlays: Map<string, MapLibreImageOverlayHandle>;
|
|
36
38
|
}
|
|
39
|
+
interface MapLibrePaintSurface {
|
|
40
|
+
readonly layerId: string;
|
|
41
|
+
readonly channel: PaintChannel;
|
|
42
|
+
readonly featureId: string | number;
|
|
43
|
+
}
|
|
37
44
|
/**
|
|
38
45
|
* Last-applied scalar state for a rendered image overlay, used to skip
|
|
39
46
|
* MapLibre's `ImageSource.updateImage` (which re-fetches and re-decodes the
|
|
@@ -73,7 +80,7 @@ declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLib
|
|
|
73
80
|
private reconcileGroupImageOverlays;
|
|
74
81
|
private clearGroupBundles;
|
|
75
82
|
private requiredIconIds;
|
|
76
|
-
private
|
|
83
|
+
private reconcileGroupPaintSurfaces;
|
|
77
84
|
private buildGroupLayer;
|
|
78
85
|
private updateGroupBundleSource;
|
|
79
86
|
private orderGroupBundles;
|
package/dist/index.mjs
CHANGED
|
@@ -16,19 +16,11 @@ function coordinatesEqual(a, b) {
|
|
|
16
16
|
}
|
|
17
17
|
return true;
|
|
18
18
|
}
|
|
19
|
-
const PAINT_CHANNELS = [
|
|
20
|
-
"fill",
|
|
21
|
-
"fill-pattern",
|
|
22
|
-
"line",
|
|
23
|
-
"circle",
|
|
24
|
-
"icon",
|
|
25
|
-
"text"
|
|
26
|
-
];
|
|
27
19
|
function firstBundleLayerId(bundle) {
|
|
28
20
|
for (const overlay of bundle.overlays.values()) return overlay.layerId;
|
|
29
|
-
for (const
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
21
|
+
for (const key of bundle.paintOrder) {
|
|
22
|
+
const surface = bundle.paintSurfaces.get(key);
|
|
23
|
+
if (surface) return surface.layerId;
|
|
32
24
|
}
|
|
33
25
|
}
|
|
34
26
|
function sameOrder(a, b) {
|
|
@@ -771,22 +763,20 @@ function featureStyle(feature) {
|
|
|
771
763
|
const style = feature.properties?.style;
|
|
772
764
|
return style && typeof style === "object" ? style : {};
|
|
773
765
|
}
|
|
774
|
-
function
|
|
775
|
-
const channels =
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
if (featureStyle(feature).fillColor !== void 0 || style?.fillColor !== void 0 || isHatchPattern(fillPattern)) channels.add(isHatchPattern(fillPattern) ? "fill-pattern" : "fill");
|
|
780
|
-
}
|
|
781
|
-
if (geometryIncludes(feature, LINE_GEOMETRIES)) channels.add("line");
|
|
782
|
-
if (!geometryIncludes(feature, POINT_GEOMETRIES)) continue;
|
|
783
|
-
const properties = feature.properties ?? {};
|
|
784
|
-
const hasIcon = properties.iconId != null;
|
|
785
|
-
const hasText = style?.text !== void 0 && (style.text.labelProperty ? properties[style.text.labelProperty] != null : style.text.text !== void 0);
|
|
786
|
-
if (hasIcon) channels.add("icon");
|
|
787
|
-
if (hasText) channels.add("text");
|
|
788
|
-
if (!hasIcon && !hasText) channels.add("circle");
|
|
766
|
+
function featurePaintChannels(feature, style) {
|
|
767
|
+
const channels = [];
|
|
768
|
+
if (geometryIncludes(feature, POLYGON_GEOMETRIES)) {
|
|
769
|
+
const fillPattern = featureStyle(feature).fillPattern;
|
|
770
|
+
if (featureStyle(feature).fillColor !== void 0 || style?.fillColor !== void 0 || isHatchPattern(fillPattern)) channels.push(isHatchPattern(fillPattern) ? "fill-pattern" : "fill");
|
|
789
771
|
}
|
|
772
|
+
if (geometryIncludes(feature, LINE_GEOMETRIES)) channels.push("line");
|
|
773
|
+
if (!geometryIncludes(feature, POINT_GEOMETRIES)) return channels;
|
|
774
|
+
const properties = feature.properties ?? {};
|
|
775
|
+
const hasIcon = properties.iconId != null;
|
|
776
|
+
const hasText = style?.text !== void 0 && (style.text.labelProperty ? properties[style.text.labelProperty] != null : style.text.text !== void 0);
|
|
777
|
+
if (hasIcon) channels.push("icon");
|
|
778
|
+
if (hasText) channels.push("text");
|
|
779
|
+
if (!hasIcon && !hasText) channels.push("circle");
|
|
790
780
|
return channels;
|
|
791
781
|
}
|
|
792
782
|
var MapLibreAdapter = class extends BaseMapAdapter {
|
|
@@ -917,7 +907,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
917
907
|
bundle.revision = group.revision;
|
|
918
908
|
bundle.requiredIconIds = requiredIconIds;
|
|
919
909
|
if (this.reconcileGroupImageOverlays(handle, bundle, group.imageOverlays ?? [])) bundlesNeedingOrder.add(group.id);
|
|
920
|
-
if (this.
|
|
910
|
+
if (this.reconcileGroupPaintSurfaces(handle, bundle, group.features)) bundlesNeedingOrder.add(group.id);
|
|
921
911
|
if (contentChanged || resourcesChanged) this.updateGroupBundleSource(bundle);
|
|
922
912
|
}
|
|
923
913
|
handle.groupOrder = groups.map((group) => group.id);
|
|
@@ -932,7 +922,9 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
932
922
|
});
|
|
933
923
|
return {
|
|
934
924
|
sourceId,
|
|
935
|
-
|
|
925
|
+
paintSurfaces: /* @__PURE__ */ new Map(),
|
|
926
|
+
paintOrder: [],
|
|
927
|
+
nextPaintSurfaceId: 0,
|
|
936
928
|
features: [],
|
|
937
929
|
sourceFeatures: [],
|
|
938
930
|
sourceInitialized: false,
|
|
@@ -946,7 +938,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
946
938
|
if (this.map.getLayer(overlay.layerId)) this.map.removeLayer(overlay.layerId);
|
|
947
939
|
if (this.map.getSource(overlay.sourceId)) this.map.removeSource(overlay.sourceId);
|
|
948
940
|
}
|
|
949
|
-
for (const
|
|
941
|
+
for (const surface of bundle.paintSurfaces.values()) if (this.map.getLayer(surface.layerId)) this.map.removeLayer(surface.layerId);
|
|
950
942
|
if (this.map.getSource(bundle.sourceId)) this.map.removeSource(bundle.sourceId);
|
|
951
943
|
}
|
|
952
944
|
reconcileGroupImageOverlays(handle, bundle, overlays) {
|
|
@@ -1016,22 +1008,43 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
1016
1008
|
}));
|
|
1017
1009
|
return new Set(group.iconResources.map((resource) => resource.id).filter((resourceId) => referenced.has(resourceId)));
|
|
1018
1010
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
}
|
|
1011
|
+
reconcileGroupPaintSurfaces(handle, bundle, features) {
|
|
1012
|
+
const desired = features.flatMap((feature) => featurePaintChannels(feature, handle.style).map((channel) => ({
|
|
1013
|
+
key: `${typeof feature.id}:${JSON.stringify(feature.id)}:${channel}`,
|
|
1014
|
+
featureId: feature.id,
|
|
1015
|
+
channel
|
|
1016
|
+
})));
|
|
1017
|
+
const desiredKeys = new Set(desired.map(({ key }) => key));
|
|
1018
|
+
let structureChanged = false;
|
|
1019
|
+
for (const [key, surface] of bundle.paintSurfaces) {
|
|
1020
|
+
if (desiredKeys.has(key)) continue;
|
|
1021
|
+
if (this.map.getLayer(surface.layerId)) this.map.removeLayer(surface.layerId);
|
|
1022
|
+
bundle.paintSurfaces.delete(key);
|
|
1023
|
+
structureChanged = true;
|
|
1033
1024
|
}
|
|
1034
|
-
|
|
1025
|
+
for (const { key, featureId, channel } of desired) {
|
|
1026
|
+
if (bundle.paintSurfaces.has(key)) continue;
|
|
1027
|
+
const layerId = `${bundle.sourceId}-surface-${++bundle.nextPaintSurfaceId}-${channel}`;
|
|
1028
|
+
const layer = {
|
|
1029
|
+
...this.buildGroupLayer(layerId, bundle.sourceId, channel, handle.style),
|
|
1030
|
+
filter: [
|
|
1031
|
+
"==",
|
|
1032
|
+
"_fid",
|
|
1033
|
+
featureId
|
|
1034
|
+
]
|
|
1035
|
+
};
|
|
1036
|
+
this.map.addLayer(layer, handle.mlLayerIds[0]);
|
|
1037
|
+
bundle.paintSurfaces.set(key, {
|
|
1038
|
+
layerId,
|
|
1039
|
+
channel,
|
|
1040
|
+
featureId
|
|
1041
|
+
});
|
|
1042
|
+
structureChanged = true;
|
|
1043
|
+
}
|
|
1044
|
+
const desiredOrder = desired.map(({ key }) => key);
|
|
1045
|
+
const orderChanged = !sameOrder(bundle.paintOrder, desiredOrder);
|
|
1046
|
+
bundle.paintOrder = desiredOrder;
|
|
1047
|
+
return structureChanged || orderChanged;
|
|
1035
1048
|
}
|
|
1036
1049
|
buildGroupLayer(id, sourceId, channel, style) {
|
|
1037
1050
|
switch (channel) {
|
|
@@ -1107,9 +1120,9 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
1107
1120
|
if (!bundle) continue;
|
|
1108
1121
|
if (forcedGroupIds.has(groupId) || stableGroupIds !== null && !stableGroupIds.has(groupId)) {
|
|
1109
1122
|
for (const overlay of bundle.overlays.values()) if (this.map.getLayer(overlay.layerId)) this.map.moveLayer(overlay.layerId, beforeId);
|
|
1110
|
-
for (const
|
|
1111
|
-
const
|
|
1112
|
-
if (
|
|
1123
|
+
for (const key of bundle.paintOrder) {
|
|
1124
|
+
const surface = bundle.paintSurfaces.get(key);
|
|
1125
|
+
if (surface && this.map.getLayer(surface.layerId)) this.map.moveLayer(surface.layerId, beforeId);
|
|
1113
1126
|
}
|
|
1114
1127
|
}
|
|
1115
1128
|
beforeId = firstBundleLayerId(bundle) ?? beforeId;
|
|
@@ -1118,7 +1131,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
1118
1131
|
nativeLayerIds(handle) {
|
|
1119
1132
|
return [
|
|
1120
1133
|
...handle.mlLayerIds,
|
|
1121
|
-
...[...handle.groupBundles.values()].flatMap((bundle) => [...bundle.
|
|
1134
|
+
...[...handle.groupBundles.values()].flatMap((bundle) => [...bundle.paintSurfaces.values()].map((surface) => surface.layerId)),
|
|
1122
1135
|
...[...handle.groupBundles.values()].flatMap((bundle) => [...bundle.overlays.values()].map((overlay) => overlay.layerId))
|
|
1123
1136
|
];
|
|
1124
1137
|
}
|
|
@@ -1346,9 +1359,9 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
1346
1359
|
if (channel) this.applyChannelStyle(mlId, channel, style, resolvedStyle);
|
|
1347
1360
|
}
|
|
1348
1361
|
const bundlesNeedingOrder = /* @__PURE__ */ new Set();
|
|
1349
|
-
for (const [groupId, bundle] of handle.groupBundles) if (this.
|
|
1362
|
+
for (const [groupId, bundle] of handle.groupBundles) if (this.reconcileGroupPaintSurfaces(handle, bundle, bundle.features)) bundlesNeedingOrder.add(groupId);
|
|
1350
1363
|
this.orderGroupBundles(handle, handle.groupOrder, handle.groupOrder, bundlesNeedingOrder);
|
|
1351
|
-
for (const bundle of handle.groupBundles.values()) for (const
|
|
1364
|
+
for (const bundle of handle.groupBundles.values()) for (const surface of bundle.paintSurfaces.values()) this.applyChannelStyle(surface.layerId, surface.channel, style, resolvedStyle, surface.featureId);
|
|
1352
1365
|
if (style.text && !handle.mlLayerIds.some((id) => id.endsWith("-symbol"))) {
|
|
1353
1366
|
const symbolId = `${layerId}-symbol`;
|
|
1354
1367
|
this.map.addLayer(buildSymbolLayer(symbolId, handle.sourceId, resolvedStyle.text));
|
|
@@ -1363,7 +1376,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
1363
1376
|
if (layerId.endsWith("-icon")) return "icon";
|
|
1364
1377
|
if (layerId.endsWith("-symbol")) return "text";
|
|
1365
1378
|
}
|
|
1366
|
-
applyChannelStyle(layerId, channel, styleUpdate, resolvedStyle) {
|
|
1379
|
+
applyChannelStyle(layerId, channel, styleUpdate, resolvedStyle, featureId) {
|
|
1367
1380
|
if (!this.map.getLayer(layerId)) return;
|
|
1368
1381
|
switch (channel) {
|
|
1369
1382
|
case "fill":
|
|
@@ -1385,7 +1398,11 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
1385
1398
|
this.map.setPaintProperty(layerId, "circle-color", paint["circle-color"]);
|
|
1386
1399
|
this.map.setPaintProperty(layerId, "circle-stroke-color", paint["circle-stroke-color"]);
|
|
1387
1400
|
this.map.setPaintProperty(layerId, "circle-stroke-width", paint["circle-stroke-width"]);
|
|
1388
|
-
this.map.setFilter(layerId, buildCirclePointFilter(resolvedStyle)
|
|
1401
|
+
this.map.setFilter(layerId, featureId === void 0 ? buildCirclePointFilter(resolvedStyle) : [
|
|
1402
|
+
"==",
|
|
1403
|
+
"_fid",
|
|
1404
|
+
featureId
|
|
1405
|
+
]);
|
|
1389
1406
|
break;
|
|
1390
1407
|
}
|
|
1391
1408
|
case "icon": break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orbat-mapper/tactical-draw-adapter-maplibre",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
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.
|
|
41
|
-
"@orbat-mapper/tactical-draw": "^0.4.
|
|
40
|
+
"@orbat-mapper/control-measures": "^0.8.0",
|
|
41
|
+
"@orbat-mapper/tactical-draw": "^0.4.2"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"maplibre-gl": ">=5.8.0 <7.0.0"
|