Skip to main content
API behineh-sazi masir: ba 50 khat kod, hazineye taslim ra 30 darsad
Tutorials

API behineh-sazi masir: ba 50 khat kod, hazineye taslim ra 30 darsad

Az MapAtlas Routing API baray behineh-sazi masirha-ye taslim ba chand toghf, ejra-ye panjareha-ye zamani, ejtanab az monategh-e enteshat-e payin-e EU va namayesh-e

Brent van der Heiden10 min read
#route optimization#delivery route#routing api#last mile delivery#multi-stop route#logistics api

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

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

پیاده‌سازی Python کمتر از 55 خط است. نمایش نقشه JavaScript 30 خط اضافی است.

مشکل هزینه آخرین مایل

To understand what optimisation actually saves, run the numbers for a realistic delivery scenario:

  • Fleet: 10 vans
  • Stops per van per day: 18
  • Current average distance: 210 km/van/day
  • Fuel cost: €0.38/km (diesel, EU average)
  • Driver cost: €22/hour
  • Average current route time: 7.5 hours/day

Current daily cost per van: (210 × €0.38) + (7.5 × €22) = €79.80 + €165 = €244.80/van/day

A 30% distance reduction (achievable with good optimisation on a dense urban network) and a 20% time saving produces:

  • Optimised distance: 147 km → fuel cost: €55.86
  • Optimised time: 6 hours → driver cost: €132
  • Optimised daily cost per van: €187.86/van/day

Saving per van per day: €56.94. For 10 vans over 250 working days: €142,350/year, from one API integration.

The benchmarks above reflect real published figures from last-mile logistics studies. Your specific numbers will vary by geography, vehicle type, and stop density. Dense urban areas see the largest gains because naive sequential routes waste the most distance on unnecessary backtracking.

Naive vs Optimised Routes: A Visual Comparison

The difference between a naive (sequential) route and an optimised one is stark on a map.

Naive routing happens when you feed stops in the order they were entered, first customer who placed an order is first on the route, regardless of geography. In a city like Amsterdam or Berlin, this creates the "spaghetti route" problem: your driver constantly crossing their own path.

Optimisation solves the Travelling Salesman Problem (TSP) for your stop set. For 15–20 stops this is computationally tractable in milliseconds. For larger fleets with hundreds of stops, vehicle routing problem (VRP) solvers handle the additional constraints of multiple vehicles and capacity limits.

Step 1: Structure Your Delivery Data

Each stop needs a location and, for time-windowed deliveries, a time_window specifying when the delivery is acceptable.

import requests
import json

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.mapatlas.eu/v1"

# Depot (start and end point)
depot = {
    "lat": 52.3402,
    "lng": 4.8952,
    "name": "Warehouse - Sloterdijk"
}

# Delivery stops with optional time windows
stops = [
    { "lat": 52.3726, "lng": 4.8971, "name": "Albert Heijn Jordaan",
      "time_window": { "start": "09:00", "end": "12:00" } },
    { "lat": 52.3601, "lng": 4.9123, "name": "Café De Jaren",
      "time_window": { "start": "08:00", "end": "11:00" } },
    { "lat": 52.3780, "lng": 4.8801, "name": "Westergasfabriek Events",
      "time_window": { "start": "10:00", "end": "14:00" } },
    { "lat": 52.3545, "lng": 4.9041, "name": "Hotel V Nesplein",
      "time_window": None },
    { "lat": 52.3620, "lng": 4.8820, "name": "Vondelpark Paviljoen",
      "time_window": { "start": "07:00", "end": "10:00" } }
]

Step 2: Call the Route Optimisation Endpoint

POST the depot and stop list to the optimised routing endpoint. The API returns the stops in the most efficient visit order along with the total route distance and duration.

