Pokupo corporate blog
Website development
PHP
22 August 2012

Гео-модуль для PHP приложений

image Вопрос «Где?» возникает сразу же после вопроса «Что?» эта закономерность верна и в вебразработке. Многие сайты запрашивают информацию у пользователя, предлагая ему ввести свой адрес, т. е. страну, регион, город, улицу, дом почтовый индекс. Но как потом обрабатывать эти данные, если они были указаны в свободной форме? В своих первых проектах мы использовали свой «велосипед», но по мере роста и развития это «чудо» превратилось в «чудовище», которое поставило крест на эффективной обработке гео-информации о наших пользователях. Мне была поставлена задача прибить этого монстра, заменив его стандартизованной гео-базой и простым интерфейсом для работы с ней. Гугление на эту тему не дали готового решения, поэтому пришлось отбросить простой вариант и сделать свой гео-модуль.

База адресов России


Для начала, нужно было найти базу адресов России. В поисках базы адресов я наткнулся на две и вполне возможно, что единственные:
  1. КЛАДР (Классификатор адресов РФ) распространяется в *.dbf формате
  2. ФИАС (Федеральная информационная адресная система). По умолчанию распространятся в виде 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 сервисы

  • Geo IP сервис — этих ребят blog.ipgeobase.ru
  • EMS расчёт доставки emspost.ru
  • RussianPost(иначе почта России) расчёт доставки postcalc.ru
  • Yandex карта 2.0

Что умеем

  • Делать запросы в КЛАДР
  • Делать запросы в ipgeo, для получения расположения пользователя по его ip
  • Делать запросы в EMS API для расчёта доставки
  • Делать запросы в PostCalc для расчёта доставки
  • Серверное гео-кодирование yandex map

Процесс устроен так: при загрузки страницы, получаем по ip адресу geo расположения пользователя, для этого сначала смотрим существует ли запись с ключом(ip адресом) в таблице geoLocation, если есть берём от туда, если нет, делаем запрос в сервис ipGeo полученные данные сохраняем в табличке geoLocation, для последующего сокращения трафика запросов в ipgeo сервис. Имеющиеся данные о расположении пользователя используем для предсказания расположения пользователя, т.е. три (регион/округ/город) выпадающих списках, заполняем данными и выделяем в них нужное расположение. Если расположение угадано верно, то пользователю останется указать только улицу и дом, а индекс (если он есть в кладре) подставится автоматически. Впрочем если по ip адресу расположение было не угадано, то регион/округ/город можно изменить. Для этого был написал простенки js плагин, который согласно выбранного региона запрашивает округ и большие города региона, а согласно выбранного округа, города округа.

Есть yandex карта для отображения сохраненного расположения пользователя. Впрочем, можно сделать что бы карта на лету реагировала, на выбор пользователем локации.

В конце страницы с формой задания адреса, для демонстрации имеется блок с расчётом доставки. Сам пакет доставки реализован, как «декоратор», поэтому можно настраивать калькуляцию сумм или время доставки. Собственно как работает калькуляция, должно быть понятно, нужно знать откуда и куда, а далее дело техники. Расчёт доставки можно расширить, путём добавления нового *поставщика*.

Удачно то, что структура указания адресов ФИАС схожа с КЛАДР, прошу заметить не идентична, а схожа поэтому если немного от-рефакторить код репозитория \Entities\KadrRepository, то можно использовать и ФИАС как хранилище адресов, вопрос только нужно ли это.

Подробное описание API модуля публикую в WiKi проекта на github

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

+51
30.7k 423
Comments 36