Tutorial: Enrutamiento de vehículos eléctricos mediante Jupyter Notebooks (Python)
Azure Maps es una cartera de API de servicio geoespacial integrada en Azure, lo que permite a los desarrolladores crear aplicaciones compatibles con la ubicación para diversos escenarios, como IoT, movilidad y seguimiento de recursos.
Las API de REST de Azure Maps admiten lenguajes como Python y R para el análisis de datos geoespacial y el aprendizaje automático, lo que ofrece API de enrutamiento sólidas para calcular rutas basadas en condiciones como el tipo de vehículo o el área accesible.
Este tutorial guía a los usuarios a través del enrutamiento de vehículos eléctricos mediante las API de Azure Maps junto con Jupyter Notebook en VS Code y Python para encontrar la estación de carga más cercana cuando la batería es baja.
En este tutorial, aprenderá lo siguiente:
- Cree y ejecute un Jupyter Notebook en VS Code.
- Llamada a las API REST de Azure Maps en Python.
- Búsqueda de un intervalo accesible en función del modelo de consumo del vehículo eléctrico
- Búsqueda de estaciones de carga de vehículos eléctricos en el rango de alcance o isócrono.
- Representación del límite del intervalo accesible y de las estaciones de carga en un mapa
- Búsqueda y visualización de la ruta a la estación de carga de vehículos eléctricos más cercana en función del tiempo de conducción.
Prerrequisitos
- Una cuenta de Azure Maps
- Una clave de suscripción
- Visual Studio Code
- Conocimientos prácticos de Cuadernos de Jupyter Notebook en VS Code
- Entorno configurado para trabajar con Python en Cuadernos de Jupyter Notebook. Para obtener más información, consulte Configuración del entorno.
Nota:
Para más información sobre la autenticación en Azure Maps, consulte Administración de la autenticación en Azure Maps.
Instalación de los paquetes de nivel de proyecto
El proyecto de Enrutamiento y rango accesible EV tiene dependencias en las bibliotecas aiohttp y IPython python. Puede instalarlos en el terminal de Visual Studio mediante pip:
pip install aiohttp
pip install ipython
Apertura de Jupyter Notebook en Visual Studio Code
Después, abra el Cuaderno usado en este tutorial:
Abra el archivo EVrouting.ipynb en el repositorio AzureMapsJupyterSamples en GitHub.
Seleccione el botón Descargar archivo sin procesar en la esquina superior derecha de la pantalla para guardar el archivo localmente.
Para abrir el Cuaderno descargado en Visual Studio Code, haga clic con el botón derecho en el archivo y seleccione Abrir con > Visual Studio Code, o a través del Explorador de archivos de VS Code.
Carga de los módulos y marcos necesarios
Una vez agregado el código, puede ejecutar una celda mediante el icono Ejecutar a la izquierda de la celda y la salida se muestra debajo de la celda de código.
Ejecute el siguiente script para cargar todos los módulos y marcos necesarios.
import time
import aiohttp
import urllib.parse
from IPython.display import Image, display
Solicitud del límite del intervalo de alcance
Una empresa de entrega de paquetes opera una flota que incluye algunos vehículos eléctricos. Estos vehículos tienen que recargarse durante el día sin volver al almacén. Cuando la carga restante cae por debajo de una hora, se realiza una búsqueda para encontrar estaciones de carga dentro de un alcance accesible. A continuación, se obtiene la información de límites del alcance de estas estaciones de carga.
El solicitadorouteType
es eco equilibrar la economía y la velocidad. El siguiente script llama a la API Obtener rango de ruta del servicio de enrutamiento de Azure Maps mediante el uso de parámetros para el modelo de consumo del vehículo. Después, el script analiza la respuesta para crear un objeto poligonal en formato GeoJSON, que representa el rango máximo de alcance del automóvil.
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
]
}
}
Búsqueda de estaciones de carga de vehículos eléctricos en el intervalo de alcance
Después de determinar el rango de alcance (isócrono) del vehículo eléctrico, puede buscar las estaciones de carga en esa zona.
En el siguiente script se usa la API Búsqueda de post en la geometría de Azure Maps para encontrar estaciones de carga dentro del rango máximo accesible del vehículo. A continuación, analiza la respuesta en una matriz de ubicaciones cubiertas.
# 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)
Representación de las estaciones de carga y el intervalo de alcance en un mapa
Llame al servicio de Obtener imagen de mapa para representar los puntos de carga y el alcance máximo en la imagen de mapa estática mediante la ejecución del siguiente script:
# 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))
Búsqueda de la estación de carga óptima
En primer lugar, identifique todas las posibles estaciones de carga dentro del alcance accesible del vehículo. A continuación, determine a cuál de estas estaciones se puede acceder en el menor tiempo posible.
El siguiente script llama a Matrix Routing API de Azure Maps. Devuelve la ubicación del vehículo, el tiempo de desplazamiento y la distancia hasta cada estación de carga. El script siguiente analiza esta respuesta para identificar la estación de carga más cercana que se puede alcanzar en la menor cantidad de tiempo.
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)
Cálculo de la ruta a la estación de carga más cercana
Después de localizar la estación de carga más cercana, use la API Obtener instrucciones de ruta para obtener instrucciones detalladas de la ubicación actual del vehículo. Ejecute el script en la celda siguiente para generar y analizar un objeto GeoJSON que representa la ruta.
# 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
}
Visualización de la ruta
Para visualizar la ruta, use la API de Obtener imagen de mapa para representarla en el mapa.
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 este tutorial, ha aprendido a llamar directamente a las API REST de Azure Maps y a visualizar datos de Azure Maps con Python.
Para más información sobre las API de Azure Maps que se usan en este tutorial, consulte:
- Get Route Directions
- Get Route Range
- Post Route Matrix
- Post Search Inside Geometry
- Render - Get Map Image
Para obtener una lista completa de las API REST de Azure Maps, consulte API REST de Azure Maps.