Хабрахабр

Как наладить поиск адреса по координатам (и где взять нужный справочник)

Название намекает: метод принимает геокоординаты и отдает данные об адресе. Весной мы добавили в API DaData.ru фичу «Обратное геокодирование», она же «Адрес по координатам».

Но сервис «Яндекса» бесплатен только для открытых некоммерческих проектов. Солидный продукт с той же функциональностью сделал «Яндекс» — он называется «Геокодер». Стандартный же тариф — от 120 000 ₽ в год — подходит не всем.

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

  • справочник адресов с координатами, собранный и проверенный собственноручно;
  • готовое и отлаженное API.

В статье расскажу, как мы создали «Адрес по координатам»: наладили поиск, собрали справочник и упаковали в готовый метод.

Где берем данные и чем ищем адрес

Подступаясь к задаче, мы изучили готовые решения: где взять справочник координат с адресами и как потом искать по этому справочнику географические объекты. Оказалось, за нужными инструментами даже не придется далеко ходить.

Это самый полный из открытых и официальных адресных справочников. Адресные объекты берем в ФИАС — Федеральной информационной адресной системе. Подробно о нем мы уже писали на «Хабре», а сейчас важны четыре факта:

  • в ФИАС хранятся все адресные объекты страны, от регионов и ниже — до зданий и дополнительных территорий;
  • справочник свободно доступен в форматах DBF и XML;
  • ФИАС не идеален — в нем отсутствуют десятки тысяч домов и множество улиц, особенно новых;
  • каждому адресному объекту в справочнике соответствует уникальный ID — ФИАС-код. Код объекта иногда меняют, но для нашей статьи это не так важно.

Адресные объекты, загруженные из ФИАС вместе с ID, — основа нашего справочника для обратного геокодирования.

Координаты загружаем из OpenStreetMap (OSM). OSM — проект со свободной лицензией: энтузиасты собирают координаты всевозможных объектов и выкладывают для всех желающих.

У каждого объекта свое описание, тип и набор координат. Если по-простому, OSM — это набор точек, линий и полигонов на карте. Данные OSM для России лежат по адресам needgeo.com, osm.sbin.ru/osm_dump/ и osmosis.svimik.com/latest/.


Список источников публикуют на специальной странице в «Вики» проекта

Превратить PBF в OSM XML ничего не стоит, с этим справится куча одобренных сообществом утилит. Выгрузки состоят из PBF-файлов — этот формат используют вместо XML как более компактный.

Если нашли, сохраняем объединенные данные. Для собственного справочника мы берем адресные объекты из ФИАС, а затем ищем их координаты в OSM. Получается такое пересечение ФИАС и OSM.

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

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

Задача настолько объемная, что я отвел под нее в статье отдельный раздел. Основная работа, и с большим отрывом — собрать из OSM адекватные данные и отсеять брак.

Это даже не проблема, а просто реальность, фон. Дома, которых нет в ФИАС, загружаем тоже из OSM. Выше я уже говорил, что в ФИАС отсутствуют десятки тысяч домов. Но только теми, для которых в ФИАС существует улица. Поэтому мы пополняем свой справочник домами из OSM. У пришедших из OSM зданий нет ФИАС ID, поэтому мы идентифицируем их как ФИАС-код родителя + номер дома.

За наводку спасибо сведущему индийцу, написавшему пост Indexing Geographical Data With Lucene (хорошее дополнение — материал A dive into spatial search algorithms — о k-d-деревьях, на которых построен алгоритм поиска). По справочнику ищем с помощью прекрасного Lucene — нашего многолетнего помощника.

Делов осталось — пройтись наждачкой. Как только мы узнали об у́дали Lucene, проблема с поиском решилась почти сама.

  1. Загрузили в Lucene свой справочник координат и адресов, получили поисковый индекс. Для легкости убрали из него почти все, оставив лишь ID адресов и координаты.
  2. Наладили поиск по индексу: на вход — координаты, на выход — ID найденных адресных объектов. Другой информации поиск не возвращает, поскольку индекс мы донельзя скукожили.
  3. Насытили выдачу, загружая из «большого» ФИАС данные по найденным ID. Добавляем много всего, от нужного всем адреса одной строкой до признака столицы региона у городов.
  4. Придумали, как сортировать и отдавать полученные объекты.

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

