osmareatag - плагин для osmosis для добавления тегов на базе геометрии

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

POI внутри здания наследуют адрес от этого здания, дороги внутри населённого пункта по умолчанию имеют maxspeed отличный от вне населённых пунктах и т.п. Однако далеко не везде эту проверку можно сделать легко и просто. Это легко при использовании БД с поддержкой пространственных данных (PostGIS и т.п.), что-то умеет распространённый конвертер osm2pl, однако во многих случаях готового инструментария нет, а программировать не все умеют. Поэтому и начинают плодиться теги addr:country/add:city на зданиях, addr:street/addr:housenumber на точечных POI, maxspeed=RU:urban на дорогах в населённых пунктах и т.п.

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

Исходники лежат тут: https://github.com/sergeyastakhov/OSM/tree/master/osmareatag
Примеры: https://github.com/sergeyastakhov/OSM/tree/master/osmareatag/src/main/examples
Скомпилированная версия: https://cloud.mail.ru/public/4AyX/jQ85UbSAM

Для установки плагина надо распаковать zip-файл дистрибутива в подкаталог plugins того каталога где будете запускать osmosis (должен получиться каталог osmareatag-1.1.zip внутри каталога plugins).

Для простановки кодов стран нужны данные границ этих стран. На gislab, к сожалению, границы России битые и исправлять это они не очень то торопятся. Проще всего эту границу вытянуть отдельным запросом через API (link). В несжатом виде это будет 19 Мб, в формате pbf - 900 Кб. Если нужны другие страны - можно их так же вытянуть по одному и потом объединить, либо взять дамп нужного района на geofabric и отфильтровать из него.

Получив файл с границами стран - дальше нужно сформировать пространственный индекс. Пример (create_spatial_index.bat):

del national-boundary.idx

call osmosis --read-pbf russia-boundary.osm.pbf --lp --tag-area-content file=tag-index.xml prepareOnly=true --write-null

tag-index.xml

<?xml version="1.0" encoding="UTF-8"?>
<tag-processing>
  <area id="national-boundary" cache-file="national-boundary.idx">
    <match type="relation">
      <tag k="boundary" v="administrative"/>
      <tag k="admin_level" v="2"/>
    </match>
  </area>
</tag-processing>

Параметр file задаёт файл конфигурации, параметр prepareOnly=true задаёт что обработку выполнять не нужно, только создание индексов. В данном примере задаётся, что необходимо создать пространственный индекс для объектов типа relation с тегами boundary=administrative и admin_level=2 (границы стран). После создания индекс сохраняется в файле national-boundary.idx Если этот файл уже существует, то будет выполняться попытка загрузки из него, вместо использования данных из входного потока, что позволяет создать индекс один раз и затем им пользоваться многократно. Для России индекс занимает 2.7 Мб. При работе индекс держится в памяти, поэтому для больших индексов может потребоваться увеличить кол-во доступной памяти для osmosis

Дальше можно запускать обработку данных. Пример для простановки атрибутов дорог (highway_mark.bat):

call osmosis --read-pbf RU-LEN.osm.pbf --lp --bb clipIncompleteEntities=true --tag-area-content file=tag-highway.xml --write-xml RU-LEN.hwarea.osm.gz

tag-highway.xml

