Tutorial: Rotear veículos elétricos usando o Azure Notebooks (Python)Tutorial: Route electric vehicles by using Azure Notebooks (Python)

Os Azure Mapas são um portfólio de APIs de serviço geoespacial que são nativamente integradas ao Azure.Azure Maps is a portfolio of geospatial service APIs that are natively integrated into Azure. Essas APIs permitem que desenvolvedores, empresas e ISVs desenvolvam aplicativos com reconhecimento de localização e soluções de IoT, mobilidade, logística e acompanhamento de ativos.These APIs enable developers, enterprises, and ISVs to develop location-aware apps, IoT, mobility, logistics, and asset tracking solutions.

As APIs REST dos Azure Mapas podem ser chamadas em linguagens como Python e R para habilitar a análise de dados geoespaciais e cenários de machine learning.The Azure Maps REST APIs can be called from languages such as Python and R to enable geospatial data analysis and machine learning scenarios. Os Azure Mapas oferecem um conjunto robusto de APIs de roteamento que permitem aos usuários calcular rotas entre vários pontos de dados.Azure Maps offers a robust set of routing APIs that allow users to calculate routes between several data points. Os cálculos se baseiam em várias condições, como tipo de veículo ou área acessível.The calculations are based on various conditions, such as vehicle type or reachable area.

Neste tutorial, você ajudará um motorista cuja bateria do veículo elétrico está acabando.In this tutorial, you walk help a driver whose electric vehicle battery is low. O motorista precisa encontrar a estação de carga mais próxima possível da localização do veículo.The driver needs to find the closest possible charging station from the vehicle's location.

Neste tutorial, você irá:In this tutorial, you will:

  • Criar e executar um Jupyter Notebook no Azure Notebooks na nuvem.Create and run a Jupyter notebook on Azure Notebooks in the cloud.
  • Chamar as APIs REST dos Azure Mapas no Python.Call Azure Maps REST APIs in Python.
  • Pesquisar por um intervalo alcançável com base no modelo de consumo do veículo elétrico.Search for a reachable range based on the electric vehicle's consumption model.
  • Pesquisar postos de recarga de veículos elétricos dentro do intervalo acessível ou isócrono.Search for electric vehicle charging stations within the reachable range, or isochrone.
  • Renderizar o limite do intervalo alcançável e as estações de carregamento em um mapa.Render the reachable range boundary and charging stations on a map.
  • 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.Find and visualize a route to the closest electric vehicle charging station based on drive time.

Pré-requisitosPrerequisites

Para concluir este tutorial, primeiro, é necessário criar uma conta dos Azure Mapas e obter a chave primária (chave de assinatura).To complete this tutorial, you first need to create an Azure Maps account and get your primary key (subscription key).

Para criar a assinatura de uma conta do Azure Mapas, siga as instruções em Criar uma conta.To create an Azure Maps account subscription, follow instructions in Create an account. Você precisa ter uma assinatura de conta do Azure Mapas da faixa de preço S1.You need an Azure Maps account subscription with the S1 price tier.

Para obter a chave de assinatura primária da sua conta, siga as instruções em Obter chave primária.To get the primary subscription key for your account, follow the instructions in get primary key.

Para obter mais informações sobre a autenticação nos Azure Mapas, confira Gerenciar a autenticação nos Azure Mapas.For more information on authentication in Azure Maps, see manage authentication in Azure Maps.

Criar um Azure NotebookCreate an Azure notebook

