Tutorial: Rotear veículos elétricos usando Jupyter Notebooks (Python)
O Azure Maps é um portfólio de APIs de serviço geoespacial integradas ao Azure, permitindo que os desenvolvedores criem aplicativos com reconhecimento de localização para vários cenários, como IoT, mobilidade e rastreamento de ativos.
As APIs REST do Azure Maps suportam linguagens como Python e R para análise de dados geoespaciais e aprendizagem automática, oferecendo APIs de encaminhamento robustas para calcular rotas com base em condições como o tipo de veículo ou a área alcançável.
Este tutorial orienta os utilizadores através do encaminhamento de veículos elétricos utilizando as APIs do Azure Maps juntamente com os Blocos de Notas Jupyter no VS Code e Python para encontrar a estação de carregamento mais próxima quando a bateria está fraca.
Neste tutorial, vai:
- Crie e execute um Jupyter Notebook no VS Code.
- Chame as APIs REST do Azure Maps em Python.
- Procure uma autonomia acessível com base no modelo de consumo do veículo elétrico.
- Procure estações de carregamento de veículos elétricos dentro do alcance acessível, ou isocrona.
- Renderize o limite de alcance acessível e as estações de carregamento em um mapa.
- Encontre e visualize uma rota para a estação de carregamento de veículos elétricos mais próxima com base no tempo de condução.
Pré-requisitos
- Uma conta do Azure Maps
- Uma chave de subscrição
- Visual Studio Code
- Um conhecimento prático de Jupyter Notebooks em VS Code
- Ambiente configurado para trabalhar com Python em Jupyter Notebooks. Para obter mais informações, consulte Configurando seu ambiente.
Nota
Para obter mais informações sobre autenticação no Azure Maps, consulte gerenciar autenticação no Azure Maps.
Instalar pacotes de nível de projeto
O projeto EV Routing and Reachable Range tem dependências nas bibliotecas python aiohttp e IPython . Você pode instalá-los no terminal do Visual Studio usando pip:
pip install aiohttp
pip install ipython
Abrir o Jupyter Notebook no Visual Studio Code
Faça o download e abra o Bloco de Anotações usado neste tutorial:
Abra o arquivo EVrouting.ipynb no repositório AzureMapsJupyterSamples no GitHub.
Selecione o botão Baixar arquivo raw no canto superior direito da tela para salvar o arquivo localmente.
Abra o Bloco de Anotações baixado no Visual Studio Code clicando com o botão direito do mouse no arquivo e selecionando Abrir com > o Visual Studio Code ou por meio do VS Code File Explorer.
Carregue os módulos e estruturas necessários
Depois que o código for adicionado, você poderá 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 de alcance alcançável
Uma empresa de entrega de encomendas 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 abaixo de uma hora, uma pesquisa é realizada para encontrar estações de carregamento dentro de um alcance acessível. A informação limite para o alcance destas estações de carregamento é então obtida.
O pedido routeType
é eco para equilibrar economia e velocidade. O script a seguir chama a API Get Route Range do serviço de roteamento do Azure Maps, 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, representando o alcance 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
]
}
}
Procure estações de carregamento de veículos elétricos dentro do alcance acessível
Depois de determinar o alcance acessível do veículo elétrico (isocrona), pode procurar estações de carregamento dentro dessa área.
O script a seguir usa a API Azure Maps Post Search Inside Geometry para localizar estações de carregamento dentro do alcance máximo acessível do veículo. Em seguida, ele analisa a resposta em uma matriz de locais 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 as estações de carregamento e o alcance acessível em um mapa
Chame o serviço Obter Imagem de Mapa do Azure Maps para renderizar os pontos de carregamento e o limite máximo alcançável na imagem de 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))
Encontre a estação de carregamento ideal
Primeiro, identifique todas as potenciais estações de carregamento dentro do alcance acessí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 Roteamento de Matriz do Azure Maps. Devolve a localização do veículo, o tempo de viagem e a distância a 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 o percurso até à estação de carregamento mais próxima
Depois de localizar a estação de carregamento mais próxima, utilize a API Obter direções de rota para obter direções detalhadas da localização 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
}
Visualize o percurso
Para visualizar a rota, use a API Get Map Image 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 como chamar APIs REST do Azure Maps diretamente e visualizar dados do Azure Maps usando Python.
Para obter mais informações sobre as APIs do Azure Maps usadas neste tutorial, consulte:
- Obter direções de rota
- Obter Faixa de Rota
- Matriz de Pós-Rota
- Pós-pesquisa dentro da geometria
- Renderizar - Obter imagem do mapa
Para obter uma lista completa das APIs REST do Azure Maps, consulte APIs REST do Azure Maps.