Skip to content

Commit

Permalink
Add more functions
Browse files Browse the repository at this point in the history
  • Loading branch information
JBlond committed Dec 13, 2022
1 parent 45c097f commit 7895342
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 14 deletions.
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,37 @@ composer require jblond/math-functions

## Geo distance

Calculates the great-circle distance between two points,
with the Vincenty formula.
Calculates the distance between two points. Choose your function.

- get function
- vincenty function
- Parameters
- float $latitudeFrom Latitude of start point in [deg decimal]
- float $longitudeFrom Longitude of start point in [deg decimal]
- float $latitudeTo Latitude of target point in [deg decimal]
- float $longitudeTo Longitude of target point in [deg decimal]
- float $earthRadius Mean earth radius in [m]
- OPTIONAL float|int Distance between points in [m] (same as earthRadius) default: 6371000 meters
- haversine function
- Parameters
- float $latitudeFrom Latitude of start point in [deg decimal]
- float $longitudeFrom Longitude of start point in [deg decimal]
- float $latitudeTo Latitude of target point in [deg decimal]
- float $longitudeTo Longitude of target point in [deg decimal]
- greatCircle
- Parameters
- float $latitudeFrom Latitude of start point in [deg decimal]
- float $longitudeFrom Longitude of start point in [deg decimal]
- float $latitudeTo Latitude of target point in [deg decimal]
- float $longitudeTo Longitude of target point in [deg decimal]
- equirectangularApproximation
- Parameters
- float $latitudeFrom Latitude of start point in [deg decimal]
- float $longitudeFrom Longitude of start point in [deg decimal]
- float $latitudeTo Latitude of target point in [deg decimal]
- float $longitudeTo Longitude of target point in [deg decimal]
- cosineLaw
- Parameters
- float $latitudeFrom Latitude of start point in [deg decimal]
- float $longitudeFrom Longitude of start point in [deg decimal]
- float $latitudeTo Latitude of target point in [deg decimal]
- float $longitudeTo Longitude of target point in [deg decimal]
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"classmap-authoritative": true
},
"scripts": {
"phpunit": "phpunit ./tests/"
"phpunit": "phpunit ./tests/",
"test": "phpunit --colors=always --testdox"
}
}
17 changes: 17 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
colors="true"
bootstrap="vendor/autoload.php"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd">
<coverage includeUncoveredFiles="true">
<include>
<directory>./src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="unit">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
112 changes: 105 additions & 7 deletions src/GeoDistance.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,22 @@ public function vincenty(
}

