Skip to main content

GeodesyPHP - A Great-Earth Distance library

Geodesy-PHP


Geodesy-PHP is a port of some known geodesic/math functions for getting distance from a known point A to a known point B, given their coordinates (good for working out distances between different latitude/longitude data provided by Google Geolocation or any RESTful APIs).

It also supports conversion between units of length, Polar position to Cartesian coordinates, and transforming different Reference Datums.

It provides distance calculations thru:




  • Spherical Law of Cosines
  • Haversine formula (Half a Versine - versed sine)
  • Vincenty's formula
  • Thomas' formula
  • Hubeny's formula
  • Andoyer-Lambert's formula
  • Elliptic Distance
  • Forsythe-Andoyer-Lambert Formula


  • Note: This library is a collection that solves the Inverse geodetic problem.

    Installation:

    
    composer require jtejido/geodesy-php
    

    Usage

    Distance Calculation

    All classes receives and gives all values in Metre unit of length by default.

    
    use Geodesy\Location\LatLong;
    use Geodesy\Distance\HaversineFormula;
    use Geodesy\Distance\VincentyFormula;
    use Geodesy\Distance\SphericalCosine;
    use Geodesy\Distance\ThomasFormula;
    use Geodesy\Distance\HubenyFormula;
    use Geodesy\Distance\AndoyerLambert;
    use Geodesy\Distance\ForsytheCorrection;
    use Geodesy\Distance\EllipticDistance;
    use Geodesy\Unit\KiloMetre;
    use Geodesy\Unit\Mile;
    use Geodesy\Unit\NauticalMile;
    use Geodesy\Unit\AstronomicalUnit;
    
    $lat1 = 48.148636;
    $long1 = 17.107558;
    
    $lat2 = 48.208810;
    $long2 = 16.372477;
    
    $loc1 = new LatLong;
    $loc1->setLatitude($lat1);
    $loc1->setLongitude($long1);
    
    $loc2 = new LatLong;
    $loc2->setLatitude($lat2);
    $loc2->setLongitude($long2);
    
    // See other distance options above
    $distance = new VincentyFormula($loc1, $loc2);
    $distance->setUnit(new KiloMetre); // Changing from m to Km, see other unit options above
    
    // get the distance
    print_r($distance->getDistance());
    
    /**
     * Is within range?
     * Take note that the value given here depends on the unit of length set before measuring the distance.
     */
    if($distance->isInRange(500)) {
          print_r('Yehey! your long lost friend is near you');
    }
    

    Position Conversion

    You can also convert between Polar Coordinates to ECEF (Cartesian) and Google's Web Mercator projection and vice versa.

    
    use Geodesy\Location\LatLong;
    use Geodesy\Location\ECEF;
    use Geodesy\Location\WebMercator;
    use Geodesy\Unit\KiloMetre;
    use Geodesy\Conversion\LLA2ECEF;
    use Geodesy\Conversion\ECEF2LLA;
    use Geodesy\Conversion\LLA2WebMercator;
    use Geodesy\Conversion\WebMercator2LLA;
    
    $latitude = 48.148636;
    $longitude = 17.107558;
    $altitude = 100;
    
    $location = new LatLong;
    $location->setLatitude($latitude);
    $location->setLongitude($longitude);
    $location->setAltitude($altitude);
    
    $conversion = new LLA2ECEF($location);
    $value = $conversion->convert();
    
    
    // It returns an ECEF object by default so we need to get x, y and z thru methods
    $value->getX();
    $value->getY();
    $value->getZ();
    
    // You can also convert polar coordinates to Google's WebMercator Projection
    $conversion = new LLA2WebMercator($location);
    $value = $conversion->convert();
    
    // As it returns a WebMercator object, we only have x and y values.
    $value->getX();
    $value->getY();
    
    // Then convert it back to LLA (we only get latitude and longitude back since it's only x and y)
    $conversion = new WebMercator2LLA($value);
    $value = $conversion->convert();
    
    $X = 4074.7830332832;
    $Y = 1254.1553518196;
    $Z = 4727.9191749041;
    
    $location = new ECEF;
    $location->setX($X);
    $location->setY($Y);
    $location->setZ($Z);
    
    // You can also set Unit here to specify whether the ouput and/or input had to be converted (default to Metre)
    $conversion = new ECEF2LLA($location);
    $conversion->setUnit(new KiloMetre);
    $value = $conversion->convert();
    $value->getLatitude();
    $value->getLongitude();
    $value->getAltitude();
    

    Reference Conversion

    You may have gotten your data from two different sources that uses entirely different standards (providing different sets of coordinates acquired from, say, a Foreign/Government Body/Engineering & Petroleum Industries' API for instance). Measuring distance between 2 different coordinate references can go off by a lot if we're not aware of their sources, thus we have what we call datum shift. In order to fix this, a way has been added to specify these Reference datums and normalize them to a common one thru a datum transformation method.

    So unless you're using Google's Geolocation API (or others that use an Internationally/Globally covered area like WGS84 or WGS72) as your source, you can add/create your own Datum Reference (use DatumInterface) as there has been a lot of these 'standards' covering local projections making the computation for a nearly true-Earth. More datum info and parameters is available here to be downloaded. (Take note that these datums are frequently updated - taking tectonic movements, mathematical precision and local Government's geodetic mapping/surverying into consideration)

    Geodesy-PHP's Datum Namespace (and their corresponding Ellipsoid Models in Models namespace) gives you the list of what is currently included.

    Take note that the following transformations are not time-dependent transformations, so manually updating the datums (WGS84 itself have even updated 5 times since '84) and a thorough search is needed on your end.

    I've actually worked these out since some epsg's datum have epoch rate per year info (or if your source app have a rate per year data, that would be nice too), then doing a Time-Dependent (14-parameter) Helmert transformation:











    use Geodesy\Location\LatLong;
    use Geodesy\Location\ECEF;
    use Geodesy\Unit\KiloMetre;
    use Geodesy\Conversion\LLA2ECEF;
    use Geodesy\Conversion\ECEF2LLA;
    use Geodesy\Datum\PRS92;
    use Geodesy\Distance\VincentyFormula;
    use Geodesy\Transformer\HelmertTransform;
    
    $lat1 = 25.2522;
    $long1 = 55.28;
    
    $lat2 = 14.6042;
    $long2 = 120.982;
    
    // Not setting Reference will default it to WGS84
    $loc1 = new LatLong;
    $loc1->setLatitude($lat1);
    $loc1->setLongitude($long1);
    
    $loc2 = new LatLong;
    $loc2->setLatitude($lat2);
    $loc2->setLongitude($long2);
    $loc2->setReference(new PRS92);
    
    // Note: You can get some information regarding the datum thru the following methods
    $loc2->getReference()->getArea(); // Area where the datum is normally used or applicable
    $loc2->getReference()->getName(); // The Complete Name of the datum
    $loc2->getReference()->getOrigin(); // Information about the point of origin
    $loc2->getReference()->getCRSCode(); // The datum's EPSG CSR id
    $loc2->getReference()->getScope(); // The scope of the datum vector's accuracy
    $loc2->getReference()->getSource(); // The datum's literature, for your reference
    
    
    /** 
     * The transformation is done inside, by converting the source's and destination's datum to WGS84, before measuring the distance.
     * Not setting Transformer defaults to MolodenskyBadekasTransform
     */
    $distance = new VincentyFormula($loc1, $loc2);
    $distance->setTransformer(new HelmertTransform);
    print_r($distance->getDistance());
    
    
    /**
     * Or if you only have one point that you want to just convert (and feed the info to an external file/storage), 
     * or you just wish to have it converted before getting the distance.
     * The method transform() receives the following -- transform(LatLong $source, DatumInterface $targetDatum)
     */
    $transform = new HelmertTransform;
    print_r($transform->transform($loc2, new WGS84));
    


    The following options are here for your reference.

    Important Note: Though the Molodensky-Badekas is effectively used when transforming local to a Global datum(that is why we use it as the default transformer in any of our Distance formulas, since they're all implemented to transform any non-WGS84 datums to WGS84 first before getting the distance), caution must be taken care of when using it as stand-alone conversion between two local datums (it is wise to see the datums' Area of Use by using getArea() method), because the evaluation point coordinates are in the forward direction source coordinate reference system, and the rotations have been derived about this point (thus, this transformation is known as non-reversible).

    Transformation Options

    • 3-Parameter 'Abridged' MolodenskyTransform
    • 7-Parameter HelmertTransform($isCoordinateFrameRotation = false)
    • 10-Parameter MolodenskyBadekasTransform

    Popular posts from this blog

    Basset - Information Retrieval Library in PHP

    Basset Basset is a full-text  PHP Information Retrieval library. This is a collection of developments in the field of IR and ported over to PHP for research purposes.

    Basset provides different ways of searching through documents in a collection (ad-hoc), by applying advanced and experimental IR algorithms and/or techniques gathered from different Research studies and Conferences, most notably:
    TRECSIGIRECIRACM BasicsWarning: This is a tool that is continuously under development. Please use this as a research tool for your otherwise special Production needs. Adding Documents Basset manages adding document thru the IndexWriter Class.
    It processes the documents you'll be adding in and later on commit to an external file.
    It takes a directory path, and overwrite (they both default to '../index/' and true consecutively).
    Setting overwrite to false means that you won't be accidentally overwriting any existing index inside the directory.

    Methods:

    addDocument(DocumentInterface $…

    Hermes - My Need For An Alternate Key-Value Store

    Hermes
    Yet another concurrent, lightweight, fast and efficient In-Memory Key-Value Store alternative. This was a sub-component of a pet IR system in Go. I thought this is helpful enough and can stand on its own and can help alleviate disk I/O operations (database, file, etc.) in a quite-not-so-small-but-single-machine kind of systems.
    What makes Hermes unique is its use of the ff:
    LRFU cache (which makes for a swift transition between LRU and LFU).Bloom filter implementation (Cuckoo Filter, to allow for item deletion in every eviction done by the policy) to avoid caching one-hit-wonders (disabled by default, please see the toml file to enable it) for memory efficiency.HAMT data structure (which makes it memory efficient at a nearly O(1) operation)
    Use this in your app like so (as embedded): package main import ( "fmt" "github.com/jtejido/hermes/hermes" "github.com/jtejido/hermes/config" "github.com/BurntSushi/toml" "sync" "st…