Часть 1. Трагическая. “За что мне все это?!”


Столкнулись мы как-то с необходимостью ввода корректной информации о местонахождении (прописке, регистрации) пользователей, и с тем, что проблема эта решается не совсем так легко и просто, как бы нам хотелось. Сначала мы попробовали КЛАДР, в его бесплатной ипостаси. Не то чтобы нам прямо-таки решительно все не понравилось, но было как минимум одно весьма раздражающее обстоятельство — некоторые адреса отсутствовали в справочнике. Например, дом 10 есть, а 10к1 — извините, не завезли. Вообще КЛАДР был привлекателен тем, что у него есть простой API и плагины (jQuery в частности), которые можно легко встроить в приложение, но отталкивал наполнением. Мы задумались — если нет такого ресурса, содержащего полную и наиболее актуальную адресную информацию, с API и плагинами, то единственный выход — создать такой ресурс самим.

Часть 2. Искательская. “Где собака зарыта?!”


И прослышали мы про ФИАС. О том как он бесконечно полон и прекрасен. А это выход! На сайте ФИАС есть базы, и все что нужно молодой растущей информационной системе! Правда, размер базы составляет более 4 Gb, ну да ладно, это же вся Россия! Обновляется база регулярно, так что есть где разгуляться. Дело за малым — развернуть базу, прикрутить API и плагины. Надо оговориться, что существует несколько релевантных и важных статей. К примеру, цикл вот этих статей, которые изначально очень помогли.

Часть 3. «Как это работает»


Проблемы начались с того, что база данных ФИАС поставляется в формате .dbf, данные из которого надо было как-то перенести в PostgreSQL, который было решено использовать в качестве БД.

Нужно отметить, что кроме формата .dbf, база представлена в формате xml. Как известно, человеку хочется всего и сразу. Чтобы включил и работает. Но разобраться сразу с тем, что же дает ФИАС довольно проблематично.

Итак, по адресу fias.nalog.ru в разделе обновлений имеется следующее:

  • Информация про обновления: по официальным данным — выходят не менее раза в неделю. Специфика следующая. Иногда выходят битые обновления. Их размер примерно 1,2 Mb и при скачивании дается битый архив, поэтому торопиться настраиваться только на последнее обновление — вызывает вопросы.

  • Обновления в виде fias_delta_. — в форматах .dbf и .xml.
  • Собственно база данных fias_. — в форматах .dbf и .xml.
  • База данных в формате КЛАДР. Наименование файлов “Base.*” arj или 7z Отличается тем, что отсутствуют многие поля (касательно налоговых данных и т.д.), поиск осуществляется по КЛАДРовскому id. Изначально присутствует соблазн использовать именно данный формат, так как он более легок для понимания и последующего внедрения. Но при более внимательном изучении портала, обнаружится, что данная версия будет поддерживаться только до конца 2017 года, а затем будет deprecated.

Примечание: для работы с .dbf файлами необходимо установить стороннее расширение для php_dbase.dll.

Так какую же все таки выбрать версию для скачивания .xml или .dbf? Отличия следующие:

  • xml — каждый файл в архиве — отдельная таблица. В чем состоит проблема. К примеру, размер файла “HOUSEXX” где хранятся дома, составляет более 20 Gb в разархивированном виде. Как его парсить? С одной стороны, если у вас более 30 Gb RAM, то это не составит проблемы. Также, если вы знаток грамотных библиотек, таких как SAX Parser для Java, которые не загружают файл целиком в память, а парсят частями, то может быть что-то и получится.

  • dbf — файлы не только разделяются как таблицы, но и подразделяются по регионам. Окажется более приемлемым вариантом, если вам не нужны адреса необъятной Родины, а только одного региона (как в нашем конкретном случае).

Итак, распаковали файлы. Какая информация нам нужна? Видим следующий набор файлов:

  • ADDROBХХ.DBF, где ХХ — номер региона — содержит данные непосредственно о регионе, автономных округах, городах и прочих населенных пунктах, улицах.
  • HOUSEХХ.DBF, где ХХ — номер региона — содержит информацию о номерах домов.
  • NORDOCХХ.DBF, где ХХ — номер региона — содержит информацию о причинах изменений в различных записях.
  • ROOMХХ.DBF, где ХХ — номер региона — содержит информацию о помещениях.
  • STEADХХ.DBF, где ХХ — номер региона — содержит информацию о земельных участках.
  • SOCRBASE.DBF — содержит информацию о сокращениях.
  • STRSTAT.DBF — содержит информацию о типе строения.

Кроме данных таблиц существует еще ряд других — служебных таблиц, которые содержат информацию о сокращениях в других таблицах.

В большинстве случаев достаточно сформировать адрес вплоть до дома. Хотя, если кому надо, то можно углубиться и дальше.

Таким образом, создадим 2 таблицы в БД postgresql.

Таблица с адресами:

CREATE TABLE addrs
(
    "ACTSTATUS" integer,
    "AOGUID" character varying(36) COLLATE pg_catalog."default",
    "AOID" character varying(36) COLLATE pg_catalog."default",
    "AOLEVEL" integer,
    "AREACODE" integer,
	"AUTOCODE" integer,
    "CENTSTATUS" integer,
    "CITYCODE" integer,
	"CODE" character varying(20) COLLATE pg_catalog."default",
	"CURRSTATUS" integer,
	"ENDDATE" timestamp,
    "FORMALNAME" character varying(120) COLLATE pg_catalog."default",
	"IFNSFL" integer,
	"IFNSUL" integer,
	"NEXTID" character varying(36) COLLATE pg_catalog."default",
    "OFFNAME" character varying(120) COLLATE pg_catalog."default",
	"OKATO" VARCHAR(11),
	"OKTMO" VARCHAR(11),
	"OPERSTATUS" integer,
	"PARENTGUID" character varying(36) COLLATE pg_catalog."default",
	"PLACECODE" integer,
	"PLAINCODE" character varying(20) COLLATE pg_catalog."default",
    "POSTALCODE" integer,
	"PREVID" character varying(36) COLLATE pg_catalog."default",
	"REGIONCODE" integer,
    "SHORTNAME" character varying(15) COLLATE pg_catalog."default",
	"STARTDATE" timestamp,
	"STREETCODE" integer,
	"TERRIFNSFL" integer,
	"TERRIFNSUL" integer,
	"UPDATEDATE" timestamp,
	"CTARCODE" integer,
	"EXTRCODE" integer,
	"SEXTCODE" integer,
	"LIVESTATUS" integer,
    "NORMDOC" character varying(36) COLLATE pg_catalog."default",
	"PLANCODE" integer,
	"CADNUM" integer,
	"DIVTYPE" integer
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

ALTER TABLE address
    OWNER to postgres;

Таблица с номерами домов:

CREATE TABLE hous
(
    "AOGUID" character varying(36) COLLATE pg_catalog."default",
    "BUILDNUM" character varying(10) COLLATE pg_catalog."default",
	"ENDDATE" timestamp,
	"ESTSTATUS" integer,
    "HOUSEGUID" character varying(36) COLLATE pg_catalog."default",
    "HOUSEID" character varying(36) COLLATE pg_catalog."default",
    "HOUSENUM" character varying(15) COLLATE pg_catalog."default",
	"STATSTATUS" integer,
	"IFNSFL" integer,
	"IFNSUL" integer,
	"OKATO" VARCHAR(11),
	"OKTMO" VARCHAR(11),
	"POSTALCODE" integer,
	"STARTDATE" timestamp,
	"STRUCNUM" VARCHAR(15),
	"STRSTATUS" integer,
	"TERRIFNSFL" integer,
	"TERRIFNSUL" integer,
	"UPDATEDATE" timestamp,
	"NORMDOC" character varying(36) COLLATE pg_catalog."default",
	"COUNTER" integer,
	"CADNUM" VARCHAR(50),
	"DIVTYPE" integer
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

Импорт данных осуществляется простым способом. Открываем файлы в Excel и сохраняем их как csv. Дополнительно рекомендуется изменить кодировку, так как в отличии от xml файлов, которые представлены в кодировке utf-8, dbf файлы — в кодировке win-866. Открываем файлы в редакторе (для данной цели подойдет notepad++) и преобразуем в utf-8.

Импорт таблицы с адресами:

COPY addrs FROM 'PathToTheFile\ADDROB01.csv' DELIMITER ';' CSV;

Импорт таблицы с домами:

COPY addrs FROM 'PathToTheFile\HOUSE30.csv' DELIMITER ';' CSV;

Из чего сделана таблица ADDROBXX?

Несмотря на обилие полей, понадобятся только некоторые из них.

  • ACTSTATUS — поле содержит значения 0 и 1. При внимательном просмотре обнаружится, что в таблице множество улиц с одним и тем же названием. Дело в том, что налоговая база содержит историю объектов, т.е. изменения, которые происходили с каким-либо элементов адреса. К примеру, улиц Ленина может быть 10 штук. Из них 9 — будут неактуальными. Они могли быть переименованы или изменены другим образом. ACTSTATUS — 0 — неактуальные адреса, а 1 — будет единственным актуальным.
  • AOGUID — ID записи. Для разных исторических записей будет один и тот же AOGUID. Различаться они будут по полю AOID.
  • AOLEVEL — уровень объекта. Под номером 1 — субъект федерации, 4 — город, 7 — улица и т.д.
  • CENTSTATUS — статус центра. Например, города в областном центре.
  • FORMALNAME — название объекта.
  • PARENTGUID — ID родительского элемента. При поиске города к которому принадлежит улица. PARENTGUID улицы будет совпадать с AOGUID улицы.
  • POSTALCODE — почтовый индекс.
  • REGIONCODE — код субъекта федерации.

Примечание: на fias-nalog.ru можно найти полное описание всех полей.

Важное в таблице HOUSEXX:

  • AOGUID — ID улицы в ADDROBXX, в которой находится дом.
  • HOUSEGUID — ID дома.
  • Сложность заключается в наименовании дома. Оно состоит из 4 полей:
  • BUILDNUM — номер корпуса.
  • HOUSENUM — номер дома.
  • STRUCNUM — номер строения.
  • STRSTATUS — признак строения (от 0 до 4, где 0 — никакого, 1 — строение, 2 — сооружение, 3 — литера).

Поля могут содержать схожую или идентичную информацию и выходить из ситуации приходится экспериментальным путем. В классе QuerryController (ссылка на репозиторий — ниже) есть метод: chooseBuilding, в котором мы попытались решить эту задачу. Может быть у кого-то получится лучше. Запросы к БД находятся там же.

Далее, после того, как база была импортирована в Postgres — мы занялись созданием API и плагина для нашей системы.

Для API, дабы не изгаляться, использовали Laravel. Схема запросов получилась достаточно простой. Иерархия объектов выглядит следующим образом:

  • Субъект федерации
  • Регион субъекта федерации
  • Населенный пункт
  • Улица
  • Дом

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

Схема запросов выглядит просто:


Для последнего пункта формируется список домов с литерами, корпусами, строениями, и прочим.
После всех мытарств сделали плагин на vue.js, для работы со справочником, и его альтернативу на jQuery.

Регион и район можно убрать за ненадобностью, так как они подтягиваются вместе с городами.
При вводе высплывают autocomplete-подсказки, как и в КЛАДР. Правда разница заключается в том что КЛАДР-плагин предназначен для автодополнения, а здесь валидным считается только адрес, выбранный из подсказок.

В папке ASPUDcomponent — находится VueJs компонент для работы с адресной базой.
Исходники доступны в нашем репозитории.

Часть 4. Как это все обновлять?


С обновлениями ситуация следующая: для начала необходимо по протоколу SOAP получить версии обновлений. Посмотреть как это делается можно в классе UpdateController (метод: filesVersions()).

Примечание: версия, которая указана последней в полученном списке — не обязательно совпадает с той, которую можно скачать на главной странице. Но не стоит спешить скачивать только последнюю версию, так как она может оказаться “битой”. Случались и таким прецеденты. Далее скачивается архив с последней версией и распаковывается. Для работы необходимо использование расширения для php (php_rar.dll).

Ну а далее выбирается необходимый файл региона (или при необходимости все файлы) для обновления БД.
Поделиться с друзьями
-->

Комментарии (14)


  1. TimsTims
    16.07.2017 12:54

    Семь бед — один ответ


    1. SDSWanderer
      16.07.2017 15:52

      Костыль и велосипед!


  1. dopusteam
    16.07.2017 13:52
    +11

    CREATE TABLE addrs

    CREATE TABLE hous


    Что мотивирует Вас использовать такие названия таблиц?


  1. SkyCat
    16.07.2017 14:42

    Можно использовать велосипеды из статьи (один метод открытия dbf через Excel чего стоит), а можно поставить это приложение на Python и горя не знать. Оно за вас не только базу будет импортировать, но еще и правильные запросы для поискового движка Sphinx создаст.


  1. netpilgrim
    17.07.2017 02:15

    возможность подать заявку на включение отсутствующего адреса в справочную систему

    Могли бы поподробнее рассказать как реализовали данную возможность?


    Иерархия объектов выглядит следующим образом:
    Субъект федерации
    Регион субъекта федерации
    Населенный пункт
    Улица
    Дом

    Населенный пункт может находится в населенном пункте. Пример: поселок Малино в Зеленограде


    1. hayk
      17.07.2017 04:02

      Населенный пункт может находится в населенном пункте. Пример: поселок Малино в Зеленограде

      А сам Зеленоград находится в Москве. В данном случае Москва — регион, Зеленоград — НП, а поселение Малино — улица.


      1. Borz
        17.07.2017 17:41

        а улица Садовая тогда что? дом?
        http://bit.ly/2uvfseD


        1. hayk
          17.07.2017 18:38

          Выше ошибся, Зеленограда в цепочке нет и НП — это поселение Малино.
          Но в качестве улицы могут быть поселения, санатории, дачные кооперативы и прочие объекты, у которых нет деления на улицы, а только номера домов.


          1. netpilgrim
            17.07.2017 19:25
            +1

            Все же Зеленоград в цепочке должен быть http://www.ifias.ru/fias71.html?i=292dc42c-1a6a-4a27-9241-75753bf86ba2


            1. hayk
              18.07.2017 17:55

              Ну вот НП с кодом 7700000200400 в kladr-api записан как «Малино», а в dadata «г Зеленоград, п Малино».


    1. il_kow
      17.07.2017 09:39

      Алгоритм действий для добавления адреса примерно такой (примеры будут на VueJS, но думаю для других фреймов будет примерно то же самое):
      Посмотреть можно здесь: https://github.com/KOVCHENKO/fias/tree/master/ASPUDcomponent

      Пользователь вводит информацию о своем местоположении: город/поселок/село. Далее у населенного пункта может не оказаться улиц вообще. Или домов на выбранной улице. Появляется возможность открыть модальное окно с вводом адреса. Далее введенный адрес отправляется на модерацию (отправляется по email или сохраняется в дополнительную таблицу БД для проверки).
      Метод здесь:
      https://github.com/KOVCHENKO/fias/blob/master/app/Http/Controllers/ApplicationController.php

      Ну и далее вносится руками в БД. Согласен, что нужно проработать более автоматизированный способ, но пока что руки до этого не доходят.


      1. netpilgrim
        17.07.2017 12:21

        При обновлении ФИАС такие адреса не теряются?


  1. hayk
    17.07.2017 04:03

    А почему бы не использовать готовое решение? Например kladr-api или dadata (я уверен что существуют и другие подобные решения)?


    1. il_kow
      17.07.2017 09:46

      Да. kladr наиболее известная штука и далеко не самая плохая, если требуются к примеру адреса регионального центра, но если дело касается поселков, то все становится сложнее. Не вспомню сейчас точный пример, но сталкивалсь со следующим: при обращении к API КЛАДР для выбора домов улицы, выдаются результаты таблицы HOUSEINT (такая тоже есть в БД ФИАС). Она выдает интервалы домов, которые необходимы для налоговой. В таблице HOUSE с домами есть дома для этой улицы, но получается, что они остаются вне выборки.

      P.S. Кроме того, для проекта требовались другие дополнительные поля в БД.