Как собрали базу координат и адресов

Для начала выложу багаж: прочитав статью, быстро сделать подобный справочник не получится. Мы собираем его с 2014 года, постоянно дополняя. Об этом чертовски длинном пути я и расскажу.

На старте мы выверяли их как могли, в том числе руками. Самое сложное при составлении справочника — перебрать кординаты, которые пришли из OSM. Теперь, когда таких точек много, проверять новые данные вручную почти не приходится. Главная цель тогда — получить опорные точки в крупных городах и сделать из них эталонный справочник. За раз мы добавляем в эталонный справочник 200 000–300 000 адресов с координатами, и вот как это делаем.

Формируем из OSM-тегов полные адреса́. В OSM-выгрузках составные части адресов разбросаны по разным тегам:

  • addr:city — д. Булатниково;
  • addr:street — Центральная улица;
  • addr:housenumber — 103.

Пробегаем по тегам и собираем из них полный адрес: д. Булатниково, Центральная улица, 103.

Прогоняем каждый новый адрес через API стандартизации «Дадаты». Сервис приводит адреса к единому формату «Как в ФИАС»:

  • исправляет опечатки;
  • расшифровывает сокращения вроде «НиНо» и «Мск»;
  • меняет старые названия на новые;
  • находит по индексу пропущенный в адресе город;
  • определяет ФИАС-код.

Адреса́ приходят от API чистыми, хоть сейчас шли письмо или бандероль.
Стандартизованные дома, улицы и населенные пункты мы храним как одну точку. Для улицы и населенного пункта эта точка — центр. В итоге все адресные объекты лежат в одной таблице, внутри — адрес, ФИАС ID, широта и долгота.
Разбираем адреса, которые «Дадата» не стандартизовала. Адреса́, которые не получилось сопоставить с ФИАС, сервис помечает флажком. Их проверяем вручную, вариантов здесь несколько.

  1. Адрес пришел не в положенных тегах выгрузки OSM, а черт знает где. Встречали и вовсе не заполненные адресные теги, и город в теге улицы, и еще много всего.
  2. В OSM лежит экзотический объект вроде детской площадки, вузовского футбольного поля или вовсе кладбища. В ФИАС ничего такого нет, да и для наших целей эти результаты не годятся. Такие объекты просто отсеиваем.
  3. Ошибка — и не ошибка вовсе. Например, из OSM пришел район города, которого в ФИАС нет. Или в ОSМ объект находится в населенном пункте, а в ФИАС этот населенный пункт присоединили к городу и удалили. Тогда мы допиливаем алгоритм под загруженные данные и запускаем снова.


Распарсили выгрузку, а там — путаница в тегах

Если адрес сообщает, что объект находится в Омской области, будь добр по координатам попадать в ее полигон. Проверяем, насколько адекватны загруженные координаты. Для этого специальной утилитой смотрим, попадают ли координаты нового объекта в полигон родительского региона или района. Вхождения в город не требуем — не все города точно освещены в OSM, для многих данные не обновляют.

Чтобы выбрать, к какому полигону примерить точку, смотрим в отдельную таблицу. Эталонные полигоны мы загружаем из OSM и храним как есть — в формате GeoJSON. В ней мы сопоставили префиксы КЛАДР-кодов и ID полигонов: находишь для адреса КЛАДР-код и видишь, какой полигон выбрать.

Находить этот код для адреса умеет миллион сервисов
КЛАДР-код — это уникальный идентификатор, который использовали еще до появления ФИАС.

Это правило добавили из-за шоссе, которые часто выходят за границы региона. Утилита разрешает объекту отстоять от положенного полигона на 1 700 метров. Но расстояние больше 1 700 метров — признак ошибки, так говорит статистика.

На этом для городов и улиц проверка заканчивается.

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

  1. Берет адрес нового дома и находит для него соседей в эталонном справочнике.
  2. По координатам считает расстояние между непроверенным новым домом и надежными соседними.


Соседей найти несложно: 1. Берем новый дом и находим ФИАС ID родителя. 2. Выбираем из эталонного справочника дома́, у родителей которых тот же ФИАС ID

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

