Skip to main content
Chetour naghshe radyabi zende ranande baray barnamehay delivery
Tutorials

Chetour naghshe radyabi zende ranande baray barnamehay delivery

Ba estefadeh az MapAtlas Maps va Routing APIs, yek naghshe radyabi ranande dar zaman-e vaghai ba behroozresanihay GPS az tarigh WebSocket, daroonabi rowon-e neshangaram

Brent van der Heiden9 min read
#live tracking map#driver tracking#real-time map#websocket map#delivery tracking#rideshare app

نقشه‌ی پیگیری رانندگی بیشترین صفحه‌ی درگیرکننده در هر اپلیکیشن تحویل غذا یا سواری‌اشتراکی است. این صفحه‌ای است که مشتریان در حین انتظار به آن نگاه می‌کنند. انجام صحیح این کار، حرکت روان، زمان‌های رسیدن دقیق، و خط مسیر واضح تفاوت میان اپلیکیشنی حرفه‌ای و یکی نامعتبر است.

این درس‌نامه نقشه‌ی پیگیری را از اصول پایه‌ای می‌سازد: بک‌اند‌ی که موقعیت‌های GPS را از طریق WebSocket پخش می‌کند، فرانت‌اند‌ی که آن‌ها را دریافت می‌کند و نشان‌گر راننده را بدون جهش حرکت می‌دهد، خط مسیری از MapAtlas Routing API، و نمایش زمان رسیدن زنده. کل پیاده‌سازی کمتر از 70 خط جاوااسکریپت کلاینت‌سو است که برای یکپارچگی با هر بک‌اند‌ی که می‌تواند پیام‌های WebSocket بفرستد طراحی شده است.

این معماری برای تحویل غذا، خوراک‌فروشی، سواری‌اشتراکی، خدمات میدانی، و هر مورد استفاده‌ای دیگری که خودروی کاری به مقصد ثابتی حرکت می‌کند و مشتری آن را در زمان واقعی می‌بیند کار می‌کند.

نمای کلی معماری

قبل از نوشتن کد، درک روند داده‌ها مفید است:

  1. اپلیکیشن راننده (موبایل، سخت‌افزار GPS) هر 3 تا 5 ثانیه طول و عرض جغرافیایی را به بک‌اند شما می‌فرستد.
  2. بک‌اند (Node.js، Python، Go، یا انتخاب شما) آخرین موقعیت شناخته‌شده را ذخیره می‌کند و آن را از طریق WebSocket به تمام مشترکین سفارش متصل پخش می‌کند.
  3. مرورگر مشتری پیام‌های WebSocket را دریافت می‌کند و نشان‌گری را بر روی نقشه با استفاده از انیمیشن درون‌یابی حرکت می‌دهد.
  4. Routing API یک بار هنگام ایجاد سفارش فراخوانی می‌شود تا مسیر برنامه‌ریزی‌شده را واکشی کند. پلی‌لاین رمزگشایی‌شده به عنوان لایه‌ی خطی نمایش داده می‌شود.
  5. 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" برای غیرفعال کردن حالت پیگیری اگر می‌خواهند نقشه را کاوش کنند بدهید.

مراحل بعدی

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.

این مفید بود؟ آن را به اشتراک بگذارید.

درباره نویسنده

Brent van der Heiden

نوشته

Brent van der Heiden

Co-Founder & CEO at MapAtlas

Brent built MapAtlas out of a conviction that developers deserve location APIs with fair pricing and genuine end-user privacy. He writes about geospatial infrastructure, AI search visibility, and how location data powers the products people rely on every day.

مشاهده همه مقالات
بازگشت به وبلاگ