<?xml version="1.0" encoding="UTF-8"?>
<tag-processing>
  <area id="national-boundary" cache-file="national-boundary.idx">
    <match type="relation">
      <tag k="boundary" v="administrative"/>
      <tag k="admin_level" v="2"/>
    </match>
  </area>
  <area id="boundary-administrative">
    <match type="relation">
      <tag k="boundary" v="administrative"/>
    </match>
  </area>
  <area id="place">
    <match>
      <tag k="place" v="city|town|village|hamlet|isolated_dwelling|allotments"/>
    </match>
  </area>

  <transform>
    <name>Motorway speeds</name>
    <match type="way">
      <tag k="highway" v="motorway"/>
      <inside area="national-boundary"/>
    </match>
    <output>
      <add-tag k="maxspeed" v="${ISO3166-1}:motorway" context-area="national-boundary"/>
    </output>
  </transform>

  <transform>
    <name>Rural speeds</name>
    <match type="way">
      <tag k="highway" v="motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link|unclassified|residential"/>
      <inside area="national-boundary"/>
      <outside area="place"/>
    </match>
    <output>
      <add-tag k="maxspeed" v="${ISO3166-1}:rural" context-area="national-boundary"/>
    </output>
  </transform>

  <transform>
    <name>Urban speeds</name>
    <match type="way">
      <tag k="highway" v="motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link|unclassified|residential"/>
      <inside area="national-boundary"/>
      <inside area="place"/>
    </match>
    <output>
      <add-tag k="maxspeed" v="${ISO3166-1}:urban" context-area="national-boundary"/>
    </output>
  </transform>

  <transform>
    <name>Urban living street speeds</name>
    <match type="way">
      <tag k="highway" v="pedestrian|living_street|service"/>
      <inside area="national-boundary"/>
      <inside area="place"/>
    </match>
    <output>
      <add-tag k="maxspeed" v="${ISO3166-1}:living_street" context-area="national-boundary"/>
    </output>
  </transform>

  <transform>
    <name>Administrative units</name>
    <match type="way">
      <tag k="highway" v=".*"/>
      <inside area="boundary-administrative"/>
    </match>
    <output>
      <add-tag k="administrative:${admin_level}" v="${name}" context-area="boundary-administrative"/>
    </output>
  </transform>

</tag-processing>

В данном примере проставляются атрибуты maxspeed на основе вхождения/невхождения в населённые пункты + добавляются атрибуты administrative:* для привязки к соответствующим административным границам. Инструкция add-tag добавляет тег при его отсутствии, если тег уже есть, он оставляется нетронутым.

Пример для простановки адресных данных на точечных POI (poi_addr.bat):

call osmosis --read-pbf RU-LEN.osm.pbf --lp --tag-area-content file=tag-poi-addr.xml --write-xml RU-LEN.poi.osm.gz 

tag-poi-addr.xml

<?xml version="1.0" encoding="UTF-8"?>
<tag-processing>
  <area id="national-boundary" cache-file="national-boundary.idx">
    <match type="relation">
      <tag k="boundary" v="administrative"/>
      <tag k="admin_level" v="2"/>
    </match>
  </area>
  <area id="region-boundary">
    <match type="relation">
      <tag k="boundary" v="administrative"/>
      <tag k="admin_level" v="4"/>
    </match>
  </area>
  <area id="place">
    <match>
      <tag k="place" v="city|town|village|hamlet|isolated_dwelling|allotments"/>
    </match>
  </area>
  <area id="addr-building">
    <match>
      <tag k="building" v=".*"/>
      <tag k="addr:housenumber" v=".*"/>
    </match>
  </area>

  <transform>
    <name>POI Addresses</name>
    <match type="node">
      <tag k="amenity|shop" v=".*"/>
      <inside area="national-boundary"/>
      <inside area="region-boundary"/>
      <inside area="place"/>
      <inside area="addr-building"/>
    </match>
    <output>
      <add-tag k="addr:country" v="${ISO3166-1}" context-area="national-boundary"/>
      <add-tag k="addr:region" v="${name}" context-area="region-boundary"/>
      <add-tag k="addr:city" v="${name}" context-area="place"/>
      <add-tag k="addr:street" v="${addr:street}" context-area="addr-building"/>
      <add-tag k="addr:housenumber" v="${addr:housenumber}" context-area="addr-building"/>
    </output>
  </transform>
</tag-processing>

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

Здорово, а на каких разработках основан индекс?

Для геометрии используется библиотека JTS (Java Topology Suite), индекс - класс STRtree. Комментарии из него:

Очень круто! Правда, не уверен, что хоть кто-то сейчас вспомнит, как работать с osmosis :slight_smile:

А с какой версией osmosis работает? Минимально необходимая версия например.

Если highway проходит через на, то ему разрешенную скорость не поставить. Делить на три части же надо.

Минимальная должна быть 0.43, проверял и тестировал на последней, 0.45

В смысле - автоматом? Заморочно это, надо новые объекты генерить, причём так чтобы отношения не ломались. Лёша в своём конвертере тоже отказался от этой идеи. Тут лучше делить руками в самом OSM.

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

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

