자습서: Azure Notebooks를 사용하여 전기 차량 라우팅(Python)

Azure Maps는 Azure에 통합된 지리 공간적 서비스 API의 포트폴리오로, 개발자가 IoT, 이동성 및 자산 추적과 같은 다양한 시나리오에 대한 위치 인식 애플리케이션을 만들 수 있습니다.

Azure Maps REST API는 지리 공간적 데이터 분석 및 기계 학습을 위해 Python 및 R과 같은 언어를 지원하며, 차량 유형 또는 도달 가능한 영역과 같은 조건에 따라 경로를 계산하기 위한 강력한 라우팅 API 를 제공합니다.

이 자습서에서는 Azure Notebooks 및 Python과 함께 Azure Maps API를 사용하여 전기 자동차를 라우팅하여 배터리가 부족할 때 가장 가까운 충전소를 찾는 방법을 안내합니다.

이 자습서에서는 다음을 수행합니다.

  • 클라우드의 Azure Notebooks에서 Jupyter Notebook 파일을 만들고 실행합니다.
  • Python에서 Azure Maps REST API를 호출합니다.
  • 전기 차량의 소비 모델에 따라 도달 가능한 범위를 검색합니다.
  • 도달 가능한 범위 또는 등시 범위 내의 전기 자동차 충전소를 검색합니다.
  • 지도에서 도달 가능한 범위 경계와 충전소를 렌더링합니다.
  • 주행 시간을 기준으로 가장 가까운 전기 차량 충전소까지의 경로를 찾아 시각화합니다.

필수 조건

참고 항목

Azure Maps의 인증에 대한 자세한 내용은 Azure Maps의 인증 관리를 참조하세요.

Azure Notebooks 프로젝트 만들기

이 자습서를 진행하려면 Azure Notebooks 프로젝트를 만들고 Jupyter Notebook 파일을 다운로드하여 실행해야 합니다. 이 파일에는 이 자습서에 제시된 시나리오를 보여 주는 Python 코드가 포함되어 있습니다.

다음 단계에 따라 Azure Notebooks 프로젝트를 만들고 Jupyter Notebook 문서를 업로드합니다.

  1. Azure Notebooks로 가서 로그인합니다.

  2. 퍼블릭 프로필 페이지의 위쪽에서 내 프로젝트를 선택합니다.

    내 프로젝트 단추

  3. 내 프로젝트 페이지에서 새 프로젝트를 선택합니다.

    새 프로젝트 단추

  4. 새 프로젝트 만들기 창에서 프로젝트 이름과 프로젝트 ID를 입력합니다.

    새 프로젝트 만들기 창

  5. 만들기를 실행합니다.

  6. 프로젝트가 생성된 후 Azure Maps Jupyter Notebook 리포지토리에서 이 Jupyter Notebook 문서 파일을 다운로드합니다.

  7. 내 프로젝트 페이지의 프로젝트 목록에서 프로젝트를 선택한 다음, 업로드를 선택하여 Jupyter Notebook 문서 파일을 업로드합니다.

    Jupyter Notebook 업로드

  8. 컴퓨터에서 파일을 업로드한 다음, 완료를 선택합니다.

  9. 성공적으로 업로드되면 파일이 프로젝트 페이지에 표시됩니다. 파일을 두 번 클릭하여 Jupyter Notebook으로 엽니다.

Jupyter Notebook 파일에 구현된 기능을 숙지합니다. Jupyter Notebook 애플리케이션의 맨 위에 있는 실행 단추를 선택하여 Jupyter Notebook 셀 내에서 코드를 한 번에 한 셀씩 실행 합니다.

실행 단추

프로젝트 수준 패키지 설치

Jupyter Notebook에서 코드를 실행하려면 다음 단계에 따라 프로젝트 수준에서 패키지를 설치합니다.

  1. Azure Maps Jupyter Notebook 리포지토리에서 requirements.txt 파일을 다운로드한 다음, 프로젝트에 업로드합니다.

  2. 프로젝트 대시보드에서 프로젝트 설정을 선택합니다.

  3. 프로젝트 설정 창에서 환경 탭을 선택한 다음, 추가를 선택합니다.

  4. 환경 설정 단계 아래에서 다음(a)을 수행합니다. 첫 번째 드롭다운 목록에서 Requirements.txt를 선택합니다.
    b. 두 번째 드롭다운 목록에서 requirements.txt 파일을 선택합니다.
    c. 세 번째 드롭다운 목록에서 Python 버전을 선택합니다. 버전 3.11은 이 자습서를 만들 때 사용되었습니다.

  5. 저장을 선택합니다.

    패키지 설치

필요한 모듈 및 프레임워크 로드

다음 스크립트를 실행하여 필요한 모든 모듈 및 프레임워크를 로드합니다.

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

도달 가능한 범위 경계 요청

패키지 배달 회사는 일부 전기 자동차를 포함하는 함대를 운영하고 있습니다. 이러한 차량은 창고로 돌아가지 않고 하루 동안 충전해야합니다. 나머지 요금이 1시간 미만으로 떨어지면 도달 가능한 범위 내에서 충전소를 찾기 위한 검색이 수행됩니다. 그런 다음 이러한 충전소의 범위에 대한 경계 정보를 얻습니다.

요청된 routeType은 경제와 속도의 균형을 맞추기 위한 에코입니다. 다음 스크립트는 차량의 소비 모델과 관련된 매개 변수를 사용하여 Azure Maps 라우팅 서비스의 경로 범위 가져오기 API를 호출합니다. 그런 다음 스크립트는 응답을 구문 분석하여 GeoJSON 형식의 다각형 개체를 만들어 자동차의 최대 도달 가능 범위를 나타냅니다.

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

도달 가능한 범위 내에서 전기 차량 충전소 검색

전기 자동차의 도달 가능한 범위(등시)를 결정한 후 해당 지역 내의 충전소를 검색할 수 있습니다.

다음 스크립트는 Azure Maps Post Search Inside Geometry API를 사용하여 차량의 최대 도달 가능한 범위 내에서 충전소를 찾습니다. 그런 다음 응답을 연결 가능한 위치 배열로 구문 분석합니다.

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

지도에서 충전소 및 도달 가능한 범위 렌더링

다음 스크립트를 실행하여 Azure Maps Get Map Image 서비스를 호출하여 정적 맵 이미지에서 충전 지점 및 최대 도달 가능한 경계를 렌더링합니다.

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

위치 범위를 보여 주는 지도

최적의 충전소 찾기

먼저 차량의 도달 가능한 범위 내의 모든 잠재적 충전소를 식별합니다. 다음으로, 가능한 가장 짧은 시간 안에 액세스할 수 있는 스테이션을 결정합니다.

다음 스크립트는 Azure Maps Matrix 라우팅 API를 호출합니다. 차량의 위치, 이동 시간 및 각 충전소까지의 거리를 반환합니다. 후속 스크립트는 이 응답을 구문 분석하여 최소 시간 내에 도달할 수 있는 가장 가까운 충전소를 식별합니다.

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)

가장 가까운 충전소까지의 경로 계산

가장 가까운 충전소를 찾은 후 경로 방향 가져오기 API를 사용하여 차량 현재 위치에서 자세한 방향을 가져옵니다. 다음 셀에서 스크립트를 실행하여 경로를 나타내는 GeoJSON 개체를 생성하고 구문 분석합니다.

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

경로 시각화

경로를 시각화하려면 지도 이미지 가져오기 API를 사용하여 지도에 렌더링합니다.

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

경로를 보여 주는 지도

이 자습서에서는 Python을 사용하여 Azure Maps REST API를 직접 호출하고 Azure Maps 데이터를 시각화하는 방법을 알아보았습니다.

이 자습서에서 사용되는 Azure Maps API를 살펴보려면 다음을 참조하세요.

다음 단계

Azure Notebooks에 대한 자세한 내용은 다음을 참조하세요.