def optimise_route(depot, stops, vehicle_profile="van-euro6"):
    """
    Request an optimised multi-stop route from the MapAtlas Routing API.
    vehicle_profile options: van-euro6, van-diesel-euro5, electric-van, bike
    """
    waypoints = [
        {
            "lat": s["lat"],
            "lng": s["lng"],
            "name": s["name"],
            **({"time_window": s["time_window"]} if s.get("time_window") else {})
        }
        for s in stops
    ]

    payload = {
        "origin": { "lat": depot["lat"], "lng": depot["lng"] },
        "destination": { "lat": depot["lat"], "lng": depot["lng"] },  # return to depot
        "waypoints": waypoints,
        "optimise": True,
        "vehicle_profile": vehicle_profile,
        "avoid_low_emission_zones": True  # auto-avoids LEZs for non-compliant profiles
    }

    response = requests.post(
        f"{BASE_URL}/routing/optimise",
        json=payload,
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json"
        }
    )
    response.raise_for_status()
    return response.json()

result = optimise_route(depot, stops)

Step 3: Parse and Display the Optimised Route

The API response includes the stops in optimised order, cumulative ETAs for each stop, total distance, and total duration.

def display_route_summary(result):
    route = result["route"]
    print(f"\n--- Optimised Route Summary ---")
    print(f"Total distance : {route['total_distance_km']:.1f} km")
    print(f"Total duration : {route['total_duration_min']:.0f} min")
    print(f"Stops          : {len(route['waypoints'])}\n")

    print(f"  START  {depot['name']}")
    for i, stop in enumerate(route["waypoints"], 1):
        eta     = stop["eta"]
        tw      = stop.get("time_window")
        on_time = "(on time)" if tw and tw["start"] <= eta <= tw["end"] else ""
        print(f"  {i:>2}.   {stop['name']:<35} ETA {eta}  {on_time}")
    print(f"  END    {depot['name']}")

    print(f"\nEstimated fuel saving vs sequential: "
          f"{result.get('saving_vs_naive_km', 0):.1f} km "
          f"({result.get('saving_pct', 0):.0f}%)")

display_route_summary(result)

Sample output for the five stops above:

--- Optimised Route Summary ---
Total distance : 38.4 km
Total duration : 94 min
Stops          : 5

  START  Warehouse - Sloterdijk
   1.   Vondelpark Paviljoen               ETA 07:48  (on time)
   2.   Café De Jaren                      ETA 08:31  (on time)
   3.   Albert Heijn Jordaan               ETA 09:15  (on time)
   4.   Hotel V Nesplein                   ETA 10:02
   5.   Westergasfabriek Events            ETA 10:44  (on time)
  END    Warehouse - Sloterdijk

Estimated fuel saving vs sequential: 14.2 km (27%)

Step 4: EU Low Emission Zone Handling

Amsterdam's ZTL zone, Paris's Crit'Air system, and Berlin's Umweltzone restrict certain vehicle types from central areas at specified times. A route that looks efficient on distance alone may be invalid for your vehicle.

The avoid_low_emission_zones: true parameter combined with the vehicle_profile automatically routes around restricted zones for non-compliant vehicles. For electric and Euro 6 vehicles, LEZs are passable and the parameter has no effect.

# Example: diesel Euro 5 van, will be re-routed around Amsterdam ZTL
result_euro5 = optimise_route(depot, stops, vehicle_profile="van-diesel-euro5")

# Example: electric van, LEZ restrictions do not apply
result_electric = optimise_route(depot, stops, vehicle_profile="electric-van")

print(f"Euro 5 route distance  : {result_euro5['route']['total_distance_km']:.1f} km")
print(f"Electric route distance: {result_electric['route']['total_distance_km']:.1f} km")
# Electric route will typically be shorter as it can use LEZ-restricted roads

For logistics operations planning a transition from diesel to electric, comparing these two outputs per route provides a direct quantification of the range improvement available from electrification.

Step 5: Display the Optimised Route on a Map

