Экспортируем нужные нам данные из openStreetMap

Иногда возникает ситуация, в которой нам жизненно необходимо получить структурированные геоданные для своих проектов. Но где их искать? Делать на коленке парсер google maps?

На самом деле нет — всё куда прозаичнее. Их можно без зазрения совести скоммуниздить из прекрасного проекта openStreetMaps, попутно дополнив и исправив текущие данные в их базах.

Краткая справка для тех, кто в танке: openStreetMaps (или кратко OSM) — open source картографический проект, где каждый пользователь может чуть-чуть дорисовать карту. И по принципу "с миру по нитке" собирается свободная и бесплатная карта мира. (как с википедией, только карта)

Процесс раскулачивания геосервиса запущен

Псс, парень, не хочешь немного геоданных?

Итак, в моём конкретном случае, мне нужно получить данные по всем железнодорожным станциям России: название, её код в какой-нибудь международной классификации + координаты. У вас это могут любые другие данные, принцип получения от этого толком не изменяется.

Для вытаскивания небольших данных из OSM есть онлайновый инструмент, который позволяет делать запросы и получать результат в JSON.

Но лично для меня этот язык запросов выглядит слишком странным + этот инструмент работает до невозможности медленно (а иногда благополучно лежит). overpass-turbo

Но можно пойти куда более простым и понятным путём:

  1. Скачать дамп всех данных OSM (или маленький кусочек нужной нам территории)
  2. Отфильтровать объекты (оставить только те типы объектов, которые нам нужны)
  3. Преобразовать в формат, который пригоден для загрузки в БД

Небольшая вставка теории:

Чтобы придумать, как вытащить железнодорожные станции, нужно понимать, какие данные и с какой структурой там в принципе есть.

Есть очень хорошая вводная статья на хабре.

А также имеется официальная дока.

Если кратко:

Дамп данных из OpenStreetMap можно представить в виде здоровенного XML файла, который содержит в себе помойку большое скопление тэгов трёх типов:

  1. Точки (node)
  2. Линии (way)
  3. Отношения (relation)

Внутри этих тэгов заполняется всякая информация о них в виде key:value.

Например:
Давайте попробуем найти в интерфейсе OSM любую станцию и посмотреть, что там внутри.

Станция Шушары и её информация (слева в таблице):

Станция Шушары в интерфейсе

Теперь посмотрим, как она выглядит в данных:

<node id="1377619868" lat="59.8057985" lon="30.3884693" version="9" timestamp="2019-05-08T05:34:15Z" changeset="70007449" uid="474169" user="Yevgeny Gromov">
    <tag k="esr:user" v="033004"/>
    <tag k="name" v="Шушары"/>
    <tag k="railway" v="station"/>
    <tag k="uic_ref" v="2005166"/>
    <tag k="wikidata" v="Q4528146"/>
    <tag k="wikipedia" v="ru:Шушары (станция)"/>
</node>

Данные хранятся в виде

<tag k="key" v="value"> 

внутри нашей точки "Шушары". А координаты хранятся в атрибутах самого тега "node" (lat/lon).

Замечаем, что признак "железнодорожная станция" — это вот такой тэг. На него и будем ориентироваться, когда будем фильтровать данные:

<tag k="railway" v="station"/>

Окей, мы поняли структуру, поняли что нам надо. Но где данные, Лебовски?

Ваши данные будут храниться здесь

Добываем данные

Если у вас достаточно свободного пространства на жестком диске — можете попробовать выкачать весь мир по официальным инструкциям.

Либо можно зайти на сайт openStreetMap, открыть на карте нужный квадрат и нажать на "Экспорт".

У России другой путь

Проблема России в том, что она достаточно большая, чтобы экспорт в интерфейсе openStreetMap падал из-за слишком большого количества данных, но всё ещё достаточно маленькая, чтобы ради неё выкачивать весь мир!

Специально для таких извращенцев, как мы, некоторые люди подготавливают "нарезку" мира на удобоваримые куски:

  • На geofabrik.de имеются нарезки по континентам
  • На needgeo.com есть данные по странам пост-советского пространства.

