Статьи

Где ты? Реализация геолокации с помощью Geocoder PHP

Для меня прелесть SitePoint в том, что вы можете вдохновиться, попробовать что-нибудь или рассказать о каком-нибудь крутом проекте. Интернет слишком велик, чтобы один человек мог самостоятельно его найти. Геокодер был одним из тех для меня. Я никогда не слышал об этом и не сталкивался с этим на доске авторов Trello.

Я люблю работать с картами и географической информацией, и я активно использую (обратное) геокодирование для проекта, который я сделал для клиента; CableTracks . На самом деле мы используем платный сервис для этого, но не для всего. Платные результаты содержат гораздо больше информации, чем вы получаете от бесплатных услуг. Я обнаружил, что Geocoder PHP — это то, чего мне не хватало для интеграции различных сервисов, которые мы используем.

Geocoder PHP предоставляет: «уровень абстракции для манипуляций с геокодированием». Библиотека разделена на три части: HttpAdapter для выполнения запросов, несколько провайдеров геокодирования и различные Formatter / Dumpers для выполнения форматирования вывода.

Установка

Установка Geocoder проще всего сделать с помощью composer. Добавьте следующее в ваш composer.json :

{ "require": { "willdurand/geocoder": "@stable" } } 

Или получите один из архивов с сайта Geocoder PHP .

GeoCoding

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

Чтобы использовать Geocoder, вам сначала понадобится HttpAdapter для запуска запросов в веб-сервисе. HttpAdaper — это веб-клиент, который на самом деле поставляется в 5 различных вариантах с PhpGeocoder. Вы можете использовать всегда доступные клиенты на базе CUrl или Socket . Кроме того, вы можете добавить PHP-клиенты, такие как Buzz Browser , Guzzle PHP HTTP Client , Zend HTTP и API веб-сервиса GeoIP2 . Вам нужно будет добавить их в свой проект, если вы хотите их использовать.

С помощью адаптера HTTP вы можете использовать геокодер для выполнения работы. Список поддерживаемых геокодеров длинный, очень длинный. Их можно разделить на две группы;

Большинство из них можно использовать бесплатно, а некоторые требуют регистрации на API-ключ. Большинство из них будет иметь какую-то скорость или дневной лимит на количество запросов, которые вы можете от них отобрать. Google, например, позволит вам делать 2500 запросов в день.
Качество услуг геокодирования варьируется. Некоторые поддерживают конкретные страны и, как правило, будут лучше для этих стран. Но это не то, что мы будем оценивать здесь. Это то, что вы действительно хотите проверить сами, потому что результаты будут зависеть от того, где вы геокодируете.

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

 $lookfor = 'Laan van Meerdervoort, Den Haag, Nederland'; $adapter = new \Geocoder\HttpAdapter\CurlHttpAdapter(); $geocoder = new \Geocoder\Geocoder(); $geocoder->registerProvider(new \Geocoder\Provider\GoogleMapsProvider($adapter)); $result = $geocoder->geocode($lookfor); 

Просто, правда? Вот что вы получите в результате:

 Geocoder\Result\Geocoded#1 ( [*:latitude] => 52.0739343 [*:longitude] => 4.2636776 [*:bounds] => array ( 'south' => 52.0610866 'west' => 4.2262026 'north' => 52.0868446 'east' => 4.3008719 ) [*:streetNumber] => null [*:streetName] => 'Laan Van Meerdervoort' [*:cityDistrict] => null [*:city] => 'The Hague' [*:zipcode] => null [*:county] => 'The Hague' [*:countyCode] => 'THE HAGUE' [*:region] => 'South Holland' [*:regionCode] => 'ZH' [*:country] => 'The Netherlands' [*:countryCode] => 'NL' [*:timezone] => null ) индекс Geocoder\Result\Geocoded#1 ( [*:latitude] => 52.0739343 [*:longitude] => 4.2636776 [*:bounds] => array ( 'south' => 52.0610866 'west' => 4.2262026 'north' => 52.0868446 'east' => 4.3008719 ) [*:streetNumber] => null [*:streetName] => 'Laan Van Meerdervoort' [*:cityDistrict] => null [*:city] => 'The Hague' [*:zipcode] => null [*:county] => 'The Hague' [*:countyCode] => 'THE HAGUE' [*:region] => 'South Holland' [*:regionCode] => 'ZH' [*:country] => 'The Netherlands' [*:countryCode] => 'NL' [*:timezone] => null ) 

На самом деле это самая красивая часть Geocoder PHP; какой бы геокодер вы ни использовали, в каком бы направлении вы ни геокодировали (в нормальном / обратном направлении), вы всегда получите один и тот же набор результатов. Это неоценимо, если вы когда-нибудь задумаетесь об изменении используемого геокодера.

