Compartir a través de


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

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:

  1. Abra el archivo EVrouting.ipynb en el repositorio AzureMapsJupyterSamples en GitHub.

  2. Seleccione el botón Descargar archivo sin procesar en la esquina superior derecha de la pantalla para guardar el archivo localmente.

    Captura de pantalla que muestra cómo descargar el archivo Notebook denominado EVrouting.ipynb desde el repositorio de GitHub.

  3. 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

Captura de pantalla que muestra cómo descargar la primera celda del Cuaderno que contiene las instrucciones de importación necesarias con el botón ejecutar resaltado.

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={}&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
                   ]
                }
             }

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

Captura de pantalla que muestra el intervalo de ubicación.

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

Captura de pantalla que muestra un mapa que muestra la ruta.

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:

Para obtener una lista completa de las API REST de Azure Maps, consulte API REST de Azure Maps.

Pasos siguientes