Геоданные без регистрации и СМС

5mvlhofxdeykmfl8zfiiuwdckry.jpeg

Здравствуй, дорогой читатель.

Спешу поделиться тем, как на самом деле найти геоданные без регистрации и СМС. По чесноку. Без всяких-яких. И даже «подписывайтесь на телеграмм канал» — не будет, у меня его и нет…

И речь пойдёт про инструмент Osmosis.

Побудительный мотив

Сидел я тут намедни, и наткнулся на интереснейшую вещь: почти часовой подкаст на YouTube канале Хабра: Хабр Про. В чём магия геоданных и как их найти без регистрации и СМС. И тема то небезынтересна.

Но в подкасте было всё кроме ответа на главный вопрос: «как же их найти?». И вот сижу я, обманутый, и думаю как восстановить справедливость. Вопрос поставлен, а ответа нет. И решил взять дело в свои руки — написать эту статью. Прошу любить и жаловать.

Зачем?

Не публикация, конечно, «зачем», а: «зачем получать геоданные».

Для исследований:

Кто сказал «урбанистика»?

Если создаёте свое приложение с блэкджеком… с картами:

А иногда просто по фану:

Да много всякого…

Так где?

Вопрос то поставлен ребром: без регистрации и СМС, а значит безвозмездно, то есть даром! Это ограничение оставляет нас, по большому счёту, с единственным известным мне вариантом: OSM.

В OSM прекрасно всё: он сбалансированный как инь и янь. Масштабный, открытый, универсальный, обо всём что может быть на карте, с бардаком, бесконечными спорами как надо и не надо картографировать и перманентно нестабильной документацией (посмотрите для интереса блок тегирование в новостях из мира OpenStreetMap), — всё как мы любим. Т.е. польза и энтропия зашкаливают.

Т.е. в нём есть всё, если нет — значит скоро будет, если ждать невозможно: занесите сами. Но в правилах чёрт ногу сломит. А раз правила гибкие, то и инструменты для работы с этими данными должны быть под стать: либо есть линия партии и «шаг влево, шаг вправо — расстрел», либо «что хочу, то и ворочу». А раз буква O в OSM это Open, то придерживаться будем «что хочу, то и ворочу».

Инструмент подходящий есть — osmosis, и его-то я опишу, но будьте готовы: придётся вникать не только в команды инструмента, но и в правила OSM, примеры приведу, ссылки приложу, но прогулка не будет лёгкой, на блюдечке с голубой каёмочкой вам эти данные не получить: без СМС всё-таки, а значит придётся поработать.

Osmosis это приложение для работы в командной строке. А значит будем преодолевать тяготы и лишения. Но оно того стоит.

Ну и не осмосисом единым, конечно же, есть, например, ещё osmium и д.р. см. Alternatives to osmosis.

Приготовления

Установка osmosis

Инструкции по установке доступны на OpenStreetMap Wiki.

Опишу таки как поставить на Windows:

  1. Для начала нужна Java.

  2. Качаем zip архив Osmosis c GitHub.

  3. Извлекаем архив.

По сути вся установка. Запустите командную строку. Перейдите в директорию, куда извлечён архив, найдите папку bin, например, D:\osmosis-0.49.2\bin, и выполните команду osmosis для проверки.

И так, ложка есть — инструмент установлен. Теперь пойдём добывать варенье.

Сырые данные

Данные нам нужны в формате PBF. Я их всегда качаю с geofabrik. Находим нужный регион — качаем. Есть и другие источники подробнее смотрите страницу Planet.osm на OpenStreetMap Wiki.

Данные получены. Осталось разобраться как это есть.

Работа с данными

Инструкцию по всем командам (версия 0.48) можно посмотреть всё там же: на Openstreetmap Wiki.

Важно понимать несколько вещей при работе в командной строке с Osmosis.

Термины

Задачи (Tasks)

В командной строке вы пишите последовательность задач (Tasks). Каждая задача начинается с символов --, например:

osmosis --read-pbf file=myfile.osm.pbf --write-null

Здесь две задачи --read-pbf и --write-null.

У задач есть сокращённая запись:

osmosis --rd file=myfile.osm.pbf --wn

Краткая запись полезна, когда необходимо как-то команду сделать компактнее, порой команды получаются слишком монструозные, тут и многострочный режим пригодится и краткая запись. Я в примерах буду использовать полную запись, для полноты изложения конечно же.

Аргументы (Arguments)

Аргументы относятся к задачам, после которых они следуют, и являются парой ключ-значение.

