PHP Extension to handle common geospatial functions.

Last update: Aug 11, 2022

geospatial - PHP Geospatial Extension

https://travis-ci.org/php-geospatial/geospatial.svg?branch=master https://codecov.io/gh/php-geospatial/geospatial/branch/master/graphs/badge.svg?branch=master

PHP Extension to handle common geospatial functions. The extension currently has implementations of the Haversine and Vincenty's formulas for calculating distances, an initial bearing calculation function, a Helmert transformation function to transfer between different supported datums, conversions between polar and Cartesian coordinates, conversions between Degree/Minute/Seconds and decimal degrees, a method to simplify linear geometries, as well as a method to calculate intermediate points on a LineString.

Instalation

git clone [email protected]:php-geospatial/geospatial.git
cd geospatial
phpize
./configure --enable-geospatial
make
sudo make install

Then add the extension to an ini file e.g. /etc/php.ini:

extension = geospatial.so

Usage

The extension makes use of the GeoJSON standard format for specifying points as co-ordinates. One important thing to note about this format is that points are specified longitude first i.e. longitude, latitude.

e.g.:

$greenwichObservatory = array(
    'type' => 'Point',
    'coordinates' => array( -0.001483 , 51.477917);
);

Haversine

$from = array(
    'type' => 'Point',
    'coordinates' => array( -104.88544, 39.06546 )
);
$to = array(
    'type' => 'Point',
    'coordinates' => array( -104.80, 39.06546 )
);
var_dump(haversine($to, $from));

Vincenty's Formula

Vincenty's formula attempts to provide a more accurate distance between two points than the Haversine formula. Whereas the Haversine formula assumes a spherical earth the Vincenty method models the earth as an ellipsoid:

$flinders = array(
    'type' => 'Point',
    'coordinates' => array(144.42486788889, -37.951033416667 )
);
$buninyong = array(
    'type' => 'Point',
    'coordinates' => array(143.92649552778, -37.652821138889 )
);
var_dump(vincenty($flinders, $buninyong));

Helmert Transformation

The Helmert transformation allows for the transformation of points between different datums. It can for instance be used to convert between the WGS84 ellipsoid (GEO_WGS84) used by GPS systems and OSGB36 (GEO_AIRY_1830) used by Ordnance Survey in the UK:

$greenwichObservatory = array(
    'type' => 'Point',
    'coordinates' => array(-0.0014833333333333 , 51.477916666667)
);

$greenwichObservatoryWGS84 = transform_datum($greenwichObservatory, GEO_WGS84, GEO_AIRY_1830);

var_dump($greenwichObservatoryWGS84);

Calculating Initial Bearing

The initial_bearing function calculates the initial bearing to go from the first to the second point, as expressed in a GeoJSON wrapper:

$from = array(
        'type' => 'Point',
        'coordinates' => array( 2.351, 48.857 )
);
$to = array(
        'type' => 'Point',
        'coordinates' => array( 0.119, 52.205 )
);
var_dump(initial_bearing($from, $to));

The range of the resulting heading is 0° to +360°.

Converting between polar and Cartesian Coordinates

These two functions calculate between Polar and Cartesian Coordinates, with results depending on which ellipsoid you use.

From Polar to Cartesian:

$lat = 53.38361111111;
$long = 1.4669444444;

var_dump(polar_to_cartesian($lat, $long, GEO_AIRY_1830));

And back:

$x = 3810891.6734396;
$y = 97591.624686311;
$z = 5095766.3939034;

$polar = cartesian_to_polar($x, $y, $z, GEO_AIRY_1830);
echo round($polar['lat'], 6), PHP_EOL;
echo round($polar['long'], 6), PHP_EOL;
echo round($polar['height'], 3), PHP_EOL;

Converting between Degree/Min/Sec and Decimal coordinates

From decimal to dms. The second argument is either "longitude" or "latitude":

$dms = decimal_to_dms(-1.034291666667, 'longitude');
var_dump($dms);

Which outputs:

array(4) {
  ["degrees"]=> int(1)
  ["minutes"]=> int(2)
  ["seconds"]=> float(3.4500000011994)
  ["direction"]=> string(1) "W"
}

And back from DMS to decimal, where the fourth argument is either "N", "S", "E", or "W":

$decimal = dms_to_decimal(0, 6, 9, 'S');

Which outputs:

float(-0.1025)

Simplifying LineStrings

The rdp_simplify method implements RDP to simplify a LineString according to a certain accuracy (epsilon). As first argument it takes a GeoJSON LineString (in PHP variable format), and it outputs a similar structure but then simplified

Interpolation along a Greater Circle Line

The fraction_along_gc_line function can be used to calculate intermediate points along a Greater Circle Line. For example if you need to draw lines with more accuracy with for example Leaflet. The function takes the start and end coordinates (as GeoJson Point), and calculates the intermediate point along those line. To calculate the point 25% from the start point to the end point, you would use:

$point1 = [ 'type' => 'Point', 'coordinates' => [  5, 10 ] ];
$point2 = [ 'type' => 'Point', 'coordinates' => [ 15, 10 ] ];

var_dump(fraction_along_gc_line($point1, $point2, 0.25));

Geohashing

The geohash_encode function can be used to convert GeoJSON Point to a geohash of a specific length (in this case, 12):

$point = [ 'type' => 'Point', 'coordinates' => [ 16.4, 48.2 ] ];
echo geohash_encode( $point, 12 );

Which outputs:

u2edjnw17enr

Similarly, a hashed coordinates pair can be decoded using geohash_decode function:

var_dump(geohash_decode('u2edjnw17enr'));
array(2) {
  ["type"]=>
  string(5) "Point"
  ["coordinates"]=>
  array(2) {
        [0]=>
        float(16.40000006184)
        [1]=>
        float(48.199999993667)
  }
}

GitHub

