Kurz: Směrování elektrických vozidel pomocí Azure Notebooks (Python)

Azure Mapy je portfolio rozhraní API geoprostorových služeb, která jsou nativně integrovaná do Azure. Tato rozhraní API umožňují vývojářům, podnikům a výrobcům softwaru vyvíjet aplikace pracující s umístěním, IoT, mobilitu, logistiku a řešení pro sledování aktiv.

Rozhraní AZURE Mapy REST API je možné volat z jazyků, jako je Python a R, a umožnit tak geoprostorové analýzy dat a scénáře strojového učení. Azure Mapy nabízí robustní sadu rozhraní API směrování, která uživatelům umožňují vypočítat trasy mezi několika datovými body. Výpočty vycházejí z různých podmínek, jako je typ vozidla nebo dosažitelná oblast.

V tomto kurzu budete pomáhat řidiče, jehož elektrické vozidlo baterie je nízké. Řidič musí najít nejbližší možnou nabíjecí stanici z místa vozidla.

V tomto kurzu:

  • Vytvořte a spusťte soubor Jupyter Notebook v Azure Notebooks v cloudu.
  • Volání rozhraní REST API Azure Mapy v Pythonu
  • Vyhledejte dosažitelný rozsah na základě modelu spotřeby elektrického vozidla.
  • Vyhledejte nabíjecí stanice elektrických vozidel v dosahu nebo isochronu.
  • Vykreslení hranice dosažitelného rozsahu a nabíjecích stanic na mapě
  • Najděte a vizualizujete trasu k nejbližší nabíjecí stanici pro elektrické vozidlo na základě doby jízdy.

Požadavky

Poznámka:

Další informace o ověřování v Azure Mapy najdete v tématu správa ověřování v Azure Mapy.

Vytvoření projektu Azure Notebooks

Pokud chcete postupovat podle tohoto kurzu, musíte vytvořit projekt Azure Notebooks a stáhnout a spustit soubor Jupyter Notebook. Soubor Jupyter Notebook obsahuje kód Pythonu, který implementuje scénář v tomto kurzu. Pokud chcete vytvořit projekt Azure Notebooks a nahrát do něj dokument Jupyter Notebook, postupujte takto:

  1. Přejděte do Služby Azure Notebooks a přihlaste se. Další informace najdete v tématu Rychlý start: Přihlášení a nastavení ID uživatele.

  2. V horní části stránky veřejného profilu vyberte Moje projekty.

    The My Projects button

  3. Na stránce Moje projekty vyberte Nový projekt.

    The New Project button

  4. V podokně Vytvořit nový projekt zadejte název projektu a ID projektu.

    The Create New Project pane

  5. Vyberte Vytvořit.

  6. Po vytvoření projektu si stáhněte tento soubor dokumentu Jupyter Notebook z úložiště Azure Mapy Jupyter Notebook.

  7. V seznamu projektů na stránce Moje projekty vyberte projekt a pak vyberte Nahrát a nahrajte soubor dokumentu Jupyter Notebook.

    upload Jupyter Notebook

  8. Nahrajte soubor z počítače a pak vyberte Hotovo.

  9. Po úspěšném dokončení nahrávání se soubor zobrazí na stránce projektu. Poklikáním na soubor ho otevřete jako Jupyter Notebook.

Pokuste se porozumět funkcím implementovaným v souboru Jupyter Notebook. Spusťte kód v souboru Jupyter Notebook po jedné buňce. Kód v každé buňce můžete spustit výběrem tlačítka Spustit v horní části aplikace Jupyter Notebook.

The Run button

Instalace balíčků na úrovni projektu

Pokud chcete kód spustit v aplikaci Jupyter Notebook, nainstalujte balíčky na úrovni projektu pomocí následujících kroků:

  1. Stáhněte si soubor requirements.txt z úložiště Azure Mapy Jupyter Notebook a pak ho nahrajte do projektu.

  2. Na řídicím panelu projektu vyberte Project Nastavení.

  3. V podokně Nastavení projektu vyberte kartu Prostředí a pak vyberte Přidat.

  4. V části Kroky nastavení prostředí proveďte následující kroky: a. V prvním rozevíracím seznamu vyberte Requirements.txt.
    b. V druhém rozevíracím seznamu vyberte váš soubor requirements.txt .
    c. Ve třetím rozevíracím seznamu jako vaši verzi vyberte Python verze 3.6 .

  5. Zvolte Uložit.

    Install packages

Načtení požadovaných modulů a architektur

Pokud chcete načíst všechny požadované moduly a architektury, spusťte následující skript.

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

Vyžádání hranice dosažitelného rozsahu

Společnost pro doručování zásilek má ve své flotile několik elektrických vozidel. Během dne musí být elektrická vozidla dobíjená, aniž by se museli vrátit do skladu. Pokaždé, když zbývající poplatek klesne na méně než hodinu, vyhledáte sadu nabíjecích stanic, které jsou v dosahu. V podstatě hledáte nabíjecí stanici, když je baterie nabitá. A získáte informace o hranicích pro tento rozsah nabíjecích stanic.

Vzhledem k tomu, že společnost preferuje používání tras, které vyžadují rovnováhu ekonomiky a rychlosti, je požadovaný typ trasy eco. Následující skript volá rozhraní API get Route Range služby Azure Mapy routing. Používá parametry pro model spotřeby vozidla. Skript pak parsuje odpověď na vytvoření mnohoúhelníku geojsonového formátu, který představuje maximální dosažitelný rozsah vozu.

