
Распространённая задача программистов в работе с геопространственными данными — отобразить маршруты между различными точками. Решением, которое может понадобиться в разработке веб-сайта, делимся к старту курса по Fullstack-разработке на Python.
Вы освоите:
-
Геокодирование местоположения.
-
Нахождение маршрутов между двумя точками.
Установка OSMnx
OSMnx — это пакет Python для загрузки геопространственных данных OpenStreetMap, моделирования, проектирования, визуализации и анализа реальных уличных сетей и другой геометрии геопространства.
Установка OSMnx немного хитрая: до обычных pip/conda install нужно выполнить следующие шаги:
$ conda config --prepend channels conda-forge $ conda install osmnx
Если pip/conda install не работает, выполните pip install osmnx.
Вы получите готовый к работе OSMnx.
Нахождение самого быстрого маршрута
Теперь, чтобы найти самый короткий маршрут между двумя точками, можно воспользоваться пакетом NetworkX вместе с OSMnx. NetworkX — пакет Python для создания, манипулирования, а также изучения структуры, динамики и функций сложных сетей.
Следующий фрагмент кода находит самый быстрый путь между двумя точками в Сан-Франциско:
import osmnx as ox import networkx as nx ox.config(log_console=True, use_cache=True) # define the start and end locations in latlng start_latlng = (37.78497,-122.43327) end_latlng = (37.78071,-122.41445) # location where you want to find your route place = 'San Francisco, California, United States' # find shortest route based on the mode of travel mode = 'walk' # 'drive', 'bike', 'walk' # find shortest path based on distance or time optimizer = 'time' # 'length','time' # create graph from OSM within the boundaries of some # geocodable place(s) graph = ox.graph_from_place(place, network_type = mode) # find the nearest node to the start location orig_node = ox.get_nearest_node(graph, start_latlng) # find the nearest node to the end location dest_node = ox.get_nearest_node(graph, end_latlng) # find the shortest path shortest_route = nx.shortest_path(graph, orig_node,dest_node, weight=optimizer)
Метод нахождения кратчайшего пути по умолчанию — алгоритм Дейкстры, строка dijkstra. Изменить его на bellman-ford можно, изменив параметр method в shortest_path(). Сейчас переменная shortest_route сейчас содержит коллекцию пути, позволяющую за кратчайшее время пешком пройти из одной точки в другую:
[5287124093, 65314192, 258759765, 65314189, 5429032435, 65303568, 65292734, 65303566, 2220968863, 4014319583, 65303561, 65303560, 4759501665, 65303559, 258758548, 4759501667, 65303556, 65303554, 65281835, 65303553, 65303552, 65314163, 65334128, 65317951, 65333826, 65362158, 65362154, 5429620634, 65308268, 4064226224, 7240837048, 65352325, 7240837026, 7240837027]
Отрисовка путей
Очевидно, список путей нам не очень поможет. Более осмысленный способ интерпретации результата — отрисовка путей с помощью функции plot_route_folium():
shortest_route_map = ox.plot_route_folium(graph, shortest_route) shortest_route_map
Эта функция возвращает карту folium (folium.folium.Map). В Jupyter Notebook она выглядит так:

Набор тайлов по умолчанию — cartodbpositron. Если нужно изменить его, установите аргумент tiles в соответствующее значение. Следующий фрагмент кода покажет карту с набором тайлов openstreetmap:
shortest_route_map = ox.plot_route_folium(graph, shortest_route, tiles='openstreetmap') shortest_route_map
Ниже отображён набор openstreetmap:

Если нужно позволить пользователям выбирать предпочтения во время выполнения, воспользуйтесь этим фрагментом кода:
import folium folium.TileLayer('openstreetmap').add_to(shortest_route_map) folium.TileLayer('Stamen Terrain').add_to(shortest_route_map) folium.TileLayer('Stamen Toner').add_to(shortest_route_map) folium.TileLayer('Stamen Water Color').add_to(shortest_route_map) folium.TileLayer('cartodbpositron').add_to(shortest_route_map) folium.TileLayer('cartodbdark_matter').add_to(shortest_route_map) folium.LayerControl().add_to(shortest_route_map) shortest_route_map
Теперь пользователь может выбирать набор тайлов:

Изменения типа перемещения и оптимизатор
Помимо поиска кратчайшего пути пешехода можно найти кратчайший путь для водителя:
# find shortest route based on the mode of travel mode = 'drive' # 'drive', 'bike', 'walk' # find shortest path based on distance or time optimizer = 'time' # 'length','time'
Вот он:

А что с велосипедом?
# find shortest route based on the mode of travel mode = 'bike' # 'drive', 'bike', 'walk' # find shortest path based on distance or time optimizer = 'time' # 'length','time'
Вот самый быстрый путь на велосипеде:

Можно найти не только самый быстрый путь, но и самый короткий:
# find shortest route based on the mode of travel mode = 'bike' # 'drive', 'bike', 'walk' # find shortest path based on distance or time optimizer = 'time' # 'length','time'
Вот соответствующий код для велосипедов:

Другие варианты комбинирования оставляю вам.
Геокодирование местоположения
В поиске маршрута не очень удобно определять широту и долготу, если только их уже нет в вашем наборе данных. Проще может быть другой подход: дать расположениям удобные названия. Сделать это можно с помощью геокодирования, используя модуль geopy.
Геокодирование — это преобразование адреса в координаты этого адреса. Обратное геокодирование — это преобразование пары координат в удобный адрес.
Чтобы установить geopy, выполните в терминале эту команду:
$ pip install geopy
Следующий фрагмент кода создаёт экземпляр класса геокодера Nominatim для данных OpenStreetMap. В коде вызывается метод geocode(), чтобы закодировать расположение Golden Gate Bridge. При помощи геокодирования можно извлечь широту и долготу расположения:
from geopy.geocoders import Nominatim locator = Nominatim(user_agent = "myapp") location = locator.geocode("Golden Gate Bridge") print(location.latitude, location.longitude) # 37.8303213 -122.4797496 print(location.point) # 37 49m 49.1567s N, 122 28m 47.0986s W print(type(location.point)) # <class 'geopy.point.Point'>
Проверить результат можно здесь. В поисковую строку введите широту и долготу:

Изменим оригинальный код, чтобы геокодировать начальную и конечную точки:
import osmnx as ox import networkx as nx from geopy.geocoders import Nominatim ox.config(log_console=True, use_cache=True) locator = Nominatim(user_agent = "myapp") # define the start and end locations in latlng # start_latlng = (37.78497,-122.43327) # end_latlng = (37.78071,-122.41445) start_location = "Hilton San Francisco Union Square" end_location = "Golden Gateway Tennis & Swim Club" # stores the start and end points as geopy.point.Point objects start_latlng = locator.geocode(start_location).point end_latlng = locator.geocode(end_location).point # location where you want to find your route place = 'San Francisco, California, United States' # find shortest route based on the mode of travel mode = 'bike' # 'drive', 'bike', 'walk' # find shortest path based on distance or time optimizer = 'length' # 'length','time' # create graph from OSM within the boundaries of some # geocodable place(s) graph = ox.graph_from_place(place, network_type = mode) # find the nearest node to the start location orig_node = ox.get_nearest_node(graph, start_latlng) # find the nearest node to the end location dest_node = ox.get_nearest_node(graph, end_latlng) ...
Заметьте, что функция get_nearest_node() принимает и кортеж широта — долгота, и объект geopy.point.Point. Вот самая короткая дистанция на велосипеде от отеля Hilton и до Golden Gateway Tennis & Swim Club в Сан-Франциско:

Отображение маркеров начальной и конечной точек
Карта станет понятнее, если отображать маркеры, указывающие начальную и конечную точки маршрутов. Чтобы отобразить маркер со всплывающим окном, можно воспользоваться классом Marker, о котором я рассказывал в предыдущей статье. Код ниже отображает два маркера: зелёный означает начальную точку, красный — конечную:
import folium # Marker class only accepts coordinates in tuple form start_latlng = (start_latlng[0],start_latlng[1]) end_latlng = (end_latlng[0],end_latlng[1]) start_marker = folium.Marker( location = start_latlng, popup = start_location, icon = folium.Icon(color='green')) end_marker = folium.Marker( location = end_latlng, popup = end_location, icon = folium.Icon(color='red')) # add the circle marker to the map start_marker.add_to(shortest_route_map) end_marker.add_to(shortest_route_map) shortest_route_map
Класс Marker принимает координаты только в форме кортежа. А значит, чтобы кортеж содержал широту и долготу, нужно изменить start_lating и end_lating. Вот два маркера, показывающих начало и конец пути:

Отрисовка статичного графа
Ранее мы воспользовались plot_route_folium(), чтобы показать кратчайший путь на карте folium:
shortest_route_map = ox.plot_route_folium(graph, shortest_route) shortest_route_map
Есть ещё одна интересная функция — plot_graph_route(), которая вместо вывода интерактивной карты чертит статический граф. Это полезно, когда вам нужно изображение с маршрутом между двумя точками. Следующий код выводит статичный граф для точек из предыдущего раздела статьи:
import osmnx as ox import networkx as nx ox.config(log_console=True, use_cache=True) graph = ox.graph_from_place(place, network_type = mode) orig_node = ox.get_nearest_node(graph, start_latlng) dest_node = ox.get_nearest_node(graph, end_latlng) shortest_route = nx.shortest_path(graph, orig_node, dest_node, weight=optimizer) fig, ax = ox.plot_graph_route(graph, shortest_route, save=True)
Вы увидите такой вывод:

Резюме
Надеюсь, эта статья вдохновит вас и вы используете её, чтобы создавать маршруты по достопримечательностям. Возможно, вам захочется позволить пользователям вводить их текущие местоположения и прокладывать маршруты до места назначения.
Ссылки
[1] Boeing, G. 2017. OSMnx: New Methods for Acquiring, Constructing, Analyzing, and Visualizing Complex Street Networks. Computers, Environment and Urban Systems 65, 126–139. doi:10.1016/j.compenvurbsys.2017.05.004
А мы поможем вам освоить надёжную профессию в IT с самого начала или развить ваши навыки:
Выбрать другую востребованную профессию.

Краткий каталог курсов и профессий
Data Science и Machine Learning
Python, веб-разработка
Мобильная разработка
Java и C#
От основ — в глубину
А также
ссылка на оригинал статьи https://habr.com/ru/company/skillfactory/blog/654239/
Добавить комментарий