From 6f0b97216f7d9f53936471a08acc4a82045e80c9 Mon Sep 17 00:00:00 2001 From: Tom Herrmann Date: Wed, 30 Sep 2026 11:49:40 +0200 Subject: [PATCH 1/3] Add geographic distance assertions --- src/GeographicAssertions.php | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/GeographicAssertions.php b/src/GeographicAssertions.php index 772fa70..a2e84d9 100644 --- a/src/GeographicAssertions.php +++ b/src/GeographicAssertions.php @@ -6,6 +6,8 @@ final class GeographicAssertions { + private const EARTH_RADIUS_METERS = 6_371_000; + public static function assertLatitude($actual): void { PHPUnit::assertIsNumeric($actual); @@ -28,4 +30,40 @@ public static function assertCoordinates($actual, string $lat = 'lat', string $l PHPUnit::assertArrayHasKey($lng, $actual); self::assertLongitude($actual[$lng]); } + + public static function assertDistanceGreaterThanOrEqual( + int $distance, + $from, + $to, + string $lat = 'lat', + string $lng = 'lng' + ): void { + PHPUnit::assertGreaterThanOrEqual($distance, self::distance($from, $to, $lat, $lng)); + } + + public static function assertDistanceLessThanOrEqual( + int $distance, + $from, + $to, + string $lat = 'lat', + string $lng = 'lng' + ): void { + PHPUnit::assertLessThanOrEqual($distance, self::distance($from, $to, $lat, $lng)); + } + + private static function distance($from, $to, string $lat, string $lng): float + { + self::assertCoordinates($from, $lat, $lng); + self::assertCoordinates($to, $lat, $lng); + + $fromLatitude = deg2rad((float) $from[$lat]); + $toLatitude = deg2rad((float) $to[$lat]); + $latitudeDelta = $toLatitude - $fromLatitude; + $longitudeDelta = deg2rad((float) $to[$lng] - (float) $from[$lng]); + + $a = sin($latitudeDelta / 2) ** 2 + + cos($fromLatitude) * cos($toLatitude) * sin($longitudeDelta / 2) ** 2; + + return self::EARTH_RADIUS_METERS * 2 * asin(sqrt(min(1.0, $a))); + } } From c8dbb69d89614a57061d1cf10fe29222f23d0fb3 Mon Sep 17 00:00:00 2001 From: Tom Herrmann Date: Wed, 30 Sep 2026 11:50:00 +0200 Subject: [PATCH 2/3] Test geographic distance assertions --- tests/GeographicAssertionsTest.php | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/GeographicAssertionsTest.php b/tests/GeographicAssertionsTest.php index 81b21aa..1e9cf41 100644 --- a/tests/GeographicAssertionsTest.php +++ b/tests/GeographicAssertionsTest.php @@ -58,4 +58,46 @@ public static function it_can_validate_array_of_coordinates_with_custom_keys(): GeographicAssertions::assertCoordinates($coords, $lat, $lng); } + + /** @test */ + public static function it_can_validate_distance_greater_than_or_equal(): void + { + $hamburg = ['lat' => 53.551085, 'lng' => 9.993682]; + $berlin = ['lat' => 52.520008, 'lng' => 13.404954]; + + GeographicAssertions::assertDistanceGreaterThanOrEqual(255_000, $hamburg, $berlin); + GeographicAssertions::assertDistanceGreaterThanOrEqual(0, $hamburg, $hamburg); + } + + /** @test */ + public static function it_can_validate_distance_less_than_or_equal(): void + { + $hamburg = ['lat' => 53.551085, 'lng' => 9.993682]; + $berlin = ['lat' => 52.520008, 'lng' => 13.404954]; + + GeographicAssertions::assertDistanceLessThanOrEqual(256_000, $hamburg, $berlin); + GeographicAssertions::assertDistanceLessThanOrEqual(0, $hamburg, $hamburg); + } + + /** @test */ + public static function it_can_validate_distance_with_custom_keys(): void + { + $hamburg = ['latitude' => 53.551085, 'longitude' => 9.993682]; + $berlin = ['latitude' => 52.520008, 'longitude' => 13.404954]; + + GeographicAssertions::assertDistanceGreaterThanOrEqual( + 255_000, + $hamburg, + $berlin, + 'latitude', + 'longitude' + ); + GeographicAssertions::assertDistanceLessThanOrEqual( + 256_000, + $hamburg, + $berlin, + 'latitude', + 'longitude' + ); + } } From b5b99affcb0ba2611d02f9f1121bceff6a5fb556 Mon Sep 17 00:00:00 2001 From: Tom Herrmann Date: Wed, 30 Sep 2026 11:50:16 +0200 Subject: [PATCH 3/3] Document geographic distance assertions --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 5103fc3..8552307 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,12 @@ This will prevent any method name conflicts with core, your custom or other trai 'lat' => 53.551085, 'lng' => 9.993682, ]); + +$hamburg = ['lat' => 53.551085, 'lng' => 9.993682]; +$berlin = ['lat' => 52.520008, 'lng' => 13.404954]; + +\Astrotomic\PhpunitAssertions\GeographicAssertions::assertDistanceGreaterThanOrEqual(255_000, $hamburg, $berlin); +\Astrotomic\PhpunitAssertions\GeographicAssertions::assertDistanceLessThanOrEqual(256_000, $hamburg, $berlin); ``` ### HashID