Chcete-li určit hranice dosažitelného rozsahu elektrického vozidla, spusťte skript v následující buňce:

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

Vyhledání nabíjecích stanic pro elektrické vozidlo v dosahu

Po určení dosažitelného rozsahu (isochron) pro elektrické vozidlo můžete vyhledat nabíjecí stanice v daném rozsahu.

Následující skript volá rozhraní AZURE Mapy Post Search Inside Geometry API. Hledá nabíjecí stanice pro elektrické vozidlo v rámci hranice maximálního dosažitelného rozsahu auta. Pak skript parsuje odpověď na pole dosažitelných umístění.

Pokud chcete vyhledat nabíjecí stanice elektrických vozidel v dosahu, spusťte následující skript:

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

Nahrání dostupného rozsahu a nabíjecích bodů

Je užitečné vizualizovat nabíjecí stanice a hranici maximálního dosažitelného rozsahu elektrického vozidla na mapě. Postupujte podle kroků uvedených v článku Jak vytvořit registr dat, abyste nahráli data hranic a nabíjecí stanice jako geojsonové objekty do účtu úložiště Azure a pak je zaregistrovali ve svém účtu Azure Mapy. Nezapomeňte si poznamenat jedinečnou hodnotu identifikátoru (udid) a budete ji potřebovat. Na udid objekty geojson, které jste nahráli do účtu úložiště Azure, odkazujete ze zdrojového kódu.

Vykreslení nabíjecích stanic a dosažitelného rozsahu na mapě

Po nahrání dat do účtu úložiště Azure zavolejte službu Azure Mapy Get Map Image service. Tato služba slouží k vykreslení nabíjecích bodů a maximální dosažitelné hranice na obrázku statické mapy spuštěním následujícího skriptu:

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

path = "lcff3333|lw3|la0.80|fa0.35||udid-{}".format(rangeUdid)
pins = "custom|an15 53||udid-{}||https://raw.githubusercontent.com/Azure-Samples/AzureMapsCodeSamples/master/AzureMapsCodeSamples/Common/images/icons/ev_pin.png".format(poiUdid)

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

A map showing the location range

Vyhledání optimální nabíjecí stanice

Nejprve chcete určit všechny potenciální nabíjecí stanice v dosahu. Pak chcete vědět, které z nich lze dosáhnout v minimálním časovém intervalu.

Následující skript volá rozhraní API pro směrování matice Azure Mapy. Vrátí zadanou polohu vozidla, dobu jízdy a vzdálenost do každé nabíjecí stanice. Skript v další buňce analyzuje odpověď a vyhledá nejbližší dosažitelnou nabíjecí stanici s ohledem na čas.

Pokud chcete najít nejbližší dosažitelnou nabíjecí stanici, která se dá dosáhnout v co nejmenším čase, spusťte skript v následující buňce:

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)

Výpočet trasy k nejbližší nabíjecí stanici

Teď, když jste našli nejbližší nabíjecí stanici, můžete zavolat rozhraní API Get Route Directions a požádat o podrobnou trasu z aktuální polohy elektrického vozidla do nabíjecí stanice.

Pokud chcete získat trasu do nabíjecí stanice a parsovat odpověď na vytvoření objektu geojson, který představuje trasu, spusťte skript v následující buňce:

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

Vizualizace trasy

Pokud chcete trasu vizualizovat, postupujte podle kroků popsaných v článku Vytvoření registru dat a nahrajte směrovací data jako objekt geojson do účtu úložiště Azure a pak je zaregistrujte ve svém účtu Azure Mapy. Nezapomeňte si poznamenat jedinečnou hodnotu identifikátoru (udid) a budete ji potřebovat. Na udid objekty geojson, které jste nahráli do účtu úložiště Azure, odkazujete ze zdrojového kódu. Potom zavolejte vykreslovací službu, získejte rozhraní API mapového obrázku, abyste trasu vykreslili na mapě a vizualizovali ji.

Pokud chcete získat obrázek vykreslené trasy na mapě, spusťte následující skript:

# Upload the route data to Azure Maps Data service .
routeUploadRequest = await session.post("https://atlas.microsoft.com/mapData?subscription-key={}&api-version=2.0&dataFormat=geojson".format(subscriptionKey), json = routeData)

udidRequestURI = routeUploadRequest.headers["Location"]+"&subscription-key={}".format(subscriptionKey)

while True:
    udidRequest = await (await session.get(udidRequestURI)).json()
    if 'udid' in udidRequest:
        break
    else:
        time.sleep(0.2)

udid = udidRequest["udid"]

destination = route[-1]

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

path = "lc0f6dd9|lw6||udid-{}".format(udid)
pins = "default|codb1818||{} {}|{} {}".format(str(currentLocation[1]),str(currentLocation[0]),destination[1],destination[0])


# Get boundaries for the bounding box.
minLat, maxLat = (float(destination[0]),currentLocation[0]) if float(destination[0])<currentLocation[0] else (currentLocation[0], float(destination[0]))
minLon, maxLon = (float(destination[1]),currentLocation[1]) if float(destination[1])<currentLocation[1] else (currentLocation[1], 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))

A map showing the route

V tomto kurzu jste se naučili volat rozhraní REST API Azure Mapy přímo a vizualizovat data Mapy Azure pomocí Pythonu.

Pokud chcete prozkoumat rozhraní API azure Mapy, která se používají v tomto kurzu, podívejte se na:

Vyčištění prostředků

Nejsou k dispozici žádné prostředky, které by vyžadovaly vyčištění.

Další kroky

Další informace o službě Azure Notebooks najdete v tématu