https://github.com/php-geospatial/geospatial
Comments
  • 1. Add geohashing capabilities

    I'd like to propose two new functionalities to be added to geospatial extension:

    • geohash_encode
    • geohash_decode

    Geohash is a geocoding system invented by Gustavo Niemeyer. https://en.wikipedia.org/wiki/Geohash

    There is a significant performance gain from using this extension compared to user-land libraries.

    Source code is available at https://github.com/emirb/php-geohash-ext Rather than creating a standalone extension for the sole purpose of dealing with geohashes, a mutual agreement has been met concerning merging it to the existing geospatial PHP extension.

    Reviewed by emirb at 2017-10-10 20:54
  • 2. Add functions to convert between WGS-84 and OSGB36

    Using my rudimentary knowledge of C I have implemented the helmert transformation and some other functions to make it useful.

    This may be completely the wrong way to do it and I am happy to change any of it but just thought it would be good to get the functions in there.

    I based the calculations on the spreadsheet www.ordnancesurvey.co.uk/oswebsite/gps/docs/ProjectionandTransformationCalculations.xls which seemed to be a pretty accurate source.

    Reviewed by natmchugh at 2012-11-08 13:09
  • 3. Travis tests

    • Added Travis tests (including upcoming PHP 7.2) so we can have matrix-based tests for all PHP versions.
    • Added codecov integration for generating code coverage
    Reviewed by emirb at 2017-10-03 18:58
  • 4. Helmert transformation

    Using my rudimentary knowledge of C I have implemented the helmert transformation and some other functions to make it useful.

    This may be completely the wrong way to do it and I am happy to change any of it but just thought it would be good to get the functions in there.

    I based the calculations on the spreadsheet www.ordnancesurvey.co.uk/oswebsite/gps/docs/ProjectionandTransformationCalculations.xls which seemed to be a pretty accurate source.

    Reviewed by natmchugh at 2012-12-13 15:40
  • 5. make test errors

     make test
    /bin/bash /tmp/geospatial/libtool --mode=install cp ./geospatial.la /tmp/geospatial/modules
    libtool: install: cp ./.libs/geospatial.so /tmp/geospatial/modules/geospatial.so
    libtool: install: cp ./.libs/geospatial.lai /tmp/geospatial/modules/geospatial.la
    libtool: finish: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/sbin" ldconfig -n /tmp/geospatial/modules
    ----------------------------------------------------------------------
    Libraries have been installed in:
       /tmp/geospatial/modules
    
    If you ever happen to want to link against installed libraries
    in a given directory, LIBDIR, you must either use libtool, and
    specify the full pathname of the library, or use the '-LLIBDIR'
    flag during linking and do at least one of the following:
       - add LIBDIR to the 'LD_LIBRARY_PATH' environment variable
         during execution
       - add LIBDIR to the 'LD_RUN_PATH' environment variable
         during linking
       - use the '-Wl,-rpath -Wl,LIBDIR' linker flag
       - have your system administrator add LIBDIR to '/etc/ld.so.conf'
    
    See any operating system documentation about shared libraries for
    more information, such as the ld(1) and ld.so(8) manual pages.
    ----------------------------------------------------------------------
    
    Build complete.
    Don't forget to run 'make test'.
    
    
    =====================================================================
    PHP         : /usr/bin/php7.2 
    PHP_SAPI    : cli
    PHP_VERSION : 7.2.3-1ubuntu1
    ZEND_VERSION: 3.2.0
    PHP_OS      : Linux - Linux 746209f08f68 4.15.0-13-generic #14-Ubuntu SMP Sat Mar 17 13:44:27 UTC 2018 x86_64
    INI actual  : /tmp/geospatial/tmp-php.ini
    More .INIs  :   
    ---------------------------------------------------------------------
    PHP         : /usr/bin/phpdbg7.2 
    PHP_SAPI    : phpdbg
    PHP_VERSION : 7.2.3-1ubuntu1
    ZEND_VERSION: 3.2.0
    PHP_OS      : Linux - Linux 746209f08f68 4.15.0-13-generic #14-Ubuntu SMP Sat Mar 17 13:44:27 UTC 2018 x86_64
    INI actual  : /tmp/geospatial/tmp-php.ini
    More .INIs  : 
    ---------------------------------------------------------------------
    CWD         : /tmp/geospatial
    Extra dirs  : 
    VALGRIND    : Not used
    =====================================================================
    TIME START 2018-04-07 10:13:43
    =====================================================================
    PASS WGS84 to OSGB36 for Greenwich Observertory [tests/Greenwich.phpt] 
    PASS WGS84 to OSGB36 [tests/JodrellBank.phpt] 
    PASS OSGB36 to WGS84 [tests/OSGB36_to_WGS84.phpt] 
    PASS WGS84 to OSGB36 [tests/WGS84_to_OSGB36.phpt] 
    FAIL Test for issue #16: Segfault on rdp_simplify for GeoJSON [tests/bug0016.phpt] 
    PASS Test cartesian to polar [tests/cartesian_to_polar.phpt] 
    PASS degrees minutes seconds [tests/decimal_to_dms.phpt] 
    PASS dms_to_decimal() [tests/dms_to_decimal.phpt] 
    PASS Test for "fraction_along_gc_line" #1 [tests/fraction_along-001.phpt] 
    PASS Test for "fraction_along_gc_line" #2 [tests/fraction_along-002.phpt] 
    PASS Test for "fraction_along_gc_line" #3 [tests/fraction_along-003.phpt] 
    PASS Test geohash_decode [tests/geohash_decode.phpt] 
    PASS Test geohash_encode [tests/geohash_encode.phpt] 
    PASS Check the haversine function returns the correct distance between London and Edinburgh. [tests/geospatial_haversine_london_edinburgh.phpt] 
    PASS Check the haversine function returns the correct distance between the North and South poles, using a custom radius. [tests/geospatial_haversine_polar_distance.phpt] 
    PASS haversine() function - basic test for haversine forumla [tests/haversine.phpt] 
    PASS helmert() function - basic test for helmert formula [tests/helmert.phpt] 
    PASS initial_bearing() function - basic test for initial_bearing forumla [tests/initial_bearing.phpt] 
    PASS Test polar to cartesian [tests/polar_to_cartesian.phpt] 
    PASS Test for "Vincenty" distance between FlindersPeak and Buninyong [tests/vincenty.phpt] 
    =====================================================================
    TIME END 2018-04-07 10:13:43
    
    =====================================================================
    TEST RESULT SUMMARY
    ---------------------------------------------------------------------
    Exts skipped    :    0
    Exts tested     :   15
    ---------------------------------------------------------------------
    
    Number of tests :   20                20
    Tests skipped   :    0 (  0.0%) --------
    Tests warned    :    0 (  0.0%) (  0.0%)
    Tests failed    :    1 (  5.0%) (  5.0%)
    Expected fail   :    0 (  0.0%) (  0.0%)
    Tests passed    :   19 ( 95.0%) ( 95.0%)
    ---------------------------------------------------------------------
    Time taken      :    0 seconds
    =====================================================================
    
    =====================================================================
    FAILED TEST SUMMARY
    ---------------------------------------------------------------------
    Test for issue #16: Segfault on rdp_simplify for GeoJSON [tests/bug0016.phpt]
    =====================================================================
    
    
    Reviewed by mhf-ir at 2018-04-07 10:15
  • 6. Segfault on rdp_simplify for GeoJSON

    rdp_simplify method segfaults when passing GeoJSON data as an array (returns NULL if object or a string). This works if you just pass the coordinate array, but this isn't as described in the documentation.

    To recreate:

    $geojson = json_decode('{"type":"LineString","coordinates":[[0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[0,0]]}', true);
    
    var_dump(rdp_simplify($geojson, 1)); //segfault
    var_dump(rdp_simplify($geojson['coordinates'], 1)); //works
    
    Reviewed by rickogden at 2016-01-27 15:30
  • 7. PHP 8 support

    Is there any plan or timeline for supporting PHP 8? The current version fails to compile.

    I see there's a PR for adding support with the release candidate.

    Reviewed by jaikdean at 2021-01-28 09:51
  • 8. fixed the initial_bearing test

    This test was failing, but the expected result was not matching other implementations of this algorithm. This has been updated with the correct result.

    Reviewed by rickogden at 2015-07-02 13:59
  • 9. Compile failure with PHP 5.6 on Windows (using VC11)

    The compilation fails on Windows with VC11 due to a mix of declarations and assignments in the code. VC11 does not support that (and neither does C!).

    Reviewed by derickr at 2018-01-02 10:54
  • 10. Vincenty

    Been meaning to send in this for a while. Implemented vincenty inverse problem. Based it on http://en.wikipedia.org/wiki/Vincenty's_formulae and http://www.movable-type.co.uk/scripts/latlong-vincenty.html. Test case from http://www.ga.gov.au/geodesy/datums/vincenty_inverse.jsp.

    Reviewed by natmchugh at 2013-10-04 22:58
  • 11. Add Spherical Law Of Cosines method

    Spherical Law Of Cosines is commonly used instead of Haversine due to better performance and very close results. Generally, if you need higher accuracy than SLOC, Haversine is probably too inaccurate as well.

    See here for an explaination of the quality aspect: http://gis.stackexchange.com/questions/4906/why-is-law-of-cosines-more-preferable-than-haversine-when-calculating-distance-b

    See here for documentation: https://en.wikipedia.org/wiki/Spherical_law_of_cosines

    If you want, I have PHP code for it, but it's pretty trivial to implement yourself.

    Reviewed by vanderlee at 2015-12-18 20:47
GeoSpatial integration on Laravel 5.2+ that supports MySQL and PostgreSQL.

Features GeoSpatial integration on Laravel 5.2+: Create geospatial columns using Schema and migrations Save and retrieve geospatial attributes using d

May 22, 2022
Free database of geographic place names and corresponding geospatial data

FreeGeoDB Free database of geographic place names and corresponding geospatial data Entities airports cities countries (admin-0) lakes ports railroads

Jul 27, 2022
GeoJSON implementation for PHP

GeoJson PHP Library This library implements the GeoJSON format specification. The GeoJson namespace includes classes for each data structure defined i

Aug 4, 2022
ESRI ShapeFile library for PHP

shapefile ShapeFile library for PHP Features Currently the 2D and 3D variants except MultiPatch of the ShapeFile format as defined in https://www.esri

Jun 29, 2022
The most featured Geocoder library written in PHP.

Geocoder Important: You are browsing the documentation of Geocoder 4.x. Documentation for version 3.x is available here: Geocoder 3.x documentation. D

Aug 9, 2022
Simple Yet Powerful Geo Library for PHP

phpgeo - A Simple Geo Library for PHP phpgeo provides abstractions to geographical coordinates (including support for different ellipsoids) and allows

Jul 29, 2022
Geo-related tools PHP 5.4+ library built atop Geocoder and React libraries

