Separator

 

КЛАДР: Последовательный выбор адреса и почтового индекса

Недавно понадобилось решить интересную задачку, а именно сабж. Надо признать получилось не сразу, первые попытки состряпать выбор адреса провалились на том что в КЛАДР'е не так уж и просто выбрать потомков объекта. Сложность была решена с помощью сложного условия по LIKE шаблону. Звучит наверное не очень хорошо, но я сразу поставил перед собой задачу не перемалывать базу КЛАДР во что-то свое, а оставить оригинальную структуру. Поэтому с кодами работал ища по шаблону. Но все по порядку.


А начинается все с того что скачанные dbf фораты некуда приткнуть, ибо LAMP. И начинается поиск dbf2mysql. Надо сказать я так и не нашёл нормального, либо платный, либо глючит. Поэтому все решил промежуточный формат - csv. Качаем DBF2CSV 5.0 и читаем DBF2CSV.TXT. Если вкратце - нужно в cmd вбить
path/perl4w32.exe path/dbf2csv.pl somefile.dbf
На выходе вас будет ждать somefile.csv который можно легко выгрузить через phpMyAdmin, поменяв в импортере разделитель ячеек с ";" на ",".
Однако конечно перед тем как выгружать данные таблиц, нужно их создать. Мои выглядят так:

