الملاحة
الملاحة
مستوى تحكّم في الملاحة مدرك للخادم لبناء تجربة توجيه منعطفاً بمنعطف بمستوى خرائط Google دون إعادة اختراع الهندسة الصعبة على العميل. يعتمد على التوجيه وحركة المرور: تبدأ جلسة لرحلة، وتغذّيها بقراءات GPS حيّة، فيحسب الخادم التقدّم بمحاذاة المسار، واكتشاف الخروج عن المسار، وإعادة التوجيه التلقائية، وزمن وصول متوقّع مراعٍ لحركة المرور، واكتشاف الوصول، وسجل أحداث متجدّد (مع خطافات ويب موقَّعة اختيارية).
كما يتوفّر كل مساعد عديم الحالة (off-route، progress، arrival-check) بمفرده إن كنت تفضّل إدارة الحلقة من جهة العميل.
النطاق المطلوب: routing
x-api-key). الطلبات المجهولة تعيد 401. الجلسات مملوكة لمن أنشأها؛ ومحاولة مستدعٍ آخر قراءة جلستك أو تحديثها تعيد 403.{ lat, lng } في كل جسم من أجسام الملاحة (بخلاف تمرير التوجيه منخفض المستوى الذي يستخدم lon). هندسة المسار (routeShape) هي خط مُرمَّز بدقّة 6 (المعروف بـ polyline6) — فُكّه بمعامل 1e6. تُعيد كل طلبات POST 201 عند النجاح.POST /navigation/sessions
ابدأ جلسة ملاحة مُدارة. يخطّط الخادم المسار (عبر محرّك التوجيه لدى Navvo)، ويحلّل المنعطفات مع المسافات التراكمية، ويعيد جلسة يمكنك إدارتها عبر PATCH/positions.
جسم الطلب
{ "lat": number, "lng": number }.{ "lat": number, "lng": number, "name"?: string, "placeSlug"?: string }. يتيح placeSlug لاكتشاف الوصول تحديد مدخل مُخرَّط.auto (الافتراضي)، pedestrian، bicycle، motor_scooter، truck، wheelchair.en-US، ar). الافتراضي en-US.fastest أو shortest أو balanced.tolls، highways، ferries، unpaved.curl -s -X POST "https://navvo.io/api/v1/navigation/sessions" \
-H "Authorization: Bearer $NAVVO_JWT" -H "Content-Type: application/json" \
-d '{
"origin": { "lat": 31.9539, "lng": 35.9106 },
"destination": { "lat": 31.9730, "lng": 35.9200, "name": "Sports City" },
"costing": "auto", "language": "en-US"
}'
الاستجابة (201)
{
"sessionId": "b23f9bec-516c-4038-a2fa-c3a275ec598b",
"status": "active",
"costing": "auto",
"language": "en-US",
"destination": { "lat": 31.9730, "lng": 35.9200, "name": "Sports City" },
"routeShape": "…encoded polyline6…",
"maneuvers": [
{
"index": 0, "type": 1, "instruction": "Drive north.",
"verbalAlert": "Drive north.", "verbalPre": "Drive north for 300 meters.",
"verbalPost": "Continue for 300 meters.", "streetNames": ["King Abdullah St"],
"roundaboutExit": null, "exitSign": null,
"distanceMeters": 300, "timeSeconds": 34,
"lat": 31.9539, "lng": 35.9106, "cumulativeMeters": 0
}
],
"distanceMeters": 6366,
"baseSeconds": 402,
"etaSeconds": 402,
"distanceRemainingMeters": 6366,
"durationRemainingSeconds": 402,
"maneuverIndex": 0,
"eventSeq": 0,
"expiresAt": "2026-07-20T02:00:00.000Z",
"route": { "shape": "…", "maneuvers": [ … ], "distanceMeters": 6366, "etaSeconds": 402 }
}
| الحقل | المعنى |
|---|---|
maneuvers[] | قائمة المنعطفات المحلّلة. type رمز منعطف — حلّه عبر GET /routing/maneuver-types. وcumulativeMeters هي المسافة من بداية المسار حتى ذلك المنعطف (تُستخدم للتقدّم). وحقول verbal* جاهزة للتحويل إلى كلام. |
distanceMeters / baseSeconds | إجمالي طول المسار ومدّته دون حركة مرور. |
eventSeq | عدّاد أحداث تصاعدي — استطلِع GET …/events?since= بآخر قيمة رأيتها. |
expiresAt | تنتهي صلاحية الجلسات تلقائياً بعد ~6 ساعات من آخر نشاط. |
الأخطاء: 400 إذا تعذّر تخطيط مسار بين النقطتين (أو costing: "truck" حيث توجيه الشاحنات غير مُهيّأ)؛ و401 عند عدم المصادقة.
GET /navigation/sessions/{id}
اجلب لقطة الجلسة الحالية (المسار، المنعطفات، التقدّم، status). تُعيد 404 إن كانت غير معروفة، و403 إن كانت مملوكة لمستدعٍ آخر.
PATCH /navigation/sessions/{id}
غذِّ أحدث قراءة GPS. يحاذيها الخادم مع المسار، ويقدّم مؤشّر المنعطف، ويكتشف الوصول والخروج عن المسار (مع إعادة تخطيط تلقائية عند الخروج)، ويُلحق أي أحداث، ويعيد progress المحدَّث.
جسم الطلب
الاستجابة (201)
{
"sessionId": "…", "status": "active", "maneuverIndex": 2, "eventSeq": 4,
"etaSeconds": 237, "distanceRemainingMeters": 3756, "durationRemainingSeconds": 237,
"progress": {
"offRoute": false,
"arrived": false,
"shapeIndex": 128,
"maneuverIndex": 2,
"snapped": { "lat": 31.9601, "lng": 35.9149 },
"distanceToRouteMeters": 6,
"distanceRemainingMeters": 3756,
"durationRemainingSeconds": 237,
"distanceToNextManeuverMeters": 210,
"nextManeuver": { "index": 2, "type": 15, "instruction": "Turn left onto …", "verbalAlert": "Turn left onto …", "cumulativeMeters": 3966, "lat": 31.96, "lng": 35.915 },
"progressFraction": 0.41
},
"reroute": null
}
عندما تقع القراءة خارج ممرّ المسار، تصبح progress.offRoute بقيمة true، ويُطلَق حدث off_route، ويعيد الخادم التخطيط من الموضع الحالي: عندئذٍ تحمل reroute القيم الجديدة { shape, maneuvers, distanceMeters, etaSeconds } ويُسجَّل حدث rerouted. وعند بلوغ ~35م من الوجهة تتحوّل الجلسة إلى status: "arrived" ويُطلَق حدث arrived.
DELETE /navigation/sessions/{id}
أنهِ الجلسة (status: "cancelled"). تُعيد { "ended": true, "sessionId": "…", "status": "cancelled" }.
POST /navigation/sessions/{id}/positions
أرسِل دفعة من القراءات الحديثة (حتى 50) — مفيدة لوصلة صاعدة تعمل في الخلفية/بدورة تشغيل. تقود أحدث قراءة تحديثَ التقدّم المرجعي؛ وتماثل الاستجابة PATCH إضافةً إلى accepted (العدد) وnewEvents (الأحداث الناتجة عن هذه الدفعة).
{ "positions": [ { "lat": 31.958, "lng": 35.897, "speed": 11 }, { "lat": 31.960, "lng": 35.915, "speed": 9 } ] }
GET /navigation/sessions/{id}/events
استطلِع سجل أحداث الجلسة. مرّر ?since=<eventSeq> للحصول على الأحداث الأحدث فقط.
{
"sessionId": "…", "status": "active", "eventSeq": 6,
"events": [
{ "seq": 4, "type": "maneuver_completed", "at": "2026-07-19T18:02:11.000Z", "data": { "maneuverIndex": 1, "instruction": "…" } },
{ "seq": 5, "type": "off_route", "at": "…", "data": { "distanceToRouteMeters": 67 } },
{ "seq": 6, "type": "rerouted", "at": "…", "data": { "reroutes": 1, "distanceMeters": 4175 } }
]
}
أنواع الأحداث: maneuver_approaching، maneuver_completed، off_route، rerouted، arrived. يُحتفَظ بآخر ~50 حدثاً لكل جلسة.
GET /navigation/sessions/{id}/eta
أعِد حساب زمن الوصول المتوقّع المراعي لحركة المرور للمسار المتبقّي (من آخر موضع مُحاذى فصاعداً). هذا هو البديل الزهيد المقيَّد بالجلسة عن استطلاع POST /traffic/route بشكل مبنيّ يدوياً.
{
"sessionId": "…", "available": true, "etaSeconds": 660,
"arrivalTime": "2026-07-19T18:15:00.000Z",
"delaySeconds": 120, "trafficLevel": "slow", "source": "traffic"
}
عندما تكون حركة المرور الحية غير متاحة تُعيد { "available": false, "etaSeconds": <base>, "source": "base" }.
POST /navigation/off-route
عديم الحالة. اختبر موضعاً واحداً مقابل ممرّ مسار — دون حاجة إلى جلسة.
جسم الطلب
{ "lat": number, "lng": number }.40).الاستجابة (201)
{
"provider": "navvo-navigation",
"offRoute": true,
"distanceToRouteMeters": 67.2,
"confidence": 0.91,
"snapped": { "lat": 31.9598, "lng": 35.9141 },
"shapeIndex": 128,
"alongMeters": 2606
}
snapped هي أقرب نقطة على المسار؛ وalongMeters هي مدى تقدّم تلك النقطة المُحاذاة على طول المسار. وتُعيد 400 إذا لم يفكّ routeShape إلى نقطتين على الأقل.
POST /navigation/recalculate
أعِد تخطيط المسار المتبقّي من الموضع الحالي. يحافظ على تفضيلات الرحلة. إذا مُرِّر sessionId يُستبدَل مسار الجلسة في مكانه (ويُزاد reroutes)؛ وإلا فهي عملية مفردة عديمة الحالة (زوّد destination).
جسم الطلب
destination/costing/preference قيمها الافتراضية من الجلسة.{ "lat": number, "lng": number } — نقطة البداية الجديدة.sessionId.fastest / shortest / balanced.الاستجابة (201)
{
"provider": "navvo-navigation",
"sessionId": "…or null",
"costing": "auto", "preference": "balanced",
"route": { "shape": "…encoded polyline6…", "maneuvers": [ … ], "distanceMeters": 4175, "etaSeconds": 260 }
}
POST /navigation/progress
عديم الحالة. أسقِط موضعاً على مسار معروف واحصل على المسافة/الزمن المتبقّيين، وفهرس المنعطف التالي، والوصول — نفس الحسابات التي يجريها PATCH للجلسة، دون جلسة.
جسم الطلب
{ "lat": number, "lng": number }.[{ "cumulativeMeters": number }, …] (من جلسة أو /routing/plan) كي تتمكّن الاستجابة من تقديم maneuverIndex.durationRemainingSeconds.30).الاستجابة (201)
{
"provider": "navvo-navigation",
"shapeIndex": 128, "maneuverIndex": 3,
"snapped": { "lat": 31.9598, "lng": 35.9141 },
"distanceToRouteMeters": 4.2,
"distanceRemainingMeters": 2100,
"durationRemainingSeconds": 280,
"distanceToNextManeuverMeters": 120,
"progressFraction": 0.64,
"arrived": false
}
POST /navigation/arrival-check
عديم الحالة. أكّد الوصول إلى وجهة (وحدِّد أقرب مدخل مُخرَّط عند تزويد placeSlug).
جسم الطلب
{ "lat": number, "lng": number, "placeSlug"?: string }.{ "lat": number, "lng": number } الحالي.destination.placeSlug للبحث عن المدخل.30).الاستجابة (201)
{ "provider": "navvo-navigation", "arrived": true, "distanceMeters": 12.4, "radiusMeters": 30, "entranceId": "ent_3" }
entranceId تكون null عندما لا يملك المكان مداخل مُخرَّطة (أو لم يُزوَّد أيّ منها).
POST /navigation/speak
حوِّل تعليمة منعطف إلى مُخرَج جاهز للنطق. تُعيد دائماً text المُطبَّع، وسلسلة ssml جاهزة للاستخدام (باللغة الصحيحة + معدّل نطق هادئ)، وlanguage المحلولة. وعند تهيئة مزوّد تحويل نص إلى كلام من جهة الخادم تُعيد أيضاً audioUrl؛ وإلا تكون available بقيمة false ويُركّب العملاء الصوت على الجهاز من text/ssml (المسار الافتراضي المُوصى به — نطق عربي/إنجليزي متّسق بزمن استجابة إضافي معدوم).
جسم الطلب
en-US أو ar-SA. الافتراضي en-US.الاستجابة (201)
{
"provider": "navvo-navigation",
"available": false,
"reason": "on_device",
"language": "en-US",
"text": "In 200 meters, turn right onto King Abdullah Street.",
"ssml": "<speak xml:lang=\"en-US\"><prosody rate=\"0.94\">In 200 meters, turn right onto King Abdullah Street.</prosody></speak>",
"audioUrl": null
}
خطافات أحداث الملاحة
إذا أُنشئت جلسة بـ webhookUrl، تُرسَل كل دفعة أحداث جديدة إليه عبر POST بالشكل { "sessionId": "…", "events": [ … ] }. تحمل التسليمات:
X-Navvo-Signature: sha256=<hex>— توقيع HMAC-SHA256 لجسم الطلب الخام. تحقّق منه بسرّ الخطاف الخاص بك قبل الوثوق بأي تسليم.X-Navvo-Delivery— رمز قصير لمنع تكرار التسليم (idempotency).
يجب أن يكون العنوان https وأن يُحَلّ إلى عنوان عام — تُرفَض التسليمات إلى نطاقات الاسترجاع/الخاصة (حماية من SSRF). التسليم بأفضل جهد (مهلة 5 ثوانٍ، دون إعادة محاولة)؛ استطلِع GET …/events بوصفه مصدر الحقيقة.
ذات صلة
- جدول رموز المنعطفات:
GET /routing/maneuver-types. - ضمّن المسارب وحدود السرعة مباشرةً في الخطة:
POST /routing/planمعincludeLanes/includeAttributes. - زمن وصول متوقّع مراعٍ لحركة المرور لأي شكل:
POST /traffic/route. - ساهم بمسارات GPS مجهولة لتحسين حركة المرور الحية (بعد الإرشاد لا أثناءه):
POST /telemetry/trips.