Некоторые задачи могут принимать безымянные аргументы — аргументы «по умолчанию» — ключ не обязательно указывать. Если не указан ключ, то osmosis будет подставлять тот ключ, который соответствует аргументу «по умолчанию», вот это, одно и то же:

osmosis --read-pbf file=myfile.osm.pbf --write-null
osmosis --read-pbf myfile.osm.pbf --write-null

file — аргумент по-умолчанию для задачи --read-xml.

Потоки (Pipeline)

Задачи воспринимают данные как поток. Визуально это можно представить в виде пазлов:

gvlylmifdrbnbqyg7jpta1mxca8.png

В документации к osmosis описан такой таблицей:

Pipe

Description

outPipe.0

Produces an entity stream.

  • команды которые принимают поток на вход, обрабатывают его, и возвращают результат в виде потока, в другие команды;

up3oajfyn5xasdseaorzywwr1-g.png

В документации к osmosis таблица будет выглядеть так:

Pipe

Description

inPipe.0

Consumes an entity stream.

outPipe.0

Produces an entity stream.

z7_etmhnhhwvcyl-vggtnldxi60.png

В документации к osmosis:

Pipe

Description

inPipe.0

Consumes an entity stream.

Я вместо таблиц буду иллюстрировать пазлами, очень уж они мне нравятся.

Таким образом у вас должны быть задачи создания потоков, возможно обработки потоков, возможно манипуляции потоками, и завершения потоков. И они должны выстраиваться в некую цепочку, как конвейер.
Задачи в последовательности можно ставить как угодно, — ошибки не будет, главное чтобы входящие потоки «следующей» задачи соотносились с исходящими потоками «предыдущей».

Однако это «как угодно» будет, давать разные результаты. Последовательность задач имеет значение.

Это особенности работы с инструментом, — его философия. Понимание этих простых вещей сделает вашу работу с инструментом более осмысленной.

А теперь к задачам.

Задачи чтения

Начнём, конечно же с задач чтения, полагаем что дамп данных мы скачали таки в формате pbf.

gvlylmifdrbnbqyg7jpta1mxca8.png--read-pbf (--rb)

Всё прозаично, — ничего не делает кроме как создаёт поток для чтения данных из файла, вся работа по обработке: правила выборки, сохранение — потом.

Имеет один параметр file, в котором указывается путь к файлу.

Ну и пример мы уже видели, посмотрим ещё раз:

osmosis --read-pbf file=myfile.osm.pbf --write-null

gvlylmifdrbnbqyg7jpta1mxca8.png--read-xml (--rx)

Читает данные из файла структуры OSM XML.

Задачи записи

z7_etmhnhhwvcyl-vggtnldxi60.png--write-xml (--wx)
z7_etmhnhhwvcyl-vggtnldxi60.png--write-pbf (--wb)

Записывает поток, соответственно либо в OSM XML либо в PBF. В качестве аргумента принимает как минимум имя файла.

XML хотя бы человекочитаема, в большинстве примеров будем её использовать, чтобы можно было открыть и глазками посмотреть что там происходит.

Но погодите что-то куда-то записывать. Мало ли что за pbf файл вы добыли, просто прочитать его и сохранить — задача сомнительной полезности. Попробуем его сначала как-то урезать. С файлом поменьше и работать будет попроще.

Задачи фильтрации по области

up3oajfyn5xasdseaorzywwr1-g.png--bounding-box (--bb)

Извлекает данные в пределах определённой ограничивающей рамки, задаваемой координатами широты и долготы.

И так, я скачал pbf со всем Уральским Федеральным Округом, а поскольку всё подряд мне не надо, — выберу один город, и сохраню его в отдельный pbf-файл.

osmosis \
    --read-pbf file=myfile.osm.pbf \
    --bounding-box top=57.2456 left=65.4316 bottom=57.0719 right=65.6945 \
    --write-pbf file=tyumen.osm.pbf

Координаты ставил на глаз, по гугл картам, всё что попало в этот прямоугольник, должно попасть и в итоговый pbf.

up3oajfyn5xasdseaorzywwr1-g.png--bounding-polygon (--bp)

А эта для тех кто хочет заморочиться. Если прямоугольник по каким-то причинам не устраивает, то можно задать полигон произвольной формы. Задаётся прямоугольник серией координат широта/долгота, в отдельном файле, подробнее смотреть: Polygon Filter File Format.

Есть в интернетах уже готовые полигоны смотрите http://download.openstreetmap.fr/polygons/ например.

