Руководство по маршрутизации электрических транспортных средств с помощью Записных книжек Jupyter (Python)
Azure Maps — это портфель api геопространственных служб, интегрированных в Azure, что позволяет разработчикам создавать приложения с учетом расположения для различных сценариев, таких как IoT, мобильность и отслеживание ресурсов.
REST API Azure Maps поддерживают такие языки, как Python и R для геопространственного анализа данных и машинного обучения, предлагая надежные API маршрутизации для вычисления маршрутов на основе таких условий, как тип транспортного средства или доступная область.
В этом руководстве пользователи по маршрутизации электрических транспортных средств с помощью API Azure Maps, а также Jupyter Notebook в VS Code и Python, чтобы найти ближайшую зарядную станцию при низком уровне батареи.
При работе с этим руководством вы сделаете следующее:
- Создайте и запустите Jupyter Notebook в VS Code.
- Вызывать REST API Azure Maps с помощью Python.
- Находить доступный диапазон с учетом модели использования электрического транспортного средства.
- Поиск зарядных станций электрических транспортных средств в пределах доступного диапазона или изохрона.
- Отрисовывать границы доступного диапазона и зарядные станции на карте.
- Находить и визуализировать маршрут к ближайшей зарядной станции для электрических транспортных средств с учетом времени поездки.
Необходимые компоненты
- Учетная запись Azure Maps
- Ключ подписки
- Visual Studio Code
- Рабочие знания о Записных книжках Jupyter в VS Code
- Среда, настроенная для работы с Python в Jupyter Notebook. Дополнительные сведения см. в разделе "Настройка среды".
Примечание.
Дополнительные сведения о проверке подлинности в Azure Maps см. в этой статье.
Установка пакетов на уровне проекта
Проект маршрутизации и доступного диапазона EV имеет зависимости от библиотек python aiohttp и IPython . Их можно установить в терминале Visual Studio с помощью pip:
pip install aiohttp
pip install ipython
Открытие Jupyter Notebook в Visual Studio Code
Затем скачайте записную книжку, используемую в этом руководстве:
Откройте файл EVrouting.ipynb в репозитории AzureMapsJupyterSamples в GitHub.
Нажмите кнопку "Скачать необработанный файл" в правом верхнем углу экрана, чтобы сохранить файл локально.
Откройте скачаемую записную книжку в Visual Studio Code, щелкнув правой кнопкой мыши файл, а затем выберите "Открыть с > помощью Visual Studio Code" или проводник VS Code.
Загрузка необходимых модулей и платформ
После добавления кода можно запустить ячейку с помощью значка запуска слева от ячейки, а выходные данные отображаются под ячейкой кода.
Выполните следующий скрипт, чтобы загрузить все необходимые модули и платформы.
import time
import aiohttp
import urllib.parse
from IPython.display import Image, display
Запрос границы доступного диапазона
Компания по доставке пакетов управляет парком, включающим некоторые электрические автомобили. Эти транспортные средства необходимо заряжать в течение дня, не возвращаясь в склад. Когда оставшаяся плата снижается ниже часа, поиск проводится для поиска зарядных станций в пределах доступного диапазона. Затем получается информация о границе для диапазона этих зарядных станций.
Запрашивается routeType
эко для балансировки экономики и скорости. Следующий сценарий вызывает API получения диапазона маршрутов службы маршрутизации Azure Maps, используя параметры, связанные с моделью потребления транспортного средства. Затем скрипт анализирует ответ на создание объекта многоугольника в формате 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
]
}
}
Поиск зарядных станций для электрических транспортных средств в пределах доступного диапазона
Определив доступный диапазон электрического транспортного средства (isochrone), можно найти зарядные станции в этой области.
В следующем скрипте используется API службы "Поиск после поиска Azure Maps Внутри геометрии" для поиска зарядных станций в максимально доступном диапазоне транспортного средства. Затем он анализирует ответ в массив доступных расположений.
# 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 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))
Поиск оптимальной зарядной станции
Во-первых, определите все потенциальные зарядные станции в доступном диапазоне транспортного средства. Затем определите, к каким из этих станций можно получить доступ в ближайшее время.
Следующий скрипт вызываетAPI матричной маршрутизации Azure Maps. Он возвращает расположение транспортного средства, время путешествия и расстояние до каждой зарядной станции. Последующий скрипт анализирует этот ответ, чтобы определить ближайшие зарядные станции, которые можно достичь в наименьшее время.
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))
В рамках этого руководства вы узнали, как напрямую вызывать REST API Azure Maps и визуализировать данные Azure Maps с помощью Python.
Дополнительные сведения об API Azure Maps, используемых в этом руководстве, см. в следующих руководствах.
- Route — Get Route Directions (Маршрут — получение направлений маршрута)
- Route — Get Route Range (Маршрут — получение диапазона маршрута)
- Route — Post Route Matrix Preview (Маршрут — предварительная версия запроса Post матрицы маршрута)
- Search — Post Search Inside Geometry (Поиск — запрос Post на поиск внутри геометрии)
- Render — Get Map Image (Отрисовка — получение изображения карты)
Полный список API-интерфейсов Azure Maps вы найдете на этой странице.