CREATE TABLE `doma` (
  `name` varchar(65) NOT NULL,
  `korp` varchar(2) NOT NULL,
  `cut` char(6) NOT NULL,
  `code` char(19) NOT NULL,
  `index` mediumint(6) unsigned NOT NULL,
  `gninmb` smallint(4) unsigned NOT NULL,
  `uno` varchar(4) NOT NULL,
  `ocatd` char(11) NOT NULL,
  KEY `name` (`name`),
  KEY `index` (`index`),
  KEY `code` (`code`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `kladr` (
  `name` varchar(78) NOT NULL,
  `cut` varchar(20) NOT NULL,
  `code` char(13) NOT NULL,
  `index` char(6) NOT NULL,
  `gninmb` char(4) NOT NULL,
  `uno` varchar(4) NOT NULL,
  `ocatd` char(11) NOT NULL,
  `status` char(1) NOT NULL,
  KEY `name` (`name`),
  KEY `code` (`code`),
  KEY `index` (`index`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `socrbase` (
  `level` char(1) NOT NULL,
  `cut` varchar(20) NOT NULL,
  `full` varchar(56) NOT NULL,
  `kod_t_st` smallint(3) unsigned NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `street` (
  `name` varchar(78) NOT NULL,
  `cut` varchar(20) NOT NULL,
  `code` char(17) NOT NULL,
  `index` char(6) NOT NULL,
  `gninmb` char(4) NOT NULL,
  `uno` varchar(4) NOT NULL,
  `ocatd` char(11) NOT NULL,
  KEY `name` (`name`),
  KEY `code` (`code`),
  KEY `cut` (`cut`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

КЛАДР в публичном виде содержит пустую таблицу flat, поэтому я её даже создавать не стал. Ещё есть altnames, но она мне пригодилась пока что, тоже не выгружал.

Теперь когда есть таблицы - выгружаем данные и приступаем к сприпту!

Выбор адреса начинается с выбора объектов первого уровня, у которых код имеет только 2 значащие цифры, а остальное - нули. Соответственно запрос:

SELECT T1.name, T1.code, T1.cut
FROM kladr as T1
WHERE (T1.code LIKE "__00000000000")
ORDER BY T1.name ASC

Полученные объекты группирую по сокращению и вывожу пользователю список сокращений. На первом уровне от всегда такой:

  • Автономный округ
  • Автономная область
  • Республика
  • Чувашия
  • Город
  • Край
  • Область

Когда пользователь выбрал сокращения ему показываются объекты этого типа. Когда пользователь перешёл к следующему объекту мы переходим к следущему уровню.
Что мы имеем на текущий момент? Код объекта первого уровня вида у котого 2 первые цифры значащие, а остальные нули (xx000000000000000). Что делать дальше? Искать потомков!
Первое что приходит в ум это искать все code LIKE xx%, однако это не сработает. Объясняю почему - ответ таблицы будет таков - вот вам все улицы, поселки, города и т.п. Он вернет вообще все. Проблема в том что нужно найти таких потомков у которых ближайший родитель является текущим кодом (xx). Для этого генерится подобный запрос:

SELECT T1.name, T1.code, T1.cut
FROM kladr as T1
WHERE (T1.code != "7100000000000") AND (T1.code LIKE "71___00000000" OR T1.code LIKE "71000___00000" OR T1.code LIKE "71000000___00")
ORDER BY T1.name ASC

Условие T1.code != "7100000000000" - для того чтобы не выбрать себя же. Остальное - о том что я говорил. Рассмотрим подробнее что это значит.
Код в таблице kladr (СС РРР ГГГ ППП АА) состоит из таких частей:
СС - код субъекта Российской Федерации (региона), коды регионов представлены в Приложении 2 к Описанию классификатора адресов Российской Федерации (КЛАДР);
РРР - код района;
ГГГ - код города;
ППП - код населенного пункта,
АА - признак актуальности наименования адресного объекта

В сформированных условиях актуальность всегда ставится 00, но это не суть. А суть в том что выбираются лишь те объекты у которых установлен код района ИЛИ код города ИЛИ код населенного пункта. Если у объекта установлен скажем и код населенного пункта и код города - это значит что для текущей выборке он не нужен, т.к. будет нужен для выборки когда человек обозначит город.

Вот и вся логика, хотя чтобы дойти до неё понадобилось достаточно много времени, ведь КЛАДР с самого начала пугает, хотя бы названиями своих таблиц :)

PS. К сожалению код проекта очень завязан на куче классов, поэтому чтобы выдернуть только нужно придется пол дня возиться. Но если кому то действительно очень нужно будет - постараюсь выдернуть и выложить.

update 22.01.2012:
С большой задержкой, но все таки выкладываю исходник. Да, немного там запутано все. Саму суть алгоритма смотреть в /sitedir/utils/ajax/kladr.php, остальное вспомогательное. Сами файлы:

5 комментариев:

  1. Приветствую, меня подобная система переброса понравилась, сам планировал использовать стандартные кладр'овские коды, только прогрмму я пишу на c# переброс я буду делать сторонней программой как указанно выше, но на код если есть возможность хотел бы взглянуть!))) Буду признателен!

    ОтветитьУдалить
  2. TihoN9
    эту базу еле еле перенес
    с консоли mysql

    для устранения ошибок 2006 пропишите
    SET GLOBAL max_allowed_packet=желаемый_размер_файла*1024*1024;

    и для увеличения времени
    SET SESSION wait_timeout = 600;

    а так все супер - не хватает файлов вроде для чистого демопримера

    ОтветитьУдалить
  3. Просьба автору выложить недостающий код для просмотра демки
    давно искал что-то подобное, но никак не мог наткнуться на более менее рабочий исходник. ваш набор явно на него смахивает но либо обращение к файлу kladr.php должно быть с переменными (типо kladr.php?c=7700000000000) либо в коде нехватает отображения начала выбора субъекта РФ

    кстати базу кладра проще импортировать из файлов dbf через программу navicat

    ОтветитьУдалить
  4. В самой базе есть куча глюков(((
    Например республика Адыгея (код СС-01)
    Теучежский район (код РРР - 006)
    село Гатлукай - ФОНАРЬ!

    А все потому, что оно имеет код 01 000 002 001 00 !!!
    Район указан - 000 !!!!

    И таких глюков в базе ВАЛОМ!
    Может есть в этом какая-то логика, которой я не понял?? Помогите понять! Плиз.

    ОтветитьУдалить
  5. Этот комментарий был удален автором.

    ОтветитьУдалить