자습서: Jupyter Notebook을 사용하여 전기 자동차 라우팅(Python)
Azure Maps는 Azure에 통합된 지리 공간적 서비스 API의 포트폴리오로, 개발자가 IoT, 이동성 및 자산 추적과 같은 다양한 시나리오에 대한 위치 인식 애플리케이션을 만들 수 있습니다.
Azure Maps REST API는 지리 공간적 데이터 분석 및 기계 학습을 위해 Python 및 R과 같은 언어를 지원하며, 차량 유형 또는 도달 가능한 영역과 같은 조건에 따라 경로를 계산하기 위한 강력한 라우팅 API 를 제공합니다.
이 자습서에서는 VS Code 및 Python의 Jupyter Notebook과 함께 Azure Maps API를 사용하여 전기 자동차를 라우팅하여 배터리가 부족할 때 가장 가까운 충전소를 찾는 방법을 안내합니다.
이 자습서에서는 다음을 수행합니다.
- VS Code에서 Jupyter Notebook을 만들고 실행합니다.
- Python에서 Azure Maps REST API를 호출합니다.
- 전기 차량의 소비 모델에 따라 도달 가능한 범위를 검색합니다.
- 도달 가능한 범위 또는 등시 범위 내의 전기 자동차 충전소를 검색합니다.
- 지도에서 도달 가능한 범위 경계와 충전소를 렌더링합니다.
- 주행 시간을 기준으로 가장 가까운 전기 차량 충전소까지의 경로를 찾아 시각화합니다.
필수 조건
- Azure Maps 계정
- 구독 키
- Visual Studio Code
- VS Code의 Jupyter Notebook에 대한 실무 지식
- Jupyter Notebook에서 Python과 함께 작동하도록 설정된 환경입니다. 자세한 내용은 환경 설정을 참조하세요.
참고 항목
Azure Maps의 인증에 대한 자세한 내용은 Azure Maps의 인증 관리를 참조하세요.
프로젝트 수준 패키지 설치
EV 라우팅 및 도달 가능한 범위 프로젝트에는 aiohttp 및 IPython python 라이브러리에 대한 종속성이 있습니다. pip를 사용하여 Visual Studio 터미널에 설치할 수 있습니다.
pip install aiohttp
pip install ipython
Visual Studio Code에서 Jupyter Notebook 열기
다운로드한 후 이 자습서에 사용된 Notebook을 엽니다.
GitHub의 AzureMapsJupyterSamples 리포지토리에서 EVrouting.ipynb 파일을 엽니다.
화면의 오른쪽 위 모서리에서 원시 파일 다운로드 단추를 선택하여 파일을 로컬로 저장합니다.
파일을 마우스 오른쪽 단추로 클릭한 다음 Visual Studio Code에서 열기를 선택하거나 VS Code 파일 탐색기 통해 Visual Studio Code에서 다운로드한 Notebook을 엽니다>.
필요한 모듈 및 프레임워크 로드
코드가 추가되면 셀 왼쪽의 실행 아이콘을 사용하여 셀을 실행할 수 있으며 출력은 코드 셀 아래에 표시됩니다.
다음 스크립트를 실행하여 필요한 모든 모듈 및 프레임워크를 로드합니다.
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={}¤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
]
}
}
도달 가능한 범위 내에서 전기 차량 충전소 검색
전기 자동차의 도달 가능한 범위(등시)를 결정한 후 해당 지역 내의 충전소를 검색할 수 있습니다.
다음 스크립트는 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 Maps REST API의 전체 목록은 Azure Maps REST API를 참조하세요.