В целом с пространственными ограничениями — всё.

И про это уже писали на Хабре: Как вырезать сабсет города (любого отношения) из OSM данных.

Задачи фильтрации данных по атрибутам

По сути самый важный раздел — для него всё и затевалось, остальные задачи, — это так, вспомогательные.

Для этого раздела уже понадобится понимание структуры и правил OSM.

Важно знать что элементы в OSM трёх типов:

  • узлы (node), они же точки

  • пути (way), они же линии

  • отношения (relation), они же отношения (sic!)

Все задачи фильтрации оперируют этими тремя категориями.

Помимо этого у элементов OSM есть ещё такое понятие как теги. Теги описывают конкретные особенности элементов карты, это единственный способ как-то обозначить к чему относятся данные, см. Скованные одним слоем. Т.е. данные в OSM бесконечно расширяемые и неделимы (на слои) — собственно по этой причине и написана данная статья, было бы всё по слоям — писать было бы не о чем.

Ну, а чтобы ответить на вопрос, какие теги бывают, можете заглянуть на страницу Category: Keys и оценить насколько глубока кроличья нора. И важно понимать что список не окончательный, — модель же бесконечно расширяема.

Ладно, переходим к задачам.

up3oajfyn5xasdseaorzywwr1-g.png--node-key (--nk)
up3oajfyn5xasdseaorzywwr1-g.png--way-key (--wk)

Фильтры по OSM тегам.

Учитывая список ключевых тегов (теги указываются в параметре keyList, через запятую), эти фильтры пропускают только те узлы или линии, соответственно, у которых установлен хотя бы один из этих тегов.

EBNF грамматика

EBNF грамматика

Например попробуем получить всё линии дорог. Для обозначения дорог в OSM используется тег highway:

osmosis \
    --read-pbf tyumen.osm.pbf \
    --way-key keyList=highway \
    --write-xml roads-way.osm.xml

Обратите внимание, что эти фильтры --node-key работает только с узлами, и ВЫБРАСЫВАЕТ все пути и отношения, а фильтр --way-key фильтрует только линии, но ОСТАВЛЯЕТ в потоке и узлы и отношения — никак их не фильтруя.

Можете попробовать сделать ещё аналогичный фильтр по узлам и сравнить результаты:

osmosis \
    --read-pbf tyumen.osm.pbf \
    --node-key keyList=highway \
    --write-xml roads-node.osm.xml

В итоге файл roads-way.osm.xml — распух от несвязанных с дорожной инфраструктурой данных, а roads-node.osm.xml,… ну он получился предсказуемым.

Это в целом немного странно, что поведение отличается для схожих функций. К тому же сами по себе эти две задачи бестолковые, только в комбинации с другими задачами фильтрации им можно найти применение. Полагаю что это и было причиной почему поведение отличается, — задачи воспринимались как вспомогательные для уже отфильтрованных данных.

up3oajfyn5xasdseaorzywwr1-g.png--node-key-value (--nkv)
up3oajfyn5xasdseaorzywwr1-g.png--way-key-value (--wkv)

Работают аналогично предыдущим, но помимо тегов, которые являются ключами можно добавить ещё и значения ключей, которые вас интересуют, в формате key.value. Всё что не соответствует: либо нет тегов, либо есть теги, но значения не те — будет отброшено.

В качестве аргумента keyValueList в котором и указывается список желаемых комбинаций key.value.

Давайте для примера запросим все точки, которыми отмечены камеры контроля скорости:

osmosis \
    --read-pbf tyumen.osm.pbf \
    --node-key-value keyValueList=highway.speed_camera \
    --write-xml radar.osm.xml

При необходимости в keyValueList можно передавать несколько пар key.value, через запятую, тогда они будут восприниматься как логическое ИЛИ, т.е. в итоговый поток попадут те элементы которые соответствуют или одному фильтру, или другому, или третьему и т.д. сколько бы фильтров ни было.

EBNF грамматика

EBNF грамматика

Для примера можем получить камеры и лежачие полицейские:

osmosis \
    --read-pbf tyumen.osm.pbf \
    --node-key-value keyValueList=highway.speed_camera,traffic_calming.bump \
    --write-xml road.osm 

Ну, а если мы хотим получить логическое И, скажем не просто получить камеры контроля скорости, а с ограничением скорости на 40 км/ч, (это в отдельном теге хранится), то нужно применить две задачи последовательно:

osmosis \ 
    --read-pbf tyumen.osm.pbf \
    --node-key-value keyValueList=highway.speed_camera \
    --node-key-value keyValueList=maxspeed.40 \
    --write-xml radar.osm.xml 

Вот и результат:



  
  
	
	
	
  

Собственно немного в Тюмени таких камер (но надо иметь в виду что OSM не гарантирует полноту и корректность данных), находится на пересечении улиц Щербакова и Мелиораторов, пользуясь случаем передаю всем привет — предупреждён, значит вооружён.

Можно комбинировать задачи разных типов, например если хотим просто получить камеры для которых в OSM указано хоть какое-то ограничение на скорость:

osmosis \ 
    --read-pbf tyumen.osm.pbf \
    --node-key-value keyValueList=highway.speed_camera \
    --node-key keyList=maxspeed \
    --write-xml radar.osm.xml 

В качестве примера для линий получим все автомагистрали:

osmosis \
    --read-pbf tyumen.osm.pbf  \
    --way-key-value keyValueList=highway.motorway \
    --write-xml road.osm.xml

up3oajfyn5xasdseaorzywwr1-g.png--tag-filter (--tf)

Самая полезная задача для выбора данных.

Более гибкий аналог предыдущих, гибкость в том что не ограничен одним типом, а можно настраивать, и в том что правила могут быть как разрешительные так и запретительные. В то время как в предыдущих, были разрешительные.

Принять/отклонить

В качестве параметра принимает строку, которая определяет поведение «принятия/отклонения» объекта, с которым будет работает фильтр:

  • accept-nodes,

  • accept-ways,

  • accept-relations,

  • reject-nodes,

  • reject-ways,

  • reject-relations.

Здесь accept — принять, reject — отклонить.

Каждая задача фильтрует только указанный тип объекта, пропуская все остальные типы объектов. Т.е. если мы указываем accept-ways, то он отфильтрует все линии, оставив только те, что соответствуют фильтру, а узлы и отношения пройдут дальше попав на выходной поток, о них нужно будет позаботиться отдельно.

Фильтр по тегам

Когда у объекта есть тег, соответствующий одному из этих шаблонов, объект принимается или отклоняется в соответствии с режимом фильтрации. Каждая задача фильтрации тегов фильтрует только тип объекта, указанный в строке режима, пропуская все остальные типы объектов, не затрагивая их.

Шаблоны тегов не обязательны, если их не указать, то фильтр соответствует всем объектам данного типа.

osmosis \
    --read-pbf tyumen.osm.pbf \
    --tag-filter accept-ways highway=* \
    --tag-filter reject-node \
    --tag-filter reject-relations \
    --write-xml output.osm.xml

В этом примере мы выбираем все линии дорог, отбрасываем все точки и отношения: accept-ways разрешительная задача, которая разрешает все линии которые соответствуют условию, reject-node и reject-relations — запретительные задачи, у которых нет условия, а значит они отбрасывают всё.

Множество значений

В рамках определенного шаблона тегов для одного ключа можно указать несколько значений, используя список, разделённый запятыми (работает опять же через логическое ИЛИ).

Модифицируем пример:

osmosis \
    --read-pbf tyumen.osm.pbf \
    --tag-filter accept-ways highway=* \
    --tag-filter reject-ways highway=motorway,motorway_link \
    --tag-filter reject-node \
    --tag-filter reject-relations \
    --write-xml output.osm.xml

Здесь мы выберем все линии дорог, а потом исключаем из них (в reject-ways) автомагистрали и съезды с автомагистралей.

Подстановочное значение * (одна звездочка) соответствует любому значению.

Обратите внимание, что каждая задача фильтрации тегов может принимать более одного шаблона тегов и примет (accept) или отклонит (reject) объект, если он соответствует любому из этих шаблонов. Иными словами, пар ключ=значение в одной задаче может быть несколько.

EBNF грамматика

EBNF грамматика

Например мы захотим получить все места для спорта, отдыха и туризма:

osmosis \
    --read-pbf tyumen.osm.pbf \
    --tag-filter accept-nodes sport=* tourism=* leisure=* \
    --tag-filter accept-ways sport=* tourism=* leisure=* \
    --tag-filter reject-relations \
    --write-xml rest-poi.osm.xml

Тут могла быть реклама VisitTyumen, но нет.

up3oajfyn5xasdseaorzywwr1-g.png--used-node (--un)
up3oajfyn5xasdseaorzywwr1-g.png--used-way (--uw)

