Introduction
phpgeo is a small PHP library which provides abstractions to geographical coordinates (including support for different ellipsoids), polylines ("GPS Tracks"), polygons, bounds, and more. phpgeo allows you to perform different calculations with these abstractions, as distances, track lengths, etc.
phpgeo is developed by Marcus Jaschen and all contributors.
phpgeo is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 (GPL3.0)
The project is hosted on Github:
Requirements
phpgeo requires at least PHP 5.4.0.
Installation
phpgeo is best be installed using Composer. Please visit the Composer website website for more information.
To install phpgeo, simply "require" it using Composer:
php composer.phar require mjaschen/phpgeo
phpgeo is now ready to be used in your project!
API Documentation
Detailed API documentation is available.
Geometries
phpgeo provides several geometry classes:
A Coordinate represents a geographic location, i. e. it contains a latitude and a longitude  together with an so called Ellipsoid.
A Line consists of two coordinates, while polylines and polygons are built from two or more coordinates.
Coordinate
The Coordinate
class is the most important class of phpgeo and provides the
base for all features. It’s a representation of a geographic location and
consists of three parts:

Geographic Latitude

Geographic Longitude

Ellipsoid
Geographic latitude and longitude values are float numbers between 90.0 and 90.0 (degrees latitude) and 180.0 and 180.0 (degrees longitude).
The Ellipsoid is a representation of an approximated shape of the earth and is abstracted in its own Ellipsoid class.
Line
A line consists of two points, i. e. instances of the Coordinate
class.
Length
The Line
class provides a method to calculate its own length. The method
expects an instance of a class which implements the DistanceInterface
.
<?php
use Location\Coordinate;
use Location\Distance\Haversine;
use Location\Line;
$line = new Line(
new Coordinate(52.5, 13.5),
new Coordinate(52.6, 13.4)
);
$length = $line>getLength(new Haversine()); (1)
printf("The line has a length of %.3f meters\n", $length);
1  Haversine is one of the currently two available classes for
distance calculation. The other one is named Vincenty . 
The code above will produce the output below:
The line has a length of 13013.849 meters
Bearing
The bearing of an instance can be calculated using the getBearing()
method.
An instance of BearingInterface
must be provided as method argument.
<?php
use Location\Bearing\BearingEllipsoidal;
use Location\Coordinate;
use Location\Line;
$line = new Line(
new Coordinate(52.5, 13.5),
new Coordinate(52.6, 13.4)
);
$bearing = $line>getBearing(new BearingEllipsoidal()); (1)
printf("The line has a bearing of %.2f degrees\n", $bearing);
1  BearingEllipsoidal is one of the currently two available classes for
bearing calculation. The other one is named BearingSpherical . 
The code above will produce the output below:
The line has a bearing of 328.67 degrees
This ist the so called initial bearing. There exist another bearing angle, called the final bearing. It can be calculated as well:
<?php
use Location\Bearing\BearingEllipsoidal;
use Location\Coordinate;
use Location\Line;
$line = new Line(
new Coordinate(52.5, 13.5),
new Coordinate(52.6, 13.4)
);
$bearing = $line>getFinalBearing(new BearingEllipsoidal());
printf("The line has a final bearing of %.2f degrees\n", $bearing);
The code above will produce the output below:
The line has a final bearing of 328.59 degrees
See Bearing between two points for more information about bearings.
Polyline
A polyline consists of an ordered list of locations, i. e. instances of
the Coordinate
class.
Create a polyline
To create a polyline, just instantiate the class and add points:
<?php
use Location\Coordinate;
use Location\Polyline;
$polyline = new Polyline();
$polyline>addPoint(new Coordinate(52.5, 13.5));
$polyline>addPoint(new Coordinate(54.5, 12.5));
$polyline>addPoint(new Coordinate(55.5, 14.5));
?>
It’s possible to add points to the end of the polyline at every time.
Segments
It’s possible to get a list of polyline segments. Segments are returned as an
array of Line
instances.
<?php
use Location\Coordinate;
use Location\Polyline;
$track = new Polyline();
$track>addPoint(new Coordinate(52.5, 13.5));
$track>addPoint(new Coordinate(54.5, 12.5));
$track>addPoint(new Coordinate(55.5, 14.5));
foreach ($track>getSegments() as $segment) {
printf(
"Segment length: %0.2f kilometers\n",
($segment>getLength(new Haversine()) / 1000)
);
}
The code above will produce the output below:
Segment length: 232.01 kilometers Segment length: 169.21 kilometers
Length
Length calculation is described in the Distance and Length section.
Reverse Direction
It’s possible to get a new instance with reversed direction while the original polyline stays unchanged:
<?php
use Location\Coordinate;
use Location\Polyline;
$track = new Polyline();
$track>addPoint(new Coordinate(52.5, 13.5));
$track>addPoint(new Coordinate(54.5, 12.5));
$reversed = $track>getReverse();
print_r($reversed);
The code above will produce the output below:
Location\Polyline Object ( [points:protected] => Array ( [0] => Location\Coordinate Object ( [lat:protected] => 54.5 [lng:protected] => 12.5 [ellipsoid:protected] => Location\Ellipsoid Object ( [name:protected] => WGS84 [a:protected] => 6378137 [f:protected] => 298.257223563 ) ) [1] => Location\Coordinate Object ( [lat:protected] => 52.5 [lng:protected] => 13.5 [ellipsoid:protected] => Location\Ellipsoid Object ( [name:protected] => WGS84 [a:protected] => 6378137 [f:protected] => 298.257223563 ) ) ) )
Polygon
A polygon consists of an ordered list of locations, i. e. instances of
the Coordinate
class. It’s very similar to a polyline, but its start
and end points are connected.
Create a polygon
To create a polygon, just instantiate the class and add points:
<?php
use Location\Coordinate;
use Location\Polygon;
$polygon = new Polygon();
$polygon>addPoint(new Coordinate(52.5, 13.5));
$polygon>addPoint(new Coordinate(54.5, 12.5));
$polygon>addPoint(new Coordinate(55.5, 14.5));
?>
It’s possible to add points to the end at every time.
Get list of points
getPoints()
is used to get the list of points, the number of points can be
retrieved by calling getNumberOfPoints()
:
<?php
use Location\Coordinate;
use Location\Formatter\Coordinate\DMS;
use Location\Polygon;
$polygon = new Polygon();
$polygon>addPoint(new Coordinate(52.5, 13.5));
$polygon>addPoint(new Coordinate(54.5, 12.5));
$polygon>addPoint(new Coordinate(55.5, 14.5));
printf("The polygon consists of %d points:\n", $polygon>getNumberOfPoints());
foreach ($polygon>getPoints() as $point) {
echo $point>format(new DMS()) . PHP_EOL;
}
The code above will produce the output below:
The polygon consists of 3 points: 52° 30′ 00″ 013° 30′ 00″ 54° 30′ 00″ 012° 30′ 00″ 55° 30′ 00″ 014° 30′ 00″
Segments
It’s possible to get a list of polygon segments. Segments are
returned as an array of Line
instances.
<?php
use Location\Coordinate;
use Location\Distance\Haversine;
use Location\Polygon;
$polygon = new Polygon();
$polygon>addPoint(new Coordinate(52.5, 13.5));
$polygon>addPoint(new Coordinate(54.5, 12.5));
$polygon>addPoint(new Coordinate(55.5, 14.5));
foreach ($polygon>getSegments() as $line) {
printf("%0.3f m\n", $line>getLength(new Haversine()));
}
The code above will produce the output below:
232011.020 m 169207.795 m 339918.069 m
Length/Perimeter
Length calculation is described in the Distance and Length section.
Geofence
It’s possible to check if a geometry object (point, line, polyline, polygon) lies inside a polygon. The documentation can be found in the Geofence section.
Reverse Direction
It’s possible to get a new instance with reversed direction while the original polygon stays unchanged:
<?php
use Location\Coordinate;
use Location\Polygon;
use Location\Formatter\Coordinate\DecimalDegrees;
$polygon = new Polygon();
$polygon>addPoint(new Coordinate(52.5, 13.5));
$polygon>addPoint(new Coordinate(64.1,  21.9));
$polygon>addPoint(new Coordinate(40.7,  74.0));
$polygon>addPoint(new Coordinate(33.9,  118.4));
$reversed = $polygon>getReverse();
foreach ($reversed>getPoints() as $point) {
echo $point>format(new DecimalDegrees(', ')) . PHP_EOL;
}
The code above produces the output below:
33.90000, 118.40000 40.70000, 74.00000 64.10000, 21.90000 52.50000, 13.50000
Bounds
Ellipsoid
An ellipsoid is a mathematically defined approximation of the earth’s surface.
An ellipsoid is defined by two parameters:

