npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ferstekr/nav-sdk

v0.20.2

Published

Açık kaynak, lisanssız A→B rota SDK'sı: self-hosted OSRM + MapLibre React Native. Kurye/teslimat uygulamaları için rota çizimi, ETA ve otomatik reroute.

Readme

@ferstekr/nav-sdk

Açık kaynak, lisanssız, API key'siz A→B rota SDK'sı. Self-hosted OSRM + MapLibre React Native üzerine kuruludur. Plain JS, sıfır runtime bağımlılığı (peer'lar hariç), transpile edilmemiş kaynak olarak dağıtılır — Metro/Babel node_modules içindeki JSX'i zaten derler (React Native ekosisteminde standart yaklaşım).

@ferstekr/nav-sdk            → çekirdek: client, store, hook, geo, maneuvers (sadece react + zustand ister)
@ferstekr/nav-sdk/components → RouteMap, RouteLine (@maplibre/maplibre-react-native v10 veya v11)
@ferstekr/nav-sdk/ui         → ManeuverBanner, ManeuverIcon, TripFooter (harita gerektirmez)
@ferstekr/nav-sdk/expo       → useLiveLocation (expo-location gerekir)
@ferstekr/nav-sdk/expo/background → arka plan takibi (ayrıca expo-task-manager gerekir)

Alt yol ayrımı bilinçli: harita bileşenlerini veya expo-location'ı kullanmayan bir proje bu paketleri kurmadan çekirdeği import edebilir (ikisi de peerDependenciesMeta ile optional işaretlidir).

Kurulum

npm install @ferstekr/nav-sdk zustand
npm install @maplibre/maplibre-react-native   # RouteMap/RouteLine kullanacaksanız
npm install expo-location                     # useLiveLocation kullanacaksanız
npm install expo-task-manager                 # arka plan konum takibi için
npm install react-native-svg                  # manevra ikonları için (Expo'da zaten kurulu)
npx pod-install                               # iOS

Hızlı başlangıç

import { createOsrmClient, useRoute } from "@ferstekr/nav-sdk";
import { RouteMap } from "@ferstekr/nav-sdk/components";

const client = createOsrmClient({ baseUrl: "https://osrm.sizin-sunucunuz.com" });

function DeliveryScreen({ courierLocation, order }) {
  const destination = { latitude: order.lat, longitude: order.lng };

  const { route, progress, status, remainingMeters, remainingSeconds, deviationMeters } = useRoute({ client, location: courierLocation, destination });

  return <RouteMap route={route} progress={progress} courierLocation={courierLocation} destination={destination} mapStyle="https://tiles.sizin-sunucunuz.com/styles/basic/style.json" />;
}

Kendi haritanız varsa RouteMap yerine sadece çizgiyi ekleyin:

import { RouteLine } from '@ferstekr/nav-sdk/components';

<MapView ...>
  <RouteLine geometry={route?.geometry} progress={progress} />
</MapView>

progress geçtiğinizde kuryenin geçtiği kısım çizgiden kırpılır; haritada arkada trail kalmaz.

Tam bağlanmış bir test ekranı repo'daki examples/NavigationExample.js içindedir (npm paketine dahil edilmez).

OSRM sunucusu (test için)

mkdir osrm && cd osrm
wget https://download.geofabrik.de/europe/turkey-latest.osm.pbf

docker run -t -v "$(pwd):/data" osrm/osrm-backend osrm-extract -p /opt/car.lua /data/turkey-latest.osm.pbf
docker run -t -v "$(pwd):/data" osrm/osrm-backend osrm-partition /data/turkey-latest.osrm
docker run -t -v "$(pwd):/data" osrm/osrm-backend osrm-customize /data/turkey-latest.osrm

docker run -t -i -p 5000:5000 -v "$(pwd):/data" osrm/osrm-backend osrm-routed --algorithm mld /data/turkey-latest.osrm

Doğrulama: curl "http://localhost:5000/route/v1/driving/29.061,40.1885;29.08,40.20?overview=false""code":"Ok".

baseUrl

| Ortam | baseUrl | | ------------------------- | -------------------------- | | iOS simülatörü | http://localhost:5000 | | Android emülatörü | http://10.0.2.2:5000 | | Gerçek cihaz (aynı Wi-Fi) | http://<Mac-LAN-IP>:5000 |

HTTP (cleartext) uyarısı: test ederken iOS'ta ATS (NSAllowsArbitraryLoads) ve Android'de android:usesCleartextTraffic="true" gerekir; yoksa istekler sessizce başarısız olur. Prod'da OSRM'i reverse proxy arkasında https ile sunun.

API referansı

createOsrmClient({ baseUrl, profile?, timeoutMs? })

Saf HTTP istemcisi. fetchRoute(origin, destination, { steps? }){ geometry, distance (m), duration (sn), legs, raw }. Hatalar OsrmError (code: 'NoRoute' | 'timeout' | 'network' | 'http_error' | ...).

useRoute(params)

| Parametre | Varsayılan | Açıklama | | --------- | ---------- | -------- | | client, location, destination | — | Veri | | enabled | true | false ise store sıfırlanır ve istek atılmaz | | withSteps | false | Manevra adımlarını da iste (turn-by-turn) | | locale | 'tr' | Talimat dili | | rerouteThresholdMeters | 50 | Bu sapmanın üstünde reroute. Eşiğe location.accuracy eklenir | | rerouteCooldownMs | 8000 | İki istek arası minimum süre | | rerouteMinIntervalMs | 2500 | Açıkça rotadan çıkıldığında geçerli kısa bekleme | | offRouteConfirmMs | 5000 | Sapmanın eşiğin üstünde ne kadar kalması gerektiği | | maxAccuracyMeters | 150 | Bundan kötü fix'ler ilerleme hesabına alınmaz | | errorRetryMs | 15000 | Hata sonrası otomatik yeniden deneme; 0 kapatır | | snapForwardMeters / snapBackwardMeters | 600 / 80 | Eşleştirme penceresinin ileri tavanı / geri genişliği | | snapMinForwardMeters | 40 | Pencere bu değerin altına inmez | | snapAssumedSpeedMps | 20 | Fix hız bildirmiyorsa varsayılan hız | | staleFixMs | 15000 | Bu aradan sonra tüm rota yeniden taranabilir | | store | paylaşılan | createNavigationStore() çıktısı |

| Alan | Açıklama | | -------------------------------------- | ----------------------------------------------------------- | | status | 'idle' \| 'loading' \| 'active' \| 'rerouting' \| 'error' | | route | Aktif rota (geometry: GeoJSON LineString) | | remainingMeters / remainingSeconds | Kuryenin rota üzerindeki konumuna göre kalan mesafe / ETA | | deviationMeters | Rotadan dik uzaklık | | traveledMeters | Rota başından kat edilen mesafe | | progress | { segmentIndex, segmentT } — kuryenin rota üzerindeki izdüşümü; RouteMap/RouteLine'a geçirin ki geçilen yol çizgiden kırpılsın | | steps | Manevra adımları (withSteps: true verilmişse; yoksa boş dizi) | | currentStep / nextStep | Üzerinde ilerlenen adım / yaklaşılan manevra | | thenStep | nextStep'ten sonraki manevra (şeridin "sonra" satırı) | | distanceToManeuver | nextStep'in manevrasına kalan mesafe (m) | | isOffRoute | Sapma eşiği aştı — ekrandaki talimat bayat | | rerouteCount | Yeniden rota sayısı | | refetch() / clear() | Manuel yenileme / navigasyonu bitirme |

Reroute: sapma eşiği aşarsa ve son istekten bu yana rerouteCooldownMs geçtiyse mevcut konumdan otomatik yeni rota çekilir. Uçuşta bir istek varsa iptal edilir — geç dönen eski cevap rotayı geri sarmaz.

Rota eşleştirme penceresi. Kuryenin rota üzerindeki yeri, rotanın tamamında değil bir önceki izdüşümün etrafındaki pencerede aranır ve pencere kuryenin son fix'ten bu yana kat edebileceği mesafeden hesaplanır (hız × geçen süre; taban snapMinForwardMeters, tavan snapForwardMeters).

Bu, iki ayrı hatayı birden kapatır. Rota kendine yaklaştığında (U dönüşü, gidiş-dönüş kolları) global arama tek bir gürültülü fix'te karşı kola kilitlenirdi. Ve kurye bir dönüşü kaçırıp düz devam ettiğinde, rotanın ileride geri gelen kolu ona en yakın nokta olur; izdüşüm oraya atlayınca sanki dönmüş gibi bir sonraki talimat gösterilir, kalan mesafe çöker ve sapma küçük okunduğu için reroute tetiklenmez. Üç saniyede 150 metre gidilemeyeceğini bilen bir pencerede ikisi de imkânsızdır.

Fix'ler düzenli akarken tüm rotanın yeniden taranması kapalıdır — kurye ışınlanamaz. Tarama yalnızca staleFixMs'ten uzun bir aradan sonra (uygulama arka planda kalmış, tünelden çıkılmış) yapılır; orada sıçrama istenen davranıştır.

Rotadan çıkınca. isOffRoute sapmanın eşiği aştığını söyler. Karar tek fix'e bakarak verilmez ve fix sayısıyla da ölçülmez, süreyle ölçülür: bayrağın kalkması için sapmanın eşiğin üstünde offRouteConfirmMs kadar kalması gerekir. Üst üste birkaç fix beklemek yalnızca bağımsız gürültüye karşı işe yarar; gerçek GPS hatası zamanla ilintilidir — köprü altında art arda beş on kötü fix olağandır. Bayrağın inmesi için ise sapmanın eşiğin %60'ına düşmesi gerekir (histerezis, yoksa şerit yanıp söner). Rotaya dönmek anında sayılır. Yeni rota gelene kadar nextStep bayattır — kurye o manevrayı zaten kaçırmıştır — o yüzden şeride geçirin, yanlış dönüş yerine "Rotadan çıkıldı" yazsın:

<ManeuverBanner step={nextStep} distance={distanceToManeuver} offRoute={isOffRoute} />

Reroute iki kademelidir: sapma eşiğin 2.5 katını aşarsa tam rerouteCooldownMs yerine rerouteMinIntervalMs (2.5 sn) uygulanır, çünkü 8 saniye boyunca yanlış talimat göstermek kabul edilemez. Sınırdaki sapmalar istek yağmuru yaratmasın diye tam cooldown'a tabidir.

Konum doğruluğu. location.accuracy verirseniz (useLiveLocation verir) reroute eşiği kadarıyla gevşer: ±80 m'lik bir fix sabit 50 m eşiği kurye rotadan hiç çıkmadan aşardı. maxAccuracyMeters'tan kötü fix'ler ilerleme hesabına hiç girmez — nokta yine haritada çizilir, sadece rota eşleştirmesi o fix'i atlar.

Turn-by-turn talimatlar

withSteps: true verin; SDK OSRM'den manevra adımlarını da ister ve kat edilen mesafeye göre yaklaşılan manevrayı çözer.

import { useRoute } from "@ferstekr/nav-sdk";
import { ManeuverBanner } from "@ferstekr/nav-sdk/ui";

const { nextStep, thenStep, distanceToManeuver, isOffRoute } = useRoute({ client, location, destination, withSteps: true });

// büyük "82 km", altında "Gebze Orhangazi Karayolu", alt şeritte "sonra ↰"
<ManeuverBanner step={nextStep} thenStep={thenStep} distance={distanceToManeuver} offRoute={isOffRoute} />;

ManeuverBanner MapLibre'ye bağlı değildir — sadece react-native kullanır, o yüzden maplibre v10'da da v11'de de (hatta haritasız da) çalışır. Haritanın üstüne position: "absolute" ile koyun.

nextStep yaklaşılan manevradır (içinde bulunulan adım currentStep); alanları:

| Alan | Örnek | | ---- | ----- | | instruction | "Çatalda soldan devam edin" — hazır Türkçe cümle | | icon | "fork-left" — sabit semantik ad, kendi ikon setinize eşleyin | | name / ref | "Muallimköy Kavşağı" / "O-4" | | type / modifier / exit | Ham OSRM alanları ("fork" / "slight left" / 2) | | distance / startDistance / endDistance | Adım uzunluğu ve rota başına göre kümülatif sınırlar | | maneuver | Ham OSRM maneuver nesnesi |

Tüm liste steps'te; distanceToManeuver manevraya kalan mesafedir (m).

ManeuverBanner props

step, thenStep, distance, offRoute, road (yol adını kendiniz verin), formatInstruction (step) => string, renderIcon (step) => ReactNode, locale, hideWhenEmpty, style, iconStyle, distanceStyle, roadStyle, thenStyle.

Düzen: büyük olan mesafedir, talimat değil — sürücü dönüşü ikondan zaten okur, gözüyle aradığı sayı kaç metre kaldığıdır. Alt şeritte thenStep'in yalnızca ikonu durur; "sağa dön, sonra hemen sola" bilgisi dönüşe girmeden önce gerekir.

İkonlar hazır gelir ve iki tonludur: gitmediğiniz yol soluk, gittiğiniz yol parlak ve ok ucu onda. Ayrım kozmetik değil — tek renkli bir glifte çatalda hangi kolun sizin olduğu okunmaz; "Sağdaki çıkışı kullanın" derken ekranda ana yolun mu rampanın mı vurgulandığı belirsiz kalır. Google Maps de navigasyonda düz Material gliflerini değil iki tonlu ikonları kullanır.

Eşleştirme tablosu yazmanız gerekmez, iconColor ve iconSize dışında yapılacak bir şey yoktur. Çizim react-native-svg ile yapılır; kurulu değilse unicode oklara düşülür. Google'ın tek renkli Material setini tercih ederseniz iconSet="material" ile geri alınır.

Yön gerektiren durumları SDK çözer: göbeğin hangi yöne döndüğünü OSRM'in modifier'ından, U dönüşünün hangi yöne kırıldığını trafiğin aktığı taraftan (drivingSide) çıkarır. Bu yüzden step.icon her zaman yönüyle birlikte gelir — roundabout-right, uturn-left — ve sizde dallanma kalmaz.

Kendi setinizi kullanmak isterseniz renderIcon gömülü ikonları tamamen devre dışı bırakır:

<ManeuverBanner step={nextStep} renderIcon={(s) => <Icon name={MY_ICONS[s.icon] ?? "arrow-up"} size={30} />} />

Adların tamamı maneuvers.MANEUVER_ICON_NAMES'te; maneuvers.materialSymbols semantik adı Material adına çevirir.

Talimatları kendiniz üretmek (başka dil, kısaltma vb.) isterseniz formatInstruction veya doğrudan maneuvers yardımcıları:

import { maneuvers } from "@ferstekr/nav-sdk";

maneuvers.buildSteps(route); // adım listesi
maneuvers.resolveManeuver(steps, traveledMeters); // { currentStep, nextStep, distanceToManeuver }
maneuvers.maneuverInstruction(maneuver, { name }); // Türkçe cümle
maneuvers.maneuverIcon(maneuver); // semantik ikon adı
maneuvers.formatManeuverDistance(m); // "82 km" | "350 m" | "Şimdi"

Pürüzsüz konum güncellemesi

GPS donanımı ~1 Hz. Navigasyon uygulamaları daha sık veri almaz, aradaki boşluğu doldurur. SDK bunu iki yerde yapar:

  • Kamera animasyonu fix aralığına uyar. Sabit bir süre, fix aralığından kısa kaldığında kamerayı "kay, dur, kay, dur" ettirir — tick hissinin asıl kaynağı budur. Süre ölçülen aralıktan hesaplanır, followAnimationMaxMs ile tavanlanır (tüm aralığa yaymak gösterilen konumu bir aralık kadar geriye atardı).
  • İşaretçi kamerayla aynı süreyi kullanır (animateCourier). Kamera yumuşak kayarken işaretçi ham koordinata anında çizilseydi çapasının önüne geçip geri süzülürdü. v10 ve v11'de çalışır.

Üçüncü ve en büyük etken fix aralığının kendisidir — ve pil sandığınız yerde harcanmaz.

useLiveLocation doğruluk modu (accuracy) GNSS alıcısını ne kadar çalıştıracağını belirler ve fatura oradadır. distanceInterval / timeInterval ise yalnızca sonucun ne sıklıkta JS'e iletileceğini süzer; radyoya ek yük getirmez. Yani teslimat sıklığını kısarak pil kazanmazsınız, sadece hareketi kekeme yaparsınız.

Doğru kol rota durumudur:

useLiveLocation({
  enabled: isFocused,
  accuracy: destination ? "navigation" : "balanced",
  distanceInterval: destination ? 0 : 25,
  timeInterval: destination ? 1000 : 10000,
});

Boştayken balanced (~100 m, şebeke/Wi-Fi ağırlıklı) kuryenin nerede olduğunu bilmeye fazlasıyla yeter ve alıcıyı sürekli açık tutmaz. Rota aktifken tam hassasiyet ve sık teslimat. Bu, sabit navigation'dan daha az pil harcar.

Arka plan konum takibi

Foreground watch, kurye telefonu cebine koyup ekranı kapattığında durur. Teslimat uygulamasında bu kabul edilemez; arka plan takibi üç adımla açılır.

1. Görevi uygulama girişinde, modül seviyesinde tanımlayın. React ağacı kurulmadan önce kayıtlı olmalı — OS görevi uyandırdığında tanımsız bulursa konumlar sessizce kaybolur:

// index.js / App.js'in EN ÜSTÜ
import { defineBackgroundLocationTask } from "@ferstekr/nav-sdk/expo/background";
defineBackgroundLocationTask();

2. app.json:

{
  "ios": {
    "infoPlist": {
      "UIBackgroundModes": ["location"],
      "NSLocationAlwaysAndWhenInUseUsageDescription": "Teslimat sırasında rotanızı takip etmek için."
    }
  },
  "android": { "permissions": ["ACCESS_BACKGROUND_LOCATION", "FOREGROUND_SERVICE"] },
  "plugins": [["expo-location", { "isAndroidBackgroundLocationEnabled": true }]]
}

3. Takip başlatın ve hook'u arka plandan besleyin:

import { startBackgroundLocation, stopBackgroundLocation } from "@ferstekr/nav-sdk/expo/background";

// teslimat başlarken
await startBackgroundLocation();

// hook artık kendi watch'ını açmaz, görevin yayınını dinler
const { location } = useLiveLocation({ background: true });

// teslimat biterken
await stopBackgroundLocation();

Başlatmayı hook'un içinde yapmıyoruz bilerek: arka plan takibi Android'de kalıcı bildirimli bir foreground service açar, iOS'ta mavi durum çubuğu yakar. Teslimat yokken bunun çalışmaması gerekir, o karar sizin.

expo-task-manager yalnızca bu alt yolda import edilir; kurmayan projeler @ferstekr/nav-sdk/expo'yu sorunsuz kullanmaya devam eder.

Dil

Talimatlar tr ve en ile gelir:

useRoute({ client, location, destination, withSteps: true, locale: "en" });

Kendi dilinizi ekleyin — bir dil iki fonksiyondan ibarettir (dilbilgisi çok değiştiği için düz string tablosu yerine):

import { maneuvers } from "@ferstekr/nav-sdk";

maneuvers.registerLocale("de", {
  instruction: (maneuver, step) => (maneuver.type === "turn" ? `Nach ${maneuver.modifier} abbiegen` : "Geradeaus"),
  distance: (m) => (m < 1000 ? `${Math.round(m / 10) * 10} m` : `${(m / 1000).toFixed(1)} km`),
});

maneuvers.setDefaultLocale("de");

TripFooter

Yolculuk özeti: kalan mesafe, tahmini varış saati, kalan süre, ilerleme çubuğu ve iptal.

import { TripFooter } from "@ferstekr/nav-sdk/ui";

<TripFooter
  remainingMeters={remainingMeters}
  remainingSeconds={remainingSeconds}
  traveledMeters={traveledMeters}
  routeDistance={route?.distance}
  onCancel={cancelTrip}
/>;

Çubuk trafik göstermez, ilerleme gösterir. Navigasyon uygulamalarında bu çubuk yeşil-sarı-kırmızı boyanıp rota üzerindeki anlık akışı gösterir; OSRM canlı trafik görmediği için burada öyle bir veri yok. Uydurma bir trafik renklendirmesi, kuryenin güzergâh kararını etkileyecek bir yerde olmayan veriyi varmış gibi göstermek olurdu. Aynı sebeple ETA'lar iyimserdir — şehir içinde remainingSeconds'a katsayı uygulamak isteyebilirsiniz.

Props: remainingMeters, remainingSeconds, traveledMeters, routeDistance, color, onCancel, cancelLabel, renderLeading, locale, hideWhenEmpty, style, textStyle, trackColor, fillColor.

style ile zemini değiştirirseniz color'ı da verin. İkisi de koyu bir şerit varsayar; açık bir zemine oturttuğunuzda varsayılan beyaz metin görünmez olur. color metni, ikonu, iptal düğmesini ve çubuğun zeminini birlikte sürer — aynısı ManeuverBanner için de geçerli.

useNavigationStore

Zustand store — harita dışındaki bileşenler doğrudan okuyabilir:

const eta = useNavigationStore((s) => s.remainingSeconds);

Bu paylaşılan tekil örnektir ve tek rota gösteren uygulamalar için yeterlidir. Aynı anda birden fazla rota izliyorsanız (iki ekran, sipariş listesinde birden çok kurye) her biri için ayrı store açın, yoksa hepsi aynı state'i ezer:

import { createNavigationStore, useRoute } from "@ferstekr/nav-sdk";

const store = useMemo(() => createNavigationStore(), []);
const nav = useRoute({ client, location, destination, store });

RouteMap props

| Prop | Varsayılan | Açıklama | | ---- | ---------- | -------- | | route, courierLocation, destination | — | Veri | | progress | null | useRoute().progress — geçilen yolu çizgiden kırpar | | trimTraveled | true | false yaparsanız tüm rota çizilir (eski davranış) | | mapStyle, lineColor, lineCasingColor, fitPadding, style, children | — | Görünüm | | initialFollow | false | true ise takip modu açık başlar | | followMode | "north" | "course" sürüş görünümü: harita gidiş yönüne döner, eğilir, yakınlaşır, kurye ekranın altına iner | | followZoom / followPitch | moddan | Takip açıldığında uygulanır; sonrasında kullanıcının elle yaptığı zoom korunur. Prop'u değiştirmek yeniden uygular | | followHeadingMinSpeedMps | 1.4 | Bu hızın altında GPS yönü güvenilmez; son geçerli yön korunur | | courierVariant | "auto" | Aktif rota ve bilinen yön varsa ok, yoksa nokta | | snapCourierToRoute | false | İşaretçiyi ham fix yerine rotanın üzerine oturt (aşağıya bakın) | | snapMaxDeviationMeters | 12 | Yapıştırmanın izin verdiği en büyük kayma | | offRoute | false | useRoute().isOffRoute — yapıştırmayı kapatır | | courierColor / courierStrokeColor / courierSize | #F5B428 / #FFFFFF / 26 | Ok görünümü | | renderCourier | — | ({ variant, rotation, heading }) => ReactNode | | rerouting | false | status === "rerouting" — çizgi soluklaşır | | followAnchor | moddan | Kuryenin ekranda durduğu dikey oran — 0.5 tam orta, 0.82 (course) alta yakın. Kamera padding'i ile yapılır. 0.5..0.9 arasına kırpılır | | followMinMoveMeters | 4 | Ölü bölge — bu mesafeden az oynayan GPS fix'lerinde kamera hiç kıpırdamaz | | followHeading | false | true ise harita konumun heading'ine göre döner | | followAnimationMs | ölçülen | Kamera animasyon süresi; verilmezse fix aralığından hesaplanır | | followAnimationMaxMs | 1200 | Hesaplanan sürenin tavanı | | animateCourier | true | İşaretçiyi fix'ler arasında kaydır (MapLibre v11 gerekir) | | stopFollowOnPan | true | Kullanıcı haritayı elle sürüklerse takip kapanır | | showFollowButton | true | Sağ üstteki takip butonu | | followButtonStyle / renderFollowButton | — | Butonu stille veya ({ following, toggle }) => ReactNode ile tamamen değiştirin | | onFollowChange | — | (following: boolean) => void |

Yeni rota geldiğinde kamera otomatik fitBounds yapar — takip modu kapalıyken. Takip açıkken kamera kuryeden kopmaz.

İki takip karakteri. north kuşbakışıdır: kuzey hep yukarıda, rotayı bir bütün olarak okumak için iyidir. course sürüş görünümüdür — harita gidiş yönüne döner, 55° eğilir, 17.5'e yakınlaşır ve kurye ekranın alt beşte birine iner. Fark yalnızca estetik değil: kuzey yukarıdayken "sağa dön" talimatı ekranda aşağıyı işaret ediyor olabilir, course modunda talimattaki sağ ile ekrandaki sağ aynı yerdir.

<RouteMap followMode="course" initialFollow route={route} progress={progress} courierLocation={location} />

Kurye işaretçisi. Aktif rota ve bilinen yön varsa mavi nokta yerine yön oku çizilir. Ok varsayılan olarak ham konuma çizilir.

Rotaya yapıştırma (snapCourierToRoute) varsayılan olarak kapalıdır ve bu bilinçli. Yapıştırma ok'u yola oturtur, titremeyi gizler — ama kurye gerçekten başka bir yoldaysa onu rotanın üstünde gösterir. Otoyol kavşaklarında paralel kollar 20-40 m aralıklıdır; çatalda yanlış kolu alan biri, sapma fark edilene kadar doğru gittiğini sanır. Kurye uygulamasında bu bedel, ok'un kaldırımda görünmesinden ağırdır.

Açarsanız snapMaxDeviationMeters (12 m) güvenliği sağlar: yalnızca kayma yol genişliği + GPS gürültüsü mertebesindeyken yapıştırılır, üstünde çizim ham konuma düşer. Yapıştırma açıkken ok'un yönü segmentin yönünden gelir (GPS heading'i yavaşta savrulur), kapalıyken cihazın kendi heading'inden. Ok haritaya göre döner — course modunda hep yukarı bakar. GPS yönü dururken savrulduğu için followHeadingMinSpeedMps'in altında son geçerli yön korunur; harita kırmızı ışıkta kendi etrafında dönmez. CourierPuck ayrıca export edilir, kendi haritanızda da kullanabilirsiniz.

