Ariadna. Зачем нужен еще один геокодер для ОСМ?

    Всем привет!

    Совсем недавно я закончил делать геокодер для своих целей Ariadna
    Под катом рассказ о том, зачем я его делал и что он умеет.

    В статье не будет ни строчки кода на Go. Зато будет полное описание работы геокодера и проблем, с которыми я встречался. А код можете посмотреть на гитхабе.

    Предыстория


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

    Чего у нас нет:

    • Полной карты Яндекса, Гугла или 2Гис
    • Доверия к GPS данным

    Что у нас есть:

    • Очень разношерстные данные на входе
    • Openstreetmap
    • Своя накопленная база адресов с координатами

    Что пользователи могут вводить?


    Пользователи могут вводить адреса в разных форматах:

    • Улица дом
    • Перекресток
    • Название заведения
    • Название точки
    • микрорайон дом
    • микрорайон улица дом

    И таких вариантов очень много, например
    Киевская 28
    Киевская Советская
    5-42
    5 микрорайон советская 42
    ЦУМ
    кафе у Ашота
    шлагбаум
    И так далее

    Постановка задачи


    Сделать геокодер, который бы умел принимать любые данные на вход и отдавать им координаты
    Язык поиска — только русский.

    Как докатился до такой жизни


    Перед тем, как делать свой велосипед я решил поресерчить решения, которые уже есть.
    Были:


    Чем не устраивал номинатим, который у нас был:

    • Сложен сам по себе
    • Сделан на php+с(Это не потому что пхп плохой, а потому что только для этого инструмента у нас стоит апач и пхп)
    • Сложная логика в хранимых процедурах Postgresql

    Чем понравился Pelias:

    • Может работать с многими источниками геоданных
    • Поиск организован на ElasticSearch

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

    1. Я хочу разобраться с данными в OSM и импортить только нужные для поиска
    2. Я могу обрабатывать геоданные перед занесением в индекс
    3. Мне не нравится жаваскрипт и node.js, отсюда и не желание делать поиск на основе пелиаса

    Проектирование


    Был заложен следующий алгоритм:

    1. Сначала получаем геометрию по крупным населенным пунктам(города, столицы, деревни, жилые массивы)
    2. Выгружаем все возможные адреса и соотносим их к нужному жилому массиву, городу и другому населенному пункту, выставляя нужное значение
    3. Выгружаем все дороги
    4. Ищем пересечение дорог
    5. Кладем все в индекс
    6. Ищем

    Для реализации я выбрал Go, учитывая проекты типа pbf2json, golang-geo и многих других для обработки геоданных. Также хотелось покачать скилл именно в нем.

    Реализация


    С получением и парсингом данных с осм вроде разобрался. Для жилых массивов используем теги place=city,place=village,place=suburb,place=town,place=neighbourhood для фильтрации. Для получения адресов, зданий addr:street+addr:housenumber,amenity,shop,addr:housenumber
    Все дороги можно получить с помощью тега highway

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

    1. Простая автоматическая транслитерация в русский. В итоге получалось абсурдной и не корректной. Пример конвертации данных был таким: City House -> Цити Хоусе
    2. Давай попробуем преобразовывать так. Получать транскрипцию слова и ее уже транслитерировать. Получилось что-то вроде Adrenaline rush -> Эрденалин Рэш. Сносно, но нужен русский акцент, типа адреналин раш.
    3. Подошел такой механизм. Автоматически транслитеризуем все данные, применяя словарь замен. Все-таки простая и тупая транслитерация работает сносно. Словарь наполнился в принципе быстро через несколько прогонов на данных.

    С этим разобрались к этому моменту мы уже получаем данные, которые:

    1. Нормализированы и приведены к русскому языку
    2. адреса приведены к формату Страна, город, село или поселок, микрорайон или жилой массив, улица, дом

    Следующая часть квеста — найти пересечения дорог. Сделал ее по быстрому и получил очень медленную реализацию, сложностью O(n^2). Как временный выход использую Postgres+postgis для нахождения пересечений, пока не нашел хорошего алгоритма для поиска пересечений.

    В итоге получился хороший парсер данных с осм, который кладет данные в ElasticSearch. Который получил простое название importer

    Автоматизируй это


    Учитывая то, что постоянно выкачивать и создавать индексы в эластиксерче в скоре надоело, появился компонент updater. Появилась также автоматическая конфигурация в JSON формате.

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

    Как это работает:

    1. Updater качает файл
    2. Узнает текущую версию индекса с конфига
    3. Инкрементит версию и создает новый индекс
    4. Заполняет его данными
    5. Меняет алиасы
    6. Удаляет старый индекс

    Получил такие бенефиты от этого:

    1. Пишем конфиг
    2. Запускаем ./ariadna update
    3. Идем пить кофе
    4. Получаем готовый настроенный индекс.

    Также для удобства прикрутил простой вебинтерфейс с картой и возможностью поиска.

    Автоматическое пополнение данными


    Помимо ОСМ у нас еще есть много водителей и операторы, которые забивают заказы.
    Соотвественно у нас есть имя и координаты
    Сделана такая схема:

    1. Треки водителей хранятся в индексе drivers_data
    2. Данные с ОСМ хранятся в индексе osm_data
    3. Объединены они через алиас addresses по которому и происходит поиск адресов

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

    Итого


    Получился геокодер который умеет:

    1. Искать координаты по синонимам. например ШВК — ШампанВинКомбинат
    2. Умеет искать адреса в определенном радиусе (например для себя с сделал поиск адресов в 30 км от центра города)
    3. Искать по названию заведений (кафе у Ашота например)
    4. Искать перекрестки
    5. Искать адреса в микрорайонах и жил массивах
    6. Делать реверс геокодинг
    7. Автоматически пополнятся новыми данными от водителей

    Состоит из трех компонентов:

    1. Импортер данных
    2. Апдейтер данных
    3. Веб интерфейс

    Минусы


    1. Протестирован только для Кыргызстана
    2. Нет демки
    3. Нет поддержки всех схем адресации

    Поэтому надеюсь кто-нибудь поможет его допилить и для хорошего поиска по другим странам и городам.

    Если кому-то проект показался интересным, то я не против любой критики, пул реквестов, issues на гитхабе и фидбека в целом.
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 14
    • 0
              {"address":
                  {"properties":
                      {
                          "centroid": {
                              "type": "geo_point" # need to reverse geocoding
                          }
      
                      }
                  }
              }
      


      Судя по этому фрагменту конфига для эластика, обратный геокодинг не гарантирует нахождение если визуально попали в здание, алгоритм может посчитать иначе?
      • 0
        Да. Сейчас у меня в апи ищеться по дистанции 20 метров.
        На продакшне же по дистанции 200 метров и отсортированы по возрастанию.
        Проблем с реверсом нет
      • –1
        А ссылку на GitHub дадите?
      • 0
        Данные geofabrik.de для чего-нибудь вроде Кубы пробовали?
        • 0
          Посмотрел Кубу. Ничего хорошего сказать не могу :(
        • +1
          Есть ещё Gazetteer
          • 0
            Когда были проблемы (очень долго) поиска пересечений, разбивал карту на NxN частей, для каждой части считал попадающие куски, после чего на каждой части для кусков искал пересечения.
            • 0
              Gen1us2k, спасибо! можно подробнее о том, как Вы делаете «Автоматически пополнятся новыми данными от водителей»?
              • 0
                Оператор оформляет заказ, мы получаем по нему координаты из ES.
                Водитель когда забирает клиента, шлет свои координаты. Мы смотрим, если расстояние между точкой, откуда забрал водитель и точкой, которая разрезолвилась по поиску больше 200 метров, то кладем данные в рядом стоящий индекс. В дальнейшем может быть будем как-то использовать эти данные для импорта в осм например
                • 0
                  Не нужно импорт. Максимум заметки автоматом ставить.
                  • 0
                    Прям все-все водители с координатами?
                    • 0
                      Есть проверка на нулевые координаты. Импортить данные автоматически в ОСМ будет сложно
                • 0
                  Имел честь испытать потыкать на данных Челябинской области. В поиск поэтому лучше всего было вбивать в начале город, а то непредсказуемо кидало по разным НП. Короче (а долго тыкать не стал) в первую очередь поискал свой дом с дробью. Дробь не ищется, вместо нее следует ставить пробел, и то только в полном формате (см. выше). Худо-бедно получилось, вот только иногда вместо дома с дробью находит например детский сад с таким же номером на той же улице (так совпало), но естественно с другим адресом. Видимо надо приоритеты подкручивать.
                  По словам экспериментатора (Го-адепт все дела) индексация одной области занимает весьма существенное время, так что про целую страну нечего и думать на домашних мощностях. В общем ожидайте от него пуллреквестов и успеха в развитии проекта! Да собственно вы уже в слаке вовсю общаетесь, чего я тут распинаюсь… ))

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.