Die letzte Meile ist der teuerste Teil einer Lieferkette. Branchenbenchmarks zeigen konstant, dass die Kosten der letzten Meile 53 Prozent der Gesamtversandkosten ausmachen. Der wichtigste kontrollierbare Faktor ist dabei die Routeneffizienz. Ein Fahrer, der 15 Stopps in der falschen Reihenfolge anfährt, kann um 40 Prozent mehr Kilometer fahren als nötig, verschleiß Treibstoff und das Fahrzeug und verfehlt möglicherweise Lieferzeitfenster, die zu Gebühren für Neulieferungen führen.
Routenoptimierung ist heute in Code nicht mehr schwierig zu lösen. Was früher teure spezialisierte Logistiksoftware erforderte, ist jetzt ein API-Aufruf. Dieses Tutorial baut einen kompletten Mehrfach-Stopp-Optimierer mit der MapAtlas Routing API: ein Python-Skript, das eine Liste von Lieferstopps sendet, eine optimierte Sequenz mit Gesamtdistanz und Zeit zurückbekommt, Zeitfenster-Constraints anwendet und EU-Umweltzonen-Beschränkungen für urbane Lieferungen handhabt. Ein JavaScript-Snippet zeichnet das Ergebnis dann auf einer Karte.
Die Python-Implementierung umfasst unter 55 Zeilen. Die JavaScript-Kartendarstellung weitere 30.
Das Last-Mile-Kostenproblem
Um zu verstehen, was Optimierung tatsächlich spart, berechnen Sie die Zahlen für ein realistisches Lieferszenario:
- Flotte: 10 Transporter
- Stopps pro Transporter pro Tag: 18
- Aktuelle durchschnittliche Distanz: 210 km/Transporter/Tag
- Treibstoffkosten: 0,38 EUR/km (Diesel, EU-Durchschnitt)
- Fahrerkosten: 22 EUR/Stunde
- Aktuelle durchschnittliche Routenzeit: 7,5 Stunden/Tag
Aktuelle tägliche Kosten pro Transporter: (210 × 0,38) + (7,5 × 22) = 79,80 EUR + 165 EUR = 244,80 EUR/Transporter/Tag
Eine 30-prozentige Entfernungsreduktion (erreichbar mit guter Optimierung in dichten Stadtnetzen) und eine 20-prozentige Zeiteinsparung ergeben:
- Optimierte Entfernung: 147 km → Treibstoffkosten: 55,86 EUR
- Optimierte Zeit: 6 Stunden → Fahrerkosten: 132 EUR
- Optimierte tägliche Kosten pro Transporter: 187,86 EUR/Transporter/Tag
Einsparung pro Transporter pro Tag: 56,94 EUR. Für 10 Transporter über 250 Arbeitstage: 142.350 EUR/Jahr aus einer API-Integration.
Die obigen Benchmarks spiegeln echte veröffentlichte Zahlen aus Last-Mile-Logistik-Studien wider. Ihre spezifischen Zahlen variieren je nach Geographie, Fahrzeugtyp und Stop-Dichte. Dichte urbane Bereiche verzeichnen die höchsten Gewinne, da naive sequentielle Routen das meiste unnötige Backtracking verschleiß.
Naive vs. optimierte Routen: Ein visueller Vergleich
Der Unterschied zwischen einer naiven (sequentiellen) Route und einer optimierten ist auf einer Karte stark.
Naive Routenplanung tritt auf, wenn Sie Stopps in der Reihenfolge ihres Eingangs eingeben, der erste Kunde, der eine Bestellung aufgegeben hat, ist erste auf der Route, unabhängig von der Geographie. In einer Stadt wie Amsterdam oder Berlin erzeugt dies das "Spaghetti-Routen"-Problem: Ihr Fahrer kreuzt ständig seinen eigenen Weg.
Optimierung löst das Travelling Salesman Problem (TSP) für Ihren Stopp-Satz. Für 15-20 Stopps ist dies rechnerisch in Millisekunden durchführbar. Für größere Flotten mit Hunderten von Stopps handhabt Vehicle Routing Problem (VRP) Solver die zusätzlichen Constraints von mehreren Fahrzeugen und Kapazitätslimits.
Schritt 1: Strukturieren Sie Ihre Lieferdaten
Jeder Stopp benötigt einen Standort und für zeitfenstergesteuerte Lieferungen ein time_window, das angibt, wann die Lieferung akzeptabel ist.
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" } }
]
Schritt 2: Rufen Sie den Routenoptimierungs-Endpoint auf
POSTen Sie das Depot und die Stop-Liste an den optimierten Routing-Endpoint. Die API gibt die Stopps in der effizientesten Besuchsreihenfolge zusammen mit der Gesamtroutendistanz und Dauer zurück.
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)
Schritt 3: Analysieren und zeigen Sie die optimierte Route an
Die API-Antwort beinhaltet die Stopps in optimierter Reihenfolge, kumulative ETAs für jeden Stopp, Gesamtdistanz und Gesamtdauer.
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)
Beispielausgabe für die fünf obigen Stopps:
--- 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%)
Schritt 4: EU-Umweltzonenhandhabung
Amsterdams ZTL-Zone, Pariser Crit'Air-System und Berlins Umweltzone beschränken bestimmte Fahrzeugtypen in zentralen Bereichen zu bestimmten Zeiten. Eine Route, die nur nach Entfernung effizient aussieht, kann für Ihr Fahrzeug ungültig sein.
Der Parameter avoid_low_emission_zones: true kombiniert mit dem vehicle_profile leitet automatisch um eingeschränkte Zonen für nicht konforme Fahrzeuge herum. Für elektrische und Euro 6 Fahrzeuge sind UZs passierbar und der Parameter hat keine Wirkung.
# 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
Für Logistikbetriebe, die einen Übergang von Diesel zu elektrisch planen, bietet der Vergleich dieser beiden Ausgaben pro Route eine direkte Quantifizierung der Reichweitenverbesserung durch Elektrifizierung.
Schritt 5: Zeigen Sie die optimierte Route auf einer Karte an
Nehmen Sie die Routengeometrie aus der API-Antwort und rendern Sie sie als eine Zeilenlage 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);
Berechnen Sie Ihre echten Einsparungen
Sobald Sie die API-Antwort in Händen halten, ist die Einsparungsberechnung einfach. Das Feld saving_vs_naive_km in der Antwort gibt Ihnen direkt die eingesparte Distanz. Daraus leiten Sie Kosteneinsparungen ab:
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)
Zeitfenster-Optimierung
Lieferung an eine Bäckerei um 06:00 Uhr und ein Restaurant um 14:00 Uhr bei gleichzeitiger Minimierung der Gesamtroutendistanz ist ein konstantes Optimierungsproblem. Die API handhabt dies automatisch, Sie müssen nur die Fenster bereitstellen:
# 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" } }
]
Wenn eine Zeitfensterbegrenzung angesichts der Depot-Abfahrtszeit und des aktuellen Verkehrsmodells nicht erfüllt werden kann, gibt die API ein Array constraint_violations zurück, das auflistet, welche Stopps nicht rechtzeitig erreicht werden konnten. Ihr Dispatchsystem kann dann den Fahrer benachrichtigen oder eine frühere Abfahrt vorschlagen.
Was auf diesem Fundament zu bauen ist
Routenoptimierung ist die Grundlage. Sobald es läuft, sind die natürlichen Erweiterungen:
- Live-Fahrterverfolgung: Speisen Sie optimierte Routenkoordinaten in das Live Driver Tracking Map Tutorial und zeigen Sie Kunden Echtzeit-ETA-Updates.
- Isochron-basierte Abdeckungsplanung: Nutzen Sie die Travel Time API, um zu visualisieren, welche Postleitzahlen Ihre Flotte innerhalb Ihres Lieferfensters erreichen kann. Der Artikel Isochrone Karten erklärt zeigt wie.
- Massen-Adressvalidierung: Validieren Sie vor der Routenoptimierung alle Lieferadressen mit der Geocoding API, um Tippfehler und veraltete Postleitzahlen zu fangen. Siehe So verwenden Sie die Geocoding API zum Validieren von 10.000 Adressen in Bulk.
Die Seite Logistik und Lieferung und die Seite Flottenmanagement behandeln zusätzliche MapAtlas-Funktionen relevant für Dispatchsoftware, einschließlich Multi-Vehicle VRP und Return-to-Depot-Optimierung.
Erste Schritte
- Kostenlos MapAtlas API Key erhalten, die kostenlose Version beinhaltet Routing- und Optimierungsaufrufe, keine Kreditkarte erforderlich
- Lesen Sie die Routing API Dokumentation für die vollständige Liste von Fahrzeugprofilen, Zeitfenster-Parametern und Multi-Vehicle-Optionen
- Erkunden Sie die Route Planning and Navigation Capabilities Seite für einen Produktüberblick
Häufig gestellte Fragen
Wie reduziert Routenoptimierung die Lieferkosten?
Routenoptimierung ordnet Mehrfach-Liefersequenzen um, um Gesamtdistanz und Fahrtzeit zu minimieren. Studien zeigen konstant 20-35 Prozent Reduktion der gefahrenen Distanz gegenüber einer naiven sequentiellen Route. Für ein Fahrzeug, das 200 km/Tag bei 0,35 EUR/km Treibstoffkosten fährt, spart eine 30-prozentige Reduktion etwa 21 EUR pro Fahrzeug pro Tag, rund 5.000 EUR pro Jahr pro Fahrzeug.
Was sind Zeitfenster in der Routenoptimierung?
Zeitfenster sind Lieferconstraints, die erfordern, dass ein Stopp innerhalb eines bestimmten Zeitbereichs besucht wird, beispielsweise ein Geschäft, das Lieferungen nur zwischen 09:00 und 12:00 Uhr akzeptiert. Der Optimierer muss alle Zeitfenster respektieren, während er die Gesamtroutendistanz minimiert, was ein erheblich schwierigeres rechnerisches Problem ist als unkonstante Optimierung.
Handhabt die MapAtlas Routing API EU-Umweltzonen?
Ja. Die MapAtlas Routing API umfasst Straßenbeschränkungsdaten für EU-Umweltzonen, einschließlich Amsterdams ZTL, der Paris Crit'Air Zone und Berlins Umweltzone. Geben Sie das Fahrzeugprofil (Diesel Euro 5, Benzin, elektrisch) als Parameter ein und der Router vermeidet automatisch eingeschränkte Zonen für nicht konforme Fahrzeuge.

