{"id":13405267,"url":"https://github.com/thephpleague/geotools","last_synced_at":"2025-05-14T06:11:33.526Z","repository":{"id":6811730,"uuid":"8059663","full_name":"thephpleague/geotools","owner":"thephpleague","description":"Geo-related tools PHP 7.3+ library built atop Geocoder and React libraries","archived":false,"fork":false,"pushed_at":"2024-03-14T22:30:26.000Z","size":1741,"stargazers_count":1385,"open_issues_count":25,"forks_count":125,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-04-10T02:40:50.256Z","etag":null,"topics":["geo","geolocation","geometry","geotools","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thephpleague.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-02-06T20:42:40.000Z","updated_at":"2025-04-09T01:13:23.000Z","dependencies_parsed_at":"2023-01-13T14:06:55.253Z","dependency_job_id":"4843e69f-ddaf-4ace-848b-2ba19721f70b","html_url":"https://github.com/thephpleague/geotools","commit_stats":{"total_commits":417,"total_committers":41,"mean_commits":"10.170731707317072","dds":0.3165467625899281,"last_synced_commit":"e74454a4d66cc89be87dd3abd6027b693f944d35"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thephpleague%2Fgeotools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thephpleague%2Fgeotools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thephpleague%2Fgeotools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thephpleague%2Fgeotools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thephpleague","download_url":"https://codeload.github.com/thephpleague/geotools/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248313454,"owners_count":21082861,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["geo","geolocation","geometry","geotools","php"],"created_at":"2024-07-30T19:01:58.151Z","updated_at":"2025-04-10T23:24:19.164Z","avatar_url":"https://github.com/thephpleague.png","language":"PHP","funding_links":[],"categories":["PHP","Table of Contents","目录","地理位置 Geolocation","类库"],"sub_categories":["Geolocation","地理位置 Geolocation","未归类"],"readme":"Geotools\n========\n\n**Geotools** is a PHP geo-related library, built atop [Geocoder](https://github.com/willdurand/Geocoder) and\n[React](https://github.com/reactphp/react) libraries.\n\n[![Latest Version](https://poser.pugx.org/league/geotools/v/stable)](https://github.com/thephpleague/geotools/releases)\n[![Total Downloads](https://poser.pugx.org/league/geotools/downloads)](https://packagist.org/packages/league/geotools)\n[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/geotools.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/geotools/?branch=master)\n\nFeatures\n--------\n\n* **Batch** geocode \u0026 reverse geocoding request(s) in **series** / in **parallel** against one or a\n**set of providers**. [»](#batch)\n* **Cache** geocode \u0026 reverse geocoding result(s) with **PSR-6** to improve performances. [»](#batch)\n* Compute geocode \u0026 reverse geocoding in the **command-line interface** (CLI) + dumpers and formatters. [»](#cli)\n* Accept **almost** all kind of WGS84\n[geographic coordinates](http://en.wikipedia.org/wiki/Geographic_coordinate_conversion) as coordinates.\n[»](#coordinate--ellipsoid)\n* Support **23 different ellipsoids** and it's easy to provide a new one if needed. [»](#coordinate--ellipsoid)\n* **Convert** and **format** decimal degrees coordinates to decimal minutes or degrees minutes seconds coordinates.\n[»](#convert)\n* **Convert** decimal degrees coordinates in the\n[Universal Transverse Mercator](http://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system)\n(UTM) projection. [»](#convert)\n* Compute the distance in **meter** (by default), **km**, **mi** or **ft** between two coordinates using **flat**,\n**great circle**, **haversine** or **vincenty** algorithms. [»](#distance)\n* Compute the initial and final **bearing** from the origin coordinate to the destination coordinate in degrees.\n[»](#point)\n* Compute the initial and final **cardinal point** (direction) from the origin coordinate to the destination\ncoordinate, read more in [wikipedia](http://en.wikipedia.org/wiki/Cardinal_direction). [»](#point)\n* Compute the **half-way point** (coordinate) between the origin and the destination coordinates. [»](#point)\n* Compute the **destination point** (coordinate) with given bearing in degrees and a distance in meters. [»](#point)\n* Encode a coordinate to a **geo hash** string and decode it to a coordinate, read more in\n[wikipedia](http://en.wikipedia.org/wiki/Geohash) and on [geohash.org](http://geohash.org/). [»](#geohash)\n* Encode a coordinate via the 10:10 algorithm. [»](#1010)\n* **Polygon** class provides methods to check either a poing (coordinate) is in, or on the polygon's boundaries.\n[»](#polygon)\n* A **command-line interface** (CLI) for **Distance**, **Point**, **Geohash** and **Convert** classes. [»](#cli)\n* Integration with Frameworks: **Laravel 4**, **Silex** ... [»](#integration-with-frameworks)\n* ... more to come ...\n\n\nInstallation\n------------\n\n**Geotools** can be found on [Packagist](https://packagist.org/packages/league/geotools).\nThe recommended way to install **Geotools** is through [composer](http://getcomposer.org).\n\nRun the following on the command line:\n\n```\ncomposer require league/geotools\n```\n\n**Important:** you should use the `0.4` version if you use Geocoder `2.x` or/and PHP `5.3`.\n\nAnd install dependencies:\n\n```\ncomposer install\n```\n\nNow you can add the autoloader, and you will have access to the library:\n\n```php\n\u003c?php\n\nrequire 'vendor/autoload.php';\n```\n\n\nUsage \u0026 API\n-----------\n\n## Coordinate \u0026 Ellipsoid\n\nThe default geodetic datum is [WGS84](http://en.wikipedia.org/wiki/World_Geodetic_System) and coordinates are in\ndecimal degrees.\n\nHere are the available ellipsoids: `AIRY`, `AUSTRALIAN_NATIONAL`, `BESSEL_1841`, `BESSEL_1841_NAMBIA`,\n`CLARKE_1866`, `CLARKE_1880`, `EVEREST`, `FISCHER_1960_MERCURY`, `FISCHER_1968`, `GRS_1967`, `GRS_1980`,\n`HELMERT_1906`, `HOUGH`, `INTERNATIONAL`, `KRASSOVSKY`, `MODIFIED_AIRY`, `MODIFIED_EVEREST`,\n`MODIFIED_FISCHER_1960`, `SOUTH_AMERICAN_1969`, `WGS60`, `WGS66`, `WGS72`, and `WGS84`.\n\nIf you need to use an other ellipsoid, just create an array like this:\n``` php\n\u003c?php\n\n$myEllipsoid = \\League\\Geotools\\Coordinate\\Ellipsoid::createFromArray([\n    'name' =\u003e 'My Ellipsoid', // The name of the Ellipsoid\n    'a'    =\u003e 123.0, // The semi-major axis (equatorial radius) in meters\n    'invF' =\u003e 456.0 // The inverse flattening\n]);\n```\n\n**Geotools** is built atop [Geocoder](https://github.com/willdurand/Geocoder). It means it's possible to use the\n`\\Geocoder\\Model\\Address` directly but it's also possible to use a *string* or a simple *array* with its\nlatitude and longitude.\n\nIt supports [valid and acceptable geographic coordinates](http://en.wikipedia.org/wiki/Geographic_coordinate_conversion)\nlike:\n* 40:26:46N,079:56:55W\n* 40:26:46.302N 079:56:55.903W\n* 40°26′47″N 079°58′36″W\n* 40d 26′ 47″ N 079d 58′ 36″ W\n* 40.446195N 79.948862W\n* 40.446195, -79.948862\n* 40° 26.7717, -79° 56.93172\n\nLatitudes below -90.0 or above 90.0 degrees are *capped* through `\\League\\Geotools\\Coordinate\\Coordinate::normalizeLatitude()`.\nLongitudes below -180.0 or above 180.0 degrees are *wrapped* through `\\League\\Geotools\\Coordinate\\Coordinate::normalizeLongitude()`.\n\n```php\n\u003c?php\n\nuse League\\Geotools\\Coordinate\\Coordinate;\nuse League\\Geotools\\Coordinate\\Ellipsoid;\n\n// from an \\Geocoder\\Model\\Address instance within Airy ellipsoid\n$coordinate = new Coordinate($geocoderResult, Ellipsoid::createFromName(Ellipsoid::AIRY));\n// or in an array of latitude/longitude coordinate within GRS 1980 ellipsoid\n$coordinate = new Coordinate([48.8234055, 2.3072664], Ellipsoid::createFromName(Ellipsoid::GRS_1980));\n// or in latitude/longitude coordinate within WGS84 ellipsoid\n$coordinate = new Coordinate('48.8234055, 2.3072664');\n// or in degrees minutes seconds coordinate within WGS84 ellipsoid\n$coordinate = new Coordinate('48°49′24″N, 2°18′26″E');\n// or in decimal minutes coordinate within WGS84 ellipsoid\n$coordinate = new Coordinate('48 49.4N, 2 18.43333E');\n// the result will be:\nprintf(\"Latitude: %F\\n\", $coordinate-\u003egetLatitude()); // 48.8234055\nprintf(\"Longitude: %F\\n\", $coordinate-\u003egetLongitude()); // 2.3072664\nprintf(\"Ellipsoid name: %s\\n\", $coordinate-\u003egetEllipsoid()-\u003egetName()); // WGS 84\nprintf(\"Equatorial radius: %F\\n\", $coordinate-\u003egetEllipsoid()-\u003egetA()); // 6378136.0\nprintf(\"Polar distance: %F\\n\", $coordinate-\u003egetEllipsoid()-\u003egetB()); // 6356751.317598\nprintf(\"Inverse flattening: %F\\n\", $coordinate-\u003egetEllipsoid()-\u003egetInvF()); // 298.257224\nprintf(\"Mean radius: %F\\n\", $coordinate-\u003egetEllipsoid()-\u003egetArithmeticMeanRadius()); // 6371007.772533\n// it's also possible to modify the coordinate without creating an other coodinate\n$coordinate-\u003esetFromString('40°26′47″N 079°58′36″W');\nprintf(\"Latitude: %F\\n\", $coordinate-\u003egetLatitude()); // 40.446388888889\nprintf(\"Longitude: %F\\n\", $coordinate-\u003egetLongitude()); // -79.976666666667\n```\n\n## Convert\n\nIt provides methods (and aliases) to convert *decimal degrees* WGS84 coordinates to *degrees minutes seconds*\nor *decimal minutes* WGS84 coordinates. You can format the output string easily.\n\nYou can also convert them in the Universal Transverse Mercator (UTM) projection (Southwest coast of Norway and the\nregion of Svalbard are covered).\n\n```php\n\u003c?php\n\n$geotools   = new \\League\\Geotools\\Geotools();\n$coordinate = new \\League\\Geotools\\Coordinate\\Coordinate('40.446195, -79.948862');\n$converted  = $geotools-\u003econvert($coordinate);\n// convert to decimal degrees without and with format string\nprintf(\"%s\\n\", $converted-\u003etoDecimalMinutes()); // 40 26.7717N, -79 56.93172W\n// convert to degrees minutes seconds without and with format string\nprintf(\"%s\\n\", $converted-\u003etoDegreesMinutesSeconds('\u003cp\u003e%P%D:%M:%S, %p%d:%m:%s\u003c/p\u003e')); // \u003cp\u003e40:26:46, -79:56:56\u003c/p\u003e\n// convert in the UTM projection (standard format)\nprintf(\"%s\\n\", $converted-\u003etoUniversalTransverseMercator()); // 17T 589138 4477813\n```\n\nHere is the mapping:\n\n**Decimal minutes** | Latitude | Longitude\n--- | --- | ---\nPositive or negative sign | `%P` | `%p`\nDirection | `%L` | `%l`\nDegrees | `%D` | `%d`\nDecimal minutes | `%N` | `%n`\n\n**Degrees minutes seconds** | Latitude | Longitude\n--- | --- | ---\nPositive or negative sign | `%P` | `%p`\nDirection | `%L` | `%l`\nDegrees | `%D` | `%d`\nMinutes | `%M` | `%m`\nSeconds | `%S` | `%s`\n\n## Batch\n\nIt provides a very handy way to batch geocode and reverse geocoding requests in *serie* or in *parallel* against\na set of providers.\nThanks to [Geocoder](https://github.com/willdurand/Geocoder) and [React](https://github.com/reactphp/react) libraries.\n\nIt's possible to batch *one request* (a string) or a *set of request* (an array) against *one provider* or\n*set of providers*.\n\nYou can use a provided **cache engine** or use your own by setting a cache object which should implement\n`League\\Geotools\\Cache\\CacheInterface` and extend `League\\Geotools\\Cache\\AbstractCache` if needed.\n\nAt the moment Geotools supports any PSR-6 cache.\n\nNB: Before you implement caching in your app please be sure that doing so does not violate the Terms of Service\nfor your(s) geocoding provider(s).\n\n```php\n\u003c?php\n\n$geocoder = new \\Geocoder\\ProviderAggregator(); // or \\Geocoder\\TimedGeocoder\n$httpClient  = HttpClientDiscovery::find();\n\n$geocoder-\u003eregisterProviders([\n    new \\Geocoder\\Provider\\GoogleMaps\\GoogleMaps($httpClient),\n    new \\Geocoder\\Provider\\OpenStreetMap\\OpenStreetMap($httpClient),\n    new \\Geocoder\\Provider\\BingMaps\\BingMaps($httpClient, '\u003cFAKE_API_KEY\u003e'), // throws InvalidCredentialsException\n    new \\Geocoder\\Provider\\Yandex\\Yandex($httpClient),\n    new \\Geocoder\\Provider\\FreeGeoIp\\FreeGeoIp($httpClient),\n    new \\Geocoder\\Provider\\Geoip\\Geoip(),\n]);\n\ntry {\n    $geotools = new \\League\\Geotools\\Geotools();\n    $cache    = new \\Cache\\Adapter\\PHPArray\\ArrayCachePool();\n\n    $results  = $geotools-\u003ebatch($geocoder)-\u003esetCache($cache)-\u003egeocode([\n        'Paris, France',\n        'Copenhagen, Denmark',\n        '74.200.247.59',\n        '::ffff:66.147.244.214'\n    ])-\u003eparallel();\n} catch (\\Exception $e) {\n    die($e-\u003egetMessage());\n}\n\n$dumper = new \\Geocoder\\Dumper\\WktDumper();\nforeach ($results as $result) {\n    // if a provider throws an exception (UnsupportedException, InvalidCredentialsException ...)\n    // an custom /Geocoder/Result/Geocoded instance is returned which embedded the name of the provider,\n    // the query string and the exception string. It's possible to use dumpers\n    // and/or formatters from the Geocoder library.\n    printf(\"%s|%s|%s\\n\",\n        $result-\u003egetProviderName(),\n        $result-\u003egetQuery(),\n        '' == $result-\u003egetExceptionMessage() ? $dumper-\u003edump($result) : $result-\u003egetExceptionMessage()\n    );\n}\n```\n\nYou should get 24 results (4 values to geocode against 6 providers) something like:\n\n```\ngoogle_maps|Paris, France|POINT(2.352222 48.856614)\ngoogle_maps|Copenhagen, Denmark|POINT(12.568337 55.676097)\ngoogle_maps|74.200.247.59|The GoogleMapsProvider does not support IP addresses.\ngoogle_maps|::ffff:66.147.244.214|The GoogleMapsProvider does not support IP addresses.\nopenstreetmap|Paris, France|POINT(2.352133 48.856506)\nopenstreetmap|Copenhagen, Denmark|POINT(12.570072 55.686724)\nopenstreetmap|74.200.247.59|Could not execute query http://nominatim.openstreetmap.org/search?q=74.200.247.59\u0026format=xml\u0026addressdetails=1\u0026limit=1\nopenstreetmap|::ffff:66.147.244.214|The OpenStreetMapProvider does not support IPv6 addresses.\nbing_maps|Paris, France|Could not execute query http://dev.virtualearth.net/REST/v1/Locations/?q=Paris%2C+France\u0026key=\u003cFAKE_API_KEY\u003e\nbing_maps|Copenhagen, Denmark|Could not execute query http://dev.virtualearth.net/REST/v1/Locations/?q=Copenhagen%2C+Denmark\u0026key=\u003cFAKE_API_KEY\u003e\nbing_maps|74.200.247.59|The BingMapsProvider does not support IP addresses.\nbing_maps|::ffff:66.147.244.214|The BingMapsProvider does not support IP addresses.\nyandex|Paris, France|POINT(2.341198 48.856929)\nyandex|Copenhagen, Denmark|POINT(12.567602 55.675682)\nyandex|74.200.247.59|The YandexProvider does not support IP addresses.\nyandex|::ffff:66.147.244.214|The YandexProvider does not support IP addresses.\nfree_geo_ip|Paris, France|The FreeGeoIpProvider does not support Street addresses.\nfree_geo_ip|Copenhagen, Denmark|The FreeGeoIpProvider does not support Street addresses.\nfree_geo_ip|74.200.247.59|POINT(-122.415600 37.748400)\nfree_geo_ip|::ffff:66.147.244.214|POINT(-111.613300 40.218100)\ngeoip|Paris, France|The GeoipProvider does not support Street addresses.\ngeoip|Copenhagen, Denmark|The GeoipProvider does not support Street addresses.\ngeoip|74.200.247.59|POINT(-122.415604 37.748402)\ngeoip|::ffff:66.147.244.214|The GeoipProvider does not support IPv6 addresses.\n```\n\nBatch reverse geocoding is something like:\n\n```php\n\u003c?php\n\n// ... $geocoder like the previous example ...\n// If you want to reverse one coordinate\ntry {\n    $results = $geotools-\u003ebatch($geocoder)-\u003ereverse(\n        new \\League\\Geotools\\Coordinate\\Coordinate([2.307266, 48.823405])\n    )-\u003eparallel();\n} catch (\\Exception $e) {\n    die($e-\u003egetMessage());\n}\n// Or if you want to reverse geocoding 3 coordinates\n$coordinates = [\n    new \\League\\Geotools\\Coordinate\\Coordinate([2.307266, 48.823405]),\n    new \\League\\Geotools\\Coordinate\\Coordinate([12.568337, 55.676097]),\n    new \\League\\Geotools\\Coordinate\\Coordinate('-74.005973 40.714353')),\n];\n$results = $geotools-\u003ebatch($geocoder)-\u003ereverse($coordinates)-\u003eparallel();\n// ...\n```\n\nIf you want to batch it in serie, replace the method `parallel()` by `serie()`.\n\nTo optimize batch requests you need to register providers according to their **capabilities** and what you're\n**looking for** (geocode street addresses, geocode IPv4, geocode IPv6 or reverse geocoding),\nplease read more at the [Geocoder library doc](https://github.com/willdurand/Geocoder#freegeoipprovider).\n\n## Distance\n\nIt provides methods to compute the distance in *meter* (by default), *km*, *mi* or *ft* between two coordinates\nusing *flat* (most performant), *great circle*, *haversine* or *vincenty* (most accurate) algorithms.\n\nThose coordinates should be in the same ellipsoid.\n\n```php\n\u003c?php\n\n$geotools = new \\League\\Geotools\\Geotools();\n$coordA   = new \\League\\Geotools\\Coordinate\\Coordinate([48.8234055, 2.3072664]);\n$coordB   = new \\League\\Geotools\\Coordinate\\Coordinate([43.296482, 5.36978]);\n$distance = $geotools-\u003edistance()-\u003esetFrom($coordA)-\u003esetTo($coordB);\n\nprintf(\"%s\\n\",$distance-\u003eflat()); // 659166.50038742 (meters)\nprintf(\"%s\\n\",$distance-\u003egreatCircle()); // 659021.90812846\nprintf(\"%s\\n\",$distance-\u003ein('km')-\u003ehaversine()); // 659.02190812846\nprintf(\"%s\\n\",$distance-\u003ein('mi')-\u003evincenty()); // 409.05330679648\nprintf(\"%s\\n\",$distance-\u003ein('ft')-\u003eflat()); // 2162619.7519272\n```\n\n## Point\n\nIt provides methods to compute the initial and final *bearing* in degrees, the initial and final *cardinal direction*,\nthe *middle point* and the *destination point*. The middle and the destination points returns a\n`\\League\\Geotools\\Coordinate\\Coordinate` object with the same ellipsoid.\n\n```php\n\u003c?php\n\n$geotools = new \\League\\Geotools\\Geotools();\n$coordA   = new \\League\\Geotools\\Coordinate\\Coordinate([48.8234055, 2.3072664]);\n$coordB   = new \\League\\Geotools\\Coordinate\\Coordinate([43.296482, 5.36978]);\n$vertex    =  $geotools-\u003evertex()-\u003esetFrom($coordA)-\u003esetTo($coordB);\n\nprintf(\"%d\\n\", $vertex-\u003einitialBearing()); // 157 (degrees)\nprintf(\"%s\\n\", $vertex-\u003einitialCardinal()); // SSE (SouthSouthEast)\nprintf(\"%d\\n\", $vertex-\u003efinalBearing()); // 160 (degrees)\nprintf(\"%s\\n\", $vertex-\u003efinalCardinal()); // SSE (SouthSouthEast)\n\n$middlePoint = $vertex-\u003emiddle(); // \\League\\Geotools\\Coordinate\\Coordinate\nprintf(\"%s\\n\", $middlePoint-\u003egetLatitude()); // 46.070143125815\nprintf(\"%s\\n\", $middlePoint-\u003egetLongitude()); // 3.9152401085931\n\n$destinationPoint = $geotools-\u003evertex()-\u003esetFrom($coordA)-\u003edestination(180, 200000); // \\League\\Geotools\\Coordinate\\Coordinate\nprintf(\"%s\\n\", $destinationPoint-\u003egetLatitude()); // 47.026774650075\nprintf(\"%s\\n\", $destinationPoint-\u003egetLongitude()); // 2.3072664\n```\n\n## Geohash\n\nIt provides methods to get the *geo hash* and its *bounding box's coordinates* (SouthWest \u0026 NorthEast)\nof a coordinate and the *coordinate* and its *bounding box's coordinates* (SouthWest \u0026 NorthEast) of a geo hash.\n\n```php\n\u003c?php\n\n$geotools       = new \\League\\Geotools\\Geotools();\n$coordToGeohash = new \\League\\Geotools\\Coordinate\\Coordinate('43.296482, 5.36978');\n\n// encoding\n$encoded = $geotools-\u003egeohash()-\u003eencode($coordToGeohash, 4); // 12 is the default length / precision\n// encoded\nprintf(\"%s\\n\", $encoded-\u003egetGeohash()); // spey\n// encoded bounding box\n$boundingBox = $encoded-\u003egetBoundingBox(); // array of \\League\\Geotools\\Coordinate\\CoordinateInterface\n$southWest   = $boundingBox[0];\n$northEast   = $boundingBox[1];\nprintf(\"http://www.openstreetmap.org/?minlon=%s\u0026minlat=%s\u0026maxlon=%s\u0026maxlat=%s\u0026box=yes\\n\",\n    $southWest-\u003egetLongitude(), $southWest-\u003egetLatitude(),\n    $northEast-\u003egetLongitude(), $northEast-\u003egetLatitude()\n); // http://www.openstreetmap.org/?minlon=5.2734375\u0026minlat=43.2421875\u0026maxlon=5.625\u0026maxlat=43.41796875\u0026box=yes\n\n// decoding\n$decoded = $geotools-\u003egeohash()-\u003edecode('spey61y');\n// decoded coordinate\nprintf(\"%s\\n\", $decoded-\u003egetCoordinate()-\u003egetLatitude()); // 43.296432495117\nprintf(\"%s\\n\", $decoded-\u003egetCoordinate()-\u003egetLongitude()); // 5.3702545166016\n// decoded bounding box\n$boundingBox = $decoded-\u003egetBoundingBox(); //array of \\League\\Geotools\\Coordinate\\CoordinateInterface\n$southWest   = $boundingBox[0];\n$northEast   = $boundingBox[1];\nprintf(\"http://www.openstreetmap.org/?minlon=%s\u0026minlat=%s\u0026maxlon=%s\u0026maxlat=%s\u0026box=yes\\n\",\n    $southWest-\u003egetLongitude(), $southWest-\u003egetLatitude(),\n    $northEast-\u003egetLongitude(), $northEast-\u003egetLatitude()\n); // http://www.openstreetmap.org/?minlon=5.3695678710938\u0026minlat=43.295745849609\u0026maxlon=5.3709411621094\u0026maxlat=43.297119140625\u0026box=yes\n```\n\nYou can also get information about neighbor points ([image](art/geohash_neighbor_points.png)).\n\n```php\n\u003c?php\n\n$geotools = new \\League\\Geotools\\Geotools();\n\n// decoding\n$decoded = $geotools-\u003egeohash()-\u003edecode('spey61y');\n// get neighbor geohash\nprintf(\"%s\\n\", $decoded-\u003egetNeighbor(\\League\\Geotools\\Geohash\\Geohash::DIRECTION_NORTH)); // spey64n\nprintf(\"%s\\n\", $decoded-\u003egetNeighbor(\\League\\Geotools\\Geohash\\Geohash::DIRECTION_SOUTH_EAST)); // spey61x\n// get all neighbor geohashes\nprint_r($decoded-\u003egetNeighbors(true));\n/**\n * Array\n * (\n *     [north] =\u003e spey64n\n *     [south] =\u003e spey61w\n *     [west] =\u003e spey61v\n *     [east] =\u003e spey61z\n *     [north_west] =\u003e spey64j\n *     [north_east] =\u003e spey64p\n *     [south_west] =\u003e spey61t\n *     [south_east] =\u003e spey61x\n * )\n */\n```\n\n## 10:10\n\nRepresent a location with 10m accuracy using a 10 character code that includes features to prevent errors in\nentering the code. Read more about the algorithm [here](http://blog.jgc.org/2006/07/simple-code-for-entering-latitude-and.html).\n\n```php\n\u003c?php\n\n$tenten = new \\League\\Geotools\\Tests\\Geohash\\TenTen;\n$tenten-\u003eencode(new Coordinate([51.09559, 1.12207])); // MEQ N6G 7NY5\n```\n\n## Vertex\n\nRepresents a segment with a direction.\nYou can find if two vertexes are on the same line.\n\n```php\n\u003c?php\n\t$vertexA-\u003esetFrom(48.8234055);\n\t$vertexA-\u003esetTo(2.3072664);\n\n\t$vertexB-\u003esetFrom(48.8234055);\n\t$vertexB-\u003esetTo(2.3072664);\n\t$vertexA-\u003eisOnSameLine($vertexB);\n```\n\n## Polygon\n\nIt helps you to know if a point (coordinate) is in a Polygon or on the Polygon's boundaries and if this in on\na Polygon's vertex.\n\nFirst you need to create the polygon, you can provide:\n- an array of arrays\n- an array of `Coordinate`\n- a `CoordinateCollection`\n\n```php\n\u003c?php\n\n$polygon = new \\League\\Geotools\\Polygon\\Polygon([\n    [48.9675969, 1.7440796],\n    [48.4711003, 2.5268555],\n    [48.9279131, 3.1448364],\n    [49.3895245, 2.6119995],\n]);\n\n$polygon-\u003esetPrecision(5); // set the comparision precision\n$polygon-\u003epointInPolygon(new \\League\\Geotools\\Coordinate\\Coordinate([49.1785607, 2.4444580])); // true\n$polygon-\u003epointInPolygon(new \\League\\Geotools\\Coordinate\\Coordinate([49.1785607, 5])); // false\n$polygon-\u003epointOnBoundary(new \\League\\Geotools\\Coordinate\\Coordinate([48.7193486, 2.13546755])); // true\n$polygon-\u003epointOnBoundary(new \\League\\Geotools\\Coordinate\\Coordinate([47.1587188, 2.87841795])); // false\n$polygon-\u003epointOnVertex(new \\League\\Geotools\\Coordinate\\Coordinate([48.4711003, 2.5268555])); // true\n$polygon-\u003epointOnVertex(new \\League\\Geotools\\Coordinate\\Coordinate([49.1785607, 2.4444580])); // false\n$polygon-\u003egetBoundingBox(); // return the BoundingBox object\n```\n\n## CLI\n\nIt provides command lines to compute methods provided by **Distance**, **Point**, **Geohash** and **Convert** classes.\nThanks to the [Symfony Console Component](https://github.com/symfony/Console).\n\n```bash\n$ php geotools list // list of available commands\n$ php geotools help distance:flat // get the help\n$ php geotools distance:flat \"40° 26.7717, -79° 56.93172\" \"30°16′57″N 029°48′32″W\" // 4690203.1048522\n$ php geotools distance:haversine \"35,45\" \"45,35\" --ft  // 4593030.9787593\n$ php geotools distance:vincenty \"35,45\" \"45,35\" --km  // 1398.4080717661\n$ php geotools d:v \"35,45\" \"45,35\" --km --ellipsoid=WGS60 // 1398.4145201642\n$ php geotools point:initial-cardinal \"40:26:46.302N 079:56:55.903W\" \"43.296482, 5.36978\" // NE (NordEast)\n$ php geotools point:final-cardinal \"40:26:46.302N 079:56:55.903W\" \"43.296482, 5.36978\" // ESE (EastSouthEast)\n$ php geotools point:destination \"40° 26.7717, -79° 56.93172\" 25 10000 // 40.527599285543, -79.898914904538\n$ php geotools p:d \"40° 26.7717, -79° 56.93172\" 25 10000 --ellipsoid=GRS_1980 // 40.527599272782, -79.898914912379\n$ php geotools geohash:encode \"40° 26.7717, -79° 56.93172\" --length=3 // dpp\n$ php geotools convert:dm \"40.446195, -79.948862\" --format=\"%P%D°%N %p%d°%n\" // 40°26.7717 -79°56.93172\n$ php geotools convert:dms \"40.446195, -79.948862\" --format=\"%P%D:%M:%S, %p%d:%m:%s\" // 40:26:46, -79:56:56\n$ php geotools convert:utm \"60.3912628, 5.3220544\" // 32V 297351 6700644\n$ php geotools c:u \"60.3912628, 5.3220544\" --ellipsoid=AIRY // 32V 297371 6700131\n...\n```\n\nCompute street addresses, IPv4s or IPv6s geocoding and reverse geocoding right in your console.\n\nIt's possible to define and precise your request through these options:\n* `--provider`: `bing_maps`, `yahoo`, `maxmind`... `google_maps` is the default one. See the full list\n[here](https://github.com/willdurand/Geocoder#providers).\n* `--raw`: the result output in RAW format, shows Adapter, Provider and Arguments if any.\n* `--json`: the result output in JSON string format.\n* `--args`: this option accepts multiple values (e.g. --args=\"API_KEY\" --args=\"LOCALE\") if your provider needs or\ncan have arguments.\n* `--dumper`: this option is available for geocoding, `gpx`, `geojson`, `kml`, `wkb` and `wkt` by default.\nRead more [here](https://github.com/willdurand/Geocoder#dumpers).\n* `--format`: this option is available for reverse geocoding, see the mapping\n[here](https://github.com/willdurand/Geocoder#formatter).\n\n```bash\n$ php geotools help geocoder:geocode // get the help\n$ php geotools geocoder:geocode \"Copenhagen, Denmark\" // 55.6760968, 12.5683371\n$ php geotools geocoder:geocode \"74.200.247.59\" --provider=\"free_geo_ip\" // 37.7484, -122.4156\n$ php geotools geocoder:geocode Paris --args=\"fr_FR\" --args=\"France\" --args=\"true\" // 48.856614, 2.3522219\n$ php geotools geocoder:geocode Paris --dumper=wkt // POINT(2.352222 48.856614)\n...\n$ php geotools geocoder:reverse \"48.8631507, 2.388911\" // Avenue Gambetta 10, 75020 Paris\n$ php geotools geocoder:reverse \"48.8631507, 2.388911\" --format=\"%L, %A1, %C\" // Paris, Île-De-France, France\n$ php geotools geocoder:reverse \"48.8631507, 2.388911\" --format=\"%L, %A1, %C\" --provider=\"openstreetmap\"\n// Paris, Île-De-France, France Métropolitaine\n...\n$ php geotools geocoder:geocode \"Tagensvej 47, Copenhagen\" --raw --args=da_DK --args=Denmark\n```\n\nThe last command will show an output like this:\n\n```\nHttpClient:    \\Http\\Client\\Curl\\Client\nProvider:      \\Geocoder\\Provider\\GoogleMaps\nCache:         \\League\\Geotools\\Cache\\Redis\nArguments:     da_DK,Denmark\n---\nLatitude:      55.699953\nLongitude:     12.552736\nBounds\n - South: 55.699953\n - West:  12.552736\n - North: 55.699953\n - East:  12.552736\nStreet Number: 47\nStreet Name:   Tagensvej\nZipcode:       2200\nCity:          Copenhagen\nCity District: København N\nCounty:        København\nCounty Code:   KØBENHAVN\nRegion:        Capital Region Of Denmark\nRegion Code:   CAPITAL REGION OF DENMARK\nCountry:       Denmark\nCountry Code:  DK\nTimezone:\n```\n\nIntegration with Frameworks\n---------------------------\n\n* [Laravel 4 \u0026 5](https://github.com/toin0u/Geotools-laravel)\n* [Silex](https://github.com/toin0u/Geotools-silex)\n* ...\n\n\nUnit Tests\n----------\n\nTo run unit tests, you'll need the `cURL` extension and a set of dependencies, you can install them using Composer:\n\n```bash\n$ php composer.phar install --dev\n```\n\nOnce installed, just launch the following command:\n\n```bash\n$ phpunit --coverage-text\n```\n\n\nCredits\n-------\n\n* [Antoine Corcy](https://twitter.com/toin0u)\n* [Pascal Borreli](https://twitter.com/pborreli)\n* [Phil Sturgeon](https://twitter.com/philsturgeon)\n* [Gabriel Bull](mailto:me@gabrielbull.com)\n* [All contributors](https://github.com/toin0u/Geotools/contributors)\n\n\nAcknowledgments\n---------------\n* [Geocoder](https://github.com/willdurand/Geocoder) -\n[MIT](https://raw.github.com/willdurand/Geocoder/master/LICENSE)\n* [ReactPHP](https://github.com/reactphp/) -\n[MIT](https://raw.github.com/reactphp/react/master/LICENSE)\n* [Symfony Console Component](https://github.com/symfony/Console) -\n[MIT](https://raw.github.com/symfony/Console/master/LICENSE)\n* [Symfony Serializer Component](https://github.com/symfony/Serializer) -\n[MIT](https://raw.github.com/symfony/Serializer/master/LICENSE)\n* [PHP client library for Redis](https://github.com/nrk/predis) -\n[MIT](https://raw.github.com/nrk/predis/master/LICENSE)\n* [Geokit](https://github.com/jsor/Geokit),\n[Geotools-for-CodeIgniter](https://github.com/weejames/Geotools-for-CodeIgniter),\n[geotools-php](https://github.com/jillesvangurp/geotools-php) ...\n\n\nChangelog\n---------\n\n[See the changelog file](https://github.com/thephpleague/geotools/blob/master/CHANGELOG.md)\n\nContributing\n------------\n\nPlease see [CONTRIBUTING](https://github.com/thephpleague/geotools/blob/master/CONTRIBUTING.md) for details.\n\nSupport\n-------\n\nBugs and feature request are tracked on [GitHub](https://github.com/thephpleague/geotools/issues)\n\nContributor Code of Conduct\n---------------------------\n\nAs contributors and maintainers of this project, we pledge to respect all people\nwho contribute through reporting issues, posting feature requests, updating\ndocumentation, submitting pull requests or patches, and other activities.\n\nWe are committed to making participation in this project a harassment-free\nexperience for everyone, regardless of level of experience, gender, gender\nidentity and expression, sexual orientation, disability, personal appearance,\nbody size, race, age, or religion.\n\nExamples of unacceptable behavior by participants include the use of sexual\nlanguage or imagery, derogatory comments or personal attacks, trolling, public\nor private harassment, insults, or other unprofessional conduct.\n\nProject maintainers have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct. Project maintainers who do not follow the\nCode of Conduct may be removed from the project team.\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by opening an issue or contacting one or more of the project\nmaintainers.\n\nThis Code of Conduct is adapted from the [Contributor\nCovenant](https://contributor-covenant.org), version 1.0.0, available at\n[https://contributor-covenant.org/version/1/0/0/](https://contributor-covenant.org/version/1/0/0/)\n\nLicense\n-------\n\nGeotools is released under the MIT License. See the bundled\n[LICENSE](https://github.com/thephpleague/geotools/blob/master/LICENSE) file for details.\n\n[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/toin0u/Geotools/trend.png)](https://bitdeli.com/free \"Bitdeli Badge\")\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthephpleague%2Fgeotools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthephpleague%2Fgeotools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthephpleague%2Fgeotools/lists"}