Självstudie: Dirigera elfordon med hjälp av Azure Notebooks (Python)

Azure Maps är en portfölj med geospatiala tjänst-API:er som är integrerade i Azure, vilket gör det möjligt för utvecklare att skapa platsmedvetna program för olika scenarier som IoT, mobilitet och tillgångsspårning.

Rest-API:er för Azure Maps stöder språk som Python och R för geospatial dataanalys och maskininlärning, och erbjuder robusta routnings-API:er för att beräkna vägar baserat på förhållanden som fordonstyp eller nåbart område.

Den här självstudien vägleder användare genom att dirigera elfordon med hjälp av Azure Maps-API:er tillsammans med Jupyter Notebooks i VS Code och Python för att hitta den närmaste laddningsstationen när batteriet är lågt.

I den här självstudien kommer vi att:

  • Skapa och kör en Jupyter Notebook i VS Code.
  • Anropa REST-API:er för Azure Maps i Python.
  • Sök efter ett räckviddsintervall baserat på elbilens förbrukningsmodell.
  • Sök efter laddningsstationer för elfordon inom räckvidden, eller isochron.
  • Rendera räckviddsgräns och laddningsstationer på en karta.
  • Hitta och visualisera en väg till närmaste laddningsstation för elfordon baserat på körtid.

Förutsättningar

Kommentar

Mer information om autentisering i Azure Maps finns i Hantera autentisering i Azure Maps.

Installera paket på projektnivå

Projektet EV Routing and Reachable Range har beroenden för python-biblioteken aiohttp och IPython. Du kan installera dessa i Visual Studio-terminalen med pip:

pip install aiohttp
pip install ipython

Öppna Jupyter Notebook i Visual Studio Code

Ladda ned och öppna sedan notebook-filen som används i den här självstudien:

  1. Öppna filen EVrouting.ipynblagringsplatsen AzureMapsJupyterSamples i GitHub.

  2. Välj knappen Ladda ned råfil i det övre högra hörnet på skärmen för att spara filen lokalt.

    En skärmbild som visar hur du laddar ned notebook-filen med namnet EVrouting.ipynb från GitHub-lagringsplatsen.

  3. Öppna den nedladdade notebook-filen i Visual Studio Code genom att högerklicka på filen och sedan välja Öppna med > Visual Studio Code eller via VS Code-Utforskaren.

Läs in de moduler och ramverk som krävs

När koden har lagts till kan du köra en cell med hjälp av ikonen Kör till vänster om cellen och utdata visas under kodcellen.

Kör följande skript för att läsa in alla nödvändiga moduler och ramverk.

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

En skärmbild som visar hur du laddar ned den första cellen i notebook-filen som innehåller nödvändiga importinstruktioner med körningsknappen markerad.

Begär gränsen för det nåbara intervallet

Ett paketleveransföretag driver en flotta som innehåller vissa elfordon. Dessa fordon måste laddas under dagen utan att återvända till lagret. När den återstående avgiften sjunker under en timme utförs en sökning för att hitta laddningsstationer inom ett nåbart intervall. Gränsinformationen för intervallet för dessa laddningsstationer erhålls sedan.

Det begärda routeType är eko för att balansera ekonomi och hastighet. Följande skript anropar API:et Hämta routningsintervall för Azure Maps-routningstjänsten med hjälp av parametrar relaterade till fordonets förbrukningsmodell. Skriptet parsar sedan svaret för att skapa ett polygonobjekt i GeoJSON-format, vilket representerar bilens maximala räckvidd.

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
                   ]
                }
             }

Sök efter laddningsstationer för elfordon inom räckvidden

När du har fastställt elbilens räckvidd (isochron) kan du söka efter laddningsstationer inom det området.

Följande skript använder API:et För Azure Maps Post Search Inside Geometry för att hitta laddningsstationer inom fordonets maximala räckvidd. Svaret parsas sedan till en matris med nåbara platser.

# 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)

Rendera laddningsstationerna och räckviddsintervallet på en karta

Anropa Azure Maps Get Map Image-tjänsten för att återge laddningspunkterna och den maximala gränsen som kan nås på den statiska kartbilden genom att köra följande skript:

# 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))

En skärmbild som visar platsintervallet.

Hitta den optimala laddningsstationen

Identifiera först alla potentiella laddningsstationer inom fordonets räckvidd. Bestäm sedan vilken av dessa stationer som kan nås på kortast möjliga tid.

Följande skript anropar API:et för matrisroutning i Azure Maps. Den returnerar fordonets plats, restid och avstånd till varje laddningsstation. Det efterföljande skriptet parsar det här svaret för att identifiera den närmaste laddningsstationen som kan nås under minst tid.

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)

Beräkna vägen till närmaste laddningsstation

När du har lokaliserat närmaste laddningsstation använder du API:et Get Route Directions för att hämta detaljerade anvisningar från fordonens aktuella plats. Kör skriptet i nästa cell för att generera och parsa ett GeoJSON-objekt som representerar vägen.

# 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
     }

Visualisera vägen

Om du vill visualisera vägen använder du API:et Hämta kartbild för att återge den på kartan.

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))

En skärmbild som visar en karta som visar vägen.

I den här självstudien har du lärt dig hur du anropar REST-API:er för Azure Maps direkt och visualiserar Azure Maps-data med hjälp av Python.

Mer information om Azure Maps-API:er som används i den här självstudien finns i:

En fullständig lista över REST-API:er för Azure Maps finns i Rest-API:er för Azure Maps.

Nästa steg