Para acompanhar este tutorial, você precisará criar um projeto do Azure Notebook e baixar e executar o arquivo do Jupyter Notebook.To follow along with this tutorial, you need to create an Azure notebook project and download and run the Jupyter notebook file. O arquivo do notebook contém o código Python, que implementa o cenário neste tutorial.The notebook file contains Python code, which implements the scenario in this tutorial. Para criar um projeto de notebook do Azure e fazer upload do documento do Jupyter Notebook nele, siga estas etapas:To create an Azure notebook project and upload the Jupyter notebook document to it, do the following steps:

  1. Vá até Azure Notebooks e entre.Go to Azure Notebooks and sign in. Para saber mais, confira Início Rápido: Entrar e definir uma ID de usuário.For more information, see Quickstart: Sign in and set a user ID.

  2. Na parte superior da página de seu perfil público, selecione Meus Projetos.At the top of your public profile page, select My Projects.

    O botão Meus Projetos

  3. Na página Meus Projetos, selecione Novo Projeto.On the My Projects page, select New Project.

    O botão Novo Projeto

  4. No painel Criar Projeto, insira um nome e uma ID de projeto.In the Create New Project pane, enter a project name and project ID.

    O painel Criar Projeto

  5. Selecione Criar.Select Create.

  6. Após a criação do projeto, baixe este arquivo de documento do Jupyter Notebook do repositório do Jupyter Notebook dos Azure Mapas.After your project is created, download this Jupyter notebook document file from the Azure Maps Jupyter notebook repository.

  7. Na lista de projetos da página Meus Projetos, selecione o projeto e, em seguida, selecione Upload para fazer upload do arquivo de documento do Jupyter Notebook.In the projects list on the My Projects page, select your project, and then select Upload to upload the Jupyter notebook document file.

    fazer upload do notebook

  8. Faça upload do arquivo do computador e, em seguida, selecione Concluído.Upload the file from your computer, and then select Done.

  9. Depois que o upload for concluído com êxito, o arquivo será exibido na página do projeto.After the upload has finished successfully, your file is displayed on your project page. Clique duas vezes no arquivo para abri-lo como um Jupyter Notebook.Double-click on the file to open it as a Jupyter notebook.

Tente entender a funcionalidade implementada no arquivo de notebook.Try to understand the functionality that's implemented in the notebook file. Execute o código, no arquivo de notebook, uma célula por vez.Run the code, in the notebook file, one cell at a time. Execute o código em cada célula selecionando o botão Executar na parte superior do aplicativo do notebook.You can run the code in each cell by selecting the Run button at the top of the notebook app.

O botão Executar

Instalar pacotes do nível do projetoInstall project level packages

Para executar o código no notebook, instale os pacotes no nível do projeto seguindo estas etapas:To run the code in the notebook, install packages at the project level by doing the following steps:

  1. Baixe o arquivo requirements.txt do repositório do Jupyter Notebook nos Azure Mapas e, em seguida, faça upload dele no projeto.Download the requirements.txt file from the Azure Maps Jupyter notebook repository, and then upload it to your project.

  2. No painel do projeto, selecione Configurações do Projeto.On the project dashboard, select Project Settings.

  3. No painel Configurações do Projeto, selecione a guia Ambiente e, em seguida, selecione Adicionar.In the Project Settings pane, select the Environment tab, and then select Add.

  4. Em Etapas de Instalação do Ambiente, faça o seguinte:Under Environment Setup Steps, do the following:
    a.a. Na primeira lista suspensa, selecione Requirements.txt.In the first drop-down list, select Requirements.txt.
    b.b. Na segunda lista suspensa, selecione o arquivo requirements.txt.In the second drop-down list, select your requirements.txt file.
    c.c. Na terceira lista suspensa, selecione Python Versão 3.6 como a versão.In the third drop-down list, select Python Version 3.6 as your version.

  5. Clique em Salvar.Select Save.

    Instalar Pacotes

Carregar as estruturas e os módulos necessáriosLoad the required modules and frameworks

Para carregar todas as estruturas e todos os módulos necessários, execute o script a seguir.To load all the required modules and frameworks, run the following script.

import time
import aiohttp
import urllib.parse
from IPython.display import Image, display

Solicitar o limite do intervalo acessívelRequest the reachable range boundary

Uma empresa de entrega de pacotes tem alguns veículos elétricos na frota.A package delivery company has some electric vehicles in its fleet. Durante o dia, os veículos elétricos precisam ser recarregados sem a necessidade de retornar ao depósito.During the day, electric vehicles need to be recharged without having to return to the warehouse. Sempre que a carga restante cai para menos de uma hora, você pesquisa por um conjunto de estações de carga que estão em um intervalo acessível.Every time the remaining charge drops to less than an hour, you search for a set of charging stations that are within a reachable range. Essencialmente, você pesquisa por uma estação de carga quando a bateria está com pouca carga.Essentially, you search for a charging station when the battery is low on charge. Além disso, você obtém as informações de limite referentes a esse intervalo de estações de carga.And, you get the boundary information for that range of charging stations.