the semimajor axis a (equatorial radius)

the semiminor axis b (polar radius)
a and b together define the flattening of the ellipsoid f:
\(f = \frac{ab}{a}\)
phpgeo’s ellipsoids are defined by a and 1/f instead of a and b. That’s not a problem because each of the three values can be calculated from the other two. 
phpgeo supports arbitrary ellipsoids. WGS84 is used as default when no other ellipsoid is given. For daytoday calculations it’s not needed to care about ellipsoids in the most cases.
It’s possible to create an instance of the Ellipsoid class either by specifing a name or by providing the three parameters name, a, and 1/f.
use Location\Ellipsoid;
$ellipsoid = Ellipsoid::createDefault('WGS84'); (1)
printf(
"%s: a=%f; b=%f; 1/f=%f\n",
$ellipsoid>getName(),
$ellipsoid>getA(),
$ellipsoid>getB(),
$ellipsoid>getF()
);
$ellipsoid = new Ellipsoid('GRS80', 6378137, 298.257222); (2)
printf(
"%s: a=%f; b=%f; 1/f=%f\n",
$ellipsoid>getName(),
$ellipsoid>getA(),
$ellipsoid>getB(),
$ellipsoid>getF()
);
1  Create ellipsoid instance from one of the default configurations 
2  Create custom ellipsoid by providing name, a, and 1/f 
The code above will produce the output below:
WGS84: a=6378137.000000; b=6356752.314245; 1/f=298.257224 GRS80: a=6378137.000000; b=6356752.314133; 1/f=298.257222
Please take a look into the Ellipsoid
source file
for a list of predefined ellipsoids.
Calculations
Distance and Length
Distance Between Two Coordinates (Vincenty’s Formula)
Use the calculator object directly:
<?php
use Location\Coordinate;
use Location\Distance\Vincenty;
$coordinate1 = new Coordinate(19.820664, 155.468066); // Mauna Kea Summit
$coordinate2 = new Coordinate(20.709722, 156.253333); // Haleakala Summit
$calculator = new Vincenty();
echo $calculator>getDistance($coordinate1, $coordinate2);
The code above will produce the output below:
128130.850
or call the getDistance()
method of a Coordinate object by injecting
a calculator object:
<?php
use Location\Coordinate;
use Location\Distance\Vincenty;
$coordinate1 = new Coordinate(19.820664, 155.468066); // Mauna Kea Summit
$coordinate2 = new Coordinate(20.709722, 156.253333); // Haleakala Summit
echo $coordinate1>getDistance($coordinate2, new Vincenty());
The code above will produce the output below:
128130.850
Distance Between Two Coordinates (Haversine Formula)
There exist different methods for calculating the distance between two points. The Haversine formula is much faster than Vincenty’s method but less precise:
<?php
use Location\Coordinate;
use Location\Distance\Haversine;
$coordinate1 = new Coordinate(19.820664, 155.468066); // Mauna Kea Summit
$coordinate2 = new Coordinate(20.709722, 156.253333); // Haleakala Summit
echo $coordinate1>getDistance($coordinate2, new Haversine());
The code above will produce the output below:
128384.515
Length of a Polyline
phpgeo has a polyline implementation which can be used to calculate the
length of a GPS track or a route. A polyline consists of at least two points.
Points are instances of the Coordinate
class.
For more details about polylines/GPS tracks see the Polyline section.
<?php
use Location\Coordinate;
use Location\Polyline;
use Location\Distance\Vincenty;
$track = new Polyline();
$track>addPoint(new Coordinate(52.5, 13.5));
$track>addPoint(new Coordinate(54.5, 12.5));
echo $track>getLength(new Vincenty());
Perimeter of a Polygon
The perimeter is calculated as the sum of the length of all segments. The result is given in meters.
<?php
use Location\Distance\Vincenty;
use Location\Coordinate;
use Location\Polygon;
$polygon = new Polygon();
$polygon>addPoint(new Coordinate(10, 10));
$polygon>addPoint(new Coordinate(10, 20));
$polygon>addPoint(new Coordinate(20, 20));
$polygon>addPoint(new Coordinate(20, 10));
echo $polygon>getPerimeter(new Vincenty());
The code above will produce the output below:
4355689.472
Bearing and Destination
phpgeo can be used to calculate the bearing between two points and to get a destination point for a given start point together with a bearing angle and a distance.
Multiple calculation algorithms are supported. Currently phpgeo provides methods for calculations with a spherical earth model and with an ellipsoidal model. The spherical calculations are very fast, compared to the ellipsoidal methods. The ellipsoidal algorithms are a bit more precise on the other hand.
Bearing between two points
Given two points, it’s possible to calculate the bearing angled between those points.
phpgeo can calculate the initial bearing (bearing as seen from the first point) and the final bearing (bearing as seen approaching the destination point).
Calculation with a spherical earth model
<?php
use Location\Bearing\BearingSpherical;
use Location\Coordinate;
$berlin = new Coordinate(52.5, 13.5);
$london = new Coordinate(51.5, 0.12);
$bearingCalculator = new BearingSpherical();
$startTime = microtime(true);
var_dump($bearingCalculator>calculateBearing($berlin, $london));
var_dump($bearingCalculator>calculateFinalBearing($berlin, $london));
$endTime = microtime(true);
printf("Time elapsed: %0.6f s\n", ($endTime  $startTime));
The code above will produce the following output:
double(268.60722336693) double(257.85494586285) Time elapsed: 0.000285 s
Calculation with an ellipsoidal earth model
<?php
use Location\Bearing\BearingEllipsoidal;
use Location\Coordinate;
$berlin = new Coordinate(52.5, 13.5);
$london = new Coordinate(51.5, 0.12);
$bearingCalculator = new BearingEllipsoidal();
$startTime = microtime(true);
var_dump($bearingCalculator>calculateBearing($berlin, $london));
var_dump($bearingCalculator>calculateFinalBearing($berlin, $london));
$endTime = microtime(true);
printf("Time elapsed: %0.6f s\n", ($endTime  $startTime));
The code above will produce the following output:
double(268.62436347111) double(257.87203657292) Time elapsed: 0.000304 s
Both calculations finish in roughly the same time. One would expect the second calculation to be clearly slower than the first one. It seems the exit condition for the iteration is reached quite fast. There might exist other conditions where the ellipsoidal calculation is noticeable slower.
Destination point for given bearing and distance
As an example, starting from Berlin, calculate the destination point in 56.1 km distance with an initial bearing of 153 degrees:
<?php
use Location\Bearing\BearingEllipsoidal;
use Location\Bearing\BearingSpherical;
use Location\Coordinate;
use Location\Formatter\Coordinate\DecimalDegrees;
$berlin = new Coordinate(52.5, 13.5);
$bearingSpherical = new BearingSpherical();
$bearingEllipsoidal = new BearingEllipsoidal();
$destination1 = $BearingSpherical>calculateDestination($berlin, 153, 56100);
$destination2 = $bearingEllipsoidal>calculateDestination($berlin, 153, 56100);
echo "Spherical: " . $destination1>format(new DecimalDegrees()) . PHP_EOL;
echo "Ellipsoidal: " . $destination2>format(new DecimalDegrees()) . PHP_EOL;
The code above will produce the output below:
Spherical: 52.04988 13.87628 Ellipsoidal: 52.05020 13.87126
Oh, look, what a beautiful spot on earth it is. ;)
Final Bearing for a calculated destination
phpgeo can calculate the final bearing angle for a given starting point, an initial bearing, and the distance to the destination.
<?php
use Location\Bearing\BearingEllipsoidal;
use Location\Coordinate;
use Location\Formatter\Coordinate\DecimalDegrees;
$berlin = new Coordinate(52.5, 13.5);
$bearingEllipsoidal = new BearingEllipsoidal();
$finalBearing = $bearingEllipsoidal>calculateDestinationFinalBearing($berlin, 153, 56100);
var_dump($finalBearing);
The code above will produce the output below:
float(153.29365182147)
Perpendicular Distance
The perpendicular distance is defined as the shortest distance between a point
and a line (in the twodimensional plane) respectively between a point and a
great circle on a spherical surface.
With phpgeo it is possible to calculate the perpendicular distance between a
point (instance of the Coordinate
class) and a line (instance of the
Line
class). A line is defined by two coordinates, exactly as a great
circle — both are interchangeable in this case.
<?php
use Location\Coordinate;
use Location\Line;
use Location\Utility\PerpendicularDistance;
$point = new Coordinate(52.44468, 13.57455);
$line = new Line(
new Coordinate(52.4554, 13.5582),
new Coordinate(52.4371, 13.5623)
);
$pdCalc = new PerpendicularDistance();
printf(
"perpendicular distance: %.1f meters\n",
$pdCalc>getPerpendicularDistance($point, $line)
);
The code above will produce the output below:
perpendicular distance: 936.7 meters
Geofence
phpgeo has a polygon implementation which can be used to determinate if a geometry (point, line, polyline, polygon) is contained in it or not. A polygon consists of at least three points.
The calculation gives wrong results if the polygons crosses the 180/180 degrees meridian. 
<?php
use Location\Coordinate;
use Location\Polygon;
$geofence = new Polygon();
$geofence>addPoint(new Coordinate(12.085870,77.016261));
$geofence>addPoint(new Coordinate(12.086373,77.033813));
$geofence>addPoint(new Coordinate(12.102823,77.030938));
$geofence>addPoint(new Coordinate(12.098669,77.006476));
$outsidePoint = new Coordinate(12.075452, 76.985079);
$insidePoint = new Coordinate(12.092542, 77.021540);
var_dump($geofence>contains($outsidePoint)); // returns bool(false) the point is outside the polygon
var_dump($geofence>contains($insidePoint)); // returns bool(true) the point is inside the polygon
Transformations and Processing
Simplifying a polyline
Polylines can be simplified to save storage space or bandwidth.
phpgeo provides two implementations for simplifying a polyline.
The first implementation uses the Ramer–Douglas–Peucker algorithm (also known as DouglasPeucker algorithm). The other implementation examines the bearings of the polyline segments and removes a segment when its bearing angle is similar to the bearing angle of its predecessor segment. I named it the DeltaBearing algorithm.
RamerDouglasPeucker Algorithm
<?php
use Location\Coordinate;
use Location\Formatter\Coordinate\DecimalDegrees;
use Location\Polyline;
use Location\Processor\Polyline\SimplifyDouglasPeucker;
$polyline = new Polyline();
$polyline>addPoint(new Coordinate(10.0, 10.0));
$polyline>addPoint(new Coordinate(20.0, 20.0));
$polyline>addPoint(new Coordinate(30.0, 10.0));
$processor = new SimplifyDouglasPeucker(1500000); (1)
$simplified = $processor>simplify($polyline);
foreach ($simplified>getPoints() as $point) {
echo $point>format(new DecimalDegrees()) . PHP_EOL;
}
1  remove all points which perpendicular distance is less than 1,500,000 meters (1,500 km) from the surrounding points. 
The code above produces the output below:
10.00000 10.00000 30.00000 10.00000
DeltaBearing Algorithm
<?php
use Location\Coordinate;
use Location\Formatter\Coordinate\DecimalDegrees;
use Location\Polyline;
use Location\Processor\Polyline\SimplifyBearing;
$polyline = new Polyline();
$polyline>addPoint(new Coordinate(10.0, 10.0));
$polyline>addPoint(new Coordinate(20.0, 20.0));
$polyline>addPoint(new Coordinate(30.0, 10.0));
$processor = new SimplifyBearing(90); (1)
$simplified = $processor>simplify($polyline);
foreach ($simplified>getPoints() as $point) {
echo $point>format(new DecimalDegrees()) . PHP_EOL;
}
1  The constructor argument is the minimum required angle between two adjacent polyline segments so that no points will be removed. If the bearing angle difference is less that the given value, the middle point will be removed from the resulting polyline. 
The code above produces the output below:
10.00000 10.00000 30.00000 10.00000
The following image shows both a polyline and its simplified version. The simplification was done with the DeltaBearing Algorithm with a threshold angle of 20 degrees. The original polyline is painted in blue, the simplified polyline is magenta.
Formatting and Output
Coordinates
You can format a coordinate in different styles.
Decimal Degrees
<?php
use Location\Coordinate;
use Location\Formatter\Coordinate\DecimalDegrees;
$coordinate = new Coordinate(19.820664, 155.468066); // Mauna Kea Summit
echo $coordinate>format(new DecimalDegrees());
The code above produces the output below:
19.82066 155.46807
Degrees/Minutes/Seconds (DMS)
<?php
use Location\Coordinate;
use Location\Formatter\Coordinate\DMS;
$coordinate = new Coordinate(18.911306, 155.678268); // South Point, HI, USA
$formatter = new DMS();
echo $coordinate>format($formatter) . PHP_EOL;
$formatter>setSeparator(", ")
>useCardinalLetters(true)
>setUnits(DMS::UNITS_ASCII);
echo $coordinate>format($formatter) . PHP_EOL;
The code above produces the output below:
18° 54′ 41″ 155° 40′ 42″ 18° 54' 41" N, 155° 40' 42" W
Decimal Minutes
This format is commonly used in the Geocaching community.
<?php
use Location\Coordinate;
use Location\Formatter\Coordinate\DecimalMinutes;
$coordinate = new Coordinate(43.62310, 70.20787); // Portland Head Light, ME, USA
$formatter = new DecimalMinutes();
echo $coordinate>format($formatter) . PHP_EOL;
$formatter>setSeparator(", ")
>useCardinalLetters(true)
>setUnits(DecimalMinutes::UNITS_ASCII);
echo $coordinate>format($formatter) . PHP_EOL;
The code above produces the output below:
43° 37.386′ 070° 12.472′ 43° 37.386' N, 070° 12.472' W
GeoJSON
<?php
use Location\Coordinate;
use Location\Formatter\Coordinate\GeoJSON;
$coordinate = new Coordinate(18.911306, 155.678268); // South Point, HI, USA
echo $coordinate>format(new GeoJSON());
The code above produces the output below:
{"type":"Point","coordinates":[155.678268,18.911306]}
Polylines
You can format a polyline in different styles.
GeoJSON
<?php
use Location\Coordinate;
use Location\Polyline;
use Location\Formatter\Polyline\GeoJSON;
$polyline = new Polyline;
$polyline>addPoint(new Coordinate(52.5, 13.5));
$polyline>addPoint(new Coordinate(62.5, 14.5));
$formatter = new GeoJSON;
echo $formatter>format($polyline);
The code above produces the output below:
{"type":"LineString","coordinates":[[13.5,52.5],[14.5,62.5]]}
Polygons
You can format a polygon in different styles.
GeoJSON
<?php
use Location\Coordinate;
use Location\Polygon;
use Location\Formatter\Polygon\GeoJSON;
$polygon = new Polygon;
$polygon>addPoint(new Coordinate(10, 20));
$polygon>addPoint(new Coordinate(20, 40));
$polygon>addPoint(new Coordinate(30, 40));
$polygon>addPoint(new Coordinate(30, 20));
$formatter = new GeoJSON;
echo $formatter>format($polygon);
The code above produces the output below:
{"type":"Polygon","coordinates":[[20,10],[40,20],[40,30],[20,30]]}
Parsing and Input
Coordinates Parser
phpgeo comes with a parser for several types of coordinate formats.
The parser works as a factory which creates an instance of the
Coordinate
class.
Supported Formats
Decimal Degrees with or without cardinal letters, with or without a comma as separator, with or without whitespace between values and cardinal letters:
52.5, 13.5 52.5 13.5 52.5 13.5 52.345 N, 13.456 E N52.345 E13.456
Decimal Minutes with or without cardinal letters, with or without degree and minute signs, with or without a comma as separator, with or without whitespace between values and cardinal letters:
N52° 12.345, E13° 34.567 52° 12.345′ N, E13° 34.567′ E 52 12.345, 013 34.567 52 12.345, 013 34.567
The unit test shows some more examples.
Example
use Location\Factory\CoordinateFactory;
use Location\Formatter\Coordinate\DecimalDegrees;
require_once __DIR__ . '/vendor/autoload.php';
$point = CoordinateFactory::fromString('52° 13.698′ 020° 58.536′');
echo $point>format(new DecimalDegrees());
The code above produces the output below:
52.22830 20.97560