Geotools
Geo-related tools PHP 7.3+ library built atop Geocoder and React libraries
Install / Use
/learn @thephpleague/GeotoolsREADME
Geotools
Geotools is a PHP geo-related library, built atop Geocoder and React libraries.
Features
- Batch geocode & reverse geocoding request(s) in series / in parallel against one or a set of providers. »
- Cache geocode & reverse geocoding result(s) with PSR-6 to improve performances. »
- Compute geocode & reverse geocoding in the command-line interface (CLI) + dumpers and formatters. »
- Accept almost all kind of WGS84 geographic coordinates as coordinates. »
- Support 23 different ellipsoids and it's easy to provide a new one if needed. »
- Convert and format decimal degrees coordinates to decimal minutes or degrees minutes seconds coordinates. »
- Convert decimal degrees coordinates in the Universal Transverse Mercator (UTM) projection. »
- Compute the distance in meter (by default), km, mi or ft between two coordinates using flat, great circle, haversine or vincenty algorithms. »
- Compute the initial and final bearing from the origin coordinate to the destination coordinate in degrees. »
- Compute the initial and final cardinal point (direction) from the origin coordinate to the destination coordinate, read more in wikipedia. »
- Compute the half-way point (coordinate) between the origin and the destination coordinates. »
- Compute the destination point (coordinate) with given bearing in degrees and a distance in meters. »
- Encode a coordinate to a geo hash string and decode it to a coordinate, read more in wikipedia and on geohash.org. »
- Encode a coordinate via the 10:10 algorithm. »
- Polygon class provides methods to check either a poing (coordinate) is in, or on the polygon's boundaries. »
- A command-line interface (CLI) for Distance, Point, Geohash and Convert classes. »
- Integration with Frameworks: Laravel 4, Silex ... »
- ... more to come ...
Installation
Geotools can be found on Packagist. The recommended way to install Geotools is through composer.
Run the following on the command line:
composer require league/geotools
Important: you should use the 0.4 version if you use Geocoder 2.x or/and PHP 5.3.
And install dependencies:
composer install
Now you can add the autoloader, and you will have access to the library:
<?php
require 'vendor/autoload.php';
Usage & API
Coordinate & Ellipsoid
The default geodetic datum is WGS84 and coordinates are in decimal degrees.
Here are the available ellipsoids: AIRY, AUSTRALIAN_NATIONAL, BESSEL_1841, BESSEL_1841_NAMBIA,
CLARKE_1866, CLARKE_1880, EVEREST, FISCHER_1960_MERCURY, FISCHER_1968, GRS_1967, GRS_1980,
HELMERT_1906, HOUGH, INTERNATIONAL, KRASSOVSKY, MODIFIED_AIRY, MODIFIED_EVEREST,
MODIFIED_FISCHER_1960, SOUTH_AMERICAN_1969, WGS60, WGS66, WGS72, and WGS84.
If you need to use an other ellipsoid, just create an array like this:
<?php
$myEllipsoid = \League\Geotools\Coordinate\Ellipsoid::createFromArray([
'name' => 'My Ellipsoid', // The name of the Ellipsoid
'a' => 123.0, // The semi-major axis (equatorial radius) in meters
'invF' => 456.0 // The inverse flattening
]);
Geotools is built atop Geocoder. It means it's possible to use the
\Geocoder\Model\Address directly but it's also possible to use a string or a simple array with its
latitude and longitude.
It supports valid and acceptable geographic coordinates like:
- 40:26:46N,079:56:55W
- 40:26:46.302N 079:56:55.903W
- 40°26′47″N 079°58′36″W
- 40d 26′ 47″ N 079d 58′ 36″ W
- 40.446195N 79.948862W
- 40.446195, -79.948862
- 40° 26.7717, -79° 56.93172
Latitudes below -90.0 or above 90.0 degrees are capped through \League\Geotools\Coordinate\Coordinate::normalizeLatitude().
Longitudes below -180.0 or above 180.0 degrees are wrapped through \League\Geotools\Coordinate\Coordinate::normalizeLongitude().
<?php
use League\Geotools\Coordinate\Coordinate;
use League\Geotools\Coordinate\Ellipsoid;
// from an \Geocoder\Model\Address instance within Airy ellipsoid
$coordinate = new Coordinate($geocoderResult, Ellipsoid::createFromName(Ellipsoid::AIRY));
// or in an array of latitude/longitude coordinate within GRS 1980 ellipsoid
$coordinate = new Coordinate([48.8234055, 2.3072664], Ellipsoid::createFromName(Ellipsoid::GRS_1980));
// or in latitude/longitude coordinate within WGS84 ellipsoid
$coordinate = new Coordinate('48.8234055, 2.3072664');
// or in degrees minutes seconds coordinate within WGS84 ellipsoid
$coordinate = new Coordinate('48°49′24″N, 2°18′26″E');
// or in decimal minutes coordinate within WGS84 ellipsoid
$coordinate = new Coordinate('48 49.4N, 2 18.43333E');
// the result will be:
printf("Latitude: %F\n", $coordinate->getLatitude()); // 48.8234055
printf("Longitude: %F\n", $coordinate->getLongitude()); // 2.3072664
printf("Ellipsoid name: %s\n", $coordinate->getEllipsoid()->getName()); // WGS 84
printf("Equatorial radius: %F\n", $coordinate->getEllipsoid()->getA()); // 6378136.0
printf("Polar distance: %F\n", $coordinate->getEllipsoid()->getB()); // 6356751.317598
printf("Inverse flattening: %F\n", $coordinate->getEllipsoid()->getInvF()); // 298.257224
printf("Mean radius: %F\n", $coordinate->getEllipsoid()->getArithmeticMeanRadius()); // 6371007.772533
// it's also possible to modify the coordinate without creating an other coodinate
$coordinate->setFromString('40°26′47″N 079°58′36″W');
printf("Latitude: %F\n", $coordinate->getLatitude()); // 40.446388888889
printf("Longitude: %F\n", $coordinate->getLongitude()); // -79.976666666667
Convert
It provides methods (and aliases) to convert decimal degrees WGS84 coordinates to degrees minutes seconds or decimal minutes WGS84 coordinates. You can format the output string easily.
You can also convert them in the Universal Transverse Mercator (UTM) projection (Southwest coast of Norway and the region of Svalbard are covered).
<?php
$geotools = new \League\Geotools\Geotools();
$coordinate = new \League\Geotools\Coordinate\Coordinate('40.446195, -79.948862');
$converted = $geotools->convert($coordinate);
// convert to decimal degrees without and with format string
printf("%s\n", $converted->toDecimalMinutes()); // 40 26.7717N, -79 56.93172W
// convert to degrees minutes seconds without and with format string
printf("%s\n", $converted->toDegreesMinutesSeconds('<p>%P%D:%M:%S, %p%d:%m:%s</p>')); // <p>40:26:46, -79:56:56</p>
// convert in the UTM projection (standard format)
printf("%s\n", $converted->toUniversalTransverseMercator()); // 17T 589138 4477813
Here is the mapping:
Decimal minutes | Latitude | Longitude
--- | --- | ---
Positive or negative sign | %P | %p
Direction | %L | %l
Degrees | %D | %d
Decimal minutes | %N | %n
Degrees minutes seconds | Latitude | Longitude
--- | --- | ---
Positive or negative sign | %P | %p
Direction | %L | %l
Degrees | %D | %d
Minutes | %M | %m
Seconds | %S | %s
Batch
It provides a very handy way to batch geocode and reverse geocoding requests in serie or in parallel against a set of providers. Thanks to Geocoder and React libraries.
It's possible to batch one request (a string) or a set of request (an array) against one provider or set of providers.
You can use a provided cache engine or use your own by setting a cache object which should implement
League\Geotools\Cache\CacheInterface and extend League\Geotools\Cache\AbstractCache if needed.
At the moment Geotools supports any PSR-6 cache.
NB: Before you implement caching in your app please be sure that doing so does not violate the Terms of Service for your(s) geocoding provider(s).
<?php
$geocoder = new \Geocoder\ProviderAggregator(); // or \Geocoder\TimedGeocoder
$httpClient = HttpClientDiscovery::find();
$geocoder->registerProviders([
new \Geocoder\Provider\GoogleMaps\GoogleMaps($httpClient),
new \Geocoder\Provider\OpenStreetMap\OpenStreetMap($httpClient),
new \Geocoder\Provider\BingMaps\BingMaps($httpClient, '<FAKE_API_KEY>'), // throws InvalidCredentialsException
new \Geocoder\Provider\Yandex\Yandex($httpClient),
new \Geocoder\Provider\FreeGeoIp\FreeGeoIp($httpClient),
new \Geocoder\Provider\Geoip\Geoip(),
]);
try {
$geotools = new \League\Geotools\Geotools();
$cache = new \Cache\Adapter\PHPArray\ArrayCachePool();
$results = $geotools->batch($geocoder)->setCache($cache)->geocode([
'Paris, France',
'Copenhagen, Denmark',
'74.200.247.59',
'::ffff:66.147.244.214'