/**
* @param $lat1
* @param $lon1
* @param $lat2
* @param $lon2
* The Haversine formula is a simple and efficient way to calculate the distance between two points on the
* Earth's surface. It is often used in navigation and geolocation applications.
* However, it does not account for the Earth's elliptical shape.
*
* @param float $lat1
* @param float $lon1
* @param float $lat2
* @param float $lon2
* @return float|int
*/
function haversine($lat1, $lon1, $lat2, $lon2)
{
public function haversine(
float $lat1,
float $lon1,
float $lat2,
float $lon2
) {
// convert latitude and longitude to radians
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
Expand All @@ -62,7 +70,97 @@ function haversine($lat1, $lon1, $lat2, $lon2)
// apply the Haversine formula
$a = sin($lat_diff / 2) * sin($lat_diff / 2) + cos($lat1) * cos($lat2) * sin($lon_diff / 2) * sin($lon_diff / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return 6371 * $c; // 6371 is the approximate radius of the Earth in kilometers
return 6371000 * $c; // 6371000 is the approximate radius of the Earth in meters
}

/**
* The Great Circle formula is similar to the Haversine formula, but it uses a slightly different formula
* to calculate the central angle between the two points. Both methods are based on the assumption
* that the Earth is a perfect sphere, so they are not as accurate as the Vincenty formula for distances
* over a few hundred kilometers. However, the Great Circle formula is simpler and faster to compute
* than the Vincenty formula, so it may be a good choice for applications that do not require high accuracy.
*
* @param float $lat1
* @param float $lon1
* @param float $lat2
* @param float $lon2
* @return float|int
*/
public function greatCircle(
float $lat1,
float $lon1,
float $lat2,
float $lon2
) {
// convert latitude and longitude to radians
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);

// calculate the differences
$lat_diff = $lat2 - $lat1;
$lon_diff = $lon2 - $lon1;

// apply the Great Circle formula
$a = sin($lat_diff / 2) * sin($lat_diff / 2) + cos($lat1) * cos($lat2) * sin($lon_diff / 2) * sin($lon_diff / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return 6371000 * $c; // 6371000 is the approximate radius of the Earth in meters
}

/**
* This would return the distance in kilometers between the two points with the given latitude and longitude.
* The equirectangular approximation is a simple formula that is fast to compute,
* but it is not very accurate for distances over a few hundred kilometers. For more accurate calculations,
* you should use a different method, such as the Haversine formula.
*
* @param float $lat1
* @param float $lon1
* @param float $lat2
* @param float $lon2
* @return float|int
*/
public function equirectangularApproximation(
float $lat1,
float $lon1,
float $lat2,
float $lon2
) {
// convert latitude and longitude to radians
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);

// apply the formula
$x = ($lon2 - $lon1) * cos(($lat1 + $lat2) / 2);
$y = $lat2 - $lat1;
return sqrt($x * $x + $y * $y) * 6371000; // 6371000 is the radius of the Earth in meters
}

/**
* @param float $lat1
* @param float $lon1
* @param float $lat2
* @param float $lon2
* @return float|int
*/
public function cosineLaw(
float $lat1,
float $lon1,
float $lat2,
float $lon2
) {

// convert latitude and longitude to radians
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);

// apply the formula
$cos_theta = sin($lat1) * sin($lat2) + cos($lat1) * cos($lat2) * cos($lon2 - $lon1);
$theta = acos($cos_theta);
return $theta * 6371000; // 6371000 is the radius of the Earth in meters
}
}
90 changes: 87 additions & 3 deletions tests/math/GeoDistanceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class GeoDistanceTest extends TestCase
* Should be like https://www.luftlinie.org/Hamburg,DEU/M%C3%BCnchen,Bayern,DEU
* @return void
*/
public function testGet(): void
public function testVincenty(): void
{
$distance = new GeoDistance();
$this->assertEquals(
Expand Down Expand Up @@ -50,7 +50,7 @@ public function testHaversineDistance(): void
{
$distance = new GeoDistance();
$this->assertEquals(
612.3947203510587,
612394.7203510588,
$distance->haversine(
// Hamburg
53.553406,
Expand All @@ -62,7 +62,7 @@ public function testHaversineDistance(): void
);
// https://www.luftlinie.org/Hamburg,DEU/Los-Angeles,CA,USA
$this->assertEquals(
9075.31474469208,
9075314.74469208,
$distance->haversine(
// Hamburg
53.553406,
Expand All @@ -72,6 +72,90 @@ public function testHaversineDistance(): void
-118.243680
)
);
}

public function testGreatCircle(): void
{
$distance = new GeoDistance();
$this->assertEquals(
612394.7203510588,
$distance->greatCircle(
// Hamburg
53.553406,
9.992196,
// munich
48.137108,
11.575382
)
);
// https://www.luftlinie.org/Hamburg,DEU/Los-Angeles,CA,USA
$this->assertEquals(
9075314.74469208,
$distance->greatCircle(
// Hamburg
53.553406,
9.992196,
// Los Angeles
34.052230,
-118.243680
)
);
}

public function testEquirectangularApproximation(): void
{
$distance = new GeoDistance();
$this->assertEquals(
612436.6348023742,
$distance->equirectangularApproximation(
// Hamburg
53.553406,
9.992196,
// munich
48.137108,
11.575382
)
);
// https://www.luftlinie.org/Hamburg,DEU/Los-Angeles,CA,USA
$this->assertEquals(
// 9075.31474469208 is the true value. Equirectangular Approximation is no very accurate
10517193.640868774,
$distance->equirectangularApproximation(
// Hamburg
53.553406,
9.992196,
// Los Angeles
34.052230,
-118.243680
)
);
}

public function testCosineLaw(): void
{
$distance = new GeoDistance();
$this->assertEquals(
612394.7203510538,
$distance->cosineLaw(
// Hamburg
53.553406,
9.992196,
// munich
48.137108,
11.575382
)
);
// https://www.luftlinie.org/Hamburg,DEU/Los-Angeles,CA,USA
$this->assertEquals(
9075314.74469208,
$distance->cosineLaw(
// Hamburg
53.553406,
9.992196,
// Los Angeles
34.052230,
-118.243680
)
);
}
}

0 comments on commit 7895342

Please sign in to comment.