Ранее мы писали что каждая задача фильтрует только указанный тип объекта, пропуская все остальные типы объектов, а об остальных придётся побеспокоиться отдельно.

Например фильтруем мы линии, что делать с узлами? Отбросить? Можно и отбросить конечно, а что если мы хотим оставить те, что используется в линиях, которые мы ранее отфильтровали, а остальные отбросить?!

Вот для этого и существует --used-node — ограничивает поток узлов теми, которые используются в линиях и/или отношениях которые присутствуют во входном потоке.

Модифицируем уже рассмотренный ранее пример:

osmosis \
    --read-pbf tyumen.osm.pbf \
    --tag-filter accept-ways highway=* \ 
    --tag-filter reject-ways highway=motorway,motorway_link \
    --tag-filter reject-relations \
    --used-node \
    --write-xml output.osm.xml

Вещь полезная, ибо way — не совсем линии, координаты точек они не хранят, только ссылки на них (ref):


	
	
	
	

Координаты хранятся отдельно в узлах:


Поэтому если важна геометрия линии (way), то мало выбрать линии, надо ещё и выбрать узлы, а --used-node — единственный способ получить только используемые точки — отбросив остальные.

Задача --used-way аналогична, ограничивает поток линий (way) теми, которые используются в отношениях (relation) которые присутствуют во входном потоке.

Задачи по управлению потоком

Эти задачи позволяют манипулировать структурой потока. Манипуляций с данными, не проводят.

z7_etmhnhhwvcyl-vggtnldxi60.png--write-null (--wn)

Отбрасывает все входные данные. По сути прерывает поток. Это полезно для проверки целостности входных файлов или тестирования каких-то задач на корректность написания.

Мы его уже использовали в некоторых примерах, чтобы избавить себя от необходимости сохранения результата в файл.

--merge (--m)

Объединяет два потока в один. Только два. Если вам нужно объединить три источника данных, придётся указывать эту задачу дважды. И так далее.

Если бы такой пазл существовал он бы выглядел вот так (головоломка выходит на новый уровень):

fvbbl6raul8uuyjpdehwnijfbhq.png

Для примера откроем три потока из разных файлов:

osmosis \
    --read-pbf tyumen.osm.pbf \
    --read-pbf tobolsk.osm.pbf \
    --read-pbf ishim.osm.pbf \
    --merge \
    --merge \
    --write-pbf merged.osm.pbf

Первый merge объединяет tyumen.osm.pbf и tobolsk.osm.pbf, создаёт поток, второй merge объединяет этот поток и поток из ishim.osm.pbf, создаёт поток, и затем мы сохраняем его в отдельный pbf файл, который содержит все три города.

Следующая команда создаст выходной файл, содержащий кафе, а также все пешеходные дороги и точки, на которые они ссылаются.

osmosis \
    --read-pbf tyumen.osm.pbf \
    --tag-filter reject-relations \
    --tag-filter accept-nodes amenity=cafe  \
    --tag-filter reject-ways \
    \
    --read-pbf tyumen.osm.pbf \
    --tag-filter reject-relations \
    --tag-filter accept-ways highway=footway \
    --used-node \
    \
    --merge \
    --write-xml amenity-and-motorway.osm

В общем merge задача полезная — позволяет одной командой получить разношерстные данные, написав несколько последовательных блоков команд, и не мучаться со всякими reject и accept путаясь всё впихнуть в один tag-filter.

По сути из задач, полезных для добычи данных — всё. Если конечно не копать в сторону взаимодействия с БД, но это уже совсем другая история. Про эту историю уже тоже есть очерк на Хабре: Импорт OpenStreetMap. От бинарного исходника к таблице в БД в несколько шагов.

Заключение

Что ж, надеюсь я помог Вам разобраться в теме добычи геоданных из выгрузки OSM. Это не все возможности osmosis, но для получения данных в виде XML (почти человеческом) — достаточные.

Если Вам хочется глубже погрузится в мир OSM со всеми его преимуществами и недостатками, то вот несколько ссылок

И небольшой совет по поиску тегов OSM: не надо вычитывать всю документацию в поисках нужного тега — сначала смотрите как картографируют другие. Т.е. находите на карте (https://www.openstreetmap.org) один из интересующих Вас объктов, на правой панели инструментов выбираете «Что здесь?», кликаете на карту на интересующий Вас объект, и смотрите как он закартографирован.

Просмотр тегов на карте

Просмотр тегов на карте

Это поможет понять в каком направлении копать документацию.

Удачи!

© Habrahabr.ru