В новых данных пришли дома № 5, 6 и 7 по той же улице. Допустим, в эталонном справочнике хранятся дома № 1, 2 и 3 по улице Коммунаров. Утилита видит, что дом № 5 находится рядом с домами № 1, 2 и 3 и добавляет его в эталонный справочник. Судя по координатам, новые дома стоят рядом. Значит, дома № 6 и 7 тоже проходят проверку.

А дальше решается судьба пришедших из OSM данных:

  • дома, прошедшие обе проверки: на полигоны и на соседей, добавляем в эталонный справочник;
  • если объект не попадает в полигон, новые данные не подходят. То же самое, если расстояние между домом и соседями слишком велико;
  • дома, у которых нет соседей, мы пока откладываем. Они лежат в отдельной базе, когда-нибудь разберем.

Делим прошедшие проверку объекты на две части. Они пойдут в разные таблички нашего эталонного справочника.

Во второй — дома́ и ссылка на родителя из первой таблицы
В первой таблице — все объекты c ФИАС ID до домов: регионы, населенные пункты, улицы.

У них нет собственного ФИАС-кода, поэтому делаем вот как: Две таблицы нужны, чтобы назначить ключи отсутствующим в ФИАС домам.

  • в одну таблицу собираем адресные объекты до дома, у каждого из них свой ФИАС-код;
  • во вторую — только дома́, при этом ссылаясь на родителя в первой таблице.

В итоге здания без ФИАС-кода мы идентифицируем по ключу ФИАС ID родителя + номер дома.

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

Главное после обновления — чтобы не стало хуже.

Обратное геокодирование глазами пользователя

На вход метод принимает три параметра: координаты, количество результатов и радиус поиска. Радиус по умолчанию — 100 метров, максимальный — километр. Точное значение задают в настройках.

curl -X POST \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -H "Authorization: Token $" \ -d '{ "lat": 55.878, "lon": 37.653, "radius_meters": 50 }' \ https://suggestions.dadata.ru/suggestions/api/4_1/rs/geolocate/address

Обратно метод возвращает найденные объекты: дома, улицы и населенные пункты. При этом сортирует их по убыванию точности.

  1. Дома́.
  2. Улицы.
  3. Населенные пункты.
  4. Города́.

Затем сортирует еще раз — по расстоянию от заданных координат. Если метод нашел четыре дома и улицу, сначала встанут дома́ в порядке удаленности от заданной точки. За ними — улица.

После всех этих рокировок метод наконец-то возвращает объекты, которые нашел.

{ "suggestions": [ { "value": "г Москва, ул Сухонская, д 11", "unrestricted_value": "г Москва, ул Сухонская, д 11", "data": {...} }, { "value": "г Москва, ул Сухонская, д 11А", "unrestricted_value": "г Москва, ул Сухонская, д 11А", "data": {...} } ]
}

Внутри — много разного о найденных объектах: строки с полным и сокращенным адресом, актуальное и устаревшие названия, почтовый индекс, ФИАС-код родительского объекта и так далее.


Все данные, которые отдает метод — в документации

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

  • Москва — 96%,
  • Санкт-Петербург — 88%,
  • другие города-миллионники — 74%,
  • остальная Россия — 47%.

А вот это — покрытие по улицам:

  • Москва — 92%,
  • Санкт-Петербург — 79%,
  • другие города-миллионники — 75%,
  • остальная Россия — 67%.

По городам не считали — в масштабах России зыбок даже сам факт принадлежности к гордому званию города. Например, Ярославская обл, Пошехонский р-н, с/о Федорковский — это город, согласно официальному справочнику ФИАС. А по факту, да и по адресу — сельский округ. Физически сельский округ напоминает объединение нескольких деревень в большую кляксу. Сложно не только определить центр, но даже найти населенный пункт на карте.

Следим за спросом и решаем, вкладывать ли силы. Уже думаем, что добавить к методу: разрешить фильтрацию по типам объектов, возвращать расстояния до заданной точки, еще что-нибудь.

До 10 000 запросов в сутки — бесплатно, больше — по подписке от 5 000 ₽ в год. А в остальном все уже на проде. Если нужны адреса по координатам для коммерческого проекта, а «Геокодер» слишком дорог — попробуйте API «Дадаты».

Оригинал статьи опубликован в блоге HFLabs.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть