ФИАС: различия между версиями

Материал из GIS-Lab
Перейти к навигации Перейти к поиску
Строка 72: Строка 72:
</syntaxhighlight>
</syntaxhighlight>


}}
}}Схема подходит для всех таблиц ФИАС. Для работы требуется процессор, поддерживающий XSLT 2.0. Например, [http://sourceforge.net/projects/saxon/ Saxon XSLT Processor] (MPL). Тогда обработка файлов принимает вид (версии .NET, Windows):
 
Схема подходит для всех таблиц ФИАС. Для работы требуется процессор, поддерживающий XSLT 2.0. Например, [http://sourceforge.net/projects/saxon/ Saxon XSLT Processor] (MPL). Тогда обработка файлов принимает вид (версии .NET, Windows):


{{Скрытый
{{Скрытый
Строка 96: Строка 94:
</syntaxhighlight>
</syntaxhighlight>


}}
}}Далее, полученные файлы SQL желательно откорректировать, чтобы явно назначить ключи (primary keys).
 
Далее полученные файлы SQL желательно откорректировать, чтобы явно назначить ключи (primary keys).


//// Ссылка на готовые файлы
//// Ссылка на готовые файлы

Версия от 16:04, 2 февраля 2013

Эта страница является черновиком статьи.


Введение

Вопросы/пожелания/замечания - http://gis-lab.info/forum/viewtopic.php?f=3&t=12568

Федеральная информационная адресная система (ФИАС) создана Распоряжением Правительства Российской Федерации от 10.06.2011 №1011-р. С целью...

Статья "КЛАДР: Настоящее, прошлое и будущее" (примерно 2004 г.)

Замена КЛАДР... Тем не менее классификатор КЛАДР продолжает публиковаться и обновляться - версия 4.0 от 24.12.2012.

С момента первой публикации структура и содержание ФИАС многократно критиковались. Тем не менее, за время существования базы, она объективно улучшается и часть ошибок уже устранена. Поэтому указанные в этом документе несоответствия могут быть исправлены в будущем и их следует уточнять на свежих данных.

//ToDo - понятие "адрес" из докум. "Перечень типовых вопросов и предложений по использованию ПО «ФИАС»" // Секретность

Загрузка данных

Процесс импорта данных из файлов XML в реляционную базу данных дается на примере PostgreSQL. Все применяемые инструменты являются кросплатформенными. Для других БД (MySQL, Oracle и т.п.) процедура потребует незначительной доработки. См. также гл. 2.3, в которой приводятся ссылки на сторонние проекты, предоставляющие подготовленные данные в других форматах.

Создание таблиц

На сайте ФИАС представлены схемы XSD, описывающие структуру данных. Для преобразования схемы в формат SQL (CREATE TABLE...) применим XSL Transformation (XSLT). В зависимости от БД может потребоваться изменить типы данных колонок.

Схема подходит для всех таблиц ФИАС. Для работы требуется процессор, поддерживающий XSLT 2.0. Например, Saxon XSLT Processor (MPL). Тогда обработка файлов принимает вид (версии .NET, Windows):

Далее, полученные файлы SQL желательно откорректировать, чтобы явно назначить ключи (primary keys).

//// Ссылка на готовые файлы

Импорт данных

Для работы с большими файлами XML предпочтительнее применять потоковые парсеры: используется фиксированное количество оперативной памяти (в приведенном скрипте - порядка 80 Мб) на протяжении всего процесса работы.

Ниже приводится скрипт ETL Scriptella (Apache License) на базе парсера SAX. Он подходит для любой реляционной БД, для которой есть java-драйвер.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE etl SYSTEM "http://scriptella.javaforge.com/dtd/etl.dtd">
<etl>
    <connection id="in" driver="janino"/>
    <connection id="out" driver="postgresql" url="jdbc:postgresql://localhost:5432/address" user="postgres" password="secret">
        autocommit.size=100
    </connection>

    <query connection-id="in">
        import javax.xml.parsers.SAXParser;
        import javax.xml.parsers.SAXParserFactory;
        import org.xml.sax.Attributes;
        import org.xml.sax.SAXException;
        import org.xml.sax.helpers.DefaultHandler;

        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser saxParser = factory.newSAXParser();

        DefaultHandler handler = new DefaultHandler() {
            boolean isObject = false;
            int recordCnt = 0;

            public void startElement(String uri, String localName,String qName, Attributes attr) throws SAXException {
                if (qName.equalsIgnoreCase("Object")) {
                    String names = "";
                    String values = "";
                    
                    int attrCnt = attr.getLength();
                    for (int i=0; i &lt; attrCnt; i++) {
                        names += attr.getQName(i) + ",";
                        values += "'" + attr.getValue(i).replaceAll("'", "''") + "',";
                    }

                    names = names.substring(0, names.length()-1);
                    values = values.substring(0, values.length()-1);
                    set("sql_names", names);
                    set("sql_values", values);
                    <script connection-id="out">
                        INSERT INTO ###TABLE### ($sql_names) VALUES ($sql_values);
                    </script>
                    next();

                    recordCnt++;
                    System.out.print("\rprocessed: " + recordCnt);
                }
            }
        };

        System.out.println("\n\n\nConverting...");
        saxParser.parse("../xml/###TABLE###.xml", handler);
    </query>
</etl>

Примечания:

  1. Паттерн ###TABLE### следует заменить на имя файла/таблицы (например, ADDROBJ);
  2. Да работы требуются дополнительные библиотеки: Janino, PostgreSQL JDBC Driver;
  3. Выполняется простейшее экранирование значений (одиночная кавычка ').

Другие источники

Данные ФИАС также распространяются в формате DBF. Для работы с ними рекомендуются утилиты типа PgDBF (импорт ФИАС).

  • BasicData.ru - WebAPI и файлы для импорта в БД MySQL. Также может быть интересна БД "Почтовые отделения".
  • Классификаторы России (rus-ref)
  • zXML Parser - "Парсинг баз ФИАС в MySQL (потоковый XML парсер)". Программа из данных XML создает файл с sql-запросами.

Структура данных ФИАС (описание таблиц)

Для понимания структуры данных ФИАС требуются следующие документы:

  1. Сведения о составе информации Федеральной информационной адресной системы (DOC);
  2. Описание Классификатора адресов российской федерации (КЛАДР) (в архиве DOCUM.ARJ)

Таблицы

Основные

  • ADDROBJ - Классификатор адресообразующих элементов (край > область > город > район > улица)
  • HOUSE - Сведения по номерам домов улиц городов и населенных пунктов, номера земельных участков и т.п
  • HOUSEINT - Интервалы домов
  • LANDMARK - Описание мест расположения имущественных объектов
  • NORMDOC - Сведения по нормативному документу, являющемуся основанием присвоения адресному элементу наименования

Справочники

  • SOCRBASE - Типы адресных объектов (условные сокращения и уровни подчинения)
  • CURENTST - Статус актуальности КЛАДР 4.0
  • ACTSTAT - Статус актуальности ФИАС
  • OPERSTAT - Статус действия
  • CENTERST - Статус центра
  • INTVSTAT - Статус интервала домов
  • HSTSTAT - Статус состояния объектов недвижимости
  • ESTSTAT - Признак владения
  • STRSTAT - Признак строения
  • NDOCTYPE - Тип нормативного документа (закон, приказ, справка) // не упоминается в официальной документации

ADDROBJ

//ToDo:

  • текстовые элементы адреса
  • иерархия
  • статус актуальности
  • исторические названия
  • ссылки, классификаторы

Текстовые элементы адреса

//ToDo: буква Ё

Иерархия административных единиц

В таблице ADDROBJ иерархия построена по типу плоского дерева. И родительские и дочерние элементы хранятся в одной таблице. Воссоздание иерархии выполняется с помощью полей:

  • AOGUID - Глобальный уникальный идентификатор адресного объекта. Не смотря на название, уникальным в пределах таблицы он не является. Могут существовать несколько исторический версий и одна единственная актуальная для данного объекта. Подробнее см. раздел "Статус актуальности".
  • PARENTGUID - Идентификатор объекта родительского объекта. Содержит ссылку на AOGUID родительского элемента.
  • AOLEVEL - Уровень адресного объекта. Всего существует 8 основных уровней и несколько дополнительных (см. табл. SOCRBASE):
    1 - край/область
    4 - город/поселок
    6 - микрорайон / нас. пункт в составе муниципального образования
    7 - улица/квартал
    8 - дом [Прим. - записей этого уровня в таблице нет]
    //ToDo - исправить по документу "Перечень типовых вопросов и предложений по использованию ПО «ФИАС»"

Получение полного адреса (от младшего к старшему):

WITH RECURSIVE child_to_parents AS (
  SELECT addrobj.* FROM addrobj
      WHERE aoid = '51f21baa-c804-4737-9d5f-9da7a3bb1598'
  UNION ALL
  SELECT addrobj.* FROM addrobj, child_to_parents
      WHERE addrobj.aoguid = child_to_parents.parentguid
        AND addrobj.currstatus = 0
)
SELECT * FROM child_to_parents ORDER BY aolevel;

AOLEVEL отражает административно-правовое подчинение, поэтому одни и те же текстовые элементы адреса могут располагаться на разных уровнях. Например, в снт. "Волжанка" (AOLEVEL=90) улицы имеют уровень 91 (против более распространенного 7):

(1) обл. Самарская
(3) р-н Сызранский
(90) снт Волжанка
(91) ул. Ягодная
(91) ул. Дачная
(91) ул. Рябиновая

Аналогично, существуют населенные пункты без улиц. Например, в пос. Лужки иерархия заканчивается на уровне 6:

(1) обл. Орловская
(3) р-н Мценский
(6) п. Лужки

Таким образом, построение таблицы полных адресов следует начинать от верхних элементов (AOLEVEL=1) к нижним (AOLEVEL=91), как правило, запрос оформляется в виде множества подзапросов (subquery). Или рекурсивно подниматься от нижних, не имеющих дочерних элементов. Рассмотрим для примера второй вариант. Поскольку число полей в обоих случаях не определено, то полный адрес будет формироваться единой строкой:

-- функция получения полного адреса (аналогично предыдущему примеру)
-- DROP FUNCTION IF EXISTS make_full(text);
CREATE FUNCTION make_full(IN id text, OUT fulladdress text) AS $$
  -- "id" is an unique identificator (use AOID)
  WITH RECURSIVE to_parents AS (
    SELECT format('<addr type="%s">%s</addr>', shortname, formalname) AS fulladdress,
           parentguid, aolevel
      FROM addrobj
     WHERE aoid = id
    UNION ALL
    SELECT format('<addr type="%s">%s</addr> %s',
                  addrobj.shortname, addrobj.formalname, to_parents.fulladdress
           ) AS fulladdress,
           addrobj.parentguid, addrobj.aolevel
      FROM addrobj, to_parents
     WHERE addrobj.aoguid = to_parents.parentguid
       AND addrobj.currstatus = 0
  )
  SELECT fulladdress FROM to_parents WHERE aolevel = 1
$$ LANGUAGE SQL;

-- сбор всех parentguid
WITH all_parents AS (
  SELECT DISTINCT parentguid
    FROM addrobj
   WHERE (currstatus = 0) AND (parentguid IS NOT NULL)
)
-- выбор элементов самого нижнего уровня (не входящих в список all_parents)
-- и получение полного адреса
SELECT make_full(aoid)
  FROM addrobj
 WHERE (currstatus = 0) AND (aoguid NOT IN (SELECT parentguid FROM all_parents))
 LIMIT 100

Образцы результатов:

<addr type="обл">Волгоградская</addr> <addr type="г">Волгоград</addr> <addr type="ул">Лавровая</addr>
<addr type="обл">Орловская</addr> <addr type="р-н">Мценский</addr> <addr type="п">Лужки</addr>
<addr type="обл">Челябинская</addr> <addr type="г">Челябинск</addr> <addr type="ул">Балтийская</addr>
<addr type="край">Приморский</addr> <addr type="г">Артем</addr> <addr type="ул">Григорьева</addr>
<addr type="обл">Калужская</addr> <addr type="р-н">Ульяновский</addr> <addr type="д">Бродок</addr>
<addr type="обл">Свердловская</addr> <addr type="г">Екатеринбург</addr> <addr type="ул">Окраинная</addr>
<addr type="Респ">Тыва</addr> <addr type="р-н">Чаа-Хольский</addr> <addr type="м">Сесеге</addr>

Примечания:

  1. Ранее [дата] все ГСК и СНТ вносились базу ФИАС на уровень 7 (улицы). Позднее они были перенесены на уровень 90 (дополнительная территории).

Статус актуальности

В базе ФИАС никогда не удаляются элементы. Они могут быть только переведены в разряд "отключенных" (устаревшие, измененные и т.п.). Поведение аналогично работе КЛАДР. Рассмотрим поля, определяющие действительность объекта:

Для выбора актуальных записей рекомендуется ориентироваться на поля CURRSTATUS и LIVESTATUS.

Рассмотрим подробнее:

  • LIVESTATUS - Признак действующего адресного объекта. Принимает значения: 0 - действующий, 1 - не действующий (см. прим. 1).
  • CURRSTATUS - Статус актуальности КЛАДР 4. Принимает значения: 0 - актуальный, 1-50 - исторический, 51 - переподчиненный (см. табл. CURENTST и гл. 1.2. "Коды адресных объектов" документации КЛАДР).
  • OPERSTATUS - Статус действия. Принимает значения: 1 - Инициация, 10 - Добавление, 20 - Изменение и др (см. табл. OPERSTAT).
  • ACTSTATUS - Статус актуальности адресного объекта ФИАС. Принимает значения: 1 - актуальный, 0 – не актуальный (см. табл. ACTSTAT). Отвечает непосредственно за актуальность "имени". Если объект был переименован (чаще это исправление опечаток), то старая запись получает CURRSTATUS=1 и ACTSTATUS=0. Если же административная единица была ликвидирована или переподчинена, то имя останется по-прежнему актуальным: CURRSTATUS=99/51 и ACTSTATUS=1. В тоже время, при внесении изменений, не касающихся непосредственно адресной части, признак актуальности все равно сбрасывается (ACTSTATUS=0).

Также, в категорию актуальности можно отнести поля STARTDATE, ENDDATE, UPDATEDATE. Но, на данный момент (декабрь 2012 г.) значения этих полей редко бывают заполнены правильно.

Покажем на примере выбор актуальных и исторический записей. "Пермский край" был образован 01.12.2005 объединением "Пермской области" и "Коми-Пермяцкого АО". В базе ФИАС это отразилось следующим образом (см. табл. 1):

  1. Создана новая запись "Пермский край" (1-й столбец);
  2. Запись "Пермская область" (2-й столбец) сохранена, причем AOGUID обеих записей идентичен (для связи с дочерними объектами);
  3. Запись "Коми-Пермяцкий АО" (3-й столбец) также сохранена.

Таблица 1

Примечание Актуальная запись Пермская обл. Коми-Пермяцкий АО
AOID c685f6ca-d7e1-4938-8b21-7c20035652d2 744e2599-d2bb-4364-9922-c13febb16e81 68824dc3-da0f-42d3-bd0b-1ceb91ff27bf
AOGUID 4f8b1a21-e4bb-422f-9087-d3cbf4bebc14 4f8b1a21-e4bb-422f-9087-d3cbf4bebc14 e3d95b95-cc2d-440d-95c6-65577fae076e
AOLEVEL 1 1 1
PREVID 744e2599-d2bb-4364-9922-c13febb16e81 68824dc3-da0f-42d3-bd0b-1ceb91ff27bf
NEXTID c685f6ca-d7e1-4938-8b21-7c20035652d2 744e2599-d2bb-4364-9922-c13febb16e81
ACTSTATUS 1 0 1
OPERSTATUS 1 1 1
CURRSTATUS 0 1 51
LIVESTATUS 1 0 0
UPDATEDATE 2011-09-13 2011-09-13 2011-09-13
STARTDATE 1900-01-01 1900-01-01 1900-01-01
ENDDATE 2079-06-06 2079-06-06 2079-06-06
FORMALNAME Пермский Пермская Коми-Пермяцкий
OFFNAME Пермский Пермская Коми-Пермяцкий
SHORTNAME край обл АО

Значения полей:

  1. ACTSTATUS для Коми-Пермяцкого АО сохранен в значении "Актуальный";
  2. OPERSTATUS не изменился - ошибочно(?);
  3. CURRSTATUS правильный (0 - актуальный, 1 - исторический, 51 - переподчиненный);
  4. LIVESTATUS показывает единственную актуальную запись, но значения инвертированы (см. прим. 1).

//ToDo - изменения в подчиненных объектах

Примечания:

  1. Все актуальные записи (CURRSTATUS = 0) имеют значение (LIVESTATUS = 1) и наоборот. Очевидно, что поведение LIVESTATUS не соответствует описанному в документации. Поле можно использовать для выбора актуальных записей, но с осторожностью до выяснения ситуации в будущем.

Исторические названия

Поля AOID, PREVID, NEXTID в совокупности составляют цепочку от современного к устаревшим наименованиям объекта. Хотя, если имеется два прямых предка (как в примере про "Пермский край"), то наследование невозможно отразить полностью однозначно.

В общем виде получение исторических вариантов наименований объекта имеет вид:

WITH RECURSIVE old_names AS (
    SELECT *
        FROM addrobj
        -- вариации отдельной записи:
        WHERE aoid = '002ff6b9-c2db-46e5-99da-8cf30d239b27'
        -- полный список:
        -- WHERE currstatus = 0 AND previd IS NOT NULL
    UNION
    SELECT addrobj.*
        FROM old_names, addrobj
        WHERE (addrobj.nextid = old_names.aoid)
)
SELECT * FROM old_names

Примеры:

shortname formalname actstatus operstatus currstatus livestatus updatedate startdate enddate
д Малое Пермиево 1 21 0 1 2012-09-08 2012-02-01 2079-06-06
д Малое Пермиево 0 1 2 0 2011-09-14 1900-01-01 2012-02-01
с Малое Пермиево 0 1 1 0 2011-09-14 1900-01-01 2079-06-06
...
ул Подстанция 220 1 20 0 1 2012-03-12 1911-11-11 2079-06-06
ул Подстанция (Комиссарово) 0 1 1 0 2011-09-15 1900-01-01 1911-11-11
...
пер Рябиновый 1 20 0 1 2011-09-27 1900-01-01 2079-06-06
ул Рябиновая 0 1 1 0 2011-09-15 1900-01-01 2079-06-06
...
снт Восход(п.Селезнево) 1 1 0 1 2011-09-14 1900-01-01 2079-06-06
дп СТ Восход(п.Селезнево) 0 1 1 0 2011-09-14 1900-01-01 2079-06-06
...
ул Самбуева В.М. 1 1 0 1 2011-09-14 1900-01-01 2079-06-06
ул Ф.Энгельса 0 1 1 0 2011-09-14 1900-01-01 2079-06-06
...
проезд 3-й Лабинский 1 1 0 1 2011-09-14 1900-01-01 2079-06-06
ул Лабинская 2-я 0 1 1 0 2011-09-14 1900-01-01 2079-06-06

// ToDo:

Примечания:

  1. Наблюдение (в документации не разъяснено): при внесении изменений, предыдущая историческая запись получает значение (CURRSTATUS = 1). Если вносится повторное изменение, то аннулируемая запись принимает значение (CURRSTATUS = 2). Таким образом, цепочка от современного названия к самому старому принимает вид: актуальное (0), предыдущее (3), более раннее (2), самое старое (1).