@orbat-mapper/tactical-draw-adapter-maplibre 0.2.0-alpha.0 → 0.2.0-alpha.10
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 +10 -9
- package/dist/index.d.mts +20 -3
- package/dist/index.mjs +230 -47
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
# @orbat-mapper/tactical-draw-adapter-maplibre
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
unchanged against a MapLibre map.
|
|
3
|
+
[MapLibre GL JS](https://maplibre.org/maplibre-gl-js/docs/) bindings for
|
|
4
|
+
`@orbat-mapper/tactical-draw`. `MapLibreAdapter` implements the `MapAdapter`
|
|
5
|
+
contract, so the draw/edit sessions, draw rules, gestures, and handle
|
|
6
|
+
rendering from `tactical-draw` run unchanged on a MapLibre map.
|
|
8
7
|
|
|
9
|
-
`maplibre-gl` is
|
|
10
|
-
copy and never install an engine you don't use.
|
|
8
|
+
`maplibre-gl` is a peer dependency — your app supplies the version.
|
|
11
9
|
|
|
12
10
|
## Installation
|
|
13
11
|
|
|
@@ -34,8 +32,11 @@ const adapter = new MapLibreAdapter(map);
|
|
|
34
32
|
const draw = new TacticalDraw(adapter);
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
## Quick Links
|
|
36
|
+
|
|
37
|
+
- [`@orbat-mapper/tactical-draw`](https://www.npmjs.com/package/@orbat-mapper/tactical-draw) — the draw/edit core this adapter plugs into
|
|
38
|
+
- [`@orbat-mapper/control-measures`](https://www.npmjs.com/package/@orbat-mapper/control-measures) — generates the control measures as GeoJSON
|
|
39
|
+
- [GitHub repository](https://github.com/orbat-mapper/tactical-graphics)
|
|
39
40
|
|
|
40
41
|
## License
|
|
41
42
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseMapAdapter, EditPointerDriver, FeatureOps, LayerId, MapCursor, NativeEventEntry, PickHandler, PickOptions, PixelCoordinate, StyleOptions } from "@orbat-mapper/tactical-draw";
|
|
1
|
+
import { BaseMapAdapter, EditPointerDriver, FeatureOps, IconResource, LayerId, MapCursor, NativeEventEntry, PickHandler, PickOptions, PixelCoordinate, StyleOptions } from "@orbat-mapper/tactical-draw";
|
|
2
2
|
import maplibregl from "maplibre-gl";
|
|
3
3
|
import { Position } from "geojson";
|
|
4
4
|
|
|
@@ -13,21 +13,37 @@ type MapLibreEventToken = (e: any) => void;
|
|
|
13
13
|
interface MapLibreViewToken {
|
|
14
14
|
fn: () => void;
|
|
15
15
|
}
|
|
16
|
+
interface MapLibreAdapterOptions {
|
|
17
|
+
/**
|
|
18
|
+
* How view changes propagate to view-state refits. "continuous" (default)
|
|
19
|
+
* refits on every frame of an animated zoom/rotate/pitch; "settle" defers
|
|
20
|
+
* the refit until the animation ends.
|
|
21
|
+
*/
|
|
22
|
+
viewChangeMode?: "continuous" | "settle";
|
|
23
|
+
}
|
|
16
24
|
declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLibreEventToken, MapLibreViewToken> {
|
|
17
25
|
private map;
|
|
18
|
-
|
|
26
|
+
private readonly viewChangeMode;
|
|
27
|
+
private readonly pendingIconImages;
|
|
28
|
+
readonly scalesGroundPointSymbolsNatively = true;
|
|
29
|
+
constructor(map: maplibregl.Map, options?: MapLibreAdapterOptions);
|
|
30
|
+
private toContinuousLng;
|
|
19
31
|
protected createLayer(layerId: LayerId, style?: StyleOptions): MapLibreLayerHandle;
|
|
20
32
|
protected destroyLayer(handle: MapLibreLayerHandle): void;
|
|
21
33
|
protected applyFeatureOps(layerId: LayerId, ops: FeatureOps): void;
|
|
34
|
+
protected applyIconResources(resources: readonly IconResource[]): void;
|
|
35
|
+
protected removeIconResource(id: string): void;
|
|
22
36
|
protected attachNativeEvent(type: "click" | "pointermove" | "dblclick", onEvent: (lonLat: Position, pixel: PixelCoordinate, originalEvent: unknown) => void): NativeEventEntry<MapLibreEventToken>;
|
|
23
37
|
protected detachNativeEvent(entry: NativeEventEntry<MapLibreEventToken>): void;
|
|
24
38
|
protected attachViewChange(handler: () => void): MapLibreViewToken;
|
|
25
39
|
protected detachViewChange(token: MapLibreViewToken): void;
|
|
40
|
+
private viewChangeEvents;
|
|
26
41
|
createEditPointerDriver(): EditPointerDriver;
|
|
27
42
|
setLayerStyle(layerId: LayerId, style: StyleOptions): void;
|
|
28
43
|
toLonLat(coordinate: number[]): Position;
|
|
29
44
|
fromLonLat(position: Position): number[];
|
|
30
45
|
getPixelFromCoordinate(position: Position): PixelCoordinate | null;
|
|
46
|
+
getCoordinateFromPixel(pixel: PixelCoordinate): Position | null;
|
|
31
47
|
getViewportSize(): {
|
|
32
48
|
width: number;
|
|
33
49
|
height: number;
|
|
@@ -39,8 +55,9 @@ declare class MapLibreAdapter extends BaseMapAdapter<MapLibreLayerHandle, MapLib
|
|
|
39
55
|
setDoubleClickZoomEnabled(enabled: boolean): void;
|
|
40
56
|
getResolution(): number | undefined;
|
|
41
57
|
getZoom(): number | undefined;
|
|
58
|
+
getBearing(): number;
|
|
42
59
|
onPick(layerId: LayerId, handler: PickHandler, opts?: PickOptions): () => void;
|
|
43
60
|
setGlobeMode(enabled: boolean): void;
|
|
44
61
|
}
|
|
45
62
|
//#endregion
|
|
46
|
-
export { MapLibreAdapter };
|
|
63
|
+
export { MapLibreAdapter, type MapLibreAdapterOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { project, unproject } from "@orbat-mapper/control-measures";
|
|
2
|
-
import { BaseMapAdapter, PointerCallbackHub, TOUCH_HIT_TOLERANCE_PX, bindAbortable, pickHitTolerance, tryControlMeasureIdFromFeature } from "@orbat-mapper/tactical-draw";
|
|
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";
|
|
3
3
|
//#region src/ml-adapter.ts
|
|
4
4
|
function emptyFC() {
|
|
5
5
|
return {
|
|
@@ -52,51 +52,163 @@ function buildCirclePointFilter(style) {
|
|
|
52
52
|
"$type",
|
|
53
53
|
"Point"
|
|
54
54
|
],
|
|
55
|
+
["!has", "iconId"],
|
|
55
56
|
["!has", style.text.labelProperty]
|
|
56
57
|
] : [
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
"all",
|
|
59
|
+
[
|
|
60
|
+
"==",
|
|
61
|
+
"$type",
|
|
62
|
+
"Point"
|
|
63
|
+
],
|
|
64
|
+
["!has", "iconId"]
|
|
60
65
|
];
|
|
61
66
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
67
|
+
function buildIconLayer(id, source) {
|
|
68
|
+
return {
|
|
69
|
+
id,
|
|
70
|
+
type: "symbol",
|
|
71
|
+
source,
|
|
72
|
+
filter: [
|
|
73
|
+
"all",
|
|
74
|
+
[
|
|
75
|
+
"==",
|
|
76
|
+
"$type",
|
|
77
|
+
"Point"
|
|
78
|
+
],
|
|
79
|
+
["has", "iconId"]
|
|
80
|
+
],
|
|
81
|
+
layout: {
|
|
82
|
+
"icon-image": ["get", "iconId"],
|
|
83
|
+
"icon-size": buildIconSizeExpression(),
|
|
84
|
+
"icon-rotate": [
|
|
85
|
+
"*",
|
|
86
|
+
[
|
|
87
|
+
"coalesce",
|
|
88
|
+
["get", "iconRotation"],
|
|
89
|
+
0
|
|
90
|
+
],
|
|
91
|
+
180 / Math.PI
|
|
92
|
+
],
|
|
93
|
+
"icon-rotation-alignment": "viewport",
|
|
94
|
+
"icon-pitch-alignment": "viewport",
|
|
95
|
+
"icon-allow-overlap": true,
|
|
96
|
+
"icon-ignore-placement": true
|
|
97
|
+
},
|
|
98
|
+
paint: { "icon-opacity": [
|
|
99
|
+
"coalesce",
|
|
100
|
+
["get", "iconOpacity"],
|
|
101
|
+
1
|
|
102
|
+
] }
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function centeredSvg(resource) {
|
|
106
|
+
const halfWidth = Math.max(resource.anchor[0], resource.width - resource.anchor[0]);
|
|
107
|
+
const halfHeight = Math.max(resource.anchor[1], resource.height - resource.anchor[1]);
|
|
108
|
+
const width = halfWidth * 2;
|
|
109
|
+
const height = halfHeight * 2;
|
|
110
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><g transform="translate(${halfWidth - resource.anchor[0]} ${halfHeight - resource.anchor[1]})">${resource.svg}</g></svg>`;
|
|
111
|
+
}
|
|
112
|
+
/** Web Mercator world circumference in meters (2π × 6378137). */
|
|
113
|
+
const WEB_MERCATOR_CIRCUMFERENCE = 40075016.686;
|
|
114
|
+
/**
|
|
115
|
+
* Per-feature text size (ADR-0028): a ground-anchored label carries
|
|
116
|
+
* `textSizeMeters` and renders at `textSizeMeters / resolution`, clamped to
|
|
117
|
+
* the layer's `textSizeMinPixels`/`textSizeMaxPixels` band; a screen-anchored
|
|
118
|
+
* one carries `textSizePixels` and renders at that constant; neither renders
|
|
119
|
+
* the 14px engine default. Layout properties only take zoom as an
|
|
120
|
+
* `interpolate`/`step` input, so the ground conversion is evaluated at every
|
|
121
|
+
* integer zoom stop (resolution = world circumference / 512·2^zoom pixels,
|
|
122
|
+
* matching `getResolution`); the linear ramp between stops approximates the
|
|
123
|
+
* exponential exactly closely enough at 1-zoom granularity.
|
|
124
|
+
*/
|
|
125
|
+
function buildTextSizeExpression(text) {
|
|
126
|
+
const minPx = text.textSizeMinPixels ?? DEFAULT_TEXT_SIZE_MIN_PIXELS;
|
|
127
|
+
const maxPx = text.textSizeMaxPixels ?? DEFAULT_TEXT_SIZE_MAX_PIXELS;
|
|
128
|
+
const screenOrDefault = [
|
|
64
129
|
"coalesce",
|
|
65
130
|
["get", "textSizePixels"],
|
|
66
131
|
14
|
|
67
132
|
];
|
|
68
|
-
const
|
|
133
|
+
const sizeAtZoom = (zoom) => {
|
|
134
|
+
return [
|
|
135
|
+
"case",
|
|
136
|
+
["has", "textSizeMeters"],
|
|
137
|
+
[
|
|
138
|
+
"min",
|
|
139
|
+
maxPx,
|
|
140
|
+
[
|
|
141
|
+
"max",
|
|
142
|
+
minPx,
|
|
143
|
+
[
|
|
144
|
+
"*",
|
|
145
|
+
["get", "textSizeMeters"],
|
|
146
|
+
Math.pow(2, zoom + 9) / WEB_MERCATOR_CIRCUMFERENCE
|
|
147
|
+
]
|
|
148
|
+
]
|
|
149
|
+
],
|
|
150
|
+
screenOrDefault
|
|
151
|
+
];
|
|
152
|
+
};
|
|
153
|
+
return [
|
|
154
|
+
"interpolate",
|
|
155
|
+
["linear"],
|
|
156
|
+
["zoom"],
|
|
157
|
+
...Array.from({ length: 25 }, (_, zoom) => [zoom, sizeAtZoom(zoom)]).flat()
|
|
158
|
+
];
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Per-feature icon size, same zoom-stop scheme as `buildTextSizeExpression`:
|
|
162
|
+
* a ground-sized point symbol carries `iconSizeMeters`/`iconOctagonSize` (and
|
|
163
|
+
* optional `iconMinPixels`/`iconMaxPixels`), so the meters→pixels clamp is
|
|
164
|
+
* evaluated inside the style layer at every integer zoom stop and the symbol
|
|
165
|
+
* scales smoothly through animated zooms. Anything else falls back to the
|
|
166
|
+
* host-computed `iconScale` snapshot.
|
|
167
|
+
*/
|
|
168
|
+
function buildIconSizeExpression() {
|
|
169
|
+
const minExpr = [
|
|
69
170
|
"coalesce",
|
|
70
|
-
["get", "
|
|
171
|
+
["get", "iconMinPixels"],
|
|
71
172
|
0
|
|
72
173
|
];
|
|
73
|
-
const
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
174
|
+
const maxExpr = [
|
|
175
|
+
"coalesce",
|
|
176
|
+
["get", "iconMaxPixels"],
|
|
177
|
+
1e9
|
|
178
|
+
];
|
|
179
|
+
const scaleAtZoom = (zoom) => {
|
|
180
|
+
return [
|
|
77
181
|
"case",
|
|
78
|
-
["has", "
|
|
182
|
+
["has", "iconSizeMeters"],
|
|
79
183
|
[
|
|
80
|
-
"
|
|
81
|
-
size,
|
|
184
|
+
"/",
|
|
82
185
|
[
|
|
83
|
-
"
|
|
84
|
-
|
|
186
|
+
"max",
|
|
187
|
+
minExpr,
|
|
85
188
|
[
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
|
|
189
|
+
"min",
|
|
190
|
+
maxExpr,
|
|
191
|
+
[
|
|
192
|
+
"*",
|
|
193
|
+
["get", "iconSizeMeters"],
|
|
194
|
+
Math.pow(2, zoom + 9) / WEB_MERCATOR_CIRCUMFERENCE
|
|
195
|
+
]
|
|
89
196
|
]
|
|
90
|
-
]
|
|
197
|
+
],
|
|
198
|
+
["get", "iconOctagonSize"]
|
|
91
199
|
],
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
200
|
+
[
|
|
201
|
+
"coalesce",
|
|
202
|
+
["get", "iconScale"],
|
|
203
|
+
1
|
|
204
|
+
]
|
|
205
|
+
];
|
|
206
|
+
};
|
|
95
207
|
return [
|
|
96
208
|
"interpolate",
|
|
97
209
|
["linear"],
|
|
98
210
|
["zoom"],
|
|
99
|
-
...Array.from({ length: 25 }, (_, zoom) => [zoom,
|
|
211
|
+
...Array.from({ length: 25 }, (_, zoom) => [zoom, scaleAtZoom(zoom)]).flat()
|
|
100
212
|
];
|
|
101
213
|
}
|
|
102
214
|
/**
|
|
@@ -136,6 +248,22 @@ function buildSymbolLayer(symbolId, sourceId, text) {
|
|
|
136
248
|
],
|
|
137
249
|
"#000"
|
|
138
250
|
];
|
|
251
|
+
const textAnchor = text.labelProperty ? [
|
|
252
|
+
"case",
|
|
253
|
+
[
|
|
254
|
+
"==",
|
|
255
|
+
["get", "textAnchor"],
|
|
256
|
+
"start"
|
|
257
|
+
],
|
|
258
|
+
"left",
|
|
259
|
+
[
|
|
260
|
+
"==",
|
|
261
|
+
["get", "textAnchor"],
|
|
262
|
+
"end"
|
|
263
|
+
],
|
|
264
|
+
"right",
|
|
265
|
+
"center"
|
|
266
|
+
] : "center";
|
|
139
267
|
return {
|
|
140
268
|
id: symbolId,
|
|
141
269
|
type: "symbol",
|
|
@@ -143,8 +271,9 @@ function buildSymbolLayer(symbolId, sourceId, text) {
|
|
|
143
271
|
layout: {
|
|
144
272
|
"text-field": text.labelProperty ? ["get", text.labelProperty] : text.text ?? "",
|
|
145
273
|
"text-font": ["Open Sans Regular"],
|
|
146
|
-
"text-size": buildTextSizeExpression(),
|
|
274
|
+
"text-size": buildTextSizeExpression(text),
|
|
147
275
|
"text-rotate": textRotate,
|
|
276
|
+
"text-anchor": textAnchor,
|
|
148
277
|
"text-rotation-alignment": "map",
|
|
149
278
|
"text-pitch-alignment": "map",
|
|
150
279
|
"text-allow-overlap": true
|
|
@@ -208,9 +337,16 @@ function buildLinePaint(style) {
|
|
|
208
337
|
}
|
|
209
338
|
var MapLibreAdapter = class extends BaseMapAdapter {
|
|
210
339
|
map;
|
|
211
|
-
|
|
340
|
+
viewChangeMode;
|
|
341
|
+
pendingIconImages = /* @__PURE__ */ new Set();
|
|
342
|
+
scalesGroundPointSymbolsNatively = true;
|
|
343
|
+
constructor(map, options = {}) {
|
|
212
344
|
super();
|
|
213
345
|
this.map = map;
|
|
346
|
+
this.viewChangeMode = options.viewChangeMode ?? "continuous";
|
|
347
|
+
}
|
|
348
|
+
toContinuousLng(lng) {
|
|
349
|
+
return nearestWorldCopyLng(lng, this.map.getCenter().lng);
|
|
214
350
|
}
|
|
215
351
|
createLayer(layerId, style) {
|
|
216
352
|
const sourceId = `${layerId}-src`;
|
|
@@ -267,6 +403,9 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
267
403
|
paint: buildCirclePaint(style)
|
|
268
404
|
});
|
|
269
405
|
mlLayerIds.push(circleId);
|
|
406
|
+
const iconId = `${layerId}-icon`;
|
|
407
|
+
this.map.addLayer(buildIconLayer(iconId, sourceId));
|
|
408
|
+
mlLayerIds.push(iconId);
|
|
270
409
|
if (style?.text) {
|
|
271
410
|
const symbolId = `${layerId}-symbol`;
|
|
272
411
|
this.map.addLayer(buildSymbolLayer(symbolId, sourceId, style.text));
|
|
@@ -299,10 +438,29 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
299
438
|
features
|
|
300
439
|
});
|
|
301
440
|
}
|
|
441
|
+
applyIconResources(resources) {
|
|
442
|
+
if (typeof Image === "undefined") return;
|
|
443
|
+
for (const resource of resources) {
|
|
444
|
+
if (this.map.hasImage(resource.id) || this.pendingIconImages.has(resource.id)) continue;
|
|
445
|
+
this.pendingIconImages.add(resource.id);
|
|
446
|
+
const image = new Image();
|
|
447
|
+
image.onload = () => {
|
|
448
|
+
if (!this.pendingIconImages.has(resource.id)) return;
|
|
449
|
+
this.pendingIconImages.delete(resource.id);
|
|
450
|
+
if (!this.map.hasImage(resource.id)) this.map.addImage(resource.id, image);
|
|
451
|
+
};
|
|
452
|
+
image.onerror = () => this.pendingIconImages.delete(resource.id);
|
|
453
|
+
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(centeredSvg(resource))}`;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
removeIconResource(id) {
|
|
457
|
+
this.pendingIconImages.delete(id);
|
|
458
|
+
if (this.map.hasImage(id)) this.map.removeImage(id);
|
|
459
|
+
}
|
|
302
460
|
attachNativeEvent(type, onEvent) {
|
|
303
461
|
const mlEvent = type === "pointermove" ? "mousemove" : type === "dblclick" ? "dblclick" : "click";
|
|
304
462
|
const fn = (e) => {
|
|
305
|
-
onEvent([e.lngLat.lng, e.lngLat.lat], [e.point.x, e.point.y], e);
|
|
463
|
+
onEvent([this.toContinuousLng(e.lngLat.lng), e.lngLat.lat], [e.point.x, e.point.y], e);
|
|
306
464
|
};
|
|
307
465
|
this.map.on(mlEvent, fn);
|
|
308
466
|
return {
|
|
@@ -315,22 +473,32 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
315
473
|
}
|
|
316
474
|
attachViewChange(handler) {
|
|
317
475
|
const fn = () => handler();
|
|
318
|
-
this.map.on(
|
|
319
|
-
this.map.on("zoomend", fn);
|
|
476
|
+
for (const event of this.viewChangeEvents()) this.map.on(event, fn);
|
|
320
477
|
return { fn };
|
|
321
478
|
}
|
|
322
479
|
detachViewChange(token) {
|
|
323
|
-
this.map.off(
|
|
324
|
-
|
|
480
|
+
for (const event of this.viewChangeEvents()) this.map.off(event, token.fn);
|
|
481
|
+
}
|
|
482
|
+
viewChangeEvents() {
|
|
483
|
+
return this.viewChangeMode === "settle" ? ["moveend"] : [
|
|
484
|
+
"zoom",
|
|
485
|
+
"rotate",
|
|
486
|
+
"pitch",
|
|
487
|
+
"moveend"
|
|
488
|
+
];
|
|
325
489
|
}
|
|
326
490
|
createEditPointerDriver() {
|
|
327
491
|
const map = this.map;
|
|
492
|
+
const toContinuousLng = (lng) => this.toContinuousLng(lng);
|
|
328
493
|
const hub = new PointerCallbackHub();
|
|
329
494
|
let dragPanWasEnabled = false;
|
|
330
495
|
let disposed = false;
|
|
331
496
|
let touchDragActive = false;
|
|
332
497
|
let multiTouchCancelled = false;
|
|
333
|
-
const
|
|
498
|
+
const touchInfo = (e) => ({
|
|
499
|
+
hitTolerance: TOUCH_HIT_TOLERANCE_PX,
|
|
500
|
+
native: e.originalEvent
|
|
501
|
+
});
|
|
334
502
|
const preventMapEventDefaults = (e) => {
|
|
335
503
|
e.preventDefault();
|
|
336
504
|
e.originalEvent.preventDefault?.();
|
|
@@ -338,22 +506,32 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
338
506
|
};
|
|
339
507
|
const onDown = (e) => {
|
|
340
508
|
const px = [e.point.x, e.point.y];
|
|
341
|
-
const info = {
|
|
509
|
+
const info = {
|
|
510
|
+
altKey: e.originalEvent.altKey,
|
|
511
|
+
shiftKey: e.originalEvent.shiftKey,
|
|
512
|
+
native: e.originalEvent
|
|
513
|
+
};
|
|
342
514
|
hub.fireDown(px, info);
|
|
343
515
|
};
|
|
344
516
|
const onMove = (e) => {
|
|
345
517
|
const px = [e.point.x, e.point.y];
|
|
346
|
-
hub.fireMove(px
|
|
518
|
+
hub.fireMove(px, {
|
|
519
|
+
shiftKey: e.originalEvent.shiftKey,
|
|
520
|
+
native: e.originalEvent
|
|
521
|
+
});
|
|
347
522
|
};
|
|
348
523
|
const onUp = (e) => {
|
|
349
524
|
const px = [e.point.x, e.point.y];
|
|
350
|
-
hub.fireUp(px
|
|
525
|
+
hub.fireUp(px, {
|
|
526
|
+
shiftKey: e.originalEvent.shiftKey,
|
|
527
|
+
native: e.originalEvent
|
|
528
|
+
});
|
|
351
529
|
};
|
|
352
530
|
const onTouchStart = (e) => {
|
|
353
531
|
if (e.points.length !== 1) return;
|
|
354
532
|
const before = dragPanWasEnabled;
|
|
355
533
|
const px = [e.point.x, e.point.y];
|
|
356
|
-
hub.fireDown(px,
|
|
534
|
+
hub.fireDown(px, touchInfo(e));
|
|
357
535
|
touchDragActive = dragPanWasEnabled !== before;
|
|
358
536
|
if (touchDragActive) preventMapEventDefaults(e);
|
|
359
537
|
};
|
|
@@ -362,20 +540,20 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
362
540
|
const px = [e.point.x, e.point.y];
|
|
363
541
|
if (touchCount > 1 && touchDragActive && !multiTouchCancelled) {
|
|
364
542
|
multiTouchCancelled = true;
|
|
365
|
-
hub.fireUp(px,
|
|
543
|
+
hub.fireUp(px, touchInfo(e));
|
|
366
544
|
touchDragActive = false;
|
|
367
545
|
preventMapEventDefaults(e);
|
|
368
546
|
return;
|
|
369
547
|
}
|
|
370
548
|
if (touchCount > 1) return;
|
|
371
549
|
if (touchCount === 1) multiTouchCancelled = false;
|
|
372
|
-
hub.fireMove(px,
|
|
550
|
+
hub.fireMove(px, touchInfo(e));
|
|
373
551
|
if (touchDragActive) preventMapEventDefaults(e);
|
|
374
552
|
};
|
|
375
553
|
const onTouchEnd = (e) => {
|
|
376
554
|
multiTouchCancelled = false;
|
|
377
555
|
const px = [e.point.x, e.point.y];
|
|
378
|
-
hub.fireUp(px,
|
|
556
|
+
hub.fireUp(px, touchInfo(e));
|
|
379
557
|
touchDragActive = false;
|
|
380
558
|
};
|
|
381
559
|
map.on("mousedown", onDown);
|
|
@@ -399,12 +577,12 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
399
577
|
}
|
|
400
578
|
},
|
|
401
579
|
toPixel(lonLat) {
|
|
402
|
-
const p = map.project(lonLat);
|
|
580
|
+
const p = map.project([toContinuousLng(lonLat[0]), lonLat[1]]);
|
|
403
581
|
return [p.x, p.y];
|
|
404
582
|
},
|
|
405
583
|
fromPixel(pixel) {
|
|
406
584
|
const ll = map.unproject(pixel);
|
|
407
|
-
return [ll.lng, ll.lat];
|
|
585
|
+
return [toContinuousLng(ll.lng), ll.lat];
|
|
408
586
|
},
|
|
409
587
|
dispose() {
|
|
410
588
|
if (disposed) return;
|
|
@@ -468,9 +646,13 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
468
646
|
return project(position[0], position[1]);
|
|
469
647
|
}
|
|
470
648
|
getPixelFromCoordinate(position) {
|
|
471
|
-
const point = this.map.project(position);
|
|
649
|
+
const point = this.map.project([this.toContinuousLng(position[0]), position[1]]);
|
|
472
650
|
return [point.x, point.y];
|
|
473
651
|
}
|
|
652
|
+
getCoordinateFromPixel(pixel) {
|
|
653
|
+
const coordinate = this.map.unproject(pixel);
|
|
654
|
+
return [this.toContinuousLng(coordinate.lng), coordinate.lat];
|
|
655
|
+
}
|
|
474
656
|
getViewportSize() {
|
|
475
657
|
const container = this.map.getContainer();
|
|
476
658
|
return {
|
|
@@ -500,13 +682,14 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
500
682
|
else this.map.doubleClickZoom.disable();
|
|
501
683
|
}
|
|
502
684
|
getResolution() {
|
|
503
|
-
|
|
504
|
-
const center = this.map.getCenter();
|
|
505
|
-
return 40075016.686 * Math.cos(center.lat * Math.PI / 180) / Math.pow(2, zoom + 8);
|
|
685
|
+
return WEB_MERCATOR_CIRCUMFERENCE / Math.pow(2, this.map.getZoom() + 9);
|
|
506
686
|
}
|
|
507
687
|
getZoom() {
|
|
508
688
|
return this.map.getZoom();
|
|
509
689
|
}
|
|
690
|
+
getBearing() {
|
|
691
|
+
return this.map.getBearing() * (Math.PI / 180);
|
|
692
|
+
}
|
|
510
693
|
onPick(layerId, handler, opts) {
|
|
511
694
|
const onMlClick = (e) => {
|
|
512
695
|
const handle = this.layers.get(layerId);
|
|
@@ -530,7 +713,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
530
713
|
id: cmId,
|
|
531
714
|
feature: geojson,
|
|
532
715
|
pixel: [x, y],
|
|
533
|
-
coordinate: [e.lngLat.lng, e.lngLat.lat],
|
|
716
|
+
coordinate: [this.toContinuousLng(e.lngLat.lng), e.lngLat.lat],
|
|
534
717
|
originalEvent
|
|
535
718
|
});
|
|
536
719
|
};
|
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.10",
|
|
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.2.0-alpha.
|
|
41
|
-
"@orbat-mapper/tactical-draw": "^0.2.0-alpha.
|
|
40
|
+
"@orbat-mapper/control-measures": "^0.2.0-alpha.16",
|
|
41
|
+
"@orbat-mapper/tactical-draw": "^0.2.0-alpha.25"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"maplibre-gl": ">=4.0.0 <6.0.0"
|