@orbat-mapper/tactical-draw-adapter-maplibre 0.2.0-alpha.1 → 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/dist/index.d.mts +20 -3
- package/dist/index.mjs +230 -51
- package/package.json +3 -3
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,26 +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("rotate", fn);
|
|
320
|
-
this.map.on("pitch", fn);
|
|
321
|
-
this.map.on("moveend", fn);
|
|
476
|
+
for (const event of this.viewChangeEvents()) this.map.on(event, fn);
|
|
322
477
|
return { fn };
|
|
323
478
|
}
|
|
324
479
|
detachViewChange(token) {
|
|
325
|
-
this.map.off(
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
this.
|
|
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
|
+
];
|
|
329
489
|
}
|
|
330
490
|
createEditPointerDriver() {
|
|
331
491
|
const map = this.map;
|
|
492
|
+
const toContinuousLng = (lng) => this.toContinuousLng(lng);
|
|
332
493
|
const hub = new PointerCallbackHub();
|
|
333
494
|
let dragPanWasEnabled = false;
|
|
334
495
|
let disposed = false;
|
|
335
496
|
let touchDragActive = false;
|
|
336
497
|
let multiTouchCancelled = false;
|
|
337
|
-
const
|
|
498
|
+
const touchInfo = (e) => ({
|
|
499
|
+
hitTolerance: TOUCH_HIT_TOLERANCE_PX,
|
|
500
|
+
native: e.originalEvent
|
|
501
|
+
});
|
|
338
502
|
const preventMapEventDefaults = (e) => {
|
|
339
503
|
e.preventDefault();
|
|
340
504
|
e.originalEvent.preventDefault?.();
|
|
@@ -342,22 +506,32 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
342
506
|
};
|
|
343
507
|
const onDown = (e) => {
|
|
344
508
|
const px = [e.point.x, e.point.y];
|
|
345
|
-
const info = {
|
|
509
|
+
const info = {
|
|
510
|
+
altKey: e.originalEvent.altKey,
|
|
511
|
+
shiftKey: e.originalEvent.shiftKey,
|
|
512
|
+
native: e.originalEvent
|
|
513
|
+
};
|
|
346
514
|
hub.fireDown(px, info);
|
|
347
515
|
};
|
|
348
516
|
const onMove = (e) => {
|
|
349
517
|
const px = [e.point.x, e.point.y];
|
|
350
|
-
hub.fireMove(px
|
|
518
|
+
hub.fireMove(px, {
|
|
519
|
+
shiftKey: e.originalEvent.shiftKey,
|
|
520
|
+
native: e.originalEvent
|
|
521
|
+
});
|
|
351
522
|
};
|
|
352
523
|
const onUp = (e) => {
|
|
353
524
|
const px = [e.point.x, e.point.y];
|
|
354
|
-
hub.fireUp(px
|
|
525
|
+
hub.fireUp(px, {
|
|
526
|
+
shiftKey: e.originalEvent.shiftKey,
|
|
527
|
+
native: e.originalEvent
|
|
528
|
+
});
|
|
355
529
|
};
|
|
356
530
|
const onTouchStart = (e) => {
|
|
357
531
|
if (e.points.length !== 1) return;
|
|
358
532
|
const before = dragPanWasEnabled;
|
|
359
533
|
const px = [e.point.x, e.point.y];
|
|
360
|
-
hub.fireDown(px,
|
|
534
|
+
hub.fireDown(px, touchInfo(e));
|
|
361
535
|
touchDragActive = dragPanWasEnabled !== before;
|
|
362
536
|
if (touchDragActive) preventMapEventDefaults(e);
|
|
363
537
|
};
|
|
@@ -366,20 +540,20 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
366
540
|
const px = [e.point.x, e.point.y];
|
|
367
541
|
if (touchCount > 1 && touchDragActive && !multiTouchCancelled) {
|
|
368
542
|
multiTouchCancelled = true;
|
|
369
|
-
hub.fireUp(px,
|
|
543
|
+
hub.fireUp(px, touchInfo(e));
|
|
370
544
|
touchDragActive = false;
|
|
371
545
|
preventMapEventDefaults(e);
|
|
372
546
|
return;
|
|
373
547
|
}
|
|
374
548
|
if (touchCount > 1) return;
|
|
375
549
|
if (touchCount === 1) multiTouchCancelled = false;
|
|
376
|
-
hub.fireMove(px,
|
|
550
|
+
hub.fireMove(px, touchInfo(e));
|
|
377
551
|
if (touchDragActive) preventMapEventDefaults(e);
|
|
378
552
|
};
|
|
379
553
|
const onTouchEnd = (e) => {
|
|
380
554
|
multiTouchCancelled = false;
|
|
381
555
|
const px = [e.point.x, e.point.y];
|
|
382
|
-
hub.fireUp(px,
|
|
556
|
+
hub.fireUp(px, touchInfo(e));
|
|
383
557
|
touchDragActive = false;
|
|
384
558
|
};
|
|
385
559
|
map.on("mousedown", onDown);
|
|
@@ -403,12 +577,12 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
403
577
|
}
|
|
404
578
|
},
|
|
405
579
|
toPixel(lonLat) {
|
|
406
|
-
const p = map.project(lonLat);
|
|
580
|
+
const p = map.project([toContinuousLng(lonLat[0]), lonLat[1]]);
|
|
407
581
|
return [p.x, p.y];
|
|
408
582
|
},
|
|
409
583
|
fromPixel(pixel) {
|
|
410
584
|
const ll = map.unproject(pixel);
|
|
411
|
-
return [ll.lng, ll.lat];
|
|
585
|
+
return [toContinuousLng(ll.lng), ll.lat];
|
|
412
586
|
},
|
|
413
587
|
dispose() {
|
|
414
588
|
if (disposed) return;
|
|
@@ -472,9 +646,13 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
472
646
|
return project(position[0], position[1]);
|
|
473
647
|
}
|
|
474
648
|
getPixelFromCoordinate(position) {
|
|
475
|
-
const point = this.map.project(position);
|
|
649
|
+
const point = this.map.project([this.toContinuousLng(position[0]), position[1]]);
|
|
476
650
|
return [point.x, point.y];
|
|
477
651
|
}
|
|
652
|
+
getCoordinateFromPixel(pixel) {
|
|
653
|
+
const coordinate = this.map.unproject(pixel);
|
|
654
|
+
return [this.toContinuousLng(coordinate.lng), coordinate.lat];
|
|
655
|
+
}
|
|
478
656
|
getViewportSize() {
|
|
479
657
|
const container = this.map.getContainer();
|
|
480
658
|
return {
|
|
@@ -504,13 +682,14 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
504
682
|
else this.map.doubleClickZoom.disable();
|
|
505
683
|
}
|
|
506
684
|
getResolution() {
|
|
507
|
-
|
|
508
|
-
const center = this.map.getCenter();
|
|
509
|
-
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);
|
|
510
686
|
}
|
|
511
687
|
getZoom() {
|
|
512
688
|
return this.map.getZoom();
|
|
513
689
|
}
|
|
690
|
+
getBearing() {
|
|
691
|
+
return this.map.getBearing() * (Math.PI / 180);
|
|
692
|
+
}
|
|
514
693
|
onPick(layerId, handler, opts) {
|
|
515
694
|
const onMlClick = (e) => {
|
|
516
695
|
const handle = this.layers.get(layerId);
|
|
@@ -534,7 +713,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
534
713
|
id: cmId,
|
|
535
714
|
feature: geojson,
|
|
536
715
|
pixel: [x, y],
|
|
537
|
-
coordinate: [e.lngLat.lng, e.lngLat.lat],
|
|
716
|
+
coordinate: [this.toContinuousLng(e.lngLat.lng), e.lngLat.lat],
|
|
538
717
|
originalEvent
|
|
539
718
|
});
|
|
540
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"
|