Freigeben über


Tutorial: Routenplanung für Elektrofahrzeuge mit Azure Notebooks (Python)

Azure Maps ist ein Portfolio von Geodienst-APIs, die in Azure integriert sind, damit Entwickler standortbezogene Anwendungen für verschiedene Szenarien wie IoT, Mobilität und Ressourcennachverfolgung erstellen können.

REST-APIs von Azure Maps unterstützen Sprachen wie Python und R für Geodatenanalyse und maschinelles Lernen und bieten robuste Routenplanungs-APIs zur Berechnung von Routen auf der Grundlage von Bedingungen wie Fahrzeugtyp oder erreichbarem Bereich.

Dieses Tutorial führt Benutzer durch die Routenplanung für Elektrofahrzeuge mithilfe von Azure Maps-APIs in Verbindung mit Jupyter Notebook-Instanzen in VS Code und Python, um bei niedrigem Akkustand die nächstgelegene Ladestation zu finden.

In diesem Lernprogramm lernen Sie Folgendes:

  • Erstellen und Ausführen eines Jupyter Notebooks in VS Code
  • Aufrufen von Azure Maps-REST-APIs in Python
  • Suchen nach einem erreichbaren Bereich auf Grundlage des Verbrauchsmodells des Elektrofahrzeugs
  • Suchen nach Ladestationen für Elektrofahrzeuge in Reichweite bzw. in der Isochrone
  • Rendern der Grenze des erreichbaren Bereichs und der Ladestationen auf einer Karte
  • Ermitteln und Visualisieren einer Route zur nächstgelegenen Ladestation basierend auf der Fahrtzeit

Voraussetzungen

Hinweis

Weitere Informationen zur Authentifizierung in Azure Maps finden Sie unter Verwalten der Authentifizierung in Azure Maps.

Installieren von Paketen auf Projektebene

Das Projekt EV Routing and Reachable Range ist von den Python-Bibliotheken aiohttp und IPython abhängig. Sie können diese im Visual Studio-Terminal mit pip installieren:

pip install aiohttp
pip install ipython

Öffnen von Jupyter Notebook in Visual Studio Code

Laden Sie das Notebook herunter, das in diesem Tutorial verwendet wird, und öffnen Sie es:

  1. Öffnen Sie die Datei EVrouting.ipynb im AzureMapsJupyterSamples-Repository in GitHub.

  2. Wählen Sie auf dem Bildschirm oben rechts die Schaltfläche Rohdatei herunterladen aus, um die Datei lokal zu speichern.

    Bildschirmfoto zum Herunterladen der Notebook-Datei namens „EVrouting.ipynb“ aus dem GitHub-Repository

  3. Öffnen Sie das heruntergeladene Notebook in Visual Studio Code, indem Sie mit der rechten Maustaste auf die Datei klicken und dann Öffnen mit > Visual Studio Code auswählen, oder verwenden Sie den VS Code-Explorer.

Laden der erforderlichen Module und Frameworks

Sobald Ihr Code hinzugefügt wurde, können Sie eine Zelle über das Symbol Ausführen links neben der Zelle ausführen. Die Ausgabe wird unterhalb der Codezelle angezeigt.

Führen Sie das folgende Skript aus, um alle erforderlichen Module und Frameworks zu laden:

import time
import aiohttp
import urllib.parse
from IPython.display import Image, display

Bildschirmfoto zum Herunterladen der ersten Zelle im Notebook, die die erforderlichen Importanweisungen enthält, mit hervorgehobener Schaltfläche „Ausführen“

Anfordern der Grenze des erreichbaren Bereichs

Die Fahrzeugflotte eines Paketzustellers umfasst auch einige Elektrofahrzeuge. Diese Fahrzeuge müssen im Laufe des Tages aufgeladen werden, ohne dafür zum Lager zurückkehren zu müssen. Wenn die Restkapazität auf unter eine Stunde fällt, wird nach erreichbaren Ladestationen gesucht. Anschließend werden die Grenzinformationen für die in Frage kommenden Ladestationen abgerufen.

Der anforderte routeType ist eco, um ein ausgewogenes Verhältnis zwischen Wirtschaftlichkeit und Geschwindigkeit zu erhalten. Mit dem folgenden Skript wird die API Get Route Range des Azure Maps-Routenplanungsdiensts aufgerufen. Dabei werden Parameter im Zusammenhang mit dem Verbrauchsmodell des Fahrzeugs verwendet. Das Skript analysiert dann die Antwort, um ein Polygonobjekt im GeoJSON-Format zu erstellen, das die maximale Reichweite des Fahrzeugs darstellt.

subscriptionKey = "Your Azure Maps key"
currentLocation = [34.028115,-118.5184279]
session = aiohttp.ClientSession()

