Как стать автором
Обновить

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

Время на прочтение12 мин
Количество просмотров4.5K

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

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

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


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

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

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

Зачем?

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

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

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

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

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

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

Так где?

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

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

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

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

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

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

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

Установка osmosis

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

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

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

  2. ...

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

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

По сути вся установка. Запустите командную строку. Перейдите в директорию, куда извлечён архив, найдите папку 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)

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

  • команды которые создают поток из внешнего источника данных;

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

Pipe

Description

outPipe.0

Produces an entity stream.

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

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

Pipe

Description

inPipe.0

Consumes an entity stream.

outPipe.0

Produces an entity stream.

  • команды которые принимаю поток, и “сливают” его.

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

Pipe

Description

inPipe.0

Consumes an entity stream.

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

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

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

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

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

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

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

--read-pbf (--rb)

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

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

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

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

--read-xml (--rx)

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

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

--write-xml (--wx)
--write-pbf (--wb)

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

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

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

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

--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.

--bounding-polygon (--bp)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

--node-key (--nk)
--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,... ну он получился предсказуемым.

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

--node-key-value (--nkv)
--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 

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

<?xml version='1.0' encoding='UTF-8'?>
<osm version="0.6" generator="Osmosis 0.49.2">
  <bounds minlon="65.43160" minlat="57.07190" maxlon="65.69450" maxlat="57.24560" origin="0.49.2"/>
  <node id="7893291158" version="1" timestamp="2020-09-10T11:41:59Z" uid="0" user="" lat="57.2022677" lon="65.6050515">
	<tag k="direction" v="55"/>
	<tag k="highway" v="speed_camera"/>
	<tag k="maxspeed" v="40"/>
  </node>
</osm>

Собственно немного в Тюмени таких камер (но надо иметь в виду что 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

--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, но нет.

--used-node (--un)
--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 id="1010891689" version="1" timestamp="2021-12-10T07:49:28Z" uid="0" user="">
	<nd ref="6045517422"/>
	<nd ref="9326407121"/>
	<tag k="highway" v="service"/>
	<tag k="service" v="parking_aisle"/>
</way>

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

<node id="6045517422" version="3" timestamp="2021-12-10T07:49:28Z" uid="0" user="" lat="57.1364932" lon="65.4755765"/>

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

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

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

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

--write-null (--wn)

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

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

--merge (--m)

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

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

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

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) один из интересующих Вас объктов, на правой панели инструментов выбираете "Что здесь?", кликаете на карту на интересующий Вас объект, и смотрите как он закартографирован.

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

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

Удачи!

Теги:
Хабы:
Всего голосов 14: ↑13 и ↓1+12
Комментарии23

Публикации

Истории

Ближайшие события

One day offer от ВСК
Дата16 – 17 мая
Время09:00 – 18:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область