@mapslibvn/react-native 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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1
+ import {
2
+ __publicField
3
+ } from "./chunk-PKBMQBKP.js";
4
4
 
5
5
  // src/map.tsx
6
6
  import {
@@ -70,6 +70,7 @@ function normalizePoiSources(list) {
70
70
  function poiSourcesKey(sources) {
71
71
  return (normalizePoiSources(sources) ?? []).join(",");
72
72
  }
73
+ var latLng = ([lat, lng]) => `${lat},${lng}`;
73
74
  function createClient(options) {
74
75
  const baseUrl = options.baseUrl.replace(/\/+$/, "");
75
76
  const poiSources = normalizePoiSources(options.poiSources ?? DEFAULT_POI_SOURCES);
@@ -152,6 +153,15 @@ function createClient(options) {
152
153
  limit: opts.limit
153
154
  }),
154
155
  reverse: (lat, lng) => get("/v1/reverse", { lat, lng, sources }),
156
+ /** Chỉ đường (spec dẫn đường A). Response dùng [lng, lat]; tham số vào dùng [lat, lng]. */
157
+ directions: (opts) => get("/v1/directions", {
158
+ from: latLng(opts.from),
159
+ to: latLng(opts.to),
160
+ via: opts.via && opts.via.length > 0 ? opts.via.map(latLng).join(";") : void 0,
161
+ mode: opts.mode,
162
+ lang: opts.lang,
163
+ alternatives: opts.alternatives === void 0 ? void 0 : opts.alternatives ? 1 : 0
164
+ }),
155
165
  /** Gửi đóng góp/sửa POI (spec 6.1). Khoá phải có scope edits:write. */
156
166
  suggestEdit: (edit) => post("/v1/edits", edit)
157
167
  };
@@ -280,6 +290,10 @@ var RE_STREET_LIKE = new RegExp(
280
290
  String.raw`^${PREFIX}${NUM_LEAD}(?:/|\s|$)|\b${ALLEY_KW}\s*\d|^(?:duong|d\.|pho)\s`
281
291
  );
282
292
  var POI_LAYER_ID = "poi";
293
+ var FIRST_SYMBOL_LAYER_ID = {
294
+ light: "road_one_way_arrow",
295
+ dark: "water_name"
296
+ };
283
297
  var SOVEREIGNTY_LABEL_ID = "sovereignty-label";
284
298
  function isPoiStyleLayer(layer) {
285
299
  return layer.id === POI_LAYER_ID || layer.source === "poi";
@@ -426,12 +440,701 @@ var compile = (rules) => rules.map(([pattern, replacement]) => ({ re: new RegExp
426
440
  var WORD_START = compile(RULES2.wordStart);
427
441
  var WORD_END = compile(RULES2.wordEnd);
428
442
  var ANYWHERE = compile(RULES2.anywhere);
443
+ function decodePolyline6(encoded) {
444
+ const coords = [];
445
+ let index = 0;
446
+ let lat = 0;
447
+ let lng = 0;
448
+ const next = () => {
449
+ let result = 0;
450
+ let shift = 0;
451
+ let byte;
452
+ do {
453
+ byte = encoded.charCodeAt(index++) - 63;
454
+ result |= (byte & 31) << shift;
455
+ shift += 5;
456
+ } while (byte >= 32);
457
+ return result & 1 ? ~(result >> 1) : result >> 1;
458
+ };
459
+ while (index < encoded.length) {
460
+ lat += next();
461
+ lng += next();
462
+ coords.push([lng / 1e6, lat / 1e6]);
463
+ }
464
+ return coords;
465
+ }
466
+ var NAVIGATION_THRESHOLDS = {
467
+ walk: {
468
+ offRoute_m: 25,
469
+ offRouteFixes: 3,
470
+ offRouteSeconds: 5,
471
+ maxAccuracy_m: 60,
472
+ approach_m: 40,
473
+ pre_m: 15,
474
+ arrive_m: 15,
475
+ rerouteCooldown_s: 15,
476
+ rerouteMaxFailures: 3
477
+ },
478
+ motorbike: {
479
+ offRoute_m: 40,
480
+ offRouteFixes: 3,
481
+ offRouteSeconds: 5,
482
+ maxAccuracy_m: 100,
483
+ approach_m: 200,
484
+ pre_m: 50,
485
+ arrive_m: 25,
486
+ rerouteCooldown_s: 15,
487
+ rerouteMaxFailures: 3
488
+ },
489
+ car: {
490
+ offRoute_m: 50,
491
+ offRouteFixes: 3,
492
+ offRouteSeconds: 5,
493
+ maxAccuracy_m: 100,
494
+ approach_m: 400,
495
+ pre_m: 80,
496
+ arrive_m: 30,
497
+ rerouteCooldown_s: 15,
498
+ rerouteMaxFailures: 3
499
+ }
500
+ };
501
+ var EARTH_RADIUS_M = 63710088e-1;
502
+ var M_PER_DEG_LAT = Math.PI / 180 * EARTH_RADIUS_M;
503
+ var toRad = (deg) => deg * Math.PI / 180;
504
+ var toDeg = (rad) => rad * 180 / Math.PI;
505
+ function haversineM(a, b) {
506
+ const dLat = toRad(b[1] - a[1]);
507
+ const dLng = toRad(b[0] - a[0]);
508
+ const h = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(a[1])) * Math.cos(toRad(b[1])) * Math.sin(dLng / 2) ** 2;
509
+ return 2 * EARTH_RADIUS_M * Math.asin(Math.min(1, Math.sqrt(h)));
510
+ }
511
+ function bearingDeg(a, b) {
512
+ const phi1 = toRad(a[1]);
513
+ const phi2 = toRad(b[1]);
514
+ const dLambda = toRad(b[0] - a[0]);
515
+ const y = Math.sin(dLambda) * Math.cos(phi2);
516
+ const x = Math.cos(phi1) * Math.sin(phi2) - Math.sin(phi1) * Math.cos(phi2) * Math.cos(dLambda);
517
+ return (toDeg(Math.atan2(y, x)) + 360) % 360;
518
+ }
519
+ function angleDiffDeg(a, b) {
520
+ const d = Math.abs(((a - b) % 360 + 360) % 360);
521
+ return d > 180 ? 360 - d : d;
522
+ }
523
+ function projectOnSegment(p, a, b) {
524
+ const mPerDegLng = M_PER_DEG_LAT * Math.cos(toRad(a[1]));
525
+ const bx = (b[0] - a[0]) * mPerDegLng;
526
+ const by = (b[1] - a[1]) * M_PER_DEG_LAT;
527
+ const px = (p[0] - a[0]) * mPerDegLng;
528
+ const py = (p[1] - a[1]) * M_PER_DEG_LAT;
529
+ const len2 = bx * bx + by * by;
530
+ const t = len2 === 0 ? 0 : Math.max(0, Math.min(1, (px * bx + py * by) / len2));
531
+ const qx = bx * t;
532
+ const qy = by * t;
533
+ const point = t === 1 ? [b[0], b[1]] : [a[0] + qx / mPerDegLng, a[1] + qy / M_PER_DEG_LAT];
534
+ return { t, point, distance_m: Math.hypot(px - qx, py - qy) };
535
+ }
536
+ function cumulativeDistances(coords) {
537
+ const cum = [];
538
+ let total = 0;
539
+ for (let i = 0; i < coords.length; i++) {
540
+ const prev = coords[i - 1];
541
+ const cur = coords[i];
542
+ if (i > 0 && prev && cur) total += haversineM(prev, cur);
543
+ cum.push(total);
544
+ }
545
+ return cum;
546
+ }
547
+ function buildRouteIndex(route) {
548
+ const coords = decodePolyline6(route.geometry);
549
+ const cum = cumulativeDistances(coords);
550
+ const total_m = cum[cum.length - 1] ?? 0;
551
+ const at = (vertex) => cum[Math.min(Math.max(vertex, 0), cum.length - 1)] ?? 0;
552
+ const steps = [];
553
+ route.legs.forEach((leg, legIndex) => {
554
+ leg.steps.forEach((step, indexInLeg) => {
555
+ const begin_m = at(step.shape_begin);
556
+ steps.push({ step, legIndex, indexInLeg, begin_m, end_m: begin_m });
557
+ });
558
+ });
559
+ for (let i = 0; i < steps.length; i++) {
560
+ const current = steps[i];
561
+ if (current) current.end_m = steps[i + 1]?.begin_m ?? total_m;
562
+ }
563
+ return { coords, cum, total_m, steps, legBegin_m: route.legs.map((leg) => at(leg.shape_offset)) };
564
+ }
565
+ function stepAt(index, along_m) {
566
+ const { steps, total_m } = index;
567
+ if (steps.length === 0) return 0;
568
+ if (along_m >= total_m) return steps.length - 1;
569
+ let found = 0;
570
+ for (let i = 0; i < steps.length; i++) {
571
+ const s = steps[i];
572
+ if (!s) continue;
573
+ if (s.begin_m > along_m) break;
574
+ if (along_m < s.end_m) {
575
+ found = i;
576
+ break;
577
+ }
578
+ found = i;
579
+ }
580
+ return found;
581
+ }
582
+ function progressAt(index, along_m) {
583
+ const clamped = Math.max(0, Math.min(along_m, index.total_m));
584
+ const stepIndex = stepAt(index, clamped);
585
+ const current = index.steps[stepIndex];
586
+ const next = index.steps[stepIndex + 1];
587
+ let remaining_s = 0;
588
+ if (current) {
589
+ const length = current.end_m - current.begin_m;
590
+ const fraction = length > 0 ? Math.max(0, Math.min(1, (current.end_m - clamped) / length)) : 0;
591
+ remaining_s += fraction * current.step.duration_s;
592
+ }
593
+ for (let i = stepIndex + 1; i < index.steps.length; i++) {
594
+ remaining_s += index.steps[i]?.step.duration_s ?? 0;
595
+ }
596
+ return {
597
+ stepIndex,
598
+ legIndex: current?.legIndex ?? 0,
599
+ distanceToStep_m: next ? Math.max(0, next.begin_m - clamped) : 0,
600
+ remaining_m: Math.max(0, index.total_m - clamped),
601
+ remaining_s: Math.round(remaining_s)
602
+ };
603
+ }
604
+ var TIE_M = 10;
605
+ var LOOKBACK_SEGMENTS = 2;
606
+ function snapToRoute(index, p, opts) {
607
+ const { coords, cum } = index;
608
+ const segments = coords.length - 1;
609
+ if (segments < 1) return null;
610
+ let lo = 0;
611
+ let hi = segments - 1;
612
+ if (opts.fromShapeIndex !== null) {
613
+ const from = Math.max(0, Math.min(opts.fromShapeIndex, segments - 1));
614
+ lo = Math.max(0, from - LOOKBACK_SEGMENTS);
615
+ const limit = (cum[from] ?? 0) + opts.window_m;
616
+ hi = from;
617
+ for (let i = from + 1; i < segments; i++) {
618
+ if ((cum[i] ?? 0) <= limit) hi = i;
619
+ else break;
620
+ }
621
+ }
622
+ const heading = typeof opts.heading === "number" && Number.isFinite(opts.heading) ? opts.heading : null;
623
+ let best = null;
624
+ for (let i = lo; i <= hi; i++) {
625
+ const a = coords[i];
626
+ const b = coords[i + 1];
627
+ if (!a || !b) continue;
628
+ const proj = projectOnSegment(p, a, b);
629
+ const segStart = cum[i] ?? 0;
630
+ const segEnd = cum[i + 1] ?? segStart;
631
+ const candidate = {
632
+ shapeIndex: i,
633
+ t: proj.t,
634
+ point: proj.point,
635
+ distance_m: proj.distance_m,
636
+ along_m: segStart + proj.t * (segEnd - segStart)
637
+ };
638
+ if (!best) {
639
+ best = candidate;
640
+ continue;
641
+ }
642
+ const diff = candidate.distance_m - best.distance_m;
643
+ const adjacent = candidate.shapeIndex - best.shapeIndex <= 1;
644
+ if (!adjacent && Math.abs(diff) <= TIE_M) {
645
+ best = preferByHeadingOrFurther(coords, best, candidate, heading);
646
+ } else if (diff < 0) {
647
+ best = candidate;
648
+ }
649
+ }
650
+ return best;
651
+ }
652
+ function segmentBearing(coords, shapeIndex) {
653
+ const a = coords[shapeIndex];
654
+ const b = coords[shapeIndex + 1];
655
+ return a && b ? bearingDeg(a, b) : 0;
656
+ }
657
+ function preferByHeadingOrFurther(coords, current, candidate, heading) {
658
+ if (heading === null) return candidate;
659
+ const dCurrent = angleDiffDeg(segmentBearing(coords, current.shapeIndex), heading);
660
+ const dCandidate = angleDiffDeg(segmentBearing(coords, candidate.shapeIndex), heading);
661
+ return dCandidate <= dCurrent ? candidate : current;
662
+ }
663
+ var viNumber = (value) => value.replace(".", ",");
664
+ function formatDistance(m, lang = "vi") {
665
+ const vi = lang === "vi";
666
+ if (m < 1e3) {
667
+ const n2 = Math.max(0, Math.round(m));
668
+ return vi ? `${n2} m\xE9t` : `${n2} meters`;
669
+ }
670
+ const km = m / 1e3;
671
+ if (km < 10) {
672
+ const s = km.toFixed(1);
673
+ return vi ? `${viNumber(s)} ki-l\xF4-m\xE9t` : `${s} kilometers`;
674
+ }
675
+ const n = Math.round(km);
676
+ return vi ? `${n} ki-l\xF4-m\xE9t` : `${n} kilometers`;
677
+ }
678
+ function formatDistanceShort(m) {
679
+ if (m < 1e3) return `${Math.max(0, Math.round(m))} m`;
680
+ const km = m / 1e3;
681
+ return km < 10 ? `${viNumber(km.toFixed(1))} km` : `${Math.round(km)} km`;
682
+ }
683
+ function roundForSpeech(m) {
684
+ if (m >= 200) return Math.round(m / 50) * 50;
685
+ return Math.max(10, Math.round(m / 10) * 10);
686
+ }
687
+ function lowerFirst(text) {
688
+ return text.length === 0 ? text : text.charAt(0).toLowerCase() + text.slice(1);
689
+ }
690
+ function composeApproach(distance_m, step, lang) {
691
+ const cue = step.verbal_alert ?? step.verbal_pre;
692
+ if (!cue) return null;
693
+ const d = formatDistance(roundForSpeech(distance_m), lang);
694
+ return lang === "vi" ? `Trong ${d} n\u1EEFa, ${lowerFirst(cue)}` : `In ${d}, ${lowerFirst(cue)}`;
695
+ }
696
+ function planAnnouncements(p, th, lang, announced, stepChanged) {
697
+ const out = [];
698
+ const push = (stepIndex, kind, text, priority) => {
699
+ if (!text) return;
700
+ const key = `${stepIndex}:${kind}`;
701
+ if (announced.has(key)) return;
702
+ announced.add(key);
703
+ out.push({ text, kind, stepIndex, priority });
704
+ };
705
+ const longEnough = p.step.distance_m > th.approach_m + th.pre_m;
706
+ if (p.step.kind === "depart") push(p.stepIndex, "depart", p.step.verbal_pre, 3);
707
+ else if (stepChanged && longEnough) push(p.stepIndex, "post", p.step.verbal_post, 1);
708
+ const next = p.nextStep;
709
+ if (next) {
710
+ const nextIndex = p.stepIndex + 1;
711
+ const d = p.distanceToStep_m;
712
+ if (d <= th.approach_m && longEnough) {
713
+ push(nextIndex, "approach", composeApproach(d, next, lang), 2);
714
+ }
715
+ if (d <= th.pre_m)
716
+ push(nextIndex, next.kind === "arrive" ? "arrive" : "pre", next.verbal_pre, 3);
717
+ }
718
+ return out;
719
+ }
720
+ var SIMULATE_DEFAULT_SPEED_MPS = {
721
+ walk: 1.4,
722
+ motorbike: 8,
723
+ car: 12
724
+ };
725
+ var M_PER_DEG_LAT2 = Math.PI / 180 * 63710088e-1;
726
+ function mulberry32(seed) {
727
+ let a = seed >>> 0;
728
+ return () => {
729
+ a = a + 1831565813 >>> 0;
730
+ let t = a;
731
+ t = Math.imul(t ^ t >>> 15, t | 1);
732
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
733
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
734
+ };
735
+ }
736
+ function simulateFixes(route, opts = {}) {
737
+ const coords = decodePolyline6(route.geometry);
738
+ const cum = cumulativeDistances(coords);
739
+ const total = cum[cum.length - 1] ?? 0;
740
+ const speed = opts.speed_mps ?? SIMULATE_DEFAULT_SPEED_MPS[route.mode];
741
+ const interval = opts.interval_s ?? 1;
742
+ const accuracy = opts.accuracy_m ?? 8;
743
+ const jitter = opts.jitter_m ?? 0;
744
+ const start = opts.start_ms ?? 17e11;
745
+ const rand = mulberry32(opts.seed ?? 1);
746
+ const stepM = speed * interval;
747
+ if (coords.length === 0 || stepM <= 0) return [];
748
+ const fixes = [];
749
+ let segment = 0;
750
+ const distances = [];
751
+ for (let d = 0; d < total; d += stepM) distances.push(d);
752
+ distances.push(total);
753
+ distances.forEach((d, k) => {
754
+ while (segment < coords.length - 2 && (cum[segment + 1] ?? 0) < d) segment += 1;
755
+ const a = coords[segment];
756
+ const b = coords[segment + 1] ?? a;
757
+ if (!a || !b) return;
758
+ const segStart = cum[segment] ?? 0;
759
+ const segLen = (cum[segment + 1] ?? segStart) - segStart;
760
+ const t = segLen > 0 ? Math.min(1, (d - segStart) / segLen) : 0;
761
+ let lng = a[0] + (b[0] - a[0]) * t;
762
+ let lat = a[1] + (b[1] - a[1]) * t;
763
+ if (jitter > 0) {
764
+ const angle = rand() * 2 * Math.PI;
765
+ const radius = jitter * Math.sqrt(rand());
766
+ lat += radius * Math.cos(angle) / M_PER_DEG_LAT2;
767
+ lng += radius * Math.sin(angle) / (M_PER_DEG_LAT2 * Math.cos(lat * Math.PI / 180));
768
+ }
769
+ fixes.push({
770
+ lng,
771
+ lat,
772
+ accuracy_m: accuracy,
773
+ heading: bearingDeg(a, b),
774
+ speed_mps: speed,
775
+ timestamp: start + k * interval * 1e3
776
+ });
777
+ });
778
+ return fixes;
779
+ }
780
+ var DEFAULT_ACCURACY_M = 10;
781
+ var WINDOW_BASE_M = 300;
782
+ var WINDOW_SPEED_MPS = 40;
783
+ var WINDOW_MAX_M = 3e3;
784
+ var ARRIVE_NEAR_REMAINING_M = 150;
785
+ var MOVING_SPEED_MPS = 1;
786
+ function buildState(response, routeIndex, override) {
787
+ const route = response.routes[routeIndex];
788
+ if (!route) throw new Error(`createNavigator: response kh\xF4ng c\xF3 routes[${routeIndex}]`);
789
+ return {
790
+ response,
791
+ routeIndex,
792
+ route,
793
+ index: buildRouteIndex(route),
794
+ th: { ...NAVIGATION_THRESHOLDS[route.mode], ...override }
795
+ };
796
+ }
797
+ function createNavigator(opts) {
798
+ const rerouteMode = opts.reroute ?? "auto";
799
+ const provider = opts.provider;
800
+ if (rerouteMode === "auto" && !provider) {
801
+ throw new Error("createNavigator: reroute 'auto' c\u1EA7n provider (v\xED d\u1EE5 client c\u1EE7a createClient)");
802
+ }
803
+ const lang = opts.lang ?? "vi";
804
+ let rs = buildState(opts.response, opts.routeIndex ?? 0, opts.thresholds);
805
+ let status = "idle";
806
+ let progress = null;
807
+ let last = null;
808
+ let maxAlong_m = 0;
809
+ let offCount = 0;
810
+ let offSince = null;
811
+ let backCount = 0;
812
+ let announced = /* @__PURE__ */ new Set();
813
+ let rerouteAttempts = 0;
814
+ let lastRerouteAt = null;
815
+ let inflight = false;
816
+ let rerouteToken = 0;
817
+ let rerouteReason = null;
818
+ const listeners = {
819
+ status: /* @__PURE__ */ new Set(),
820
+ progress: /* @__PURE__ */ new Set(),
821
+ step: /* @__PURE__ */ new Set(),
822
+ waypoint: /* @__PURE__ */ new Set(),
823
+ offRoute: /* @__PURE__ */ new Set(),
824
+ reroute: /* @__PURE__ */ new Set(),
825
+ rerouteFailed: /* @__PURE__ */ new Set(),
826
+ announce: /* @__PURE__ */ new Set(),
827
+ arrive: /* @__PURE__ */ new Set()
828
+ };
829
+ const emit = (k, e) => {
830
+ for (const fn of listeners[k]) fn(e);
831
+ };
832
+ const setStatus = (next) => {
833
+ if (next === status) return;
834
+ const previous = status;
835
+ status = next;
836
+ emit("status", { status, previous });
837
+ };
838
+ const resetTracking = () => {
839
+ last = null;
840
+ maxAlong_m = 0;
841
+ offCount = 0;
842
+ offSince = null;
843
+ backCount = 0;
844
+ announced = /* @__PURE__ */ new Set();
845
+ };
846
+ const applyRoute = (response, routeIndex) => {
847
+ rs = buildState(response, routeIndex, opts.thresholds);
848
+ resetTracking();
849
+ progress = null;
850
+ };
851
+ const destinationLatLng = () => {
852
+ const w = rs.response.waypoints[rs.response.waypoints.length - 1];
853
+ const end = rs.index.coords[rs.index.coords.length - 1];
854
+ const [lng, lat] = w ? w.location : end ?? [0, 0];
855
+ return [lat, lng];
856
+ };
857
+ const rerouteRequest = (fix, legIndex) => {
858
+ const via = rs.response.waypoints.slice(legIndex + 1, -1).map((w) => [w.location[1], w.location[0]]);
859
+ const request = {
860
+ from: [fix.lat, fix.lng],
861
+ to: destinationLatLng(),
862
+ mode: rs.route.mode,
863
+ lang,
864
+ alternatives: false
865
+ };
866
+ if (via.length > 0) request.via = via;
867
+ return request;
868
+ };
869
+ async function runReroute(reason, fix, legIndex) {
870
+ if (!provider) return;
871
+ const token = ++rerouteToken;
872
+ inflight = true;
873
+ rerouteReason = reason;
874
+ lastRerouteAt = fix.timestamp;
875
+ setStatus("rerouting");
876
+ try {
877
+ const next = await provider.directions(rerouteRequest(fix, legIndex));
878
+ inflight = false;
879
+ if (token !== rerouteToken || status !== "rerouting") return;
880
+ rerouteAttempts = 0;
881
+ applyRoute(next, 0);
882
+ emit("reroute", { reason, response: next });
883
+ setStatus("navigating");
884
+ } catch (error) {
885
+ inflight = false;
886
+ if (token !== rerouteToken) return;
887
+ rerouteAttempts += 1;
888
+ if (status === "rerouting") setStatus("off_route");
889
+ emit("rerouteFailed", {
890
+ error,
891
+ attempts: rerouteAttempts,
892
+ final: rerouteAttempts >= rs.th.rerouteMaxFailures
893
+ });
894
+ }
895
+ }
896
+ const maybeAutoReroute = (fix, legIndex) => {
897
+ if (rerouteMode !== "auto" || !provider || inflight) return;
898
+ if (rerouteAttempts >= rs.th.rerouteMaxFailures) return;
899
+ if (lastRerouteAt !== null && fix.timestamp - lastRerouteAt < rs.th.rerouteCooldown_s * 1e3) {
900
+ return;
901
+ }
902
+ void runReroute("off_route", fix, legIndex);
903
+ };
904
+ const segmentBearing2 = (shapeIndex) => {
905
+ const a = rs.index.coords[shapeIndex];
906
+ const b = rs.index.coords[shapeIndex + 1];
907
+ return a && b ? bearingDeg(a, b) : 0;
908
+ };
909
+ function update(fix) {
910
+ if (status === "arrived" || status === "stopped") return;
911
+ const accuracy = fix.accuracy_m ?? DEFAULT_ACCURACY_M;
912
+ if (accuracy > rs.th.maxAccuracy_m) return;
913
+ const prev = last;
914
+ if (prev && fix.timestamp <= prev.fix.timestamp) return;
915
+ if (status === "idle") setStatus("navigating");
916
+ const dt_s = prev ? (fix.timestamp - prev.fix.timestamp) / 1e3 : 0;
917
+ const window_m = Math.min(WINDOW_MAX_M, WINDOW_BASE_M + WINDOW_SPEED_MPS * dt_s);
918
+ const here = [fix.lng, fix.lat];
919
+ const anchorShapeIndex = prev ? prev.shapeIndex : null;
920
+ const snap = snapToRoute(rs.index, here, {
921
+ fromShapeIndex: anchorShapeIndex,
922
+ window_m,
923
+ heading: fix.heading
924
+ });
925
+ if (!snap) return;
926
+ let along_m = snap.along_m;
927
+ let skippedViaStep = null;
928
+ const currentLeg = prev?.legIndex ?? 0;
929
+ const nextLegBegin = rs.index.legBegin_m[currentLeg + 1];
930
+ const nextVia = rs.response.waypoints[currentLeg + 1];
931
+ if (nextLegBegin !== void 0 && nextVia && currentLeg + 1 < rs.route.legs.length && along_m < nextLegBegin && haversineM(here, nextVia.snapped) <= rs.th.arrive_m) {
932
+ along_m = nextLegBegin;
933
+ skippedViaStep = rs.index.steps.find((s) => s.legIndex === currentLeg && s.step.kind === "arrive") ?? null;
934
+ }
935
+ const threshold = Math.max(rs.th.offRoute_m, 1.5 * accuracy);
936
+ const perpendicularOk = snap.distance_m <= threshold;
937
+ if (perpendicularOk && prev && along_m < maxAlong_m - rs.th.offRoute_m) backCount += 1;
938
+ else backCount = 0;
939
+ const onRoute = perpendicularOk && backCount < rs.th.offRouteFixes;
940
+ if (onRoute) {
941
+ offCount = 0;
942
+ offSince = null;
943
+ maxAlong_m = prev ? Math.max(maxAlong_m, along_m) : along_m;
944
+ if (status === "off_route" || status === "rerouting" && rerouteReason === "off_route") {
945
+ rerouteAttempts = 0;
946
+ setStatus("navigating");
947
+ }
948
+ } else {
949
+ offCount += 1;
950
+ offSince ?? (offSince = fix.timestamp);
951
+ if (status === "navigating" && offCount >= rs.th.offRouteFixes && fix.timestamp - offSince >= rs.th.offRouteSeconds * 1e3) {
952
+ setStatus("off_route");
953
+ emit("offRoute", { distance_m: snap.distance_m, fix });
954
+ }
955
+ }
956
+ const displayAlong_m = Math.max(along_m, maxAlong_m);
957
+ const at = progressAt(rs.index, displayAlong_m);
958
+ const rawFlat = rs.index.steps[at.stepIndex];
959
+ if (!rawFlat) return;
960
+ const rawNextFlat = rs.index.steps[at.stepIndex + 1];
961
+ const moving = (fix.speed_mps ?? 0) > MOVING_SPEED_MPS;
962
+ const bearing = moving && typeof fix.heading === "number" && Number.isFinite(fix.heading) ? fix.heading : segmentBearing2(snap.shapeIndex);
963
+ const end = rs.index.coords[rs.index.coords.length - 1];
964
+ const lastLeg = at.legIndex === rs.route.legs.length - 1;
965
+ const nearEnd = end !== void 0 && lastLeg && haversineM(here, end) <= rs.th.arrive_m && at.remaining_m <= ARRIVE_NEAR_REMAINING_M;
966
+ const arrivingNow = status === "navigating" && (at.remaining_m <= rs.th.arrive_m || nearEnd);
967
+ const rawStepChanged = prev === null || prev.stepIndex !== at.stepIndex;
968
+ const announceProgress = {
969
+ status,
970
+ route: rs.route,
971
+ routeIndex: rs.routeIndex,
972
+ legIndex: at.legIndex,
973
+ stepIndex: at.stepIndex,
974
+ step: rawFlat.step,
975
+ nextStep: rawNextFlat ? rawNextFlat.step : null,
976
+ snapped: snap.point,
977
+ bearing,
978
+ shapeIndex: snap.shapeIndex,
979
+ traveled_m: displayAlong_m,
980
+ remaining_m: at.remaining_m,
981
+ remaining_s: at.remaining_s,
982
+ distanceToStep_m: at.distanceToStep_m,
983
+ offRoute_m: snap.distance_m,
984
+ fix
985
+ };
986
+ const finalIndex = rs.index.steps.length - 1;
987
+ const finalFlat = rs.index.steps[finalIndex];
988
+ const effectiveAt = arrivingNow && finalFlat ? {
989
+ stepIndex: finalIndex,
990
+ legIndex: finalFlat.legIndex,
991
+ distanceToStep_m: 0,
992
+ remaining_m: 0,
993
+ remaining_s: 0
994
+ } : at;
995
+ const flat = arrivingNow && finalFlat ? finalFlat : rawFlat;
996
+ const nextFlat = arrivingNow ? void 0 : rawNextFlat;
997
+ const stepChanged = prev === null || prev.stepIndex !== effectiveAt.stepIndex;
998
+ const legChanged = prev !== null && effectiveAt.legIndex > prev.legIndex;
999
+ progress = {
1000
+ status,
1001
+ route: rs.route,
1002
+ routeIndex: rs.routeIndex,
1003
+ legIndex: effectiveAt.legIndex,
1004
+ stepIndex: effectiveAt.stepIndex,
1005
+ step: flat.step,
1006
+ nextStep: nextFlat ? nextFlat.step : null,
1007
+ snapped: snap.point,
1008
+ bearing,
1009
+ shapeIndex: snap.shapeIndex,
1010
+ traveled_m: arrivingNow ? rs.index.total_m : displayAlong_m,
1011
+ remaining_m: effectiveAt.remaining_m,
1012
+ remaining_s: effectiveAt.remaining_s,
1013
+ distanceToStep_m: effectiveAt.distanceToStep_m,
1014
+ offRoute_m: snap.distance_m,
1015
+ fix
1016
+ };
1017
+ last = {
1018
+ fix,
1019
+ // Neo cửa sổ chỉ đi theo fix ĐANG ở trên tuyến; fix lệch giữ nguyên neo tốt gần nhất.
1020
+ shapeIndex: onRoute ? snap.shapeIndex : anchorShapeIndex ?? snap.shapeIndex,
1021
+ along_m: arrivingNow ? rs.index.total_m : displayAlong_m,
1022
+ stepIndex: effectiveAt.stepIndex,
1023
+ legIndex: effectiveAt.legIndex
1024
+ };
1025
+ if (status === "navigating") {
1026
+ if (stepChanged && prev !== null) {
1027
+ emit("step", { stepIndex: effectiveAt.stepIndex, step: flat.step });
1028
+ }
1029
+ if (legChanged) {
1030
+ const waypoint = rs.response.waypoints[effectiveAt.legIndex];
1031
+ if (waypoint) emit("waypoint", { legIndex: effectiveAt.legIndex, waypoint });
1032
+ }
1033
+ }
1034
+ emit("progress", progress);
1035
+ if (status === "navigating") {
1036
+ if (skippedViaStep) {
1037
+ const key = `via:${currentLeg}:arrive`;
1038
+ const text = skippedViaStep.step.verbal_pre;
1039
+ if (text && !announced.has(key)) {
1040
+ announced.add(key);
1041
+ const viaStepIndex = rs.index.steps.indexOf(skippedViaStep);
1042
+ emit("announce", { text, kind: "arrive", stepIndex: viaStepIndex, priority: 3 });
1043
+ }
1044
+ }
1045
+ for (const a of planAnnouncements(announceProgress, rs.th, lang, announced, rawStepChanged)) {
1046
+ emit("announce", a);
1047
+ }
1048
+ if (arrivingNow) {
1049
+ setStatus("arrived");
1050
+ progress = { ...progress, status };
1051
+ const waypoint = rs.response.waypoints[rs.response.waypoints.length - 1];
1052
+ if (waypoint) emit("arrive", { waypoint, fix });
1053
+ }
1054
+ } else if (status === "off_route") {
1055
+ maybeAutoReroute(fix, effectiveAt.legIndex);
1056
+ }
1057
+ }
1058
+ return {
1059
+ get status() {
1060
+ return status;
1061
+ },
1062
+ get progress() {
1063
+ return progress;
1064
+ },
1065
+ update,
1066
+ setRoute(response, routeIndex = 0) {
1067
+ rerouteToken += 1;
1068
+ inflight = false;
1069
+ rerouteAttempts = 0;
1070
+ lastRerouteAt = null;
1071
+ applyRoute(response, routeIndex);
1072
+ if (status === "off_route" || status === "rerouting") setStatus("navigating");
1073
+ },
1074
+ async reroute() {
1075
+ if (!provider) throw new Error("createNavigator: kh\xF4ng c\xF3 provider \u0111\u1EC3 t\xEDnh l\u1EA1i");
1076
+ if (status === "arrived" || status === "stopped") return;
1077
+ if (!last) throw new Error("createNavigator: ch\u01B0a c\xF3 v\u1ECB tr\xED \u0111\u1EC3 t\xEDnh l\u1EA1i");
1078
+ await runReroute("manual", last.fix, last.legIndex);
1079
+ },
1080
+ stop() {
1081
+ rerouteToken += 1;
1082
+ inflight = false;
1083
+ setStatus("stopped");
1084
+ },
1085
+ on(event, handler) {
1086
+ listeners[event].add(handler);
1087
+ },
1088
+ off(event, handler) {
1089
+ listeners[event].delete(handler);
1090
+ }
1091
+ };
1092
+ }
1093
+ var EMPTY_ROUTE_FEATURES = {
1094
+ type: "FeatureCollection",
1095
+ features: []
1096
+ };
1097
+ function decodeRoutes(response) {
1098
+ return response.routes.map((route) => decodePolyline6(route.geometry));
1099
+ }
1100
+ var line = (kind, index, coordinates) => ({
1101
+ type: "Feature",
1102
+ geometry: { type: "LineString", coordinates },
1103
+ properties: { kind, index }
1104
+ });
1105
+ function routeFeatures(coords, opts) {
1106
+ const progress = opts.progress ?? null;
1107
+ const features = [];
1108
+ for (const [i, c] of coords.entries()) {
1109
+ if (i !== opts.active) {
1110
+ features.push(line("alt", i, [...c]));
1111
+ continue;
1112
+ }
1113
+ if (progress && progress.shapeIndex < c.length - 1) {
1114
+ features.push(
1115
+ line("traveled", i, [...c.slice(0, progress.shapeIndex + 1), progress.snapped]),
1116
+ line("active", i, [progress.snapped, ...c.slice(progress.shapeIndex + 1)])
1117
+ );
1118
+ } else {
1119
+ features.push(line("active", i, [...c]));
1120
+ }
1121
+ }
1122
+ if (opts.puck && progress) {
1123
+ features.push({
1124
+ type: "Feature",
1125
+ geometry: { type: "Point", coordinates: progress.snapped },
1126
+ properties: { kind: "puck", bearing: progress.bearing ?? 0 }
1127
+ });
1128
+ }
1129
+ return { type: "FeatureCollection", features };
1130
+ }
429
1131
 
430
1132
  // src/map.tsx
431
- import { useContext, useEffect as useEffect2, useMemo, useRef } from "react";
1133
+ import { useContext as useContext2, useEffect as useEffect2, useMemo, useRef } from "react";
432
1134
  import {
433
- StyleSheet as StyleSheet2,
434
- View
1135
+ AppState,
1136
+ StyleSheet as StyleSheet3,
1137
+ View as View2
435
1138
  } from "react-native";
436
1139
 
437
1140
  // src/attribution.tsx
@@ -469,6 +1172,576 @@ var styles = StyleSheet.create({
469
1172
  import { createContext } from "react";
470
1173
  var MapContext = createContext(null);
471
1174
 
1175
+ // src/navigation/map-binding.ts
1176
+ var FOLLOW_ZOOM = {
1177
+ walk: 17,
1178
+ motorbike: 16.5,
1179
+ car: 15.5
1180
+ };
1181
+ var FOLLOW_PITCH = 45;
1182
+ var SESSION_EVENTS = [
1183
+ "status",
1184
+ "progress",
1185
+ "step",
1186
+ "waypoint",
1187
+ "offRoute",
1188
+ "reroute",
1189
+ "rerouteFailed",
1190
+ "announce",
1191
+ "arrive",
1192
+ "route",
1193
+ "positionError",
1194
+ "voiceUnavailable",
1195
+ "backgroundUnavailable",
1196
+ "end"
1197
+ ];
1198
+ function createMapBinding(deps) {
1199
+ const listeners = /* @__PURE__ */ new Map();
1200
+ const emit = (event, e) => {
1201
+ for (const fn of listeners.get(event) ?? []) fn(e);
1202
+ };
1203
+ let explicit = null;
1204
+ let fallback = null;
1205
+ let attached = null;
1206
+ let detachFns = [];
1207
+ let follow = {
1208
+ pitch: FOLLOW_PITCH
1209
+ };
1210
+ let following = true;
1211
+ let lastFixTs = null;
1212
+ const camera = (p) => {
1213
+ if (!follow || !following || deps.appState.currentState !== "active") return;
1214
+ const dt = lastFixTs === null ? 500 : p.fix.timestamp - lastFixTs;
1215
+ lastFixTs = p.fix.timestamp;
1216
+ deps.camera.current?.easeTo({
1217
+ center: p.snapped,
1218
+ bearing: p.bearing,
1219
+ zoom: follow.zoom ?? FOLLOW_ZOOM[p.route.mode],
1220
+ pitch: follow.pitch,
1221
+ duration: Math.max(0, Math.min(1e3, dt)),
1222
+ ...follow.padding ? { padding: follow.padding } : {}
1223
+ });
1224
+ };
1225
+ const paint = (p) => {
1226
+ deps.store.setProgress({ shapeIndex: p.shapeIndex, snapped: p.snapped, bearing: p.bearing });
1227
+ camera(p);
1228
+ };
1229
+ const detach = () => {
1230
+ for (const fn of detachFns) fn();
1231
+ detachFns = [];
1232
+ attached = null;
1233
+ lastFixTs = null;
1234
+ deps.store.clear();
1235
+ };
1236
+ const attachTo = (session) => {
1237
+ if (attached === session) return;
1238
+ detach();
1239
+ attached = session;
1240
+ const on = (k, fn) => {
1241
+ session.on(k, fn);
1242
+ detachFns.push(() => session.off(k, fn));
1243
+ };
1244
+ for (const name of SESSION_EVENTS) {
1245
+ on(name, (e) => {
1246
+ if (name === "progress") paint(e);
1247
+ else if (name === "route") {
1248
+ const r = e;
1249
+ deps.store.show(r.response, { active: r.routeIndex });
1250
+ }
1251
+ emit(name, e);
1252
+ });
1253
+ }
1254
+ if (session.response) deps.store.show(session.response, { active: session.routeIndex });
1255
+ const p = session.state;
1256
+ if (p) paint(p);
1257
+ };
1258
+ const current = () => explicit ?? fallback;
1259
+ const resolve = () => {
1260
+ if (explicit) return explicit;
1261
+ if (!fallback) fallback = deps.createDefaultSession();
1262
+ if (attached !== fallback) attachTo(fallback);
1263
+ return fallback;
1264
+ };
1265
+ const appSub = deps.appState.addEventListener("change", (s) => {
1266
+ if (s !== "active") return;
1267
+ const p = attached?.state;
1268
+ if (p) camera(p);
1269
+ });
1270
+ const api = {
1271
+ get session() {
1272
+ return resolve();
1273
+ },
1274
+ get following() {
1275
+ return following;
1276
+ },
1277
+ recenter() {
1278
+ if (!follow) return;
1279
+ following = true;
1280
+ emit("followChange", true);
1281
+ const p = attached?.state;
1282
+ if (p) camera(p);
1283
+ },
1284
+ start: (o) => resolve().start(o),
1285
+ stop: () => current()?.stop() ?? Promise.resolve(),
1286
+ reroute: () => current()?.reroute() ?? Promise.reject(new Error("Phi\xEAn d\u1EABn \u0111\u01B0\u1EDDng ch\u01B0a start()")),
1287
+ get state() {
1288
+ return current()?.state ?? null;
1289
+ },
1290
+ get status() {
1291
+ return current()?.status ?? "idle";
1292
+ },
1293
+ on(event, handler) {
1294
+ let set = listeners.get(event);
1295
+ if (!set) {
1296
+ set = /* @__PURE__ */ new Set();
1297
+ listeners.set(event, set);
1298
+ }
1299
+ set.add(handler);
1300
+ },
1301
+ off(event, handler) {
1302
+ listeners.get(event)?.delete(handler);
1303
+ }
1304
+ };
1305
+ return {
1306
+ api,
1307
+ attach(session) {
1308
+ explicit = session;
1309
+ if (session) attachTo(session);
1310
+ else detach();
1311
+ },
1312
+ setFollow(opt) {
1313
+ if (opt === false) {
1314
+ follow = null;
1315
+ following = false;
1316
+ return;
1317
+ }
1318
+ const o = typeof opt === "object" ? opt : {};
1319
+ follow = {
1320
+ pitch: o.pitch ?? FOLLOW_PITCH,
1321
+ ...o.zoom !== void 0 ? { zoom: o.zoom } : {},
1322
+ ...o.padding ? { padding: o.padding } : {}
1323
+ };
1324
+ following = true;
1325
+ },
1326
+ userGesture() {
1327
+ if (!following) return;
1328
+ following = false;
1329
+ emit("followChange", false);
1330
+ },
1331
+ dispose() {
1332
+ detach();
1333
+ appSub.remove();
1334
+ listeners.clear();
1335
+ }
1336
+ };
1337
+ }
1338
+
1339
+ // src/navigation/route-layers.tsx
1340
+ import {
1341
+ GeoJSONSource,
1342
+ Images,
1343
+ Layer
1344
+ } from "@maplibre/maplibre-react-native";
1345
+ import { useSyncExternalStore } from "react";
1346
+
1347
+ // src/marker.tsx
1348
+ import { Marker as NativeMarker } from "@maplibre/maplibre-react-native";
1349
+ import { useContext } from "react";
1350
+ import { StyleSheet as StyleSheet2, View } from "react-native";
1351
+ import { jsx as jsx2 } from "react/jsx-runtime";
1352
+ var DEFAULT_MARKER_COLOR = "#3FB1CE";
1353
+ function Marker({
1354
+ lng,
1355
+ lat,
1356
+ color = DEFAULT_MARKER_COLOR,
1357
+ anchor = "center",
1358
+ onPress,
1359
+ children,
1360
+ testID
1361
+ }) {
1362
+ if (!useContext(MapContext)) throw new Error("useMap ph\u1EA3i \u0111\u01B0\u1EE3c g\u1ECDi b\xEAn trong <MapsLibVNMap>");
1363
+ const pin = children ?? /* @__PURE__ */ jsx2(View, { style: [styles2.pin, { backgroundColor: color }], testID: "mapslibvn-marker-pin" });
1364
+ return /* @__PURE__ */ jsx2(
1365
+ NativeMarker,
1366
+ {
1367
+ lngLat: [lng, lat],
1368
+ anchor,
1369
+ ...onPress ? { onPress: () => onPress() } : {},
1370
+ ...testID ? { testID } : {},
1371
+ children: pin
1372
+ }
1373
+ );
1374
+ }
1375
+ var styles2 = StyleSheet2.create({
1376
+ pin: {
1377
+ width: 22,
1378
+ height: 22,
1379
+ borderRadius: 11,
1380
+ borderWidth: 3,
1381
+ borderColor: "#ffffff"
1382
+ }
1383
+ });
1384
+
1385
+ // src/navigation/puck-image.ts
1386
+ var PUCK_IMAGE_KEY = "mapslibvn-puck";
1387
+ var PUCK_PNG_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAADUUlEQVR42u2bv4rVQBTG9xH2EbbwAS6+gPcF7BdsgpWNsJXbBrEUAjaWwcZqLWy0UMhi48JiJStWjqCNFoqghdWREzJxcnLyZ86dyZ1kcuBAyN1NZr58c+Z3D7kHB2ussUaIAQBbzFUIgAIzdhE28D82MQuRG0LksYpwBO04it0Ncbqiww3xuQIAMj3ri6tvZRqRxSLCIQD81LO+9/htmUbgZ4cxCJHqGX/9/huuHT8tE4+NSKNyw4Mn72oh8DgaV5hu+PXnL1y/fVYLgcd4LgpXAICq98kXH2sRdOI5I9RSRUjMWd64+7wlBJ4jkSzaDc/OP7VE0ImfLdYV1A237r/uFAI/W6wrTDcgPHWJoJMAllqKCFtzVghPQ0IQwIJFNG6qxksLoIaSAFaxKDeYADWUBLDm7QrTDRSghpIBrGKuImwajQYGoIaSANY823m08cIB1FAygJXPTYRG46UPoIaSANa8GjfUDX0ANZQMYOWzdMMYgLIErHm4wmzDjQUoAWBloYvQaLzYAJQlYIXduDEbL7YAJQCsdBZusAUoAWCF6QrqBglACQArDVEItStACQBLhSZC4gqgBICVBOuGXQBKAFgqSDe4ACgBYCXBucEFQAkAS+1bhEbjxSVAWQLWfhs3ZuPFNUAJAKsIwg2uAUoAWPtxBXWDD4ASAFYxtQgbOgIfACUArGnbebTxIgWom6cv6zdm8NgRYOVTidB6/8kWoPBJMhMoz9k6iwGsaRo31A02AIUF7tHZe67INYou/o1N4WUAK5/cDWMBCre7PgE4QcZuxwxg+XUFbcONAag7D99w8FM/Of1Sesd7l+X/4jUEgJX5EqHReBkCKFy7jGV1QyXjnljluIzeRy/BvlrEAJafxg1tvHQBFBa7V5dfOAHwO8nJmMFVop/Q7zEYeG2uoHYAVurdDRSgcCAM4EAFXskO904ovOn70wfB3N+tK6gbTIDq2Qlyl8jL1RG6w3QAVupSCMUBFFZrps2e+azYXB3BMejdi+ET5erGCb0yPoUPn3+I1r/j5dqoIzgmHBsTiXM3uFz/jrtkRc84lXM3+Fj/PuuIE1cQN3hf/z7riNgVRuNl8vXvsY5sJRdJl/SyZ7XMl/3zhzXWWGONNSaKf5vNEuTO/lstAAAAAElFTkSuQmCC";
1388
+
1389
+ // src/navigation/route-layers.tsx
1390
+ import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
1391
+ var ROUTE_SOURCE_ID = "mapslibvn-route";
1392
+ var ROUTE_LAYER_IDS = {
1393
+ alt: "mapslibvn-route-alt",
1394
+ casing: "mapslibvn-route-casing",
1395
+ line: "mapslibvn-route-line",
1396
+ traveled: "mapslibvn-route-traveled",
1397
+ puck: "mapslibvn-route-puck"
1398
+ };
1399
+ var ROUTE_COLOR = "#2458a6";
1400
+ var ALT_ROUTE_COLOR = "#9ca8ba";
1401
+ var DESTINATION_COLOR = "#d92d20";
1402
+ var kindIs = (kind) => ["==", ["get", "kind"], kind];
1403
+ var ROUND = {
1404
+ "line-join": "round",
1405
+ "line-cap": "round"
1406
+ };
1407
+ var PUCK_LAYOUT = {
1408
+ "icon-image": PUCK_IMAGE_KEY,
1409
+ "icon-rotate": ["get", "bearing"],
1410
+ "icon-rotation-alignment": "map",
1411
+ "icon-pitch-alignment": "map",
1412
+ "icon-allow-overlap": true,
1413
+ "icon-ignore-placement": true,
1414
+ "icon-size": 0.5
1415
+ };
1416
+ function RouteLayers({ store, routeStyle, beforeId, onRouteClick }) {
1417
+ const snap = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
1418
+ if (!snap.response) return null;
1419
+ const color = routeStyle?.color ?? ROUTE_COLOR;
1420
+ const altColor = routeStyle?.altColor ?? ALT_ROUTE_COLOR;
1421
+ const casingColor = routeStyle?.casingColor ?? "#ffffff";
1422
+ const traveledOpacity = routeStyle?.traveledOpacity ?? 0.35;
1423
+ const before = beforeId ? { beforeId } : {};
1424
+ const onPress = (e) => {
1425
+ const alt = e.nativeEvent.features.find((f) => f.properties?.kind === "alt");
1426
+ const index = alt?.properties?.index;
1427
+ if (typeof index === "number") onRouteClick?.(index);
1428
+ };
1429
+ const waypoints = snap.response.waypoints;
1430
+ const last = waypoints.length - 1;
1431
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1432
+ /* @__PURE__ */ jsx3(Images, { images: { [PUCK_IMAGE_KEY]: { source: { uri: PUCK_PNG_DATA_URI } } } }),
1433
+ /* @__PURE__ */ jsxs(
1434
+ GeoJSONSource,
1435
+ {
1436
+ id: ROUTE_SOURCE_ID,
1437
+ data: snap.features,
1438
+ onPress,
1439
+ children: [
1440
+ /* @__PURE__ */ jsx3(
1441
+ Layer,
1442
+ {
1443
+ type: "line",
1444
+ id: ROUTE_LAYER_IDS.alt,
1445
+ source: ROUTE_SOURCE_ID,
1446
+ filter: kindIs("alt"),
1447
+ layout: ROUND,
1448
+ paint: { "line-color": altColor, "line-width": 5 },
1449
+ ...before
1450
+ }
1451
+ ),
1452
+ /* @__PURE__ */ jsx3(
1453
+ Layer,
1454
+ {
1455
+ type: "line",
1456
+ id: ROUTE_LAYER_IDS.casing,
1457
+ source: ROUTE_SOURCE_ID,
1458
+ filter: kindIs("active"),
1459
+ layout: ROUND,
1460
+ paint: { "line-color": casingColor, "line-width": 9 },
1461
+ ...before
1462
+ }
1463
+ ),
1464
+ /* @__PURE__ */ jsx3(
1465
+ Layer,
1466
+ {
1467
+ type: "line",
1468
+ id: ROUTE_LAYER_IDS.line,
1469
+ source: ROUTE_SOURCE_ID,
1470
+ filter: kindIs("active"),
1471
+ layout: ROUND,
1472
+ paint: { "line-color": color, "line-width": 6 },
1473
+ ...before
1474
+ }
1475
+ ),
1476
+ /* @__PURE__ */ jsx3(
1477
+ Layer,
1478
+ {
1479
+ type: "line",
1480
+ id: ROUTE_LAYER_IDS.traveled,
1481
+ source: ROUTE_SOURCE_ID,
1482
+ filter: kindIs("traveled"),
1483
+ layout: ROUND,
1484
+ paint: { "line-color": color, "line-width": 6, "line-opacity": traveledOpacity },
1485
+ ...before
1486
+ }
1487
+ ),
1488
+ snap.puck ? /* @__PURE__ */ jsx3(
1489
+ Layer,
1490
+ {
1491
+ type: "symbol",
1492
+ id: ROUTE_LAYER_IDS.puck,
1493
+ source: ROUTE_SOURCE_ID,
1494
+ filter: kindIs("puck"),
1495
+ layout: PUCK_LAYOUT
1496
+ }
1497
+ ) : null
1498
+ ]
1499
+ }
1500
+ ),
1501
+ waypoints.map(
1502
+ (w, i) => i === 0 ? null : /* @__PURE__ */ jsx3(
1503
+ Marker,
1504
+ {
1505
+ lng: w.snapped[0],
1506
+ lat: w.snapped[1],
1507
+ color: i === last ? DESTINATION_COLOR : ALT_ROUTE_COLOR,
1508
+ testID: "mapslibvn-route-marker"
1509
+ },
1510
+ `${i}-${w.snapped[0]}-${w.snapped[1]}`
1511
+ )
1512
+ )
1513
+ ] });
1514
+ }
1515
+
1516
+ // src/navigation/routes-store.ts
1517
+ function createRoutesStore() {
1518
+ let coords = [];
1519
+ let snapshot = {
1520
+ response: null,
1521
+ active: 0,
1522
+ progress: null,
1523
+ puck: true,
1524
+ features: EMPTY_ROUTE_FEATURES
1525
+ };
1526
+ const listeners = /* @__PURE__ */ new Set();
1527
+ const set = (patch) => {
1528
+ const next = { ...snapshot, ...patch };
1529
+ next.features = next.response ? routeFeatures(coords, { active: next.active, progress: next.progress, puck: next.puck }) : EMPTY_ROUTE_FEATURES;
1530
+ snapshot = next;
1531
+ for (const fn of listeners) fn();
1532
+ };
1533
+ return {
1534
+ getSnapshot: () => snapshot,
1535
+ subscribe(onChange) {
1536
+ listeners.add(onChange);
1537
+ return () => {
1538
+ listeners.delete(onChange);
1539
+ };
1540
+ },
1541
+ show(response, opts = {}) {
1542
+ coords = decodeRoutes(response);
1543
+ set({ response, active: opts.active ?? 0, progress: null });
1544
+ },
1545
+ setActive(index) {
1546
+ set({ active: index, progress: null });
1547
+ },
1548
+ setProgress(cut) {
1549
+ set({ progress: cut });
1550
+ },
1551
+ setPuck(on) {
1552
+ if (on !== snapshot.puck) set({ puck: on });
1553
+ },
1554
+ clear() {
1555
+ coords = [];
1556
+ set({ response: null, progress: null });
1557
+ }
1558
+ };
1559
+ }
1560
+
1561
+ // src/navigation/session.ts
1562
+ var MISSING_SOURCE_MESSAGE = "Phi\xEAn d\u1EABn \u0111\u01B0\u1EDDng thi\u1EBFu ngu\u1ED3n v\u1ECB tr\xED: truy\u1EC1n source (v\xED d\u1EE5 expoLocationSource() t\u1EEB @mapslibvn/react-native/expo)";
1563
+ var FORWARDED = [
1564
+ "progress",
1565
+ "step",
1566
+ "waypoint",
1567
+ "offRoute",
1568
+ "reroute",
1569
+ "rerouteFailed",
1570
+ "announce",
1571
+ "arrive"
1572
+ ];
1573
+ function createNavigationSession(opts) {
1574
+ const listeners = /* @__PURE__ */ new Map();
1575
+ const emit = (event, e) => {
1576
+ for (const fn of listeners.get(event) ?? []) fn(e);
1577
+ };
1578
+ let nav = null;
1579
+ let unsubscribe = null;
1580
+ let running = false;
1581
+ let ended = false;
1582
+ let startToken = 0;
1583
+ let response = null;
1584
+ let routeIndex = 0;
1585
+ let voiceOn = false;
1586
+ let keepOn = false;
1587
+ let audioOn = false;
1588
+ const currentStatus = () => {
1589
+ if (!nav) return "idle";
1590
+ return nav.status === "idle" ? "navigating" : nav.status;
1591
+ };
1592
+ const releaseSource = () => {
1593
+ unsubscribe?.();
1594
+ unsubscribe = null;
1595
+ };
1596
+ const releaseDevice = async () => {
1597
+ if (keepOn) {
1598
+ keepOn = false;
1599
+ try {
1600
+ await opts.keepAwake?.deactivate();
1601
+ } catch {
1602
+ }
1603
+ }
1604
+ if (audioOn) {
1605
+ audioOn = false;
1606
+ try {
1607
+ await opts.audio?.deactivate();
1608
+ } catch {
1609
+ }
1610
+ }
1611
+ };
1612
+ async function stop() {
1613
+ if (!running) return;
1614
+ running = false;
1615
+ startToken += 1;
1616
+ const previous = currentStatus();
1617
+ releaseSource();
1618
+ if (voiceOn) opts.speech?.cancel();
1619
+ voiceOn = false;
1620
+ const engine = nav;
1621
+ nav = null;
1622
+ engine?.stop();
1623
+ await releaseDevice();
1624
+ emit("status", { status: "idle", previous });
1625
+ if (!ended) emit("end", { reason: "stopped" });
1626
+ ended = false;
1627
+ }
1628
+ async function start(o) {
1629
+ if (running) await stop();
1630
+ const source = opts.source;
1631
+ if (!source) throw new Error(MISSING_SOURCE_MESSAGE);
1632
+ running = true;
1633
+ ended = false;
1634
+ startToken += 1;
1635
+ const token = startToken;
1636
+ response = o.response;
1637
+ routeIndex = o.routeIndex ?? 0;
1638
+ const lang = o.lang ?? "vi";
1639
+ const navOptions = {
1640
+ response: o.response,
1641
+ routeIndex,
1642
+ provider: opts.provider,
1643
+ reroute: o.reroute ?? "auto",
1644
+ lang
1645
+ };
1646
+ if (o.thresholds) navOptions.thresholds = o.thresholds;
1647
+ const engine = createNavigator(navOptions);
1648
+ const mode = o.response.routes[routeIndex]?.mode ?? "motorbike";
1649
+ voiceOn = o.voice !== false && Boolean(opts.speech);
1650
+ if (voiceOn && opts.speech) {
1651
+ if (typeof o.voice === "object") opts.speech.setOptions?.(o.voice);
1652
+ if (opts.audio) {
1653
+ audioOn = true;
1654
+ try {
1655
+ await opts.audio.activate();
1656
+ } catch {
1657
+ }
1658
+ }
1659
+ let available = true;
1660
+ try {
1661
+ available = await opts.speech.available(lang);
1662
+ } catch {
1663
+ }
1664
+ if (token !== startToken) return;
1665
+ if (!available) emit("voiceUnavailable", void 0);
1666
+ }
1667
+ keepOn = (o.keepAwake ?? true) && Boolean(opts.keepAwake);
1668
+ if (keepOn) {
1669
+ try {
1670
+ await opts.keepAwake?.activate();
1671
+ } catch {
1672
+ }
1673
+ }
1674
+ if (token !== startToken) return;
1675
+ nav = engine;
1676
+ for (const name of FORWARDED) {
1677
+ engine.on(name, (e) => emit(name, e));
1678
+ }
1679
+ engine.on("status", (e) => {
1680
+ if (e.status === "stopped") return;
1681
+ if (e.status === "navigating" && e.previous === "idle") return;
1682
+ emit("status", e);
1683
+ });
1684
+ engine.on("announce", (a) => {
1685
+ if (voiceOn) opts.speech?.speak(a.text, a.priority, lang);
1686
+ });
1687
+ engine.on("reroute", (e) => {
1688
+ response = e.response;
1689
+ routeIndex = 0;
1690
+ emit("route", { response: e.response, routeIndex: 0 });
1691
+ });
1692
+ engine.on("arrive", () => {
1693
+ ended = true;
1694
+ releaseSource();
1695
+ void releaseDevice();
1696
+ emit("end", { reason: "arrived" });
1697
+ });
1698
+ emit("route", { response: o.response, routeIndex });
1699
+ emit("status", { status: "navigating", previous: "idle" });
1700
+ source.setMode?.(mode);
1701
+ source.onBackgroundUnavailable?.((e) => emit("backgroundUnavailable", e));
1702
+ unsubscribe = source.subscribe(
1703
+ (fix) => engine.update(fix),
1704
+ (error) => emit("positionError", error)
1705
+ );
1706
+ }
1707
+ return {
1708
+ start,
1709
+ stop,
1710
+ reroute() {
1711
+ return nav ? nav.reroute() : Promise.reject(new Error("Phi\xEAn d\u1EABn \u0111\u01B0\u1EDDng ch\u01B0a start()"));
1712
+ },
1713
+ setRoute(next, index = 0) {
1714
+ response = next;
1715
+ routeIndex = index;
1716
+ nav?.setRoute(next, index);
1717
+ emit("route", { response: next, routeIndex: index });
1718
+ },
1719
+ get status() {
1720
+ return currentStatus();
1721
+ },
1722
+ get state() {
1723
+ return nav?.progress ?? null;
1724
+ },
1725
+ get response() {
1726
+ return response;
1727
+ },
1728
+ get routeIndex() {
1729
+ return routeIndex;
1730
+ },
1731
+ on(event, handler) {
1732
+ let set = listeners.get(event);
1733
+ if (!set) {
1734
+ set = /* @__PURE__ */ new Set();
1735
+ listeners.set(event, set);
1736
+ }
1737
+ set.add(handler);
1738
+ },
1739
+ off(event, handler) {
1740
+ listeners.get(event)?.delete(handler);
1741
+ }
1742
+ };
1743
+ }
1744
+
472
1745
  // src/to-poi-feature.ts
473
1746
  function toPoiFeature(feature) {
474
1747
  if (!feature || feature.geometry.type !== "Point") return null;
@@ -538,7 +1811,7 @@ function useResolvedStyle(places, options, doFetch = globalThis.fetch) {
538
1811
  }
539
1812
 
540
1813
  // src/map.tsx
541
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1814
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
542
1815
  var DEFAULT_CENTER = [106.7, 10.776];
543
1816
  var DEFAULT_ZOOM = 12;
544
1817
  function MapsLibVNMap({
@@ -553,6 +1826,13 @@ function MapsLibVNMap({
553
1826
  compactAttribution = false,
554
1827
  bundleId,
555
1828
  containerStyle,
1829
+ navigation,
1830
+ sessionOptions,
1831
+ follow = true,
1832
+ puck = true,
1833
+ routeStyle,
1834
+ routeBeforeLayerId,
1835
+ onRouteClick,
556
1836
  onLoad,
557
1837
  onPoiClick,
558
1838
  onError,
@@ -571,8 +1851,31 @@ function MapsLibVNMap({
571
1851
  );
572
1852
  const native = useRef(null);
573
1853
  const camera = useRef(null);
574
- const handlers = useRef({ onLoad, onPoiClick, onError });
575
- handlers.current = { onLoad, onPoiClick, onError };
1854
+ const handlers = useRef({ onLoad, onPoiClick, onError, onRouteClick });
1855
+ handlers.current = { onLoad, onPoiClick, onError, onRouteClick };
1856
+ const sessionOptionsRef = useRef(sessionOptions);
1857
+ sessionOptionsRef.current = sessionOptions;
1858
+ const store = useMemo(() => createRoutesStore(), []);
1859
+ const binding = useMemo(
1860
+ () => createMapBinding({
1861
+ camera,
1862
+ store,
1863
+ appState: AppState,
1864
+ createDefaultSession: () => createNavigationSession({ provider: places, ...sessionOptionsRef.current })
1865
+ }),
1866
+ [places, store]
1867
+ );
1868
+ useEffect2(() => () => binding.dispose(), [binding]);
1869
+ useEffect2(() => {
1870
+ binding.attach(navigation ?? null);
1871
+ }, [binding, navigation]);
1872
+ const followKey = JSON.stringify(follow);
1873
+ useEffect2(() => {
1874
+ binding.setFollow(JSON.parse(followKey));
1875
+ }, [binding, followKey]);
1876
+ useEffect2(() => {
1877
+ store.setPuck(puck);
1878
+ }, [store, puck]);
576
1879
  const handle = useMemo(
577
1880
  () => ({
578
1881
  native,
@@ -590,9 +1893,15 @@ function MapsLibVNMap({
590
1893
  const b = await native.current?.getBounds();
591
1894
  if (!b) throw new Error("B\u1EA3n \u0111\u1ED3 ch\u01B0a s\u1EB5n s\xE0ng");
592
1895
  return b;
593
- }
1896
+ },
1897
+ routes: {
1898
+ show: (response, opts) => store.show(response, opts ?? {}),
1899
+ setActive: (index) => store.setActive(index),
1900
+ clear: () => store.clear()
1901
+ },
1902
+ navigation: binding.api
594
1903
  }),
595
- [places]
1904
+ [places, store, binding]
596
1905
  );
597
1906
  const resolved = useResolvedStyle(places, { style, lang, poiLayer });
598
1907
  useEffect2(() => {
@@ -608,17 +1917,22 @@ function MapsLibVNMap({
608
1917
  const poi = toPoiFeature(features?.[0]);
609
1918
  if (poi) handlers.current.onPoiClick?.(poi);
610
1919
  };
611
- return /* @__PURE__ */ jsxs(View, { style: [styles2.container, containerStyle], ...testID ? { testID } : {}, children: [
612
- resolved.status === "ready" ? /* @__PURE__ */ jsxs(
1920
+ const onRegionWillChange = (e) => {
1921
+ if (e.nativeEvent.userInteraction) binding.userGesture();
1922
+ };
1923
+ const beforeId = routeBeforeLayerId === void 0 ? isTheme(style) ? FIRST_SYMBOL_LAYER_ID[style] : null : routeBeforeLayerId;
1924
+ return /* @__PURE__ */ jsxs2(View2, { style: [styles3.container, containerStyle], ...testID ? { testID } : {}, children: [
1925
+ resolved.status === "ready" ? /* @__PURE__ */ jsxs2(
613
1926
  NativeMap,
614
1927
  {
615
1928
  ref: native,
616
- style: styles2.map,
1929
+ style: styles3.map,
617
1930
  mapStyle: resolved.mapStyle,
618
1931
  attribution: true,
619
1932
  attributionPosition: { bottom: 8, right: 8 },
620
1933
  logo: false,
621
1934
  onPress,
1935
+ onRegionWillChange,
622
1936
  onDidFinishLoadingStyle: () => {
623
1937
  if (loadedFor.current === mapKey) return;
624
1938
  loadedFor.current = mapKey;
@@ -626,13 +1940,24 @@ function MapsLibVNMap({
626
1940
  },
627
1941
  onDidFailLoadingMap: () => handlers.current.onError?.(new Error("Kh\xF4ng t\u1EA3i \u0111\u01B0\u1EE3c b\u1EA3n \u0111\u1ED3")),
628
1942
  children: [
629
- /* @__PURE__ */ jsx2(Camera, { ref: camera, initialViewState: { center, zoom } }),
630
- /* @__PURE__ */ jsx2(MapContext.Provider, { value: handle, children })
1943
+ /* @__PURE__ */ jsx4(Camera, { ref: camera, initialViewState: { center, zoom } }),
1944
+ /* @__PURE__ */ jsxs2(MapContext.Provider, { value: handle, children: [
1945
+ /* @__PURE__ */ jsx4(
1946
+ RouteLayers,
1947
+ {
1948
+ store,
1949
+ routeStyle,
1950
+ beforeId,
1951
+ onRouteClick: (index) => handlers.current.onRouteClick?.(index)
1952
+ }
1953
+ ),
1954
+ children
1955
+ ] })
631
1956
  ]
632
1957
  },
633
1958
  mapKey
634
1959
  ) : null,
635
- /* @__PURE__ */ jsx2(
1960
+ /* @__PURE__ */ jsx4(
636
1961
  Attribution,
637
1962
  {
638
1963
  compact: compactAttribution,
@@ -644,56 +1969,19 @@ function MapsLibVNMap({
644
1969
  ] });
645
1970
  }
646
1971
  function useMap() {
647
- const map = useContext(MapContext);
1972
+ const map = useContext2(MapContext);
648
1973
  if (!map) throw new Error("useMap ph\u1EA3i \u0111\u01B0\u1EE3c g\u1ECDi b\xEAn trong <MapsLibVNMap>");
649
1974
  return map;
650
1975
  }
651
- var styles2 = StyleSheet2.create({
1976
+ var styles3 = StyleSheet3.create({
652
1977
  container: { flex: 1, position: "relative" },
653
1978
  map: { flex: 1 }
654
1979
  });
655
1980
 
656
- // src/marker.tsx
657
- import { Marker as NativeMarker } from "@maplibre/maplibre-react-native";
658
- import { StyleSheet as StyleSheet3, View as View2 } from "react-native";
659
- import { jsx as jsx3 } from "react/jsx-runtime";
660
- var DEFAULT_MARKER_COLOR = "#3FB1CE";
661
- function Marker({
662
- lng,
663
- lat,
664
- color = DEFAULT_MARKER_COLOR,
665
- anchor = "center",
666
- onPress,
667
- children,
668
- testID
669
- }) {
670
- useMap();
671
- const pin = children ?? /* @__PURE__ */ jsx3(View2, { style: [styles3.pin, { backgroundColor: color }], testID: "mapslibvn-marker-pin" });
672
- return /* @__PURE__ */ jsx3(
673
- NativeMarker,
674
- {
675
- lngLat: [lng, lat],
676
- anchor,
677
- ...onPress ? { onPress: () => onPress() } : {},
678
- ...testID ? { testID } : {},
679
- children: pin
680
- }
681
- );
682
- }
683
- var styles3 = StyleSheet3.create({
684
- pin: {
685
- width: 22,
686
- height: 22,
687
- borderRadius: 11,
688
- borderWidth: 3,
689
- borderColor: "#ffffff"
690
- }
691
- });
692
-
693
1981
  // src/use-places.ts
694
- import { useContext as useContext2, useEffect as useEffect3, useState as useState2 } from "react";
1982
+ import { useContext as useContext3, useEffect as useEffect3, useState as useState2 } from "react";
695
1983
  function usePlaces(query, options = {}) {
696
- const mapClient = useContext2(MapContext)?.places ?? null;
1984
+ const mapClient = useContext3(MapContext)?.places ?? null;
697
1985
  const client = options.client ?? mapClient;
698
1986
  const [items, setItems] = useState2([]);
699
1987
  const [loading, setLoading] = useState2(false);
@@ -730,13 +2018,125 @@ function usePlaces(query, options = {}) {
730
2018
  }, [client, query, nearKey, options.limit, options.debounceMs]);
731
2019
  return { items, loading, error };
732
2020
  }
2021
+
2022
+ // src/use-navigation.ts
2023
+ import { useCallback, useContext as useContext4, useSyncExternalStore as useSyncExternalStore2 } from "react";
2024
+ var noop = () => {
2025
+ };
2026
+ function useNavigation(session) {
2027
+ const map = useContext4(MapContext);
2028
+ const binding = session ? null : map?.navigation ?? null;
2029
+ const target = session ?? binding;
2030
+ if (!target) {
2031
+ throw new Error("useNavigation ph\u1EA3i \u0111\u01B0\u1EE3c g\u1ECDi b\xEAn trong <MapsLibVNMap> ho\u1EB7c truy\u1EC1n session");
2032
+ }
2033
+ const subscribeStatus = useCallback(
2034
+ (onChange) => {
2035
+ target.on("status", onChange);
2036
+ return () => target.off("status", onChange);
2037
+ },
2038
+ [target]
2039
+ );
2040
+ const subscribeProgress = useCallback(
2041
+ (onChange) => {
2042
+ target.on("progress", onChange);
2043
+ return () => target.off("progress", onChange);
2044
+ },
2045
+ [target]
2046
+ );
2047
+ const subscribeFollow = useCallback(
2048
+ (onChange) => {
2049
+ if (!binding) return noop;
2050
+ binding.on("followChange", onChange);
2051
+ return () => binding.off("followChange", onChange);
2052
+ },
2053
+ [binding]
2054
+ );
2055
+ const status = useSyncExternalStore2(
2056
+ subscribeStatus,
2057
+ () => target.status,
2058
+ () => target.status
2059
+ );
2060
+ const progress = useSyncExternalStore2(
2061
+ subscribeProgress,
2062
+ () => target.state,
2063
+ () => target.state
2064
+ );
2065
+ const following = useSyncExternalStore2(
2066
+ subscribeFollow,
2067
+ () => binding?.following ?? false,
2068
+ () => binding?.following ?? false
2069
+ );
2070
+ return {
2071
+ status,
2072
+ progress,
2073
+ following,
2074
+ start: (opts) => target.start(opts),
2075
+ stop: () => target.stop(),
2076
+ reroute: () => target.reroute(),
2077
+ recenter: () => binding?.recenter()
2078
+ };
2079
+ }
2080
+
2081
+ // src/navigation/playback-source.ts
2082
+ function playbackSource(fixes, options = {}) {
2083
+ const rate = options.rate ?? 1;
2084
+ return {
2085
+ subscribe(onFix) {
2086
+ let stopped = false;
2087
+ let timer = null;
2088
+ let i = 0;
2089
+ const emitNext = () => {
2090
+ if (stopped) return;
2091
+ const fix = fixes[i];
2092
+ if (!fix) return;
2093
+ onFix(fix);
2094
+ i += 1;
2095
+ const next = fixes[i];
2096
+ if (!next) return;
2097
+ timer = setTimeout(emitNext, Math.max(0, (next.timestamp - fix.timestamp) / rate));
2098
+ };
2099
+ timer = setTimeout(
2100
+ rate <= 0 ? () => {
2101
+ for (const f of fixes) {
2102
+ if (stopped) break;
2103
+ onFix(f);
2104
+ }
2105
+ } : emitNext,
2106
+ 0
2107
+ );
2108
+ return () => {
2109
+ stopped = true;
2110
+ if (timer !== null) clearTimeout(timer);
2111
+ };
2112
+ }
2113
+ };
2114
+ }
733
2115
  export {
2116
+ ALT_ROUTE_COLOR,
734
2117
  COMPACT_ATTRIBUTION,
735
2118
  DEFAULT_CENTER,
736
2119
  DEFAULT_MARKER_COLOR,
737
2120
  DEFAULT_ZOOM,
2121
+ DESTINATION_COLOR,
2122
+ FOLLOW_PITCH,
2123
+ FOLLOW_ZOOM,
2124
+ MISSING_SOURCE_MESSAGE,
738
2125
  MapsLibVNMap,
739
2126
  Marker,
2127
+ NAVIGATION_THRESHOLDS,
2128
+ ROUTE_COLOR,
2129
+ ROUTE_LAYER_IDS,
2130
+ ROUTE_SOURCE_ID,
2131
+ createClient,
2132
+ createNavigationSession,
2133
+ createNavigator,
2134
+ decodePolyline6,
2135
+ formatDistance,
2136
+ formatDistanceShort,
2137
+ playbackSource,
2138
+ simulateFixes,
740
2139
  useMap,
2140
+ useNavigation,
741
2141
  usePlaces
742
2142
  };