Samouczek: kierowanie pojazdów elektrycznych przy użyciu usługi Azure Notebooks (Python)
Usługa Azure Maps to portfolio interfejsów API usług geoprzestrzennych zintegrowanych z platformą Azure, co umożliwia deweloperom tworzenie aplikacji obsługujących lokalizację dla różnych scenariuszy, takich jak IoT, mobilność i śledzenie zasobów.
Interfejsy API REST usługi Azure Maps obsługują języki takie jak Python i R na potrzeby analizy danych geoprzestrzennych i uczenia maszynowego, oferując niezawodne interfejsy API routingu do obliczania tras na podstawie warunków, takich jak typ pojazdu lub dostępny obszar.
Ten samouczek przeprowadzi użytkowników przez routing pojazdów elektrycznych przy użyciu interfejsów API usługi Azure Maps wraz z notesami Jupyter Notebook w programie VS Code i języku Python, aby znaleźć najbliższą stację ładowania, gdy bateria jest niska.
Ten samouczek obejmuje następujące kroki:
- Tworzenie i uruchamianie notesu Jupyter w programie VS Code.
- Wywoływanie interfejsów API REST usługi Azure Maps w języku Python.
- Wyszukaj dostępny zakres na podstawie modelu zużycia pojazdu elektrycznego.
- Wyszukaj stacje ładowania pojazdów elektrycznych w zasięgu osiągalnym lub isochrone.
- Renderuj osiągalne stacje granic i ładowania na mapie.
- Znajdź i wizualizuj trasę do najbliższej stacji ładowania pojazdów elektrycznych na podstawie czasu jazdy.
Wymagania wstępne
- Konto usługi Azure Maps
- Klucz subskrypcji
- Visual Studio Code
- Działająca wiedza na temat notesów Jupyter Notebook w programie VS Code
- Środowisko skonfigurowane do pracy z językiem Python w notesach Jupyter Notebook. Aby uzyskać więcej informacji, zobacz Konfigurowanie środowiska.
Uwaga
Aby uzyskać więcej informacji na temat uwierzytelniania w usłudze Azure Maps, zobacz Zarządzanie uwierzytelnianiem w usłudze Azure Maps.
Instalowanie pakietów na poziomie projektu
Projekt EV Routing i Reachable Range ma zależności od bibliotek aiohttp i IPython python. Te elementy można zainstalować w terminalu programu Visual Studio przy użyciu narzędzia:
pip install aiohttp
pip install ipython
Otwieranie notesu Jupyter w programie Visual Studio Code
Pobierz, a następnie otwórz notes używany w tym samouczku:
Otwórz plik EVrouting.ipynb w repozytorium AzureMapsJupyterSamples w usłudze GitHub.
Wybierz przycisk Pobierz nieprzetworzone pliki w prawym górnym rogu ekranu, aby zapisać plik lokalnie.
Otwórz pobrany notes w programie Visual Studio Code, klikając go prawym przyciskiem myszy, a następnie wybierając polecenie Otwórz w > programie Visual Studio Code lub za pośrednictwem Eksplorator plików programu VS Code.
Ładowanie wymaganych modułów i struktur
Po dodaniu kodu możesz uruchomić komórkę przy użyciu ikony Uruchom po lewej stronie komórki, a dane wyjściowe zostaną wyświetlone poniżej komórki kodu.
Uruchom następujący skrypt, aby załadować wszystkie wymagane moduły i struktury.
import time
import aiohttp
import urllib.parse
from IPython.display import Image, display
Zażądaj granicy osiągalnego zakresu
Firma dostarczająca pakiety obsługuje flotę, która obejmuje niektóre pojazdy elektryczne. Te pojazdy muszą być ładowane w ciągu dnia bez powrotu do magazynu. Gdy pozostałe opłaty spadną poniżej godziny, wyszukiwanie jest przeprowadzane w celu znalezienia stacji ładowania w zasięgu osiągalnym. Następnie uzyskuje się informacje graniczne dotyczące zakresu tych stacji ładowania.
Wniosek jest ekologicznyrouteType
, aby zrównoważyć gospodarkę i szybkość. Poniższy skrypt wywołuje interfejs API Pobierania zakresu tras usługi routingu Azure Maps przy użyciu parametrów związanych z modelem zużycia pojazdu. Następnie skrypt analizuje odpowiedź, aby utworzyć obiekt wielokątny w formacie GeoJSON, reprezentując maksymalny zakres osiągalny samochodu.
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={}¤tChargeInkWh={}&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
]
}
}
Wyszukiwanie stacji ładowania pojazdów elektrycznych w zasięgu osiągalnym
Po określeniu zasięgu pojazdu elektrycznego (izochrone) można wyszukać stacje ładowania w tym obszarze.
Poniższy skrypt używa interfejsu API wyszukiwania postów w usłudze Azure Maps wewnątrz geometrii , aby znaleźć stacje ładowania w maksymalnym zasięgu pojazdu. Następnie analizuje odpowiedź w tablicę osiągalnych lokalizacji.
# 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)
Renderowanie stacji ładowania i osiągalny zakres na mapie
Wywołaj usługę Azure Maps Get Map Image , aby renderować punkty ładowania i maksymalną granicę osiągalną na obrazie mapy statycznej, uruchamiając następujący skrypt:
# 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))
Znajdowanie optymalnej stacji ładowania
Najpierw zidentyfikuj wszystkie potencjalne stacje ładowania w zasięgu osiągalnego pojazdu. Następnie określ, do których z tych stacji można uzyskać dostęp w najkrótszym możliwym czasie.
Poniższy skrypt wywołuje interfejs API routingu macierzy usługi Azure Maps. Zwraca lokalizację pojazdu, czas podróży i odległość do każdej stacji ładowania. Kolejny skrypt analizuje tę odpowiedź, aby zidentyfikować najbliższą stację ładowania, która może być osiągnięta w najmniejszym czasie.
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)
Obliczanie trasy do najbliższej stacji ładowania
Po zlokalizowaniu najbliższej stacji ładowania użyj interfejsu API Get Route Directions , aby uzyskać szczegółowe wskazówki z bieżącej lokalizacji pojazdów. Uruchom skrypt w następnej komórce, aby wygenerować i przeanalizować obiekt GeoJSON reprezentujący trasę.
# 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
}
Wizualizowanie trasy
Aby zwizualizować trasę, użyj interfejsu API pobierania obrazu mapy, aby go renderować na mapie.
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))
W tym samouczku przedstawiono sposób bezpośredniego wywoływania interfejsów API REST usługi Azure Maps i wizualizowania danych usługi Azure Maps przy użyciu języka Python.
Aby uzyskać więcej informacji na temat interfejsów API usługi Azure Maps używanych w tym samouczku, zobacz:
- Uzyskiwanie wskazówek dotyczących tras
- Pobieranie zakresu tras
- Macierz po trasach
- Wyszukiwanie wewnątrz geometrii
- Renderowanie — pobieranie obrazu mapy
Aby uzyskać pełną listę interfejsów API REST usługi Azure Maps, zobacz Interfejsy API REST usługi Azure Maps.