Take the route geometry from the API response and render it as a line layer in JavaScript.

import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';

// routeResult is the parsed API JSON response passed to the frontend
function renderOptimisedRoute(map, routeResult) {
  const { waypoints, geometry, total_distance_km, total_duration_min } = routeResult.route;

  map.on('load', () => {
    // Route line
    map.addSource('optimised-route', { type: 'geojson', data: { type: 'Feature', geometry } });
    map.addLayer({
      id: 'route-line',
      type: 'line',
      source: 'optimised-route',
      layout: { 'line-join': 'round', 'line-cap': 'round' },
      paint: { 'line-color': '#2563EB', 'line-width': 4 }
    });

    // Stop markers with sequence numbers
    waypoints.forEach((stop, i) => {
      const el = document.createElement('div');
      el.textContent = i + 1;
      el.style.cssText = `
        width:28px;height:28px;border-radius:50%;background:#2563EB;color:#fff;
        display:flex;align-items:center;justify-content:center;font-weight:700;
        font-size:13px;border:2px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,0.3)
      `;

      new mapmetricsgl.Marker({ element: el })
        .setLngLat([stop.lng, stop.lat])
        .setPopup(
          new mapmetricsgl.Popup().setHTML(`
            <strong>${i + 1}. ${stop.name}</strong>
            <p>ETA: ${stop.eta}</p>
          `)
        )
        .addTo(map);
    });

    // Fit map to route bounds
    const coords = geometry.coordinates;
    const bounds = coords.reduce(
      (b, c) => b.extend(c),
      new mapmetricsgl.LngLatBounds(coords[0], coords[0])
    );
    map.fitBounds(bounds, { padding: 48 });

    // Summary panel
    document.getElementById('route-summary').innerHTML = `
      <strong>${total_distance_km.toFixed(1)} km</strong> ·
      <strong>${total_duration_min.toFixed(0)} min</strong> ·
      ${waypoints.length} stops
    `;
  });
}

const map = new mapmetricsgl.Map({
  container: 'route-map',
  style: 'https://tiles.mapatlas.eu/styles/basic/style.json?key=YOUR_API_KEY',
  center: [4.9041, 52.3676],
  zoom: 12
});

renderOptimisedRoute(map, routeResult);

Calculating Your Real Savings

Once you have the API response in hand, the saving calculation is straightforward. The saving_vs_naive_km field in the response gives you distance saved directly. From that, derive cost savings:

def calculate_savings(result, fuel_cost_per_km=0.38, driver_cost_per_hour=22.0,
                       days_per_year=250, fleet_size=10):
    saving_km    = result.get("saving_vs_naive_km", 0)
    saving_hours = saving_km / 50  # assume 50 km/h average

    daily_fuel_saving   = saving_km * fuel_cost_per_km
    daily_driver_saving = saving_hours * driver_cost_per_hour
    daily_total         = daily_fuel_saving + daily_driver_saving

    annual_fleet_saving = daily_total * days_per_year * fleet_size

    print(f"Distance saved per route : {saving_km:.1f} km")
    print(f"Time saved per route     : {saving_hours * 60:.0f} min")
    print(f"Daily saving (1 vehicle) : €{daily_total:.2f}")
    print(f"Annual saving ({fleet_size} vehicles): €{annual_fleet_saving:,.0f}")

calculate_savings(result)

Time Window Optimisation

Delivering to a bakery at 06:00 and a restaurant at 14:00 while minimising total route distance is a constrained optimisation problem. The API handles this automatically, you only need to provide the windows:

# Time-sensitive stops, the API will schedule these within their windows
stops_with_windows = [
    { "lat": 52.3726, "lng": 4.8971, "name": "Bakery",
      "time_window": { "start": "05:30", "end": "07:00" } },
    { "lat": 52.3620, "lng": 4.8820, "name": "Café",
      "time_window": { "start": "07:00", "end": "09:00" } },
    { "lat": 52.3545, "lng": 4.9041, "name": "Restaurant",
      "time_window": { "start": "13:00", "end": "15:00" } }
]