О выборе моего адреса; Laan van Meerdervoort — одна из самых длинных улиц в Нидерландах (~ 5800 м в длину). Я оставил номер дома, чтобы получить полную улицу. Разница в широте / долготе и границах делает это очевидным сейчас. Эти границы, в данном случае, содержат ограничивающий прямоугольник всей улицы. Очень практично, что вы получаете оба в своем результате; центр улицы и границы.

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

 $geocoder->registerProvider(new \Geocoder\Provider\GoogleMapsProvider($adapter, 'Ru')); $result = $geocoder->geocode('Laan van Medevoort 7, Den Haag, Nederland'); 

И получить:

 Geocoder\Result\Geocoded#1 ( [*:latitude] => 52.0739343 [*:longitude] => 4.2636776 [*:bounds] => array ( 'south' => 52.0610866 'west' => 4.2262026 'north' => 52.0868446 'east' => 4.3008719 ) [*:streetNumber] => 7 [*:streetName] => 'Laan Van Meerdervoort' [*:cityDistrict] => null [*:city] => 'Гаага' [*:zipcode] => null [*:county] => 'Гаага' [*:countyCode] => 'ГААГА' [*:region] => 'Южная Голландия' [*:regionCode] => 'ZH' [*:country] => 'Нидерланды' [*:countryCode] => 'NL' [*:timezone] => null ) индекс Geocoder\Result\Geocoded#1 ( [*:latitude] => 52.0739343 [*:longitude] => 4.2636776 [*:bounds] => array ( 'south' => 52.0610866 'west' => 4.2262026 'north' => 52.0868446 'east' => 4.3008719 ) [*:streetNumber] => 7 [*:streetName] => 'Laan Van Meerdervoort' [*:cityDistrict] => null [*:city] => 'Гаага' [*:zipcode] => null [*:county] => 'Гаага' [*:countyCode] => 'ГААГА' [*:region] => 'Южная Голландия' [*:regionCode] => 'ZH' [*:country] => 'Нидерланды' [*:countryCode] => 'NL' [*:timezone] => null ) 

Конечно, вы хотите получить лучшие результаты от вашего геокодирования. Если вы находитесь в США, возможно, вы захотите использовать кодер, который специально поддерживает США, а другой — за пределами США. Прелесть в том, что геокодер поддерживает цепочку геокодеров. Затем вы можете отправить свой запрос на набор кодеров, и будет возвращен первый действительный результат. Это здорово, потому что в противном случае вам придется попробовать / поймать целый набор кодеров, пока вы не получите результат.
Пример этого:

 $adapter = new \Geocoder\HttpAdapter\CurlHttpAdapter(); $geocoder = new \Geocoder\Geocoder(); $chain = new \Geocoder\Provider\ChainProvider([ new \Geocoder\Provider\OpenStreetMapProvider($adapter), new \Geocoder\Provider\GoogleMapsProvider($adapter), new \Geocoder\Provider\BingMapsProvider($adapter, $bingApiKey), ]); $geocoder->registerProvider($chain); 

Обратите внимание, что Geocoder вернет первый действительный результат. Это может быть не лучшим, что вы можете получить! Так что будьте осторожны в той последовательности, в которой вы объединяете своих кодеров!

Обратное геокодирование

Обратное геокодирование — это когда у вас есть точка на земном шаре, обычно координата GPS ( WGS84 ). Если ваши входные данные находятся в другой проекции, вы можете использовать PHP версию Proj4 для ее преобразования.
Синтаксис, если у вас есть экземпляр геокодера, опять-таки довольно прост. Единственное, что вам нужно помнить, это то, что сначала это широта, а затем долгота (поднимите руки, если вы случайно продолжите переключать широту и долготу…).
Убедитесь, что ваш геокодер поддерживает обратное геокодирование, потому что не все это делают. Я буду геокодировать географический центр Нидерландов:

 $result = $geocoder->reverse(52.155247, 5.387452); 

Что приводит к геокодированному результату, как этот.

 Geocoder\Result\Geocoded#1 ( [*:latitude] => 52.1551992 [*:longitude] => 5.3872474 [*:bounds] => array ( 'south' => 52.1551992 'west' => 5.3872474 'north' => 52.1551992 'east' => 5.3872474 ) [*:streetNumber] => '30' [*:streetName] => 'Krankeledenstraat' [*:cityDistrict] => 'Stadskern' [*:city] => 'Amersfoort' [*:zipcode] => '3811 BN' [*:county] => 'Amersfoort' [*:countyCode] => 'AMERSFOORT' [*:region] => 'Utrecht' [*:regionCode] => 'UT' [*:country] => 'The Netherlands' [*:countryCode] => 'NL' [*:timezone] => null ) индекс Geocoder\Result\Geocoded#1 ( [*:latitude] => 52.1551992 [*:longitude] => 5.3872474 [*:bounds] => array ( 'south' => 52.1551992 'west' => 5.3872474 'north' => 52.1551992 'east' => 5.3872474 ) [*:streetNumber] => '30' [*:streetName] => 'Krankeledenstraat' [*:cityDistrict] => 'Stadskern' [*:city] => 'Amersfoort' [*:zipcode] => '3811 BN' [*:county] => 'Amersfoort' [*:countyCode] => 'AMERSFOORT' [*:region] => 'Utrecht' [*:regionCode] => 'UT' [*:country] => 'The Netherlands' [*:countryCode] => 'NL' [*:timezone] => null ) 

