نقشهی پیگیری رانندگی بیشترین صفحهی درگیرکننده در هر اپلیکیشن تحویل غذا یا سواریاشتراکی است. این صفحهای است که مشتریان در حین انتظار به آن نگاه میکنند. انجام صحیح این کار، حرکت روان، زمانهای رسیدن دقیق، و خط مسیر واضح تفاوت میان اپلیکیشنی حرفهای و یکی نامعتبر است.
این درسنامه نقشهی پیگیری را از اصول پایهای میسازد: بکاندی که موقعیتهای GPS را از طریق WebSocket پخش میکند، فرانتاندی که آنها را دریافت میکند و نشانگر راننده را بدون جهش حرکت میدهد، خط مسیری از MapAtlas Routing API، و نمایش زمان رسیدن زنده. کل پیادهسازی کمتر از 70 خط جاوااسکریپت کلاینتسو است که برای یکپارچگی با هر بکاندی که میتواند پیامهای WebSocket بفرستد طراحی شده است.
این معماری برای تحویل غذا، خوراکفروشی، سواریاشتراکی، خدمات میدانی، و هر مورد استفادهای دیگری که خودروی کاری به مقصد ثابتی حرکت میکند و مشتری آن را در زمان واقعی میبیند کار میکند.
نمای کلی معماری
قبل از نوشتن کد، درک روند دادهها مفید است:
- اپلیکیشن راننده (موبایل، سختافزار GPS) هر 3 تا 5 ثانیه طول و عرض جغرافیایی را به بکاند شما میفرستد.
- بکاند (Node.js، Python، Go، یا انتخاب شما) آخرین موقعیت شناختهشده را ذخیره میکند و آن را از طریق WebSocket به تمام مشترکین سفارش متصل پخش میکند.
- مرورگر مشتری پیامهای WebSocket را دریافت میکند و نشانگری را بر روی نقشه با استفاده از انیمیشن درونیابی حرکت میدهد.
- Routing API یک بار هنگام ایجاد سفارش فراخوانی میشود تا مسیر برنامهریزیشده را واکشی کند. پلیلاین رمزگشاییشده به عنوان لایهی خطی نمایش داده میشود.
- ETA با مقایسهی فاصلهی باقیمانده با سرعت میانگین یا با فراخوانی دوبارهی Routing API از موقعیت جاری راننده دوباره محاسبه میشود.
پیادهسازی بکاند خارج از محدودهی این درسنامه است، اما هر سرور WebSocketی که پیامها را در این قالب میفرستد با کد فرانتاند زیر کار میکند:
{
"type": "position_update",
"orderId": "order-8821",
"lat": 52.3741,
"lng": 4.8952,
"heading": 92,
"speed": 28,
"timestamp": 1738234521000
}
مرحله 1: مقداردهی اولیه نقشه
نقشه را متمرکز بر منشأ تحویل تنظیم کنید. فراخوانی Routing API در مرحله 4 انجام میشود، بنابراین برای الآن فقط بوم را مقداردهی کنید.
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const map = new mapmetricsgl.Map({
container: 'tracking-map',
style: 'https://tiles.mapatlas.eu/styles/basic/style.json?key=YOUR_API_KEY',
center: [4.9041, 52.3676],
zoom: 14
});
// Destination marker (restaurant or pickup point)
const destination = [4.9001, 52.3791];
new mapmetricsgl.Marker({ color: '#EF4444' })
.setLngLat(destination)
.setPopup(new mapmetricsgl.Popup().setHTML('<strong>Pickup point</strong>'))
.addTo(map);
مرحله 2: نشانگر راننده با چرخش جهت
نشانگر راننده را جداگانه ایجاد کنید تا بتوانید موقعیت آن را در هر Ping GPS بهروزرسانی کنید. عنصر HTML سفارشی به شما اجازه میدهد نماد نشانگر را برای بازتاب جهت راننده بچرخانید، جزئیاتی کوچک که پیگیری را خیلی واقعگرایانهتر میکند.
// Custom element so we can rotate it
const driverEl = document.createElement('div');
driverEl.innerHTML = '🚗';
driverEl.style.cssText = 'font-size:28px;transform-origin:center;transition:transform 0.3s';
const driverMarker = new mapmetricsgl.Marker({ element: driverEl, anchor: 'center' })
.setLngLat([4.9041, 52.3676])
.addTo(map);
function setDriverHeading(heading) {
driverEl.style.transform = `rotate(${heading}deg)`;
}
مرحله 3: اتصال WebSocket و درونیابی روان
این هستهی نقشهی پیگیری است. اتصال به WebSocket یک خط است؛ قسمت جالب درونیابی موقعیت نشانگر بین Pingهای GPS است تا بجای جابهجایی فوری بهطور روان لغزش کند.
let prevPos = null; // { lat, lng }
let animFrame = null;
function interpolateMarker(fromLat, fromLng, toLat, toLng, durationMs) {
const startTime = performance.now();
function step(now) {
const elapsed = now - startTime;
const t = Math.min(elapsed / durationMs, 1); // 0 → 1
const lat = fromLat + (toLat - fromLat) * t;
const lng = fromLng + (toLng - fromLng) * t;
driverMarker.setLngLat([lng, lat]);
if (t < 1) {
animFrame = requestAnimationFrame(step);
}
}
if (animFrame) cancelAnimationFrame(animFrame);
animFrame = requestAnimationFrame(step);
}
const ws = new WebSocket('wss://your-backend.example.com/track/order-8821');
ws.addEventListener('message', (event) => {
const msg = JSON.parse(event.data);
if (msg.type !== 'position_update') return;
const { lat, lng, heading } = msg;
if (prevPos) {
// Smooth animation from previous to new position
interpolateMarker(prevPos.lat, prevPos.lng, lat, lng, 400);
} else {
// First ping, place marker immediately
driverMarker.setLngLat([lng, lat]);
map.flyTo({ center: [lng, lat], zoom: 15 });
}
setDriverHeading(heading);
updateETA(lat, lng);
prevPos = { lat, lng };
});
ws.addEventListener('close', () => {
console.log('Driver has arrived or connection closed.');
});
پنجرهی درونیابی 400 میلیثانیهای با بازهی Ping GPS معمول 3 تا 5 ثانیهای کاملاً خوب تطابق دارد، نشانگر همیشه کمی عقب از واقعیت است اما هرگز بهطور قابلتوجهی جهش نمیکند.
مرحله 4: رسم مسیر برنامهریزیشده از Routing API
مسیر کامل را هنگام اختصاص سفارش واکشی کنید. مختصات پلیلاین را ذخیره کنید و آنها را به عنوان لایهی خط GeoJSON رسم کنید. آموزش Route Optimization API سناریوهای چندمسیر را پوشش میدهد؛ برای تحویل سادهی A به B درخواست واضح است.
async function fetchAndDrawRoute(originLat, originLng, destLat, destLng) {
const url = new URL('https://api.mapatlas.eu/v1/routing/route');
url.searchParams.set('origin', `${originLat},${originLng}`);
url.searchParams.set('destination', `${destLat},${destLng}`);
url.searchParams.set('profile', 'driving');
url.searchParams.set('key', 'YOUR_API_KEY');
const res = await fetch(url);
const data = await res.json();
if (!data.routes?.length) return;
const route = data.routes[0];
map.on('load', () => {
map.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
geometry: route.geometry // GeoJSON LineString
}
});
map.addLayer({
id: 'route-line',
type: 'line',
source: 'route',
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: {
'line-color': '#3B82F6',
'line-width': 4,
'line-opacity': 0.8
}
});
});
return route.duration; // seconds
}
مرحله 5: محاسبه و نمایش ETA
ETA را با مقایسهی موقعیت جاری راننده با مقصد محاسبه کنید. برای دقت بالا، هر 30 ثانیه Routing API را از موقعیت جاری راننده دوباره فراخوانی کنید تا برآورد سفر تازهای بدست آورید.
function haversineKm(lat1, lon1, lat2, lon2) {
const R = 6371;
const dLat = ((lat2 - lat1) * Math.PI) / 180;
const dLon = ((lon2 - lon1) * Math.PI) / 180;
const a = Math.sin(dLat / 2) ** 2 +
Math.cos((lat1 * Math.PI) / 180) *
Math.cos((lat2 * Math.PI) / 180) *
Math.sin(dLon / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
let lastRouteFetch = 0;
async function updateETA(driverLat, driverLng) {
const [destLng, destLat] = destination;
const distKm = haversineKm(driverLat, driverLng, destLat, destLng);
// Fast client-side estimate (assume 30 km/h urban average)
const minutesEstimate = Math.ceil((distKm / 30) * 60);
document.getElementById('eta').textContent =
distKm < 0.1
? 'Arriving now'
: `ETA: ${minutesEstimate} min (${distKm.toFixed(1)} km)`;
// Re-fetch from Routing API every 30 seconds for accuracy
const now = Date.now();
if (now - lastRouteFetch > 30_000) {
lastRouteFetch = now;
const url = new URL('https://api.mapatlas.eu/v1/routing/route');
url.searchParams.set('origin', `${driverLat},${driverLng}`);
url.searchParams.set('destination', `${destLat},${destLng}`);
url.searchParams.set('profile', 'driving');
url.searchParams.set('key', 'YOUR_API_KEY');
const res = await fetch(url);
const data = await res.json();
if (data.routes?.length) {
const mins = Math.ceil(data.routes[0].duration / 60);
document.getElementById('eta').textContent = `ETA: ${mins} min`;
}
}
}
ملاحظات GDPR برای پیگیری راننده
مختصات GPS راننده دادههای شخصی تحت ماده 4(1) GDPR هستند. مقرراتای که پلتفرمهای تحویل غذا و سواریاشتراکی اروپا را در این زمینه کنترل میکنند مبهم نیستند:
کاهش دادهها: فقط فیلدهایی را ردیابی کنید که برای اعزام نیاز دارید، موقعیت، جهت، سرعت. تاریخچهی GPS خام را فراتر از آنچه عملیاتی ضروری است ثبت نکنید.
محدودیتهای نگهداری: دادههای ردیابی سفر دقیق باید یک بار تکمیل سفارش حذف یا بهطور غیرقابل برگشت بینام شود. دادههای مسیر جمعشده (بدون پیوند دهی به رانندهی فردی) ممکن است برای بهینهسازی شبکه طولانیتر نگهداری شود.
مبنای قانونی: منافع قانونی تحت ماده 6(1)(f) ردیابی اعزام در زمان واقعی را پوشش میدهد. برای هر استفادهی ثانویه از دادههای ردیابی (تجزیهوتحلیل، مقایسه)، باید مبنایی جداگانه مستند کنید.
شفافیت راننده: افشای ردیابی واضح را در سرویس دهی راننده گنجانید. رانندگان باید بدانند چه چیزی جمعآوری میشود، چهمدت نگهداری میشود، و چه کسی میتواند به آن دسترسی داشته باشد.
محل دادهها: MapAtlas تمام درخواستهای API را در داخل اتحادیه اروپا پردازش میکند. این نگرانی انتقال به کشور ثالث که با ارائهدهندگان نقشهبرداری مبتنی بر ایالات متحده بوجود میآید را برطرف میکند. برای دیدن تصویر کامل انطباق EU Developer's Guide to GDPR-Compliant Map APIs را ببینید.
برای مورد استفادهی ridesharing and mobility industry بهطور خاص، MapAtlas مستندات DPA و تضمینهای سرور اتحادیه اروپا را بهطور استاندارد شامل میکند. صفحهی logistics and delivery industry سناریوهای ناوگان و چندراننده را پوشش میدهد.
تقویت تولید
قبل از ارسال ویژگی پیگیری به مشتریان، این موارد را بررسی کنید:
- اتصال دوباره WebSocket:
ws.addEventListener('close', reconnect)را با بازتاب نمایی اضافه کنید. شبکههای موبایل اتصالات را اغلب قطع میکنند. - رفتار موقعیت قدیمی: اگر بهمدت 15 ثانیه بهروزرسانی نمیرسد، وضعیت "locating driver" را نشان دهید تا موقعیت آخر را ترک نکنید.
- شناسایی رسیدن: هنگامی که
distKm < 0.1، وضعیت "arrived" را فعال کنید، WebSocket را بسته کنید، و صفحهی تأیید را نشان دهید. - دوربین از راننده پیروی میکند: در هر بهروزرسانی موقعیت
map.panTo([lng, lat])را فراخوانی کنید تا راننده متمرکز بماند. به کاربران یک تغییر دهندگی "lock" برای غیرفعال کردن حالت پیگیری اگر میخواهند نقشه را کاوش کنند بدهید.
مراحل بعدی
- برای کلید API MapAtlas رایگان خود ثبت نام کنید و شروع به ساخت کنید
- آموزش Route Optimization API را بخوانید تا اعزام چندمسیر را به اپلیکیشن تحویل خود اضافه کنید
- آموزش Real Estate Property Map را کاوش کنید برای مثال دیگری از لایههای نقشهی پویا و مبتنی بر داده
Frequently Asked Questions
How do I show a driver's location moving smoothly on a map?
GPS pings arrive every few seconds, creating visible jumps if you update the marker position directly. Smooth interpolation animates the marker between the previous position and the new one over a short duration (300–500ms), using requestAnimationFrame to move the marker in small increments. This gives the appearance of continuous movement even with infrequent GPS updates.
Is driver location data subject to GDPR?
Yes. A driver's real-time GPS coordinates are personal data under GDPR Article 4. EU food delivery and rideshare platforms must minimise retention, tracking data should be deleted or anonymised once the trip is complete. Processing requires a legal basis and must be disclosed in the driver's privacy notice.
Can I use the MapAtlas Routing API to show the planned route on the tracking map?
Yes. Fetch the route from the Routing API when a trip is created, decode the polyline, and add it as a GeoJSON line layer on the map. As the driver moves, you can optionally re-fetch the route from the current position to recalculate the ETA dynamically.