Ну как нет нужды? Делить как правило нужно, т.к. свойства дороги внутри НП обычно отличны от свойств дороги вне НП - у неё другой maxspeed, часто есть своё название, освещение и т.п. Если ничего этого нет - то можно и не делить, но это исключение а не правило.

У дороги, но не у линии.

Да, у дорог есть отличия и в названиях, и в освещениях, но у линий это всё очень часто не отмечено, так что линий, пересекающих населенные пукты, тьма.

Ну это просто нехватка информации, рано или поздно её придётся добавить. Хороший повод добавить эти случаи в твой валидатор.
Данный плагин новую информацию не привносит, только переводит из одной формы в другую.

срабатывает не везде, пример населенный пункт Скоропусковский, Московская область, Сергиево-Посадский район. В нас.пункте нет наименований улиц и там плагин не работает.

А что именно не работает? Проставление адресов у POI? Или что-то другое?

Проставление адресов у POI. В этом населенном пункте нет названий улиц и вместо адреса вписывается ${addr:street} как текст адреса, ну и соответственно mkgmap не меняет название улицы и на карте у точки остается в адресе ${addr:street} как имя улицы.

Еще просьба, если можно, сделать проставление адресных атрибутов addr:country и addr:city на здания в населенных пунктах.
Объясняю, Mkgmap проставляет эти атрибуты, но он берет их из admin_level, а у некоторых (я подсчет не вел, но это есть и много) маперы уже прописали эти теги и указали в них имя населенного пункта из place (а оно отличается от имени указанном в admin_level) и соответственно не попадают в индексацию (нет адресного поиска по этим адресам). Если привести все данные к одному виду, то задав всем place (населенным пунктам) admin_level=11 или admin_level=7 все адреса индексировались бы.
А то сейчас place= Вологда, admin_level= городской округ Вологда (это еще хорошо, можно отрезать надпись городской округ),
а как быть place=Петрозаводск, admin_level= Петрозаводский городской округ (здесь отрезание городского округа не поможет, так как нет совпадения Петрозаводск и Петрозаводский).
И еще чтобы можно было указать для какой addr:country это срабатывало не трогая другие addr:country
В теме https://forum.openstreetmap.org/viewtopic.php?id=9249&p=15 сообщение 370 я описываю как борюсь с адресацией, но у меня плохо получается (локально с Россией вроде все хорошо, а вот если делать карту для бывшего СССР не выходит), весь цикл создания карты для Garmin описан мной в приведенной теме с 14й страницы.
Если можете, помогите пожалуйста с адрессацией для Garmin пользователей.

Логика с отрезанием из admin_level у вас какая-то странная.

Почему, странная? Mkgmap присваивает admin_level8 (или 6 или 4) статус city, и получается населенный пункт с именем “городской округ Вологда”, если в стиле Mkgmap я отрезаю в имени "городской округ " остается только “Вологда” и это имя соответствует имени place “Вологда”.
И теперь все здания с указанным addr:city=Вологда и без указанного addr:city на территории данного admin_level индексируются и в поиске на приборе введя название города Вологда я нахожу эти адреса. Если не отрезать, то здания с указанным addr:city=Вологда в адресный поиск не попадают. Я понимаю, что это не правильно и ищу выход. На пример присваивая всем city и town - admin_level=7 (это на локальных данных в компьютере, а не во всей базе ОСМ) willage и hmlet - admin_level=10, isolated_dwelling и allotments - admin_level=11 и тогда Mkgmap их индексирует и тогда если в этих населенных пунктах есть здания с указанными номерами домов и улицами, то они попадают в адресный поиск на приборе.
Что я делаю не так? И как сделать адресный поиск с помощью программы Mkgmap для приборов Garmin?
Пожалуйста, подскажите, помогите, если можете.
И еще почему приходится отрезать или замещать на сокращенное название - в приборе не умещается.

addr:city должно наследоваться от полигона place, а не административной границы.

Подскажите пожалуйста, как это сделать в программе Mkgmap, она скачивается вместе со стилем default http://www.mkgmap.org.uk/download/mkgmap.html . Какие изменения надо сделать в стиле, чтобы addr:city наследовался от полигона place, а не административной границы.

Не в курсе.