Como a empresa prefere usar rotas que exigem um equilíbrio entre economia e velocidade, o routeType solicitado é eco.Because the company prefers to use routes that require a balance of economy and speed, the requested routeType is eco. O script a seguir chama a API Obter Intervalo de Rotas do serviço de roteiros do Azure Mapas.The following script calls the Get Route Range API of the Azure Maps routing service. Ele usa parâmetros do modelo de consumo do veículo.It uses parameters for the vehicle's consumption model. Em seguida, o script analisa a resposta para criar um objeto de polígono do formato GeoJSON, que representa o intervalo máximo acessível do carro.The script then parses the response to create a polygon object of the geojson format, which represents the car's maximum reachable range.

Para determinar os limites do intervalo acessível do veículo elétrico, execute o script na seguinte célula:To determine the boundaries for the electric vehicle's reachable range, run the script in the following cell:

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
                   ]
                }
             }

Pesquisar postos de recarga de veículos elétricos dentro do intervalo acessívelSearch for electric vehicle charging stations within the reachable range

Depois de determinar o intervalo acessível (isócrono) para o veículo elétrico, pesquise os postos de recarga nesse intervalo.After you've determined the reachable range (isochrone) for the electric vehicle, you can search for charging stations within that range.

O script a seguir chama a API Pós-pesquisa dentro da Geometria dos Azure Mapas.The following script calls the Azure Maps Post Search Inside Geometry API. Ele pesquisa estações de recarga para o veículo elétrico, dentro dos limites do intervalo máximo alcançável do carro.It searches for charging stations for electric vehicle, within the boundaries of the car's maximum reachable range. Em seguida, o script analisa a resposta em uma matriz de localizações acessíveis.Then, the script parses the response to an array of reachable locations.

Para pesquisar os postos de recarga de veículos elétricos dentro do intervalo acessível, execute o seguinte script:To search for electric vehicle charging stations within the reachable range, run the following script:

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

Faça upload do intervalo acessível e os pontos de recarga no Serviço de Dados dos Azure MapasUpload the reachable range and charging points to Azure Maps Data Service

Em um mapa, o ideal é visualizar as estações de carga e os limites do intervalo máximo acessível para o veículo elétrico.On a map, you'll want to visualize the charging stations and the boundary for the maximum reachable range of the electric vehicle. Para fazer isso, faça upload dos dados de limite e dos dados das estações de carga como objetos geojson para o Serviço de Dados dos Azure Mapas.To do so, upload the boundary data and charging stations data as geojson objects to Azure Maps Data Service. Use a API de Upload de Dados.Use the Data Upload API.

Para fazer upload dos dados de pontos de recarga e de limite no Serviço de Dados dos Azure Mapas, execute as duas seguintes células:To upload the boundary and charging point data to Azure Maps Data Service, run the following two cells:

rangeData = {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          polyBounds
        ]
      }
    }
  ]
}

# Upload the range data to Azure Maps Data Service.
uploadRangeResponse = await session.post("https://atlas.microsoft.com/mapData/upload?subscription-key={}&api-version=1.0&dataFormat=geojson".format(subscriptionKey), json = rangeData)

rangeUdidRequest = uploadRangeResponse.headers["Location"]+"&subscription-key={}".format(subscriptionKey)

while True:
    getRangeUdid = await (await session.get(rangeUdidRequest)).json()
    if 'udid' in getRangeUdid:
        break
    else:
        time.sleep(0.2)
rangeUdid = getRangeUdid["udid"]
poiData = {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "properties": {},
        "geometry": {
            "type": "MultiPoint",
            "coordinates": reachableLocations
        }
    }
  ]
}

# Upload the electric vehicle charging station data to Azure Maps Data Service.
uploadPOIsResponse = await session.post("https://atlas.microsoft.com/mapData/upload?subscription-key={}&api-version=1.0&dataFormat=geojson".format(subscriptionKey), json = poiData)

poiUdidRequest = uploadPOIsResponse.headers["Location"]+"&subscription-key={}".format(subscriptionKey)