# Parameters for the vehicle consumption model 
travelMode = "car"
vehicleEngineType = "electric"
currentChargeInkWh=45
maxChargeInkWh=80
timeBudgetInSec=550
routeType="eco"
constantSpeedConsumptionInkWhPerHundredkm="50,8.2:130,21.3"

# Get boundaries for the electric vehicle's reachable range.
routeRangeResponse = await (await session.get("https://atlas.microsoft.com/route/range/json?subscription-key={}&api-version=1.0&query={}&travelMode={}&vehicleEngineType={}&currentChargeInkWh={}&maxChargeInkWh={}&timeBudgetInSec={}&routeType={}&constantSpeedConsumptionInkWhPerHundredkm={}"
                                              .format(subscriptionKey,str(currentLocation[0])+","+str(currentLocation[1]),travelMode, vehicleEngineType, currentChargeInkWh, maxChargeInkWh, timeBudgetInSec, routeType, constantSpeedConsumptionInkWhPerHundredkm))).json()

polyBounds = routeRangeResponse["reachableRange"]["boundary"]

for i in range(len(polyBounds)):
    coordList = list(polyBounds[i].values())
    coordList[0], coordList[1] = coordList[1], coordList[0]
    polyBounds[i] = coordList

polyBounds.pop()
polyBounds.append(polyBounds[0])

boundsData = {
               "geometry": {
                 "type": "Polygon",
                 "coordinates": 
                   [
                      polyBounds
                   ]
                }
             }

Suchen nach Ladestationen für Elektrofahrzeuge im erreichbaren Bereich

Nach Bestimmung der Reichweite (Isochrone) des Elektrofahrzeugs können Sie innerhalb dieses Bereichs nach Ladestationen suchen.

Das folgende Skript verwendet die Azure Maps-API Post Search Inside Geometry, um nach Ladestationen innerhalb der maximalen Reichweite des Fahrzeugs zu suchen. Anschließend analysiert es die Antwort, um ein Array mit erreichbaren Standorten zu erhalten.

# Search for electric vehicle stations within reachable range.
searchPolyResponse = await (await session.post(url = "https://atlas.microsoft.com/search/geometry/json?subscription-key={}&api-version=1.0&query=electric vehicle station&idxSet=POI&limit=50".format(subscriptionKey), json = boundsData)).json() 

reachableLocations = []
for loc in range(len(searchPolyResponse["results"])):
                location = list(searchPolyResponse["results"][loc]["position"].values())
                location[0], location[1] = location[1], location[0]
                reachableLocations.append(location)

Rendern der Ladestationen und des erreichbaren Bereichs auf einer Karte

Rufen Sie den Dienst zum Abrufen des Kartenbilds von Azure Maps auf, um die Ladestationen sowie die Grenze der maximalen Reichweite auf dem statischen Kartenbild zu rendern. Führen Sie dazu das folgende Skript aus:

# Get boundaries for the bounding box.
def getBounds(polyBounds):
    maxLon = max(map(lambda x: x[0], polyBounds))
    minLon = min(map(lambda x: x[0], polyBounds))

    maxLat = max(map(lambda x: x[1], polyBounds))
    minLat = min(map(lambda x: x[1], polyBounds))
    
    # Buffer the bounding box by 10 percent to account for the pixel size of pins at the ends of the route.
    lonBuffer = (maxLon-minLon)*0.1
    minLon -= lonBuffer
    maxLon += lonBuffer

    latBuffer = (maxLat-minLat)*0.1
    minLat -= latBuffer
    maxLat += latBuffer
    
    return [minLon, maxLon, minLat, maxLat]

minLon, maxLon, minLat, maxLat = getBounds(polyBounds)
polyBoundsFormatted = ('|'.join(map(str, polyBounds))).replace('[','').replace(']','').replace(',','')
reachableLocationsFormatted = ('|'.join(map(str, reachableLocations))).replace('[','').replace(']','').replace(',','')

path = "lcff3333|lw3|la0.80|fa0.35||{}".format(polyBoundsFormatted)
pins = "custom|an15 53||{}||https://raw.githubusercontent.com/Azure-Samples/AzureMapsCodeSamples/e3a684e7423075129a0857c63011e7cfdda213b7/Static/images/icons/ev_pin.png".format(reachableLocationsFormatted)

encodedPins = urllib.parse.quote(pins, safe='')

# Render the range and electric vehicle charging points on the map.
staticMapResponse =  await session.get("https://atlas.microsoft.com/map/static/png?api-version=2022-08-01&subscription-key={}&pins={}&path={}&bbox={}&zoom=12".format(subscriptionKey,encodedPins,path,str(minLon)+", "+str(minLat)+", "+str(maxLon)+", "+str(maxLat)))

poiRangeMap = await staticMapResponse.content.read()

display(Image(poiRangeMap))

Bildschirmfoto des Standortbereichs