Геокодирование IP-адресов

Я лично использовал геокодирование на IP-адресах, чтобы определить, на каком языке показывать веб-сайт (конечно, в сочетании с браузером). Вы передаете IP-адрес своего пользователя, обычно от $_SERVER['REMOTE_ADDR'] , в геокодер и получаете результат. Следует учитывать, поддерживает ли ваш сервер IPv6 и может ли он работать через IPv6. Если это так, то вам нужно будет использовать сервис, который поддерживает геокодирование IPv6, потому что не все это делают.

Доступно несколько IP-геокодеров, и использовать их так же просто, как и другие:

 $adapter = new \Geocoder\HttpAdapter\CurlHttpAdapter(); $geocoder = new \Geocoder\Geocoder(); $result = $geocoder->registerProvider( new \Geocoder\Provider\FreeGeoIpProvider($adapter) ) ->geocode($_SERVER['REMOTE_ADDR']); 

Результат, опять же, является Geocoded результатом. Я не буду показывать пример, потому что вы будете разочарованы. Результаты IP геокодеров сильно различаются по качеству. Чаще всего вы получите только страну и ничего больше. Я настроил несколько примеров для геокодирования IP-адресов, если вы заинтересованы в результатах для вашего IP.

Еще одна вещь, которую стоит отметить в последнем примере, это то, что вы всегда должны вкладывать свои попытки геокодирования с помощью Geocoder PHP в блок try / catch. Геокодер может выдавать вам несколько исключений в зависимости от вывода используемого геокодера. Большинство из них довольно очевидны, но их можно легко обработать, добавив несколько условий улова:

 try { $result = $geocoder->registerProvider( new \Geocoder\Provider\FreeGeoIpProvider($adapter) ) ->geocode($_SERVER['REMOTE_ADDR']); } catch (NoResultException $e) { $result = "No result was returned by the geocoder"; } catch (QuotaExceededException $e) { $result = "We met our daily quota"; } catch (Exception $e) { $result = "Error: " . $e->getMessage(); } 

Форматирование вывода

Последнее, что я хочу коснуться, это форматирование вывода. Geocoder имеет несколько форматеров вывода, которые позволяют легко интегрировать его результат в ваше приложение. Есть два типа вывода; Formatter строк, который имеет интерфейс, Dumpers на sprintf и Dumpers которые позволяют выводить данные для конкретного языка.

Поддерживаемые выходные дамперы — это XML-формат обмена GPS (GPX), язык разметки замочных скважин (KML) для взаимодействия с Google Планета Земля, хорошо известный двоичный файл (WKB) и хорошо известный текст (WKT) для баз данных с поддержкой геопространственных данных и, наконец, GeoJSON для легкий интерфейс JavaScript.

Например; получить геокодированный результат и передать его в форматтер или дампер;

 $adapter = new \Geocoder\HttpAdapter\CurlHttpAdapter(); $GeoJSON = new \Geocoder\Dumper\GeoJsonDumper; $geocoder = new \Geocoder\Geocoder(); $geocoder->registerProvider( new \Geocoder\Provider\OpenStreetMapProvider($adapter) ); echo $GeoJSON->dump( $geocoder->reverse(52.155247, 5.387452) ); 

Это выведет результат GeoJSON, который может быть использован, например, с OpenLayers :

 { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 5.3872095538856, 52.1551691 ] }, "properties": { "streetName": "Lieve Vrouwekerkhof", "zipcode": "3811 AA", "city": "Amersfoort", "cityDistrict": "Amersfoort", "region": "Utrecht", "country": "Nederland", "countryCode": "NL" } } 

Вывод

Нужно сделать комплименты по поводу великолепной структуры классов в Geocoder. Это настоящая библиотека OOPHP, которая хорошо использует пространства имен и интерфейсы. Это чрезвычайно прост в использовании. Расширять его или создавать свои собственные геокодеры для него должно быть очень легко.

Если вы ищете прямое геокодирование, то эта библиотека будет отлично работать для вас. Я лично вижу место для еще лучшего геокодера, потому что есть больше доступной энергии. Было бы замечательно, если бы вы могли устанавливать условия для запроса, который вы делаете, в цепочку геокодирования, чтобы получить наилучший результат, доступный для всех кодировщиков. Например: «по крайней мере, вернуть название улицы» в наборе результатов или «иметь уровень достоверности> 75%» (некоторые геокодеры предоставляют эту информацию) или «максимальная ошибка адреса менее 500 метров» (что может быть рассчитывается с учетом разницы выходного адреса и входных координат). Конечно, все это зависит от возможностей отдельных геокодеров, но, безусловно, можно выполнить простую фильтрацию.

Если вы заинтересованы в самих геокодерах; Я установил небольшую демонстрацию, чтобы проверить результаты для различных геокодеров.

Вы когда-нибудь работали с геокодерами, нуждались в такой библиотеке или, возможно, уже работали с ней? Пожалуйста, поделитесь своими мыслями с нами.