@mapslibvn/web 0.4.0 → 0.5.0
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 +75 -9
- package/THIRD_PARTY_NOTICES.md +33 -2
- package/dist/index.d.ts +111 -3
- package/dist/index.js +501 -3
- package/dist/mapslibvn.umd.js +40 -40
- package/dist/mapslibvn.umd.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -16,6 +16,351 @@ function applyLanguage(gl, lang) {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// src/navigation.ts
|
|
20
|
+
import {
|
|
21
|
+
createNavigator
|
|
22
|
+
} from "@mapslibvn/core";
|
|
23
|
+
|
|
24
|
+
// src/position-source.ts
|
|
25
|
+
function toGeoFix(position) {
|
|
26
|
+
const c = position.coords;
|
|
27
|
+
return {
|
|
28
|
+
lng: c.longitude,
|
|
29
|
+
lat: c.latitude,
|
|
30
|
+
accuracy_m: c.accuracy,
|
|
31
|
+
heading: typeof c.heading === "number" && Number.isFinite(c.heading) ? c.heading : null,
|
|
32
|
+
speed_mps: typeof c.speed === "number" && Number.isFinite(c.speed) ? c.speed : null,
|
|
33
|
+
timestamp: Number.isFinite(position.timestamp) ? position.timestamp : Date.now()
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function toPositionError(error) {
|
|
37
|
+
const code = error.code === 1 ? "denied" : error.code === 3 ? "timeout" : "unavailable";
|
|
38
|
+
return { code, message: error.message || code, raw: error };
|
|
39
|
+
}
|
|
40
|
+
function geolocationSource(options = {}) {
|
|
41
|
+
const geo = "geolocation" in options ? options.geolocation : typeof navigator !== "undefined" ? navigator.geolocation : void 0;
|
|
42
|
+
const positionOptions = {
|
|
43
|
+
enableHighAccuracy: options.enableHighAccuracy ?? true,
|
|
44
|
+
maximumAge: options.maximumAge ?? 1e3,
|
|
45
|
+
timeout: options.timeout ?? 1e4
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
subscribe(onFix, onError) {
|
|
49
|
+
if (!geo) {
|
|
50
|
+
onError?.({ code: "unavailable", message: "Tr\xECnh duy\u1EC7t kh\xF4ng c\xF3 Geolocation" });
|
|
51
|
+
return () => {
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const id = geo.watchPosition(
|
|
55
|
+
(p) => onFix(toGeoFix(p)),
|
|
56
|
+
(e) => onError?.(toPositionError(e)),
|
|
57
|
+
positionOptions
|
|
58
|
+
);
|
|
59
|
+
return () => geo.clearWatch(id);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function playbackSource(fixes, options = {}) {
|
|
64
|
+
const rate = options.rate ?? 1;
|
|
65
|
+
return {
|
|
66
|
+
subscribe(onFix) {
|
|
67
|
+
let stopped = false;
|
|
68
|
+
let timer = null;
|
|
69
|
+
let i = 0;
|
|
70
|
+
const emitNext = () => {
|
|
71
|
+
if (stopped) return;
|
|
72
|
+
const fix = fixes[i];
|
|
73
|
+
if (!fix) return;
|
|
74
|
+
onFix(fix);
|
|
75
|
+
i += 1;
|
|
76
|
+
const next = fixes[i];
|
|
77
|
+
if (!next) return;
|
|
78
|
+
timer = setTimeout(emitNext, Math.max(0, (next.timestamp - fix.timestamp) / rate));
|
|
79
|
+
};
|
|
80
|
+
timer = setTimeout(
|
|
81
|
+
rate <= 0 ? () => {
|
|
82
|
+
for (const f of fixes) {
|
|
83
|
+
if (stopped) break;
|
|
84
|
+
onFix(f);
|
|
85
|
+
}
|
|
86
|
+
} : emitNext,
|
|
87
|
+
0
|
|
88
|
+
);
|
|
89
|
+
return () => {
|
|
90
|
+
stopped = true;
|
|
91
|
+
if (timer !== null) clearTimeout(timer);
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/speech.ts
|
|
98
|
+
var BCP47 = { vi: "vi-VN", en: "en-US" };
|
|
99
|
+
function createSpeech(opts) {
|
|
100
|
+
const synth = "synth" in opts ? opts.synth : typeof speechSynthesis !== "undefined" ? speechSynthesis : void 0;
|
|
101
|
+
const prefix = opts.lang;
|
|
102
|
+
let voice = null;
|
|
103
|
+
let voicesKnown = false;
|
|
104
|
+
let reported = false;
|
|
105
|
+
let current = 0;
|
|
106
|
+
const reportUnavailable = () => {
|
|
107
|
+
if (reported) return;
|
|
108
|
+
reported = true;
|
|
109
|
+
opts.onUnavailable?.();
|
|
110
|
+
};
|
|
111
|
+
const pickVoice = () => {
|
|
112
|
+
if (!synth) return;
|
|
113
|
+
const voices = synth.getVoices();
|
|
114
|
+
if (voices.length === 0) return;
|
|
115
|
+
voicesKnown = true;
|
|
116
|
+
voice = voices.find((v) => v.lang.toLowerCase().replace("_", "-").startsWith(prefix)) ?? null;
|
|
117
|
+
if (!voice) reportUnavailable();
|
|
118
|
+
};
|
|
119
|
+
if (!synth) reportUnavailable();
|
|
120
|
+
else {
|
|
121
|
+
pickVoice();
|
|
122
|
+
if (!voicesKnown && typeof synth.addEventListener === "function") {
|
|
123
|
+
synth.addEventListener("voiceschanged", pickVoice, { once: true });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const utterance = (text) => {
|
|
127
|
+
const u = new SpeechSynthesisUtterance(text);
|
|
128
|
+
u.lang = BCP47[opts.lang];
|
|
129
|
+
if (voice) u.voice = voice;
|
|
130
|
+
u.rate = opts.rate ?? 1;
|
|
131
|
+
u.volume = opts.volume ?? 1;
|
|
132
|
+
return u;
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
get available() {
|
|
136
|
+
return Boolean(synth) && (!voicesKnown || voice !== null);
|
|
137
|
+
},
|
|
138
|
+
speak(text, priority) {
|
|
139
|
+
if (!synth || !text) return;
|
|
140
|
+
if (voicesKnown && !voice) return;
|
|
141
|
+
if ((synth.speaking || synth.pending) && priority >= current) synth.cancel();
|
|
142
|
+
const u = utterance(text);
|
|
143
|
+
current = priority;
|
|
144
|
+
u.onend = () => {
|
|
145
|
+
current = 0;
|
|
146
|
+
};
|
|
147
|
+
u.onerror = () => {
|
|
148
|
+
current = 0;
|
|
149
|
+
};
|
|
150
|
+
synth.speak(u);
|
|
151
|
+
},
|
|
152
|
+
warmUp() {
|
|
153
|
+
if (!synth) return;
|
|
154
|
+
const u = utterance("");
|
|
155
|
+
u.volume = 0;
|
|
156
|
+
synth.speak(u);
|
|
157
|
+
},
|
|
158
|
+
cancel() {
|
|
159
|
+
synth?.cancel();
|
|
160
|
+
current = 0;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/navigation.ts
|
|
166
|
+
var FOLLOW_ZOOM = {
|
|
167
|
+
walk: 17,
|
|
168
|
+
motorbike: 16.5,
|
|
169
|
+
car: 15.5
|
|
170
|
+
};
|
|
171
|
+
var FOLLOW_PITCH = 45;
|
|
172
|
+
var PUCK_STYLE = "width:0;height:0;border-left:11px solid transparent;border-right:11px solid transparent;border-bottom:26px solid #2458a6;filter:drop-shadow(0 0 2px #fff) drop-shadow(0 1px 3px rgba(0,0,0,.5));";
|
|
173
|
+
var NAV_EVENTS = [
|
|
174
|
+
"status",
|
|
175
|
+
"progress",
|
|
176
|
+
"step",
|
|
177
|
+
"waypoint",
|
|
178
|
+
"offRoute",
|
|
179
|
+
"reroute",
|
|
180
|
+
"rerouteFailed",
|
|
181
|
+
"announce",
|
|
182
|
+
"arrive"
|
|
183
|
+
];
|
|
184
|
+
function createNavigation(deps) {
|
|
185
|
+
const { gl, ml, routes } = deps;
|
|
186
|
+
const doc = "document" in deps ? deps.document : typeof document !== "undefined" ? document : void 0;
|
|
187
|
+
const wakeLockApi = "wakeLock" in deps ? deps.wakeLock : typeof navigator !== "undefined" ? navigator.wakeLock : void 0;
|
|
188
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
189
|
+
const emit = (k, e) => {
|
|
190
|
+
for (const fn of listeners.get(k) ?? []) fn(e);
|
|
191
|
+
};
|
|
192
|
+
let nav = null;
|
|
193
|
+
let unsubscribe = null;
|
|
194
|
+
let speech = null;
|
|
195
|
+
let puck = null;
|
|
196
|
+
let puckOnMap = false;
|
|
197
|
+
let running = false;
|
|
198
|
+
let following = false;
|
|
199
|
+
let follow = null;
|
|
200
|
+
let sentinel = null;
|
|
201
|
+
let lastFixTs = null;
|
|
202
|
+
const onUserMove = () => {
|
|
203
|
+
if (!following) return;
|
|
204
|
+
following = false;
|
|
205
|
+
emit("followChange", false);
|
|
206
|
+
};
|
|
207
|
+
const camera = (p) => {
|
|
208
|
+
if (!follow) return;
|
|
209
|
+
const dt = lastFixTs === null ? 500 : p.fix.timestamp - lastFixTs;
|
|
210
|
+
lastFixTs = p.fix.timestamp;
|
|
211
|
+
const options = {
|
|
212
|
+
center: p.snapped,
|
|
213
|
+
bearing: p.bearing,
|
|
214
|
+
zoom: follow.zoom,
|
|
215
|
+
pitch: follow.pitch,
|
|
216
|
+
duration: Math.max(0, Math.min(1e3, dt))
|
|
217
|
+
};
|
|
218
|
+
if (follow.padding) options.padding = follow.padding;
|
|
219
|
+
gl.easeTo(options);
|
|
220
|
+
};
|
|
221
|
+
const requestWakeLock = async () => {
|
|
222
|
+
if (!wakeLockApi) return;
|
|
223
|
+
try {
|
|
224
|
+
sentinel = await wakeLockApi.request("screen");
|
|
225
|
+
} catch {
|
|
226
|
+
sentinel = null;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const releaseWakeLock = () => {
|
|
230
|
+
void sentinel?.release();
|
|
231
|
+
sentinel = null;
|
|
232
|
+
};
|
|
233
|
+
const onVisibility = () => {
|
|
234
|
+
if (running && doc?.visibilityState === "visible" && sentinel === null) void requestWakeLock();
|
|
235
|
+
};
|
|
236
|
+
function stop() {
|
|
237
|
+
if (!running) return;
|
|
238
|
+
running = false;
|
|
239
|
+
unsubscribe?.();
|
|
240
|
+
unsubscribe = null;
|
|
241
|
+
speech?.cancel();
|
|
242
|
+
speech = null;
|
|
243
|
+
releaseWakeLock();
|
|
244
|
+
doc?.removeEventListener("visibilitychange", onVisibility);
|
|
245
|
+
gl.off("dragstart", onUserMove);
|
|
246
|
+
gl.off("wheel", onUserMove);
|
|
247
|
+
puck?.remove();
|
|
248
|
+
puck = null;
|
|
249
|
+
puckOnMap = false;
|
|
250
|
+
nav?.stop();
|
|
251
|
+
nav = null;
|
|
252
|
+
lastFixTs = null;
|
|
253
|
+
following = false;
|
|
254
|
+
follow = null;
|
|
255
|
+
}
|
|
256
|
+
function start(opts) {
|
|
257
|
+
if (running) stop();
|
|
258
|
+
running = true;
|
|
259
|
+
const lang = opts.lang ?? deps.lang;
|
|
260
|
+
const routeIndex = opts.routeIndex ?? 0;
|
|
261
|
+
const navOptions = {
|
|
262
|
+
response: opts.response,
|
|
263
|
+
routeIndex,
|
|
264
|
+
provider: opts.provider ?? deps.places,
|
|
265
|
+
reroute: opts.reroute ?? "auto",
|
|
266
|
+
lang
|
|
267
|
+
};
|
|
268
|
+
if (opts.thresholds) navOptions.thresholds = opts.thresholds;
|
|
269
|
+
const engine = createNavigator(navOptions);
|
|
270
|
+
nav = engine;
|
|
271
|
+
routes.show(opts.response, { active: routeIndex });
|
|
272
|
+
if (opts.voice !== false) {
|
|
273
|
+
const v = typeof opts.voice === "object" ? opts.voice : {};
|
|
274
|
+
const speechOptions = {
|
|
275
|
+
lang,
|
|
276
|
+
onUnavailable: () => emit("voiceUnavailable", void 0)
|
|
277
|
+
};
|
|
278
|
+
if (v.rate !== void 0) speechOptions.rate = v.rate;
|
|
279
|
+
if (v.volume !== void 0) speechOptions.volume = v.volume;
|
|
280
|
+
speech = createSpeech(speechOptions);
|
|
281
|
+
speech.warmUp();
|
|
282
|
+
}
|
|
283
|
+
if (opts.follow !== false) {
|
|
284
|
+
const f = typeof opts.follow === "object" ? opts.follow : {};
|
|
285
|
+
const mode = opts.response.routes[routeIndex]?.mode ?? "motorbike";
|
|
286
|
+
follow = { zoom: f.zoom ?? FOLLOW_ZOOM[mode], pitch: f.pitch ?? FOLLOW_PITCH };
|
|
287
|
+
if (f.padding) follow.padding = f.padding;
|
|
288
|
+
following = true;
|
|
289
|
+
gl.on("dragstart", onUserMove);
|
|
290
|
+
gl.on("wheel", onUserMove);
|
|
291
|
+
}
|
|
292
|
+
const el = doc?.createElement("div");
|
|
293
|
+
if (el) {
|
|
294
|
+
el.setAttribute("style", PUCK_STYLE);
|
|
295
|
+
el.setAttribute("aria-hidden", "true");
|
|
296
|
+
puck = new ml.Marker({ element: el, rotationAlignment: "map", pitchAlignment: "map" });
|
|
297
|
+
}
|
|
298
|
+
engine.on("progress", (p) => {
|
|
299
|
+
routes.setProgress(p.shapeIndex, p.snapped);
|
|
300
|
+
if (puck) {
|
|
301
|
+
puck.setLngLat(p.snapped).setRotation(p.bearing);
|
|
302
|
+
if (!puckOnMap) {
|
|
303
|
+
puck.addTo(gl);
|
|
304
|
+
puckOnMap = true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (following) camera(p);
|
|
308
|
+
});
|
|
309
|
+
engine.on("announce", (a) => speech?.speak(a.text, a.priority));
|
|
310
|
+
engine.on("reroute", (e) => routes.show(e.response, { active: 0 }));
|
|
311
|
+
engine.on("arrive", () => {
|
|
312
|
+
unsubscribe?.();
|
|
313
|
+
unsubscribe = null;
|
|
314
|
+
releaseWakeLock();
|
|
315
|
+
});
|
|
316
|
+
for (const name of NAV_EVENTS) {
|
|
317
|
+
engine.on(name, (e) => emit(name, e));
|
|
318
|
+
}
|
|
319
|
+
const source = opts.source ?? geolocationSource();
|
|
320
|
+
unsubscribe = source.subscribe(
|
|
321
|
+
(fix) => engine.update(fix),
|
|
322
|
+
(error) => emit("positionError", error)
|
|
323
|
+
);
|
|
324
|
+
if (opts.wakeLock !== false) {
|
|
325
|
+
void requestWakeLock();
|
|
326
|
+
doc?.addEventListener("visibilitychange", onVisibility);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
start,
|
|
331
|
+
stop,
|
|
332
|
+
recenter() {
|
|
333
|
+
if (!running || !follow) return;
|
|
334
|
+
following = true;
|
|
335
|
+
emit("followChange", true);
|
|
336
|
+
if (nav?.progress) camera(nav.progress);
|
|
337
|
+
},
|
|
338
|
+
reroute() {
|
|
339
|
+
return nav ? nav.reroute() : Promise.reject(new Error("map.navigation ch\u01B0a start()"));
|
|
340
|
+
},
|
|
341
|
+
get state() {
|
|
342
|
+
return nav?.progress ?? null;
|
|
343
|
+
},
|
|
344
|
+
get status() {
|
|
345
|
+
return nav?.status ?? "idle";
|
|
346
|
+
},
|
|
347
|
+
get following() {
|
|
348
|
+
return following;
|
|
349
|
+
},
|
|
350
|
+
on(event, handler) {
|
|
351
|
+
let set = listeners.get(event);
|
|
352
|
+
if (!set) {
|
|
353
|
+
set = /* @__PURE__ */ new Set();
|
|
354
|
+
listeners.set(event, set);
|
|
355
|
+
}
|
|
356
|
+
set.add(handler);
|
|
357
|
+
},
|
|
358
|
+
off(event, handler) {
|
|
359
|
+
listeners.get(event)?.delete(handler);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
19
364
|
// src/protocol.ts
|
|
20
365
|
import { Protocol } from "pmtiles";
|
|
21
366
|
var registered = false;
|
|
@@ -25,6 +370,130 @@ function ensurePmtilesProtocol(host) {
|
|
|
25
370
|
registered = true;
|
|
26
371
|
}
|
|
27
372
|
|
|
373
|
+
// src/routes-layer.ts
|
|
374
|
+
import {
|
|
375
|
+
EMPTY_ROUTE_FEATURES,
|
|
376
|
+
decodeRoutes,
|
|
377
|
+
routeFeatures
|
|
378
|
+
} from "@mapslibvn/core";
|
|
379
|
+
var ROUTE_SOURCE_ID = "mapslibvn-route";
|
|
380
|
+
var ROUTE_LAYER_IDS = {
|
|
381
|
+
alt: "mapslibvn-route-alt",
|
|
382
|
+
casing: "mapslibvn-route-casing",
|
|
383
|
+
line: "mapslibvn-route-line",
|
|
384
|
+
traveled: "mapslibvn-route-traveled"
|
|
385
|
+
};
|
|
386
|
+
var ROUTE_COLOR = "#2458a6";
|
|
387
|
+
var ALT_COLOR = "#9ca8ba";
|
|
388
|
+
var DESTINATION_COLOR = "#d92d20";
|
|
389
|
+
function createRoutesLayer(gl, ml, onRouteClick) {
|
|
390
|
+
let response = null;
|
|
391
|
+
let coords = [];
|
|
392
|
+
let active = 0;
|
|
393
|
+
let showMarkers = true;
|
|
394
|
+
let progress = null;
|
|
395
|
+
let markers = [];
|
|
396
|
+
let clickBound = false;
|
|
397
|
+
const firstSymbolLayerId = () => gl.getStyle()?.layers?.find((layer) => layer.type === "symbol")?.id;
|
|
398
|
+
const addLine = (id, kind, paint, before) => {
|
|
399
|
+
gl.addLayer(
|
|
400
|
+
{
|
|
401
|
+
id,
|
|
402
|
+
type: "line",
|
|
403
|
+
source: ROUTE_SOURCE_ID,
|
|
404
|
+
filter: ["==", ["get", "kind"], kind],
|
|
405
|
+
layout: { "line-join": "round", "line-cap": "round" },
|
|
406
|
+
paint
|
|
407
|
+
},
|
|
408
|
+
before
|
|
409
|
+
);
|
|
410
|
+
};
|
|
411
|
+
const ensureLayers = () => {
|
|
412
|
+
if (gl.getSource(ROUTE_SOURCE_ID)) return;
|
|
413
|
+
gl.addSource(ROUTE_SOURCE_ID, { type: "geojson", data: EMPTY_ROUTE_FEATURES });
|
|
414
|
+
const before = firstSymbolLayerId();
|
|
415
|
+
addLine(ROUTE_LAYER_IDS.alt, "alt", { "line-color": ALT_COLOR, "line-width": 5 }, before);
|
|
416
|
+
addLine(ROUTE_LAYER_IDS.casing, "active", { "line-color": "#ffffff", "line-width": 9 }, before);
|
|
417
|
+
addLine(ROUTE_LAYER_IDS.line, "active", { "line-color": ROUTE_COLOR, "line-width": 6 }, before);
|
|
418
|
+
addLine(
|
|
419
|
+
ROUTE_LAYER_IDS.traveled,
|
|
420
|
+
"traveled",
|
|
421
|
+
{ "line-color": ROUTE_COLOR, "line-width": 6, "line-opacity": 0.35 },
|
|
422
|
+
before
|
|
423
|
+
);
|
|
424
|
+
if (!clickBound) {
|
|
425
|
+
clickBound = true;
|
|
426
|
+
gl.on("click", ROUTE_LAYER_IDS.alt, (e) => {
|
|
427
|
+
const index = e.features?.[0]?.properties?.index;
|
|
428
|
+
if (typeof index === "number") onRouteClick(index);
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
const setData = () => {
|
|
433
|
+
const source = gl.getSource(ROUTE_SOURCE_ID);
|
|
434
|
+
const data = response ? routeFeatures(coords, { active, progress }) : EMPTY_ROUTE_FEATURES;
|
|
435
|
+
source?.setData(data);
|
|
436
|
+
};
|
|
437
|
+
const apply = () => {
|
|
438
|
+
if (!response) return;
|
|
439
|
+
if (!gl.isStyleLoaded()) {
|
|
440
|
+
gl.once("style.load", apply);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
ensureLayers();
|
|
444
|
+
setData();
|
|
445
|
+
};
|
|
446
|
+
const clearMarkers = () => {
|
|
447
|
+
for (const m of markers) m.remove();
|
|
448
|
+
markers = [];
|
|
449
|
+
};
|
|
450
|
+
const placeMarkers = () => {
|
|
451
|
+
clearMarkers();
|
|
452
|
+
if (!response || !showMarkers) return;
|
|
453
|
+
const last = response.waypoints.length - 1;
|
|
454
|
+
response.waypoints.forEach((w, i) => {
|
|
455
|
+
if (i === 0) return;
|
|
456
|
+
markers.push(
|
|
457
|
+
new ml.Marker({ color: i === last ? DESTINATION_COLOR : ALT_COLOR }).setLngLat(w.snapped).addTo(gl)
|
|
458
|
+
);
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
gl.on("style.load", () => {
|
|
462
|
+
if (response && !gl.getSource(ROUTE_SOURCE_ID)) {
|
|
463
|
+
ensureLayers();
|
|
464
|
+
setData();
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
return {
|
|
468
|
+
show(next, opts = {}) {
|
|
469
|
+
response = next;
|
|
470
|
+
coords = decodeRoutes(next);
|
|
471
|
+
active = opts.active ?? 0;
|
|
472
|
+
showMarkers = opts.markers ?? true;
|
|
473
|
+
progress = null;
|
|
474
|
+
apply();
|
|
475
|
+
placeMarkers();
|
|
476
|
+
},
|
|
477
|
+
setActive(index) {
|
|
478
|
+
active = index;
|
|
479
|
+
progress = null;
|
|
480
|
+
setData();
|
|
481
|
+
},
|
|
482
|
+
setProgress(shapeIndex, snapped) {
|
|
483
|
+
progress = { shapeIndex, snapped };
|
|
484
|
+
setData();
|
|
485
|
+
},
|
|
486
|
+
clear() {
|
|
487
|
+
response = null;
|
|
488
|
+
coords = [];
|
|
489
|
+
progress = null;
|
|
490
|
+
clearMarkers();
|
|
491
|
+
const source = gl.getSource(ROUTE_SOURCE_ID);
|
|
492
|
+
source?.setData(EMPTY_ROUTE_FEATURES);
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
28
497
|
// src/map.ts
|
|
29
498
|
var isTheme = (s) => s === "light" || s === "dark";
|
|
30
499
|
function createMap(opts, deps) {
|
|
@@ -57,11 +526,14 @@ function createMap(opts, deps) {
|
|
|
57
526
|
);
|
|
58
527
|
const listeners = {
|
|
59
528
|
poiClick: /* @__PURE__ */ new Set(),
|
|
60
|
-
load: /* @__PURE__ */ new Set()
|
|
529
|
+
load: /* @__PURE__ */ new Set(),
|
|
530
|
+
routeClick: /* @__PURE__ */ new Set()
|
|
61
531
|
};
|
|
62
532
|
const emit = (k, e) => {
|
|
63
533
|
for (const fn of listeners[k]) fn(e);
|
|
64
534
|
};
|
|
535
|
+
const routes = createRoutesLayer(gl, ml, (index) => emit("routeClick", { index }));
|
|
536
|
+
const navigation = createNavigation({ gl, ml, places, routes, lang: opts.lang ?? "vi" });
|
|
65
537
|
gl.on("load", () => {
|
|
66
538
|
if (opts.lang && opts.lang !== "vi") applyLanguage(gl, opts.lang);
|
|
67
539
|
if (opts.poiLayer === false) {
|
|
@@ -89,6 +561,8 @@ function createMap(opts, deps) {
|
|
|
89
561
|
return {
|
|
90
562
|
gl,
|
|
91
563
|
places,
|
|
564
|
+
routes,
|
|
565
|
+
navigation,
|
|
92
566
|
addMarker(o) {
|
|
93
567
|
const marker = new ml.Marker(o.color ? { color: o.color } : void 0).setLngLat([
|
|
94
568
|
o.lng,
|
|
@@ -116,6 +590,8 @@ function createMap(opts, deps) {
|
|
|
116
590
|
listeners[event].delete(handler);
|
|
117
591
|
},
|
|
118
592
|
remove() {
|
|
593
|
+
navigation.stop();
|
|
594
|
+
routes.clear();
|
|
119
595
|
gl.remove();
|
|
120
596
|
}
|
|
121
597
|
};
|
|
@@ -353,15 +829,37 @@ function defineAutocomplete() {
|
|
|
353
829
|
}
|
|
354
830
|
|
|
355
831
|
// src/index.ts
|
|
356
|
-
import {
|
|
832
|
+
import {
|
|
833
|
+
attributionHtml as attributionHtml2,
|
|
834
|
+
attributionText,
|
|
835
|
+
createClient as createClient3,
|
|
836
|
+
createNavigator as createNavigator2,
|
|
837
|
+
formatDistance,
|
|
838
|
+
formatDistanceShort,
|
|
839
|
+
MapsLibVNError,
|
|
840
|
+
NAVIGATION_THRESHOLDS,
|
|
841
|
+
simulateFixes
|
|
842
|
+
} from "@mapslibvn/core";
|
|
357
843
|
export {
|
|
844
|
+
FOLLOW_ZOOM,
|
|
358
845
|
MapsLibVNAutocomplete,
|
|
359
846
|
MapsLibVNError,
|
|
847
|
+
NAVIGATION_THRESHOLDS,
|
|
848
|
+
ROUTE_LAYER_IDS,
|
|
849
|
+
ROUTE_SOURCE_ID,
|
|
360
850
|
applyLanguage,
|
|
361
851
|
attributionHtml2 as attributionHtml,
|
|
362
852
|
attributionText,
|
|
363
853
|
createClient3 as createClient,
|
|
364
854
|
createMap,
|
|
855
|
+
createNavigator2 as createNavigator,
|
|
856
|
+
createSpeech,
|
|
365
857
|
defineAutocomplete,
|
|
366
|
-
|
|
858
|
+
formatDistance,
|
|
859
|
+
formatDistanceShort,
|
|
860
|
+
geolocationSource,
|
|
861
|
+
nameExpression,
|
|
862
|
+
playbackSource,
|
|
863
|
+
simulateFixes,
|
|
864
|
+
toGeoFix
|
|
367
865
|
};
|