Finden der optimalen Ladestation

Identifizieren zuerst alle potenziellen Ladestationen innerhalb der Reichweite des Fahrzeugs. Bestimmen Sie als Nächstes, welche dieser Stationen am schnellsten genutzt werden kann.

Mit dem folgenden Skript wird die API für die Matrixroutenplanung von Azure Maps aufgerufen. Diese gibt die Position des Fahrzeugs, die Fahrtzeit und die Entfernung zu den einzelnen Ladestationen zurück. Das folgende Skript analysiert diese Antwort, um die nächstgelegene Ladestation zu identifizieren, die am schnellsten erreichbar ist:

locationData = {
            "origins": {
              "type": "MultiPoint",
              "coordinates": [[currentLocation[1],currentLocation[0]]]
            },
            "destinations": {
              "type": "MultiPoint",
              "coordinates": reachableLocations
            }
         }

# Get the travel time and distance to each specified charging station.
searchPolyRes = await (await session.post(url = "https://atlas.microsoft.com/route/matrix/json?subscription-key={}&api-version=1.0&routeType=shortest&waitForResults=true".format(subscriptionKey), json = locationData)).json()

distances = []
for dist in range(len(reachableLocations)):
    distances.append(searchPolyRes["matrix"][0][dist]["response"]["routeSummary"]["travelTimeInSeconds"])

minDistLoc = []
minDistIndex = distances.index(min(distances))
minDistLoc.extend([reachableLocations[minDistIndex][1], reachableLocations[minDistIndex][0]])
closestChargeLoc = ",".join(str(i) for i in minDistLoc)

Berechnen der Route zur nächstgelegenen Ladestation

Verwenden Sie nach Ermittlung der nächstgelegenen Ladestation die API Get Route Directions, um eine detaillierte Wegbeschreibung für den aktuellen Standort des Fahrzeugs zu erhalten. Führen Sie das Skript in der nächsten Zelle aus, um ein GeoJSON-Objekt, das die Route darstellt, zu generieren und zu analysieren.

# Get the route from the electric vehicle's current location to the closest charging station. 
routeResponse = await (await session.get("https://atlas.microsoft.com/route/directions/json?subscription-key={}&api-version=1.0&query={}:{}".format(subscriptionKey, str(currentLocation[0])+","+str(currentLocation[1]), closestChargeLoc))).json()

route = []
for loc in range(len(routeResponse["routes"][0]["legs"][0]["points"])):
                location = list(routeResponse["routes"][0]["legs"][0]["points"][loc].values())
                location[0], location[1] = location[1], location[0]
                route.append(location)

routeData = {
         "type": "LineString",
         "coordinates": route
     }

Visualisieren der Route

Verwenden Sie zum Visualisieren der Route die API Get Map Image, um sie auf der Karte zu rendern.

destination = route[-1]

#destination[1], destination[0] = destination[0], destination[1]

routeFormatted = ('|'.join(map(str, route))).replace('[','').replace(']','').replace(',','')
path = "lc0f6dd9|lw6||{}".format(routeFormatted)
pins = "default|codb1818||{} {}|{} {}".format(str(currentLocation[1]),str(currentLocation[0]),destination[0],destination[1])


# Get boundaries for the bounding box.
minLon, maxLon = (float(destination[0]),currentLocation[1]) if float(destination[0])<currentLocation[1] else (currentLocation[1], float(destination[0]))
minLat, maxLat = (float(destination[1]),currentLocation[0]) if float(destination[1])<currentLocation[0] else (currentLocation[0], float(destination[1]))

# Buffer the bounding box by 10 percent to account for the pixel size of pins at the ends of the route.
lonBuffer = (maxLon-minLon)*0.1
minLon -= lonBuffer
maxLon += lonBuffer

latBuffer = (maxLat-minLat)*0.1
minLat -= latBuffer
maxLat += latBuffer

# Render the route on the map.
staticMapResponse = await session.get("https://atlas.microsoft.com/map/static/png?api-version=2022-08-01&subscription-key={}&&path={}&pins={}&bbox={}&zoom=16".format(subscriptionKey,path,pins,str(minLon)+", "+str(minLat)+", "+str(maxLon)+", "+str(maxLat)))

staticMapImage = await staticMapResponse.content.read()

await session.close()
display(Image(staticMapImage))

Bildschirmfoto einer Karte mit der Route zeigt

In diesem Tutorial haben Sie erfahren, wie Sie REST-APIs von Azure Maps direkt aufrufen und Azure Maps-Daten mit Python visualisieren.

Weitere Informationen zu den in diesem Tutorial verwendeten Azure Maps-APIs finden Sie unter:

Eine vollständige Liste mit den Azure Maps-REST-APIs finden Sie unter Azure Maps-Dokumentation.

Nächste Schritte