Случалось ли вам работать над проектом, где были необходимы картографические данные определенной местности? Например, сколько шоссе пересекают город или сколько ресторанов расположено в заданной области?
OpenStreetMap — замечательным открытый ресурс, способный дать нам информацию по названным и аналогичным вопросам. В этой картографической системе содержится очень много данных, в т.ч географических, и пояснений.
Модель OpenStreetData
Давайте взглянем, как построен этот ресурс. Модель данных OSM имеет три основных компонента — это точки, линии и отношения. Все они имеют свои идентификаторы. Многие из этих элементов имеют теги, описывающие особенности, представленные в виде пар ключ-значение.
Говоря простыми словами, точки являются объектами на карте (с указанием долготы и широты), как указано на следующем изображении Индийских Ворот в Дели.
Линии соответствует упорядоченный список точек, который может соответствовать улице либо контуру дома. Ниже приведен пример NH 24, расположенного в Индии.
Заключительный элемент данных — это отношения, которые также являются упорядоченным списком, содержащим точки, линии, а иногда и другие отношения.
Они применяются для моделирования логических или географических связей между объектами. Например, таким образом представлены крупные строения наподобие Парламента Индии, изображенного в виде нескольких полигонов.
Использование Overpass API
Теперь давайте рассмотрим процесс загрузки данных с OSM. Для формирования обращений в Overpass API используется встроенный язык запросов.
К его использованию потребуется привыкнуть, но, к счастью, существует Overpass Turbo, разработанный Мартином Райфером, который оказывается весьма удобен для интерактивной обработки запросов прямо в браузере.
Например, вы хотите сделать запрос о кафетериях. В этом случае он должен выглядеть так:
node["amenity"="cafe"]({{bbox}});
out;
Каждая инструкция в источнике запроса должна заканчиваться точкой с запятой. В начале указывается необходимый нам компонент, который в данном случае будет точкой.
Далее мы применяем фильтрацию по тегам, указав пару ключ — значение "amenity"="cafe"
. В документации ресурса вы можете найти также много других вариантов настройки фильтра.
Существует множество различных тегов, а один из распространенных ключей — это amenity
, который включает в себя такие учреждения, как кафе, рестораны и даже лавочки. Получить полную информацию о всех доступных тегах и прочих настройках загляните в свойства карт OSM или taginfo.
Еще один фильтр определяется {{bbox}}
и соответствует ограничительной рамке, в которой мы хотим осуществить поиск. Работает эта функция только в Overpass Turbo. В иных случаях вы можете определить аналогичную ограничительную рамку с помощью (south, west, north, east)
координатами широты и долготы. Выглядеть это может так:
node["amenity"="pub"]
(53.2987342,-6.3870259,53.4105416,-6.1148829);
out;
Это можно применить и в Overpass Turbo. Как мы видели ранее, в модели данных OSM существуют линии и отношения, имеющие общий атрибут.
Получить их мы можем с помощью оператора блока объединения, который собирает все результаты, полученные от последовательных инструкций внутри скобок.
Пример:
( node["amenity"="cafe"]({{bbox}});
way["amenity"="cafe"]({{bbox}});
relation["amenity"="cafe"]({{bbox}});
);
out;
Еще один способ фильтрации запросов — это по ID элемента. Вот пример запроса node(1); out;
, по которому выдается главный меридиан, имеющий долготу, приближенную к нулю.
Возможна также фильтрация по области поиска, задаваемой подобным образом area["ISO3166-1"="GB"][admin_level=2];
. Этот запрос выдаст нам область Великобритании.
Мы можем использовать этот фильтр, добавив (area)
к инструкции:
area["ISO3166-1"="GB"][admin_level=2];
node["place"="city"](area);
out;
Этот запрос возвращает все города Британии. В качестве области также допустимо использование линий или отношений. В первом случае ID области получается путем прибавления 2400000000
к ID существующей OSM линии. В случае же с отношениями к их ID потребуется прибавить уже 3600000000
.
Имейте в виду, что не все линии и отношения имеют такую обратную связь с областями (а именно отмеченные тегом area=no
, большинство мультиполигонов и не имеющие заданного name=*
).
Если мы применим отношение Великобритании к предыдущему примеру, то у нас получится следующее:
area(3600062149);
node["place"="city"](area);
out;
Мы также можем настраивать выдачу запрошенных через out данных. До этого момента мы определяли ее просто как out
, но существуют и другие дополнительные значения.
Первый набор таких значений может контролировать многословность или детализацию вывода — ids
, skel
, body
(значение по умолчанию), tags
, meta
и count
. Все они подробно описаны в документации.
Помимо всего этого, мы можем добавлять модификаторы геокодированной информации. geom
добавляет полную геометрию для каждого объекта. Это может быть очень полезно в случаях, когда возвращаются отношения или линии, не имеющие координат.
Например, запрос rel["ISO3166-1"="GB"][admin_level=2]; out geom;
без добавления geom
не вернул бы никаких координат. Значение bb
добавляет только ограничительную рамку для каждой линии и отношения, а center
дополнительно указывает только центр той же ограничительной рамки.
Порядок сортировки может быть настроен с помощью asc
и qt
, которые упорядочивают по ID, либо по индексу квадрата (quadtile
), соответственно. Последний вариант оказывается существенно быстрее. В конце концов, вы можете добавлять целочисленное значение, чтобы задать максимальное число возвращаемых элементов.
Совместив все то, чему мы только что научились, можно сформировать запрос области всей сети пивоварни Biergarten в Германии.
area["ISO3166-1"="DE"][admin_level=2];( node["amenity"="biergarten"](area);
way["amenity"="biergarten"](area);
rel["amenity"="biergarten"](area);
);
out center;
Доступ к API в Python
Для обращения в Overpass API посредством Python нужно использовать пакет overpy в качестве обертки. Ниже приведен пример того, как можно преобразовать предыдущий код с помощью этого пакета:
import overpyapi = overpy.Overpass()
r = api.query("""
area["ISO3166-1"="DE"][admin_level=2];
(node["amenity"="biergarten"](area);
way["amenity"="biergarten"](area);
rel["amenity"="biergarten"](area);
);
out center;
""")coords = []
coords += [(float(node.lon), float(node.lat))
for node in r.nodes]
coords += [(float(way.center_lon), float(way.center_lat))
for way in r.ways]
coords += [(float(rel.center_lon), float(rel.center_lat))
for rel in r.relations]
У overpy
есть одно приятное достоинство — он определяет тип содержимого ответа, а именно XML или JSON.
Читайте также:
- 7 советов по улучшению анализа данных в Python
- Простое руководство по аргументам командной строки Python
- Полное руководство по встроенным структурам данных Python
Перевод статьи Vaibhav Shukla: How To Get Open Street Map Data Using Python