Tutorial: Rotear veículos elétricos usando o Azure Notebooks (Python)
O Azure Mapas é um portfólio de APIs de serviços geoespaciais integradas ao Azure, permitindo que os desenvolvedores criem aplicativos com reconhecimento de localização para vários cenários, como IoT, mobilidade e acompanhamento de ativos.
As APIs REST do Azure Mapas dão suporte a linguagens como Python e R para análise de dados geoespaciais e aprendizado de máquina, oferecendo APIs de roteamento robustas para calcular rotas com base em condições como tipo de veículo ou área acessível.
Este tutorial orienta os usuários por meio do roteamento de veículos elétricos usando APIs do Azure Mapas, juntamente com Jupyter Notebooks no VS Code e Python, para encontrar a estação de carregamento mais próxima quando a bateria estiver baixa.
Neste tutorial, você irá:
- Crie e execute um Jupyter Notebook no VS Code.
- Chamar as APIs REST dos Azure Mapas no Python.
- Pesquisar por um intervalo alcançável com base no modelo de consumo do veículo elétrico.
- Procure estações de carregamento de veículos elétricos dentro do intervalo acessível ou isócrono.
- Renderizar o limite do intervalo alcançável e as estações de carregamento em um mapa.
- Localizar e visualizar uma rota para o posto de recarga de veículos elétricos mais próximo de acordo com o tempo de condução.
Pré-requisitos
- Uma Conta do Azure Mapas
- Uma chave de assinatura
- Visual Studio Code
- Um conhecimento prático do Jupyter Notebooks no VS Code
- Ambiente configurado para trabalhar com Python em Jupyter Notebooks. Para obter mais informações, consulte Configurar seu ambiente.
Observação
Para obter mais informações sobre a autenticação nos Azure Mapas, confira Gerenciar a autenticação nos Azure Mapas.
Instalar pacotes do nível do projeto
O projeto EV Routing and Reachable Range tem dependências das bibliotecas Python aiohttp e IPython. Você pode instalá-los no terminal do Visual Studio usando o pip:
pip install aiohttp
pip install ipython
Abra um Jupyter Notebook no Visual Studio Code
Baixe e abra o Notebook usado neste tutorial:
Abra o arquivo EVrouting.ipynb no repositório AzureMapsJupyterSamples no GitHub.
Selecione o botão Baixar arquivo bruto no canto superior direito da tela para salvar o arquivo localmente.
Abra o Notebook baixado no Visual Studio Code clicando com o botão direito do mouse no arquivo e selecionando Abrir com >Visual Studio Code, ou por meio do Explorador de Arquivos do VS Code.
Carregar as estruturas e os módulos necessários
Depois que o código for adicionado, você pode executar uma célula usando o ícone Executar à esquerda da célula e a saída será exibida abaixo da célula de código.
Execute o script a seguir para carregar todos os módulos e estruturas necessários.
import time
import aiohttp
import urllib.parse
from IPython.display import Image, display
Solicitar o limite do intervalo acessível
Uma empresa de entrega de pacotes opera uma frota que inclui alguns veículos elétricos. Esses veículos precisam ser recarregados durante o dia sem retornar ao armazém. Quando a carga restante cai para menos de uma hora, é realizada uma busca para encontrar estações de carregamento em um intervalo alcançável. As informações de limite para o intervalo dessas estações de carregamento são então obtidas.
O routeType
solicitado é eco para equilibrar a economia e a velocidade. O script a seguir chama a API Obter Intervalo de Rotas do serviço de roteamento do Azure Mapas, usando parâmetros relacionados ao modelo de consumo do veículo. Em seguida, o script analisa a resposta para criar um objeto de polígono no formato GeoJSON, que representa o intervalo máximo alcançável do carro.
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
]
}
}
Pesquisar postos de recarga de veículos elétricos dentro do intervalo acessível
Depois de determinar o intervalo alcançável do veículo elétrico (isócrono), você pode procurar estações de carregamento dentro dessa área.
O script a seguir usa a API Postar Pesquisa dentro da Geometria do Azure Mapas para localizar estações de carregamento dentro do intervalo máximo alcançável do veículo. Em seguida, o script analisa a resposta em uma matriz de localizações alcançáveis.
# 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)
Renderizar os postos de recarga e o intervalo acessível em um mapa
Chame o serviço Obter Imagem do Mapa do Azure Mapas para renderizar os pontos de carregamento e o limite máximo alcançável na imagem do mapa estático executando o seguinte 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))
Encontrar o posto de recarga ideal
Primeiro, identifique todas as possíveis estações de carregamento dentro do intervalo alcançável do veículo. Em seguida, determine quais dessas estações podem ser acessadas no menor tempo possível.
O script a seguir chama a API de Roteiros de Matriz do Azure Mapas. Retorna a localização do veículo, o tempo de viagem e a distância para cada estação de carregamento. O script subsequente analisa essa resposta para identificar a estação de carregamento mais próxima que pode ser alcançada no menor período de tempo.
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)
Calcular a rota para o posto de recarga mais próximo
Depois de localizar a estação de carregamento mais próxima, use a API Obter Direções de Rota para obter instruções detalhadas do local atual dos veículos. Execute o script na próxima célula para gerar e analisar um objeto GeoJSON que representa a rota.
# 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
}
Visualizar a rota
Para visualizar a rota, use a API Obter Imagem de Mapa para renderizá-la no 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))
Neste tutorial, você aprendeu a chamar as APIs REST dos Azure Mapas diretamente e a visualizar os dados dos Azure Mapas usando o Python.
Para obter mais informações sobre as APIs do Azure Mapas usadas neste tutorial, consulte:
- Obter Trajeto de Rota
- Obter intervalo de rotas
- Matrix de rota POST
- Pesquisa POST dentro da geometria
- Renderizar – obter imagem do mapa
Para obter uma lista completa das APIs REST dos Azure Mapas, confira APIs REST dos Azure Mapas.