Вопрос «Где?» возникает сразу же после вопроса «Что?» эта закономерность верна и в вебразработке. Многие сайты запрашивают информацию у пользователя, предлагая ему ввести свой адрес, т. е. страну, регион, город, улицу, дом почтовый индекс. Но как потом обрабатывать эти данные, если они были указаны в свободной форме? В своих первых проектах мы использовали свой «велосипед», но по мере роста и развития это «чудо» превратилось в «чудовище», которое поставило крест на эффективной обработке гео-информации о наших пользователях. Мне была поставлена задача прибить этого монстра, заменив его стандартизованной гео-базой и простым интерфейсом для работы с ней. Гугление на эту тему не дали готового решения, поэтому пришлось отбросить простой вариант и сделать свой гео-модуль.
База адресов России
Для начала, нужно было найти базу адресов России. В поисках базы адресов я наткнулся на две и вполне возможно, что единственные:
- КЛАДР (Классификатор адресов РФ) распространяется в *.dbf формате
- ФИАС (Федеральная информационная адресная система). По умолчанию распространятся в виде xml структуры, но видать пришлось еще подстраиваться под КЛАДР и из-за его архаизма ФИАС стал распространятся еще и в dbf формате
Разница между ними такая: 400 Мб
КЛАДР-а против 9 Гб
ФИАС-а — существенно, не правда ли? По этому определённо хочется пользоваться
ФИАС-ом, т. к. по объёму можно полагать, что данные у него более избыточны, что соответственно и лучше. Но работа с
ФИАС у меня не сложилась. Запросы по таблице адресов, были ужасно долгие и ни расстановка индексов, ни оптимизация запросов мне не помогли, пользовался я при этом MySQL 5.5
С
КЛАДР-ом же, работа сложилась и как выяснилось позже, его детализации для моих (стандартных) задач, вполне хватает. Обзор по структуре таблиц
КЛАДР-а можно почитать
здесь/ Для него же кто-то сделал
api, которое не работает :-), но авось ребята допилят, думаю многим будет полезно.
Для того, что бы можно было использовать КЛАДР в проекте, нужно его из распространяемого dbf формата конвертировать в что-то понятное MySQL. Впрочем можно залить базу напрямую, для этого можно использовать
navicat или
mydbf2mysql.
Таким образом, я сделал простой гео-модуль, который можно докрутить под любые нужды.
Проект можно получить на
Github.
Что из себя представляет модуль?
Проект писался с использованием php 5.3, что важно.
СУБД MySQL 5.5, что не очень важно.
Используемые библиотеки
- php Data Layer Doctrine 2
- js jquery
- js jquery.form
- js jquery.autocomplete
- css 960cs
Структура файлов проекта
.project # Корень проекта
-css\
-js\
--plugins
---autocomplete
----jquery.autocomplete.js
---jquery.form.js
--cabinet.js # скрипт для страницы с формой
--initialize.js # скрипт инициализации
--jquery-1.7.2.js
-Entities\
--GeoLocation.php # Сущность с данными локации ip адреса
--UserLocation.php # Сущность с данными локации пользователя
--Kladr.php # Сущность содержит записи с объектами первых четырех уровней классификации
--Socrbase.php # Сущность содержит записи с краткими наименованиями типов адресных объектов
--Street.php # Сущность содержит записи с объектами пятого уровня классификации
--Doma.php # Сущность содержит записи с объектами шестого уровня классификации
--KladrRepository.php
--HomeRepository.php
--StreetRepository.php
-Geo\
--Providers\
---Deliveries # Поставщики услуг доставки
----Ems\
-----EmsDeliveryDecorator.php # Класс декоратор рассчитывающий EMS доставку
-----EmsDiliveryProvider.php # Класс реализующий API сервиса EMS для расчёта доставки
-----ENUM_EMS.php # перечисления команд
----RussianPost\
-----RussianPostDeliveryDecorator.php # Класс декоратор рассчитывающий доставку Почты России
-----RussianPostDeliveryProvider.php # Класс реализующий API сервиса PostCalc для расчёта доставки
---Geocoder\ # Поставщики сервисов гео-кодирования
----Yandex\
-----Geocoder.php
----MapApiBase.php # Абстрактный класс для гео-кодирования
---Delivery.php
---DeliveryBase.php
---DeliveryDecoratorBase.php
---DeliveryDecoratorRound.php
---DeliveryProviderBase.php
--common.php
--Geo.php
--GoecoderCreater.php # Фабрика для объекта гео-кодирования
--ILocationBuilder.php
--Location.php # класс умеющий делать запросы в сервис geo-ip
--LocationBuilder.php # строитель объкта \Entities\UserLocation, по данным полученны от геокодера
-_content.php
-autobox.php # точка входа для запросов плагина выпадающих списков(оркуг/город)
-autocomlete.php # точка входа для
-Autoloader.php # мой авто-загрузчик классов
-delivery.php # точка входа для запросов на расчёт доставки
-index.php # основная точка входа
-popup.php # пример реализации через модальное popup окно colorbox
-run.php # bootstrap example приложения
-String.php # строковые общие статические методы
Используемые api сервисы
Что умеем
- Делать запросы в КЛАДР
- Делать запросы в ipgeo, для получения расположения пользователя по его ip
- Делать запросы в EMS API для расчёта доставки
- Делать запросы в PostCalc для расчёта доставки
- Серверное гео-кодирование yandex map
Процесс устроен так: при загрузки страницы, получаем по ip адресу geo расположения пользователя, для этого сначала смотрим существует ли запись с ключом(ip адресом) в таблице geoLocation, если есть берём от туда, если нет, делаем запрос в сервис ipGeo полученные данные сохраняем в табличке geoLocation, для последующего сокращения трафика запросов в ipgeo сервис. Имеющиеся данные о расположении пользователя используем для предсказания расположения пользователя, т.е. три (регион/округ/город) выпадающих списках, заполняем данными и выделяем в них нужное расположение. Если расположение угадано верно, то пользователю останется указать только улицу и дом, а индекс (если он есть в кладре) подставится автоматически. Впрочем если по ip адресу расположение было не угадано, то регион/округ/город можно изменить. Для этого был написал простенки js плагин, который согласно выбранного региона запрашивает округ и большие города региона, а согласно выбранного округа, города округа.
Есть yandex карта для отображения сохраненного расположения пользователя. Впрочем, можно сделать что бы карта на лету реагировала, на выбор пользователем локации.
В конце страницы с формой задания адреса, для демонстрации имеется блок с расчётом доставки. Сам пакет доставки реализован, как «декоратор», поэтому можно настраивать калькуляцию сумм или время доставки. Собственно как работает калькуляция, должно быть понятно, нужно знать откуда и куда, а далее дело техники. Расчёт доставки можно расширить, путём добавления нового *поставщика*.
Удачно то, что структура указания адресов ФИАС схожа с КЛАДР, прошу заметить не идентична, а схожа поэтому если немного от-рефакторить код репозитория \Entities\KadrRepository, то можно использовать и ФИАС как хранилище адресов, вопрос только нужно ли это.
Подробное описание API модуля публикую в
WiKi проекта на githubНадеюсь, что кому-то этот модуль будет полезен, но не претендую на идеальное решение, буду рад услышать замечания и предложения.