@orbat-mapper/tactical-draw-adapter-maplibre 0.2.0-alpha.1 → 0.2.0-alpha.11
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 +231 -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,10 @@ 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,
|
|
277
|
+
"text-max-width": 9999,
|
|
148
278
|
"text-rotation-alignment": "map",
|
|
149
279
|
"text-pitch-alignment": "map",
|
|
150
280
|
"text-allow-overlap": true
|
|
@@ -208,9 +338,16 @@ function buildLinePaint(style) {
|
|
|
208
338
|
}
|
|
209
339
|
var MapLibreAdapter = class extends BaseMapAdapter {
|
|
210
340
|
map;
|
|
211
|
-
|
|
341
|
+
viewChangeMode;
|
|
342
|
+
pendingIconImages = /* @__PURE__ */ new Set();
|
|
343
|
+
scalesGroundPointSymbolsNatively = true;
|
|
344
|
+
constructor(map, options = {}) {
|
|
212
345
|
super();
|
|
213
346
|
this.map = map;
|
|
347
|
+
this.viewChangeMode = options.viewChangeMode ?? "continuous";
|
|
348
|
+
}
|
|
349
|
+
toContinuousLng(lng) {
|
|
350
|
+
return nearestWorldCopyLng(lng, this.map.getCenter().lng);
|
|
214
351
|
}
|
|
215
352
|
createLayer(layerId, style) {
|
|
216
353
|
const sourceId = `${layerId}-src`;
|
|
@@ -267,6 +404,9 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
267
404
|
paint: buildCirclePaint(style)
|
|
268
405
|
});
|
|
269
406
|
mlLayerIds.push(circleId);
|
|
407
|
+
const iconId = `${layerId}-icon`;
|
|
408
|
+
this.map.addLayer(buildIconLayer(iconId, sourceId));
|
|
409
|
+
mlLayerIds.push(iconId);
|
|
270
410
|
if (style?.text) {
|
|
271
411
|
const symbolId = `${layerId}-symbol`;
|
|
272
412
|
this.map.addLayer(buildSymbolLayer(symbolId, sourceId, style.text));
|
|
@@ -299,10 +439,29 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
299
439
|
features
|
|
300
440
|
});
|
|
301
441
|
}
|
|
442
|
+
applyIconResources(resources) {
|
|
443
|
+
if (typeof Image === "undefined") return;
|
|
444
|
+
for (const resource of resources) {
|
|
445
|
+
if (this.map.hasImage(resource.id) || this.pendingIconImages.has(resource.id)) continue;
|
|
446
|
+
this.pendingIconImages.add(resource.id);
|
|
447
|
+
const image = new Image();
|
|
448
|
+
image.onload = () => {
|
|
449
|
+
if (!this.pendingIconImages.has(resource.id)) return;
|
|
450
|
+
this.pendingIconImages.delete(resource.id);
|
|
451
|
+
if (!this.map.hasImage(resource.id)) this.map.addImage(resource.id, image);
|
|
452
|
+
};
|
|
453
|
+
image.onerror = () => this.pendingIconImages.delete(resource.id);
|
|
454
|
+
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(centeredSvg(resource))}`;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
removeIconResource(id) {
|
|
458
|
+
this.pendingIconImages.delete(id);
|
|
459
|
+
if (this.map.hasImage(id)) this.map.removeImage(id);
|
|
460
|
+
}
|
|
302
461
|
attachNativeEvent(type, onEvent) {
|
|
303
462
|
const mlEvent = type === "pointermove" ? "mousemove" : type === "dblclick" ? "dblclick" : "click";
|
|
304
463
|
const fn = (e) => {
|
|
305
|
-
onEvent([e.lngLat.lng, e.lngLat.lat], [e.point.x, e.point.y], e);
|
|
464
|
+
onEvent([this.toContinuousLng(e.lngLat.lng), e.lngLat.lat], [e.point.x, e.point.y], e);
|
|
306
465
|
};
|
|
307
466
|
this.map.on(mlEvent, fn);
|
|
308
467
|
return {
|
|
@@ -315,26 +474,32 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
315
474
|
}
|
|
316
475
|
attachViewChange(handler) {
|
|
317
476
|
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);
|
|
477
|
+
for (const event of this.viewChangeEvents()) this.map.on(event, fn);
|
|
322
478
|
return { fn };
|
|
323
479
|
}
|
|
324
480
|
detachViewChange(token) {
|
|
325
|
-
this.map.off(
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
this.
|
|
481
|
+
for (const event of this.viewChangeEvents()) this.map.off(event, token.fn);
|
|
482
|
+
}
|
|
483
|
+
viewChangeEvents() {
|
|
484
|
+
return this.viewChangeMode === "settle" ? ["moveend"] : [
|
|
485
|
+
"zoom",
|
|
486
|
+
"rotate",
|
|
487
|
+
"pitch",
|
|
488
|
+
"moveend"
|
|
489
|
+
];
|
|
329
490
|
}
|
|
330
491
|
createEditPointerDriver() {
|
|
331
492
|
const map = this.map;
|
|
493
|
+
const toContinuousLng = (lng) => this.toContinuousLng(lng);
|
|
332
494
|
const hub = new PointerCallbackHub();
|
|
333
495
|
let dragPanWasEnabled = false;
|
|
334
496
|
let disposed = false;
|
|
335
497
|
let touchDragActive = false;
|
|
336
498
|
let multiTouchCancelled = false;
|
|
337
|
-
const
|
|
499
|
+
const touchInfo = (e) => ({
|
|
500
|
+
hitTolerance: TOUCH_HIT_TOLERANCE_PX,
|
|
501
|
+
native: e.originalEvent
|
|
502
|
+
});
|
|
338
503
|
const preventMapEventDefaults = (e) => {
|
|
339
504
|
e.preventDefault();
|
|
340
505
|
e.originalEvent.preventDefault?.();
|
|
@@ -342,22 +507,32 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
342
507
|
};
|
|
343
508
|
const onDown = (e) => {
|
|
344
509
|
const px = [e.point.x, e.point.y];
|
|
345
|
-
const info = {
|
|
510
|
+
const info = {
|
|
511
|
+
altKey: e.originalEvent.altKey,
|
|
512
|
+
shiftKey: e.originalEvent.shiftKey,
|
|
513
|
+
native: e.originalEvent
|
|
514
|
+
};
|
|
346
515
|
hub.fireDown(px, info);
|
|
347
516
|
};
|
|
348
517
|
const onMove = (e) => {
|
|
349
518
|
const px = [e.point.x, e.point.y];
|
|
350
|
-
hub.fireMove(px
|
|
519
|
+
hub.fireMove(px, {
|
|
520
|
+
shiftKey: e.originalEvent.shiftKey,
|
|
521
|
+
native: e.originalEvent
|
|
522
|
+
});
|
|
351
523
|
};
|
|
352
524
|
const onUp = (e) => {
|
|
353
525
|
const px = [e.point.x, e.point.y];
|
|
354
|
-
hub.fireUp(px
|
|
526
|
+
hub.fireUp(px, {
|
|
527
|
+
shiftKey: e.originalEvent.shiftKey,
|
|
528
|
+
native: e.originalEvent
|
|
529
|
+
});
|
|
355
530
|
};
|
|
356
531
|
const onTouchStart = (e) => {
|
|
357
532
|
if (e.points.length !== 1) return;
|
|
358
533
|
const before = dragPanWasEnabled;
|
|
359
534
|
const px = [e.point.x, e.point.y];
|
|
360
|
-
hub.fireDown(px,
|
|
535
|
+
hub.fireDown(px, touchInfo(e));
|
|
361
536
|
touchDragActive = dragPanWasEnabled !== before;
|
|
362
537
|
if (touchDragActive) preventMapEventDefaults(e);
|
|
363
538
|
};
|
|
@@ -366,20 +541,20 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
366
541
|
const px = [e.point.x, e.point.y];
|
|
367
542
|
if (touchCount > 1 && touchDragActive && !multiTouchCancelled) {
|
|
368
543
|
multiTouchCancelled = true;
|
|
369
|
-
hub.fireUp(px,
|
|
544
|
+
hub.fireUp(px, touchInfo(e));
|
|
370
545
|
touchDragActive = false;
|
|
371
546
|
preventMapEventDefaults(e);
|
|
372
547
|
return;
|
|
373
548
|
}
|
|
374
549
|
if (touchCount > 1) return;
|
|
375
550
|
if (touchCount === 1) multiTouchCancelled = false;
|
|
376
|
-
hub.fireMove(px,
|
|
551
|
+
hub.fireMove(px, touchInfo(e));
|
|
377
552
|
if (touchDragActive) preventMapEventDefaults(e);
|
|
378
553
|
};
|
|
379
554
|
const onTouchEnd = (e) => {
|
|
380
555
|
multiTouchCancelled = false;
|
|
381
556
|
const px = [e.point.x, e.point.y];
|
|
382
|
-
hub.fireUp(px,
|
|
557
|
+
hub.fireUp(px, touchInfo(e));
|
|
383
558
|
touchDragActive = false;
|
|
384
559
|
};
|
|
385
560
|
map.on("mousedown", onDown);
|
|
@@ -403,12 +578,12 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
403
578
|
}
|
|
404
579
|
},
|
|
405
580
|
toPixel(lonLat) {
|
|
406
|
-
const p = map.project(lonLat);
|
|
581
|
+
const p = map.project([toContinuousLng(lonLat[0]), lonLat[1]]);
|
|
407
582
|
return [p.x, p.y];
|
|
408
583
|
},
|
|
409
584
|
fromPixel(pixel) {
|
|
410
585
|
const ll = map.unproject(pixel);
|
|
411
|
-
return [ll.lng, ll.lat];
|
|
586
|
+
return [toContinuousLng(ll.lng), ll.lat];
|
|
412
587
|
},
|
|
413
588
|
dispose() {
|
|
414
589
|
if (disposed) return;
|
|
@@ -472,9 +647,13 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
472
647
|
return project(position[0], position[1]);
|
|
473
648
|
}
|
|
474
649
|
getPixelFromCoordinate(position) {
|
|
475
|
-
const point = this.map.project(position);
|
|
650
|
+
const point = this.map.project([this.toContinuousLng(position[0]), position[1]]);
|
|
476
651
|
return [point.x, point.y];
|
|
477
652
|
}
|
|
653
|
+
getCoordinateFromPixel(pixel) {
|
|
654
|
+
const coordinate = this.map.unproject(pixel);
|
|
655
|
+
return [this.toContinuousLng(coordinate.lng), coordinate.lat];
|
|
656
|
+
}
|
|
478
657
|
getViewportSize() {
|
|
479
658
|
const container = this.map.getContainer();
|
|
480
659
|
return {
|
|
@@ -504,13 +683,14 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
504
683
|
else this.map.doubleClickZoom.disable();
|
|
505
684
|
}
|
|
506
685
|
getResolution() {
|
|
507
|
-
|
|
508
|
-
const center = this.map.getCenter();
|
|
509
|
-
return 40075016.686 * Math.cos(center.lat * Math.PI / 180) / Math.pow(2, zoom + 8);
|
|
686
|
+
return WEB_MERCATOR_CIRCUMFERENCE / Math.pow(2, this.map.getZoom() + 9);
|
|
510
687
|
}
|
|
511
688
|
getZoom() {
|
|
512
689
|
return this.map.getZoom();
|
|
513
690
|
}
|
|
691
|
+
getBearing() {
|
|
692
|
+
return this.map.getBearing() * (Math.PI / 180);
|
|
693
|
+
}
|
|
514
694
|
onPick(layerId, handler, opts) {
|
|
515
695
|
const onMlClick = (e) => {
|
|
516
696
|
const handle = this.layers.get(layerId);
|
|
@@ -534,7 +714,7 @@ var MapLibreAdapter = class extends BaseMapAdapter {
|
|
|
534
714
|
id: cmId,
|
|
535
715
|
feature: geojson,
|
|
536
716
|
pixel: [x, y],
|
|
537
|
-
coordinate: [e.lngLat.lng, e.lngLat.lat],
|
|
717
|
+
coordinate: [this.toContinuousLng(e.lngLat.lng), e.lngLat.lat],
|
|
538
718
|
originalEvent
|
|
539
719
|
});
|
|
540
720
|
};
|
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.11",
|
|
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.26"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"maplibre-gl": ">=4.0.0 <6.0.0"
|