Compartir por


Tutorial: Enrutamiento de vehículos eléctricos mediante Azure Notebooks (Python)

Azure Maps es una cartera de API de servicios geoespaciales que se integran de forma nativa en Azure. Estas API permiten a los desarrolladores, las empresas y los ISV pueden desarrollar aplicaciones con reconocimiento de ubicación y soluciones de seguimiento de recursos, IoT, movilidad y logística.

Se puede llamar a las API REST de Azure Maps desde lenguajes como Python y R para habilitar el análisis de datos geoespaciales y los escenarios de aprendizaje automático. Azure Maps ofrece un sólido conjunto de API de enrutamiento que permiten a los usuarios calcular rutas entre varios puntos de datos. Los cálculos se basan en diversas condiciones, como el tipo de vehículo o el área de alcance.

En este tutorial, ayudará a un conductor cuyo vehículo eléctrico tiene poca batería. El conductor necesita encontrar la estación de carga más cercana desde la ubicación del vehículo.

En este tutorial, aprenderá lo siguiente:

  • Creación y ejecución de un archivo de Jupyter Notebook en Azure Notebooks en la nube.
  • 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 intervalo 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.

Creación de un proyecto de Azure Notebooks

Para seguir este tutorial, tendrá que crear un proyecto de Azure Notebooks y descargar y ejecutar el archivo de Jupyter Notebook. El archivo de Jupyter Notebook contiene código Python que implementa el escenario de este tutorial. Siga los pasos que se indican a continuación para crear un proyecto de Azure Notebooks y cargar el documento de Jupyter Notebook en él:

  1. Vaya a Azure Notebooks e inicie sesión. Para más información, consulte Inicio rápido: Inicio de sesión y establecimiento de un identificador de usuario.

  2. En parte superior de la página del perfil público, seleccione My Projects (Mis proyectos).

    The My Projects button

  3. En la página My Projects (Mis proyectos), seleccione New Project (Nuevo proyecto).

    The New Project button

  4. En el panel Create New Project (Crear nuevo proyecto), escriba un nombre de proyecto y un identificador de proyecto.

    The Create New Project pane

  5. Seleccione Crear.

  6. Una vez creado el proyecto, descargue este archivo del documento de Jupyter Notebook del repositorio de Jupyter Notebook para Azure Maps.

  7. En la lista de proyectos de la página My Projects (Mis proyectos), seleccione el proyecto y seleccione Upload (Cargar) para cargar el archivo del documento de Jupyter Notebook.

    upload Jupyter Notebook

  8. Cargue el archivo desde el equipo y, a continuación, seleccione Done (Listo).

  9. Una vez finalizada la carga correctamente, el archivo se muestra en la página del proyecto. Haga doble clic en el archivo para abrirlo como un cuaderno de Jupyter Notebook.

Intente comprender la funcionalidad que se implementa en el archivo de Jupyter Notebook. Ejecute el código en el archivo de Jupyter Notebook, de celda en celda. Para ejecutar el código de cada celda, seleccione el botón Run (Ejecutar) situado en la parte superior de la aplicación Jupyter Notebook.

The Run button

Instalación de los paquetes de nivel de proyecto

Para ejecutar el código de Jupyter Notebook, instale los paquetes en el nivel de proyecto; para ello, siga estos pasos:

  1. Descargue el archivo requirements.txt del repositorio de Jupyter Notebook para Azure Maps y cárguelo en el proyecto.

  2. En el panel del proyecto, seleccione Project Settings (Configuración del proyecto).

  3. En el panel Project Settings (Configuración del proyecto), seleccione la pestaña Environment (Entorno) y, después, seleccione Add (Agregar).

  4. En Environment Setup Steps (Pasos para la configuración del entorno), haga lo siguiente: En la primera lista desplegable, seleccione Requirements.txt.
    b. En la segunda lista desplegable, seleccione el archivo requirements.txt.
    c. En la tercera lista desplegable, seleccione la versión Python Version 3.6.

  5. Seleccione Guardar.

    Install packages

