@orbat-mapper/tactical-draw-adapter-maplibre 0.2.0-alpha.23 → 0.2.0-alpha.24
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 +99 -46
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -232,7 +232,7 @@ const WEB_MERCATOR_CIRCUMFERENCE = 40075016.686;
|
|
|
232
232
|
* matching `getResolution`); the linear ramp between stops approximates the
|
|
233
233
|
* exponential exactly closely enough at 1-zoom granularity.
|
|
234
234
|
*/
|
|
235
|
-
function
|
|
235
|
+
function textSizeAtZoom(text, zoom) {
|
|
236
236
|
const minPx = text.textSizeMinPixels ?? DEFAULT_TEXT_SIZE_MIN_PIXELS;
|
|
237
237
|
const maxPx = text.textSizeMaxPixels ?? DEFAULT_TEXT_SIZE_MAX_PIXELS;
|
|
238
238
|
const screenOrDefault = [
|
|
@@ -240,43 +240,53 @@ function buildTextSizeExpression(text) {
|
|
|
240
240
|
["get", "textSizePixels"],
|
|
241
241
|
14
|
|
242
242
|
];
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
243
|
+
const groundPx = [
|
|
244
|
+
"*",
|
|
245
|
+
["get", "textSizeMeters"],
|
|
246
|
+
Math.pow(2, zoom + 9) / WEB_MERCATOR_CIRCUMFERENCE
|
|
247
|
+
];
|
|
248
|
+
return [
|
|
249
|
+
"case",
|
|
250
|
+
["has", "textSizeMeters"],
|
|
251
|
+
[
|
|
250
252
|
"case",
|
|
251
|
-
["has", "
|
|
253
|
+
["has", "textMaxSizePixels"],
|
|
252
254
|
[
|
|
253
|
-
"
|
|
254
|
-
["
|
|
255
|
+
"min",
|
|
256
|
+
["get", "textMaxSizePixels"],
|
|
257
|
+
groundPx
|
|
258
|
+
],
|
|
259
|
+
[
|
|
260
|
+
"min",
|
|
261
|
+
maxPx,
|
|
255
262
|
[
|
|
256
|
-
"
|
|
257
|
-
|
|
263
|
+
"max",
|
|
264
|
+
minPx,
|
|
258
265
|
groundPx
|
|
259
|
-
],
|
|
260
|
-
[
|
|
261
|
-
"min",
|
|
262
|
-
maxPx,
|
|
263
|
-
[
|
|
264
|
-
"max",
|
|
265
|
-
minPx,
|
|
266
|
-
groundPx
|
|
267
|
-
]
|
|
268
266
|
]
|
|
269
|
-
]
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
267
|
+
]
|
|
268
|
+
],
|
|
269
|
+
screenOrDefault
|
|
270
|
+
];
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Wrap a per-zoom expression builder in the top-level zoom `interpolate` every
|
|
274
|
+
* zoom-driven property here needs. MapLibre only accepts `["zoom"]` as the
|
|
275
|
+
* input of a top-level `step`/`interpolate`, never nested inside a `case`, so
|
|
276
|
+
* the ramp must be outermost and the per-feature branching evaluated at each
|
|
277
|
+
* stop — not the other way round.
|
|
278
|
+
*/
|
|
279
|
+
function interpolateOverZoom(at) {
|
|
273
280
|
return [
|
|
274
281
|
"interpolate",
|
|
275
282
|
["linear"],
|
|
276
283
|
["zoom"],
|
|
277
|
-
...Array.from({ length: 25 }, (_, zoom) => [zoom,
|
|
284
|
+
...Array.from({ length: 25 }, (_, zoom) => [zoom, at(zoom)]).flat()
|
|
278
285
|
];
|
|
279
286
|
}
|
|
287
|
+
function buildTextSizeExpression(text) {
|
|
288
|
+
return interpolateOverZoom((zoom) => textSizeAtZoom(text, zoom));
|
|
289
|
+
}
|
|
280
290
|
/**
|
|
281
291
|
* Companion to `buildTextSizeExpression` (ADR-0037): drives `text-opacity` to
|
|
282
292
|
* 0 once a ground-anchored Text graphic's derived on-screen size exceeds its
|
|
@@ -441,33 +451,72 @@ function buildTextHaloColorExpression(text) {
|
|
|
441
451
|
* `resolveTextHaloWidth`'s own outward-depth semantics, so the `textHalo`
|
|
442
452
|
* case uses its formula directly (unlike OpenLayers's straddling canvas
|
|
443
453
|
* stroke, which must double it).
|
|
454
|
+
*
|
|
455
|
+
* The width is derived from `textSizeAtZoom` — the same size `text-size`
|
|
456
|
+
* renders at — not from `textSizePixels` alone. A ground-anchored label
|
|
457
|
+
* (ADR-0028) carries only `textSizeMeters`, so reading the pixel size directly
|
|
458
|
+
* would fall back to a constant and pin the halo at one width while the glyphs
|
|
459
|
+
* grew and shrank with zoom: far too heavy on small text, hairline on large.
|
|
460
|
+
* OL and Leaflet already halo off their resolved size; this brings MapLibre in
|
|
461
|
+
* line. The ratio stays well under MapLibre's SDF halo ceiling (~¼ em), so the
|
|
462
|
+
* halo never clips into blocky corners.
|
|
444
463
|
*/
|
|
445
464
|
function buildTextHaloWidthExpression(text) {
|
|
446
|
-
|
|
465
|
+
const fallback = text.strokeColor ? text.strokeWidth ?? 1 : [
|
|
466
|
+
"case",
|
|
467
|
+
HAS_TEXT_BACKGROUND,
|
|
468
|
+
3,
|
|
469
|
+
0
|
|
470
|
+
];
|
|
471
|
+
return interpolateOverZoom((zoom) => [
|
|
447
472
|
"case",
|
|
448
473
|
HAS_TEXT_HALO,
|
|
474
|
+
haloWidthAtZoom(text, zoom),
|
|
475
|
+
fallback
|
|
476
|
+
]);
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* `text-halo-blur` as a fraction of the halo width. MapLibre's halo edge is
|
|
480
|
+
* hard at blur 0, which reads harsher over a vector basemap than the basemap's
|
|
481
|
+
* own labels — OpenFreeMap Liberty, the preview's default style, blurs nearly
|
|
482
|
+
* every one of its halos. Scaled off the width rather than a flat pixel value
|
|
483
|
+
* so the softness stays proportional as the halo grows with zoom.
|
|
484
|
+
*
|
|
485
|
+
* MapLibre-only: OpenLayers canvas strokes have no blur, and Leaflet's stacked
|
|
486
|
+
* `text-shadow` halo would need a blur radius threaded through each shadow.
|
|
487
|
+
* The three engines' halos therefore differ slightly at the edge.
|
|
488
|
+
*/
|
|
489
|
+
const TEXT_HALO_BLUR_RATIO = .25;
|
|
490
|
+
/** The halo's outward depth at one zoom stop; shared by the width and blur builders. */
|
|
491
|
+
function haloWidthAtZoom(text, zoom) {
|
|
492
|
+
return [
|
|
493
|
+
"max",
|
|
494
|
+
1.5,
|
|
449
495
|
[
|
|
450
|
-
"
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
"/",
|
|
454
|
-
[
|
|
455
|
-
"coalesce",
|
|
456
|
-
["get", "textSizePixels"],
|
|
457
|
-
24
|
|
458
|
-
],
|
|
459
|
-
8
|
|
460
|
-
]
|
|
461
|
-
],
|
|
462
|
-
text.strokeColor ? text.strokeWidth ?? 1 : [
|
|
463
|
-
"case",
|
|
464
|
-
HAS_TEXT_BACKGROUND,
|
|
465
|
-
3,
|
|
466
|
-
0
|
|
496
|
+
"/",
|
|
497
|
+
textSizeAtZoom(text, zoom),
|
|
498
|
+
8
|
|
467
499
|
]
|
|
468
500
|
];
|
|
469
501
|
}
|
|
470
502
|
/**
|
|
503
|
+
* Per-feature `text-halo-blur`, softening only a `textHalo` halo's edge.
|
|
504
|
+
* Everything else stays at 0 so the `textBackground` backdrop and a
|
|
505
|
+
* layer-level text stroke keep the crisp edge they render today.
|
|
506
|
+
*/
|
|
507
|
+
function buildTextHaloBlurExpression(text) {
|
|
508
|
+
return interpolateOverZoom((zoom) => [
|
|
509
|
+
"case",
|
|
510
|
+
HAS_TEXT_HALO,
|
|
511
|
+
[
|
|
512
|
+
"*",
|
|
513
|
+
TEXT_HALO_BLUR_RATIO,
|
|
514
|
+
haloWidthAtZoom(text, zoom)
|
|
515
|
+
],
|
|
516
|
+
0
|
|
517
|
+
]);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
471
520
|
* Build the layout and paint objects for a symbol (text) layer.
|
|
472
521
|
* Centralises the rotation convention so `createLayer` and `setLayerStyle`
|
|
473
522
|
* stay in sync. Rotation follows the OpenLayers convention: `π − r` radians
|
|
@@ -568,6 +617,7 @@ function buildSymbolLayer(symbolId, sourceId, text) {
|
|
|
568
617
|
"text-color": textColor,
|
|
569
618
|
"text-halo-color": buildTextHaloColorExpression(text),
|
|
570
619
|
"text-halo-width": buildTextHaloWidthExpression(text),
|
|
620
|
+
"text-halo-blur": buildTextHaloBlurExpression(text),
|
|
571
621
|
"text-opacity": TEXT_OPACITY_EXPRESSION
|
|
572
622
|
}
|
|
573
623
|
};
|
|
@@ -1267,7 +1317,10 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
1267
1317
|
if (styleUpdate.text.text || styleUpdate.text.labelProperty) this.map.setLayoutProperty(layerId, "text-field", buildTextFieldExpression(resolvedStyle.text));
|
|
1268
1318
|
if (styleUpdate.text.fillColor) this.map.setPaintProperty(layerId, "text-color", resolvedStyle.text.fillColor);
|
|
1269
1319
|
if (styleUpdate.text.strokeColor) this.map.setPaintProperty(layerId, "text-halo-color", buildTextHaloColorExpression(resolvedStyle.text));
|
|
1270
|
-
if (styleUpdate.text.strokeWidth !== void 0)
|
|
1320
|
+
if (styleUpdate.text.strokeWidth !== void 0) {
|
|
1321
|
+
this.map.setPaintProperty(layerId, "text-halo-width", buildTextHaloWidthExpression(resolvedStyle.text));
|
|
1322
|
+
this.map.setPaintProperty(layerId, "text-halo-blur", buildTextHaloBlurExpression(resolvedStyle.text));
|
|
1323
|
+
}
|
|
1271
1324
|
break;
|
|
1272
1325
|
}
|
|
1273
1326
|
}
|
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.24",
|
|
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.28",
|
|
41
|
+
"@orbat-mapper/tactical-draw": "^0.2.0-alpha.39"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"maplibre-gl": ">=5.8.0 <6.0.0"
|