Yeniden hesaplama. Çizgiyi gizlemiyoruz, soluklaştırıyoruz (rerouting). Haritayı boşaltmak kuryeyi tam da yönünü kaybettiği anda büsbütün kör bırakır. Susturulması gereken çizgi değil talimattır, onu offRoute yapıyor.

Ref ile imperatif kontrol:

const mapRef = useRef(null);

<RouteMap ref={mapRef} ... />;

mapRef.current.startFollow(); // takibi başlat
mapRef.current.stopFollow();
mapRef.current.toggleFollow();
mapRef.current.recenter(); // takip açmadan tek seferlik ortala
mapRef.current.zoomTo(18.5); // zoom'u doğrudan ayarla, takibi bozmaz
mapRef.current.fitRoute(); // rotanın tamamını göster
mapRef.current.isFollowing(); // boolean

geo

haversineMeters, buildRouteProfile, locateOnRoute, routeBounds, toPointFeature, sliceRouteAhead, geometryAhead, interpolateCoord — turf'süz saf yardımcılar.

Geliştirme

npm install    # yalnızca devDependencies (zustand); paket sıfır runtime bağımlılığıyla yayınlanır
npm test       # node --test, 71 test, ek araç gerekmez
npm run check  # sözdizimi kontrolü

Testler Node'un yerleşik koşucusunu kullanır; jest/babel/vitest kurulumu yoktur. prepublishOnly ikisini de çalıştırır, yani kırık bir sürüm yayınlanamaz.