Carga de los módulos y marcos necesarios

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 tiene algunos vehículos eléctricos en su flota. Durante el día, los vehículos eléctricos deben recargarse sin tener que volver al almacén. Cada vez que la carga restante cae a menos de una hora, se busca un conjunto de estaciones de carga que estén dentro de un alcance accesible. Básicamente, se busca una estación de carga cuando la batería tiene poca carga, y se obtiene la información sobre el límite del alcance de las estaciones de carga.

Dado que la compañía prefiere el uso de rutas equilibradas por economía y velocidad, el valor de routeType solicitado es eco. El siguiente script llama a Get Route Range API del servicio de enrutamiento de Azure Maps. Usa parámetros para el modelo de consumo del vehículo. Después, el script analiza la respuesta para crear un objeto de tipo polígono con el formato GeoJSON, que representa el intervalo máximo de alcance del automóvil.

Para determinar los límites del intervalo de alcance del vehículo eléctrico, ejecute el script de la siguiente celda:

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

Una vez que haya determinado el intervalo de alcance (isócrono) del vehículo eléctrico, puede buscar las estaciones de carga en ese intervalo.

El siguiente script llama a Post Search Inside Geometry API de Azure Maps. Busca estaciones de carga para el vehículo eléctrico, dentro de los límites del intervalo máximo accesible del vehículo. A continuación, el script analiza la respuesta en una matriz de ubicaciones cubiertas.

Ejecute el siguiente script para buscar estaciones de carga de vehículos eléctricos dentro del intervalo de alcance:

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

Carga del intervalo de alcance y de los puntos de carga

Es útil visualizar en un mapa las estaciones de recarga y el límite de la autonomía máxima alcanzable del vehículo eléctrico. Siga los pasos descritos en el artículo Creación del registro de datos para cargar los datos de límites y los datos de las estaciones de carga como objetos geojson en la cuenta de almacenamiento de Azure y, a continuación, regístrelos en la cuenta de Azure Maps. Asegúrese de anotar el valor de identificador único (udid), ya que lo necesitará. udid es la forma de hacer referencia a los objetos geojson que ha cargado en la cuenta de almacenamiento de Azure desde el código fuente.

Representación de las estaciones de carga y el intervalo de alcance en un mapa

Una vez que haya cargado los datos en la cuenta de almacenamiento de datos, llame al servicio de Azure Maps Get Map Image Service. Este servicio ejecuta el siguiente script para representar los puntos de carga y el alcance máximo en la imagen de mapa estática:

# 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

Búsqueda de la estación de carga óptima

En primer lugar, quiere determinar todas las posibles estaciones de carga dentro del alcance accesible. A continuación, querrá saber cuál de ellas se puede alcanzar en el menor tiempo posible.

El siguiente script llama a Matrix Routing API de Azure Maps. Devuelve la ubicación del vehículo especificado, el tiempo de desplazamiento y la distancia hasta cada estación de carga. El script de la siguiente celda analiza la respuesta para obtener la ubicación de la estación de carga más cercana dentro del alcance en función del tiempo.

Ejecute el script de la siguiente celda para encontrar la estación de carga más cercana dentro del alcance en la mínima 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

Ahora que ha encontrado la estación de carga más cercana, puede llamar a Get Route Directions API para solicitar la ruta detallada desde la ubicación actual del vehículo eléctrico a la estación de carga.

Ejecute el script de la siguiente celda para obtener la ruta a la estación de carga y analizar la respuesta para crear un objeto GeoJSON que representa esa 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 ayudar a visualizar la ruta, siga los pasos descritos en el artículo Cómo crear un registro de datos para cargar los datos de la ruta como un objeto geojson en su cuenta de almacenamiento de Azure y luego regístrelos en su cuenta de Azure Maps. Asegúrese de anotar el valor de identificador único (udid), ya que lo necesitará. udid es la forma de hacer referencia a los objetos geojson que ha cargado en la cuenta de almacenamiento de Azure desde el código fuente. A continuación, llame al servicio de representación, Get Map Image API, para representar la ruta en el mapa y visualizarla.

Ejecute el siguiente script para obtener una imagen de la ruta representada en el mapa:

# 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

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 explorar las API de Azure Maps que se usan en este tutorial, consulte:

Limpieza de recursos

No hay recursos que requieran limpieza.

Pasos siguientes

Para más información sobre Azure Notebooks, consulte