Последняя ссылка как раз нам очень пригодится! :) Выкачиваем оттуда файл RU.pbf (который весит сущие копейки — всего 3GB)

Обрабатываем данные

Для обработки нам понадобятся две консольные утилиты: osmconvert и osmfilter.

В debian/ubuntu устанавливаются очень просто одной командой:

sudo apt install osmctools

Для остальных ОС имеются ссылки на бинарники на официальной wiki Osmfilter Osmconvert.

Во-первых, нам нужно распаковать скачанный файл .pbf в пригодный для фильтрации формат.

osmconvert RU.pbf -o=RU.o5m

На выходе получим файл RU.o5m, который занимает уже 6.2GB. (но это всё ещё бинарный не человеко-читаемый файл.)

Теперь отбрасываем всё лишнее, оставляя только точки с нужным там тегом (который мы нашли в предыдущих параграфах):

osmfilter RU.o5m --keep="railway=station" > stations.osm

Компьютер задумается на какое-то время, а затем заполнит файл stations.osm. Де-факто, уже почти всё. Мы на выходе получили XML файл со всеми нужными нам станциями (3Mb).

Попробуем проверить данные:
Открываем в любом текстовом редакторе, который способен открывать большие текстовые файлы (vim), и бегло просматриваем станции:

И тут натыкаемся на станцию метро "Озерки". Что ж, формально — это жд, и формально — станция. Но не совсем то, что мне нужно. :)

<node id="96459081" lat="60.0354511" lon="30.3210663" version="24" timestamp="2020-01-08T22:53:32Z" changeset="79358758" uid="217225" user="&#38;ergio">
    <tag k="colour" v="blue"/>
    <tag k="layer" v="-5"/>
    <tag k="name" v="Озерки"/>
    <tag k="name:de" v="Oserki"/>
    <tag k="name:en" v="Ozerki"/>
    <tag k="name:fi" v="Ozerki"/>
    <tag k="name:ru" v="Озерки"/>
    <tag k="network" v="Петербургский метрополитен"/>
    <tag k="operator" v="Петербургский метрополитен"/>
    <tag k="public_transport" v="station"/>
    <tag k="railway" v="station"/>
    <tag k="station" v="subway"/>
    <tag k="subway" v="yes"/>
    <tag k="transport" v="subway"/>
    <tag k="wikidata" v="Q2241386"/>
    <tag k="wikipedia" v="ru:Озерки (станция метро)"/>
</node>

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

<tag k="station" v="subway"/>
<tag k="subway" v="yes"/>
<tag k="transport" v="subway"/>

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

osmfilter stations.osm --drop="station=subway or subway=yes or transport=subway" > not_subway.osm

Смотрим на выходной файл — выглядит так, словно успешно отфильтровали. Теперь осталось превратить xml файл в CSV, чтобы удобно залить в базу.

Смотрим на документацию по станциям и составляем список тегов, которые будем выводить в CSV:
railway:stations

Нужные нам столбцы:

  1. id
  2. lat
  3. lon
  4. name
  5. uic_ref — уникальный код железнодорожной станции, присваиваемый UIC (International Union of Railways, Международный союз железных дорог).
  6. esr:user — уникальный код железнодорожной станции в Единой сетевой разметке (ЕСР, для станций Балтики и стран СНГ).

Экспортируем (обратите внимание, что атрибуты самого node записываются через "@"):

osmconvert not_subway.osm --all-to-nodes --csv="@id @lat @lon name uic_ref esr:user" --csv=headline > stations.csv

Теперь мы можем со спокойной совестью делать с этими данными, что душе угодно. Можем обрабатывать руками в EXCEL, можем залить в базу и использовать в своих разнообразных проектах.

Если найдете какие-то неточности в данных, очень рекомендуется исправить их не только у себя, а залить их также на саму OpenStreetMap. Так сказать, сделать свой маленький вклад в общее дело. :)

P.S. Мистическим образом, под железнодорожные станции ещё мимикрирует Волгоградский метротрам. Но его уже можно отфильтровать в итоговом CSV.