В питоне есть множество библиотек, с помощью которых можно рисовать и анализировать пространственную информацию (spatial analysis).
Вот некоторые из них:
В этом туториале пойдет речь о библиотеке Folium (https://github.com/python-visualization/folium), которая представляет собой питон-обертку над JS библиотекой Leaflet.
"Manipulate your data in Python, then visualize it in on a Leaflet map via Folium." (c) github
Большим плюсом по сравнению с другими библиотеками является интерактивность. Карты можно зумить, исследовать, кликать на маркеры, создать сложные типы визуализации. Минусом является производительность. Создание карты с большим количеством точек может составлять минуты.
К сожалению, информация по работе с библиотекой раскидана по разным сайтам и туториалам. В официальной документации информации очень мало, а в русскоязычном интернете вообще ничего нет.
Будем исследовать датасет с пабами Москвы =).
Датасет в виде списка пабов Москвы с их координатами выгружен с http://openstreetmap.ru
import json
import folium
import pandas as pd
import requests
with open('../../data/pubs.json') as json_data:
d = json.load(json_data)
columns = ['lat', 'lon', 'name_ru', 'opening_hours', 'website']
index = range(0, len(d["data"]))
pubs = pd.DataFrame(columns = columns, index = index)
for i in range(0, len(d["data"])):
pubs['lat'][i] = d["data"][i]["lat"]
pubs['lon'][i] = d["data"][i]["lon"]
pubs['opening_hours'].iloc[i] = d["data"][i]["opening_hours"]
pubs['website'][i] = d["data"][i]["website"]
pubs['name_ru'][i] = d["data"][i]["name_ru"]
pubs.head(3)
# Для центрирования карт я выбрала центральную точку Москвы
kremlin = [55.750730, 37.617322]
Самый простой вариант анализа - отобразить данные как точки (или маркеры) на карте. В popup (выноску) положим название заведения и часы работы.
Карта инициализируется с помощью синтаксиса map = folium.Map(location=kremlin, zoom_start=11)
.
Добавляем маркеры folium.Marker().add_to(map)
.
Различные типы маркеров задаются функциями:
Возможные атрибуты:
Так же в выноску можно передавать график vincent (https://github.com/wrobstory/vincent) с помощью синтаксиса:
folium.Popup().add_child(folium.Vega())
tiles
- источник тайлов карт. Я обычно использую openstreetmaps - они идут по умолчанию.
pubs_map = folium.Map(location=kremlin, zoom_start=11)
for i in range(0, len(pubs)):
folium.Marker([pubs['lat'][i], pubs['lon'][i]], popup = str(pubs['name_ru'][i]) + ": "
+ str(pubs['opening_hours'][i])).add_to(pubs_map)
pubs_map
Cluster marker - раскраска в зависимости от плотности точек. Близкие точки сливаются в один маркер.
Судя по данным, наибольшая плотность пабов в Москве - на серево-востоке от Кремля, в районе Чистых прудов.
from folium import features
pubs_map = folium.Map(location=kremlin, zoom_start=12)
mc = features.MarkerCluster()
for i in range(0, len(pubs)):
mk = features.Marker([pubs['lat'][i], pubs['lon'][i]])
mc.add_child(mk)
pubs_map.add_child(mc)
Построим Heatmap распределения пабов по Москве
import random
import numpy as np
from folium import plugins
pubs_map = folium.Map(location=kremlin, zoom_start=10)
data = [[x[0], x[1], 1] for x in np.array(pubs[['lat', 'lon']])]
HeatMap(data, radius = 20).add_to(pubs_map)
pubs_map
Добавим на карту линии, соединяющую локации. Она создаётся с помощью folium.PolyLine, который принимает координаты соединяемых точек, и настраивается параметрами, схожими с параметрами маркеров.
Здесь мы выбрали и соединили 4 заведения в Сокольниках:
sololniki = [55.791981, 37.664456]
pubs_map_sokolniki = folium.Map(location=sololniki, zoom_start=13)
path = []
for i in [100, 101, 102, 103]:
folium.Marker([pubs['lat'][i], pubs['lon'][i]], popup = str(pubs['name_ru'][i]) + ": "
+ str(pubs['opening_hours'][i])).add_to(pubs_map_sokolniki)
path.append([[pubs['lat'][i], pubs['lon'][i]], [pubs['lat'][i+1], pubs['lon'][i+1]]])
folium.PolyLine(path[0:3], color='blue', weight=4, opacity=0.7, popup=str(i)).add_to(pubs_map_sokolniki)
pubs_map_sokolniki
В заключении туториала, приведу пример отображения кратчайшего пути через пабы Москвы. Идея нагло украдена отсюда: http://www.math.uwaterloo.ca/tsp/pubs/
Для нахождения кратчайшего пути использую библиотеку Google or-tools, которая включает в себя алгоритмы решения задач нахождения маршрута. Работа с ней это тема для отдельного туториала, поэтому загружаю найденное решение из внешнего файла. Пути между заведениями тут уже строятся не отрезками, соединяющими пары пабов, а следуют кратчайшему маршруту из паба в паб, подобранному с помощью http://project-osrm.org
import pickle
# инициализируем карту
map = folium.Map(location=kremlin, zoom_start=14)
# грузим файл с данными о маршрутах
file = open('../../data/tutorial_data.pickle', 'rb')
routes = pickle.load(file,encoding='latin1')
file.close()
# Рисуем большой полилайн полного маршрута...
for path in routes['paths']:
folium.PolyLine(path, color='black', weight=3, opacity=0.8).add_to(map)
# ...и маркеры пабов
for point in routes['points']:
folium.Marker(point).add_to(map)
map
Have fun =)