npm'e yayınlama

  1. package.json içindeki scope'u kendi npm hesabınıza/organizasyonunuza göre değiştirin — scoped paket adı (@ferstekr/...) ancak o scope size aitse yayınlanabilir. Scope'unuz yoksa npm'de ücretsiz bir organizasyon açın veya paket adını unscoped yapın.
  2. repository.url'i gerçek repo adresinizle güncelleyin.
  3. Yayın:
npm login
npm publish --access public   # scoped paketlerde ilk yayında zorunlu

Sonraki sürümler: npm version patch && npm publish.

Şirket içi kalsın istiyorsanız: public npm yerine GitHub Packages (publishConfig.registry + .npmrc) veya self-hosted Verdaccio kullanabilirsiniz; ya da hiç registry'siz "@ferstekr/nav-sdk": "github:ferstekr/nav-sdk" şeklinde git dependency olarak ekleyebilirsiniz — paket transpile gerektirmediği için git'ten kurulum da sorunsuz çalışır.

Notlar

  • Jest: paket kaynak olarak dağıtıldığı için testlerde transform dışı bırakılmamalı:
    transformIgnorePatterns: ["node_modules/(?!(@ferstekr/nav-sdk|@maplibre/maplibre-react-native|expo-location)/)"];
  • Metro exports desteği: alt yollar hem exports alanıyla hem de kök dizindeki gerçek dosyalarla (components.js, expo.js) çözümlenir; bu sayede exports desteği kapalı eski Metro sürümlerinde de çalışır.
  • MapLibre v9: desteklenmez; v10 veya v11'e geçin.
  • ETA'lar trafiksizdir. OSRM süreleri OSM hız limitlerinden hesaplar; gerekirse remainingSeconds'a şehir içi katsayı uygulayın.
  • 0.5.0'dan yükseltiyorsanız: kırıcı bir şey yok. maneuverInstruction ve formatManeuverDistance'ı doğrudan çağırıyorsanız imzaları { locale } argümanı aldı. useRoute'un eski steps: true girişi hâlâ çalışıyor ama 0.7.0'da kalkacak.
  • Sesli talimat yok — ekrandaki talimat metni hazır (nextStep.instruction), TTS'e vermek size kalmış (expo-speech ile birkaç satır).
  • MapLibre v10 ve v11: RouteMap/RouteLine iki sürümde de çalışır; hangisinin kurulu olduğu çalışma anında çözülür (v11'de Map/GeoJSONSource/Layer + paint/layout, v10'da MapView/ShapeSource/LineLayer + style). Kendi haritanızı yazarken dallanmanız gerekirse isMapLibreV11 export edilir.