If any time window constraint cannot be satisfied given the depot departure time and current traffic model, the API returns a constraint_violations array listing which stops could not be reached on time. Your dispatch software can then alert the driver or suggest an earlier departure.

What to Build on Top of This

Route optimisation is the foundation. Once it is running, the natural extensions are:

The Logistics and Delivery industry page and the Fleet Management industry page cover additional MapAtlas features relevant to dispatch software, including multi-vehicle VRP and return-to-depot optimisation.

Getting Started

Frequently Asked Questions

How does route optimisation reduce delivery costs?

Route optimisation reorders multi-stop delivery sequences to minimise total distance and drive time. Studies consistently show 20–35% reductions in distance driven versus a naive sequential route. For a vehicle driving 200 km/day at €0.35/km fuel cost, a 30% reduction saves around €21 per vehicle per day, roughly €5,000 per year per vehicle.

What are time windows in route optimisation?

Time windows are delivery constraints that require a stop to be visited within a specified time range, for example, a business that accepts deliveries only between 09:00 and 12:00. The optimiser must respect all time windows while still minimising total route distance, which is a significantly harder computational problem than unconstrained optimisation.

Does the MapAtlas Routing API handle EU Low Emission Zones?

Yes. The MapAtlas Routing API includes road restriction data for EU Low Emission Zones including Amsterdam's ZTL, the Paris Crit'Air zone, and Berlin's Umweltzone. Pass the vehicle profile (diesel Euro 5, petrol, electric) as a parameter and the router will automatically avoid restricted zones for non-compliant vehicles.

سوالات متداول

بهینه‌سازی مسیر چطور هزینه‌های تحویل را کاهش می‌دهد؟

بهینه‌سازی مسیر ترتیب توقف‌های چندگانه را دوباره مرتب می‌کند تا مجموع فاصله و زمان رانندگی به حداقل برسد. مطالعات به‌طور مداوم نشان می‌دهند که ۲۰ تا ۳۵ درصد کاهش در مسافت طی‌شده نسبت به مسیر ترتیبی ساده قابل دستیابی است. برای خودرویی که روزانه ۲۰۰ کیلومتر با هزینه سوخت ۰.۳۵ EUR در کیلومتر می‌راند، کاهش ۳۰ درصدی حدود ۲۱ EUR در روز برای هر خودرو صرفه‌جویی می‌کند، یعنی تقریباً ۵,۰۰۰ EUR در سال برای هر خودرو.

پنجره‌های زمانی در بهینه‌سازی مسیر چیستند؟

پنجره‌های زمانی محدودیت‌های تحویلی هستند که بازدید از یک توقف را در یک بازه زمانی مشخص اجباری می‌کنند، مثلاً یک کسب‌وکار که فقط بین ۰۹:۰۰ و ۱۲:۰۰ تحویل می‌پذیرد. بهینه‌ساز باید همه پنجره‌های زمانی را رعایت کند و در عین حال مجموع فاصله مسیر را کمینه نماید، که این یک مسئله محاسباتی بسیار دشوارتر از بهینه‌سازی بدون محدودیت است.

آیا MapAtlas Routing API با مناطق کم‌انتشار اتحادیه اروپا کار می‌کند؟

بله. MapAtlas Routing API داده‌های محدودیت جاده برای مناطق کم‌انتشار (LEZ) اتحادیه اروپا را شامل می‌شود، از جمله ZTL آمستردام، منطقه Crit'Air پاریس و Umweltzone برلین. پروفایل خودرو (دیزل Euro 5، بنزین، برقی) را به عنوان پارامتر وارد کنید و router به‌طور خودکار مناطق ممنوع برای خودروهای غیر مجاز را دور می‌زند.

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

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

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.

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