Geotools Geotools is a PHP geo-related library, built atop Geocoder and React libraries. Features Batch geocode & reverse geocoding request(s) in seri

Aug 7, 2022
PHP library to access the OpenCage geocoding API
PHP library to access the OpenCage geocoding API

OpenCage Geocoding API Library for PHP A PHP library to use the OpenCage geocoding API. Build Status / Code Quality Overview This library attempts to

Jun 20, 2022
PHP library for determining the physical location of binaries

Bin Locator Library for searching binary files in the operating system. Requirements PHP >= 7.4 Installation Library is available as composer reposito

Jul 27, 2022
PHP library to easily get static image from French Cadastral Government map with markers and lines.
PHP library to easily get static image from French Cadastral Government map with markers and lines.

PHP French Cadastral Map Static API PHP library to easily get static image from French Cadastral Government map with markers and lines. Map source : c

May 24, 2022
PHP library to easily get static image from OpenStreetMap (OSM) with markers and lines.
PHP library to easily get static image from OpenStreetMap (OSM) with markers and lines.

PHP OpenStreetMap Static API PHP library to easily get static image from OpenStreetMap with markers and lines. ✨ Supporting ⭐ Star this repository to

Jul 28, 2022
Official PHP library for IPinfo (IP geolocation and other types of IP data)

This is the official PHP client library for the IPinfo.io IP address API, allowing you to lookup your own IP address,

Aug 14, 2022
Laravel package to work with geospatial data types and functions.

Laravel Spatial Laravel package to work with geospatial data types and functions. For now it supports only MySql Spatial Data Types and Functions. Sup

Jul 17, 2022
PHP extension for geospatial rendering with Mapnik
PHP extension for geospatial rendering with Mapnik

PHP7 Mapnik Introduction This project is an extension for PHP 7 that enables geospatial rendering with Mapnik. Create tile generation scripts, dynamic

Dec 17, 2021
GeoSpatial integration on Laravel 5.2+ that supports MySQL and PostgreSQL.

Features GeoSpatial integration on Laravel 5.2+: Create geospatial columns using Schema and migrations Save and retrieve geospatial attributes using d

May 22, 2022
Free database of geographic place names and corresponding geospatial data

FreeGeoDB Free database of geographic place names and corresponding geospatial data Entities airports cities countries (admin-0) lakes ports railroads

Jul 27, 2022
PHP Functional Programming library. Monads and common use functions.

Functional PHP PHP Functional Programming library. Monads and common use functions. Documentation Functions Monads Installation Composer $ composer re

Jul 25, 2022
Simple MySQL library for PHP 5.4+ includes Query Builder, PDO Native functions, Helper functions for quick use.

Simple MySQL library for PHP 5.4+ includes Query Builder, PDO Native functions, Helper functions for quick use.

Jul 14, 2022
Magento-Functions - A Resource of Magento Functions

Magento-Functions A Resource of Magento Functions Table of Contents Category Product User Cart Checkout General Account [Working w/ URL's] (#urls) Cat

Apr 19, 2021
This component provides a collection of functions/classes using the symfony/intl package when the Intl extension is not installed.

Symfony Polyfill / Intl: ICU This package provides fallback implementations when the Intl extension is not installed. It is limited to the "en" locale

Aug 11, 2022