while True:
    getPoiUdid = await (await session.get(poiUdidRequest)).json()
    if 'udid' in getPoiUdid:
        break
    else:
        time.sleep(0.2)
poiUdid = getPoiUdid["udid"]

Renderizar os postos de recarga e o intervalo acessível em um mapaRender the charging stations and reachable range on a map

Depois de fazer upload dos dados para o serviço de dados, chame o Serviço Obter Imagem de Mapa dos Azure Mapas.After you've uploaded the data to the data service, call the Azure Maps Get Map Image service. Esse serviço é usado para renderizar os pontos de carga e o limite máximo alcançável na imagem do mapa estático executando o seguinte script:This service is used to render the charging points and maximum reachable boundary on the static map image by running the following 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)

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=1.0&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))

Um mapa que mostra o intervalo de localização

Encontrar o posto de recarga idealFind the optimal charging station

Primeiro, determine todas as estações de carga potenciais dentro do intervalo acessível.First, you want to determine all the potential charging stations within the reachable range. Em seguida, descubra quais delas podem ser alcançadas em um período mínimo.Then, you want to know which of them can be reached in a minimum amount of time.

O script a seguir chama a API de Roteiros de Matriz do Azure Mapas.The following script calls the Azure Maps Matrix Routing API. Ele retorna a localização do veículo especificado, o tempo de viagem e a distância de cada estação carga.It returns the specified vehicle location, the travel time, and the distance to each charging station. O script na próxima célula analisa a resposta para localizar o posto de recarga acessível mais próximo com relação ao tempo.The script in the next cell parses the response to locate the closest reachable charging station with respect to time.

Para encontrar o posto de recarga acessível mais próximo ao qual se pode chegar no menor período de tempo, execute o script na seguinte célula:To find the closest reachable charging station that can be reached in the least amount of time, run the script in the following cell:

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óximoCalculate the route to the closest charging station

Agora que você encontrou o posto de recarga mais próximo, chame a API Obter Trajetos de Rota para solicitar a rota detalhada partindo da localização atual do veículo elétrico ao posto de recarga.Now that you've found the closest charging station, you can call the Get Route Directions API to request the detailed route from the electric vehicle's current location to the charging station.

Para obter a rota para o posto de recarga e analisar a resposta a fim de criar um objeto GeoJSON que representa a rota, execute o script na seguinte célula:To get the route to the charging station and to parse the response to create a geojson object that represents the route, run the script in the following cell:

# 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 rotaVisualize the route

Para ajudar a visualizar a rota, primeiro faça upload dos dados de rota como um objeto geojson no Serviço de Dados dos Azure Mapas.To help visualize the route, you first upload the route data as a geojson object to Azure Maps Data Service. Para fazer isso, use a API de Upload de Dados dos Azure Mapas.To do so, use the Azure Maps Data Upload API. Em seguida, chame o serviço de renderização, a API Obter Imagem do Mapa, para renderizar a rota no mapa e visualizá-la.Then, call the rendering service, Get Map Image API, to render the route on the map, and visualize it.

Para obter uma imagem para a rota renderizada no mapa, execute o seguinte script:To get an image for the rendered route on the map, run the following script:

# Upload the route data to Azure Maps Data Service.
routeUploadRequest = await session.post("https://atlas.microsoft.com/mapData/upload?subscription-key={}&api-version=1.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=1.0&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))

Um mapa que mostra a rota

Próximas etapasNext steps

Neste tutorial, você aprendeu a chamar as APIs REST dos Azure Mapas diretamente e a visualizar os dados dos Azure Mapas usando o Python.In this tutorial, you learned how to call Azure Maps REST APIs directly and visualize Azure Maps data by using Python.

Para explorar as APIs dos Azure Mapas que são usadas neste tutorial, confira:To explore the Azure Maps APIs that are used in this tutorial, see:

Para obter uma lista completa das APIs REST dos Azure Mapas, confira APIs REST dos Azure Mapas.For a complete list of Azure Maps REST APIs, see Azure Maps REST APIs.

Para saber mais sobre o Azure Notebooks, confira Azure Notebooks.To learn more about Azure Notebooks, see Azure Notebooks.