From 3bff2af81078872a90db65e3616ea06e5a677179 Mon Sep 17 00:00:00 2001 From: Peter Baettig Date: Fri, 24 Jan 2020 03:40:12 +0100 Subject: [PATCH] added reverse geocoding by lat/lon --- app/Interfaces/ReverseGeocoding.php | 2 + app/Support/GeoConverter.php | 78 +++++++++++++++++++++++++++++ app/SwissGeo.php | 18 +++++++ tests/SwissGeo/GeoConverterTest.php | 24 +++++++++ tests/SwissGeo/SwissGeoTest.php | 31 ++++++++---- 5 files changed, 143 insertions(+), 10 deletions(-) create mode 100644 app/Support/GeoConverter.php create mode 100644 tests/SwissGeo/GeoConverterTest.php diff --git a/app/Interfaces/ReverseGeocoding.php b/app/Interfaces/ReverseGeocoding.php index 0b79485..9a13aa6 100644 --- a/app/Interfaces/ReverseGeocoding.php +++ b/app/Interfaces/ReverseGeocoding.php @@ -7,6 +7,8 @@ interface ReverseGeocoding { public static function findCitiesNearPoint(float $x, float $y, float $radius) : ?array; + public static function findCitiesNearLatLon(float $lat, float $lon, float $radius) : ?array; public static function findCityByPoint(float $x, float $y): ?string; + public static function findCityByLatLon(float $lat, float $lon): ?string; public static function findPostcodeByPoint(float $x, float $y): ?int; } diff --git a/app/Support/GeoConverter.php b/app/Support/GeoConverter.php new file mode 100644 index 0000000..7775b44 --- /dev/null +++ b/app/Support/GeoConverter.php @@ -0,0 +1,78 @@ + $x, 'y' => $y]; + } + + public static function pointToLatLon(float $x, float $y): array + { + // Converts military to civil and to unit = 1000km + // Auxiliary values (% Bern) + $y_aux = ($y - 600000)/1000000; + $x_aux = ($x - 200000)/1000000; + + // Process lat + $lat = 16.9023892 + + 3.238272 * $x_aux + - 0.270978 * ($y_aux ** 2) + - 0.002528 * ($x_aux ** 2) + - 0.0447 * ($y_aux ** 2) * $x_aux + - 0.0140 * ($x_aux ** 3); + + // Unit 10000" to 1 " and converts seconds to degrees (dec) + $lat = $lat * 100/36; + + // Process long + $lon = 2.6779094 + + 4.728982 * $y_aux + + 0.791484 * $y_aux * $x_aux + + 0.1306 * $y_aux * ($x_aux ** 2) + - 0.0436 * ($y_aux ** 3); + + // Unit 10000" to 1 " and converts seconds to degrees (dec) + $lon = $lon * 100/36; + + return ['lat' => $lat, 'lon' => $lon]; + } + + + private static function decimalAngleToSexagesimalSeconds(float $angle) + { + // Extract DMS + $deg = (int)$angle; + $min = (int)(($angle - $deg) * 60); + $sec = ((($angle-$deg)*60)-$min)*60; + + // Result in sexagesimal seconds + return $sec + $min*60 + $deg*3600; + } +} diff --git a/app/SwissGeo.php b/app/SwissGeo.php index 33a5d75..744143a 100644 --- a/app/SwissGeo.php +++ b/app/SwissGeo.php @@ -8,6 +8,7 @@ use SwissGeo\Gateways\SwissPostGateway; use SwissGeo\Interfaces\Geocoding; use SwissGeo\Interfaces\ReverseGeocoding; +use SwissGeo\Support\GeoConverter; class SwissGeo implements Geocoding, ReverseGeocoding { @@ -63,6 +64,14 @@ public static function findCitiesNearPoint(float $x, float $y, float $radius = 0 return $cityArray; } + public static function findCitiesNearLatLon(float $lat, float $lon, float $radius = 0): ?array + { + $point = GeoConverter::latLonToPoint($lat, $lon); + if (!$point) { + return null; + } + return self::findCitiesNearPoint($point['x'], $point['y'], $radius); + } public static function findCitiesNearAddress(string $location, float $radius = 0): ?array { $currentLocation = self::findPointByAddress($location); @@ -99,4 +108,13 @@ public static function findCantonByCity(string $name): ?string { return SwissPostGateway::getCantonByCity($name); } + + public static function findCityByLatLon(float $lat, float $lon): ?string + { + $point = GeoConverter::latLonToPoint($lat, $lon); + if (!$point) { + return null; + } + return self::findCityByPoint($point['x'], $point['y']); + } } diff --git a/tests/SwissGeo/GeoConverterTest.php b/tests/SwissGeo/GeoConverterTest.php new file mode 100644 index 0000000..9f256a4 --- /dev/null +++ b/tests/SwissGeo/GeoConverterTest.php @@ -0,0 +1,24 @@ +assertEqualsWithDelta(47.041442871094, $latLon['lat'], 0.01); + $this->assertEqualsWithDelta(8.1898508071899, $latLon['lon'], 0.01); + } + + public function testCanLatLonToSwissCoordinates(): void + { + $point = GeoConverter::latLonToPoint(47.041442871094, 8.1898508071899); + $this->assertEqualsWithDelta(210318.1875, $point['x'], 1); + $this->assertEqualsWithDelta(657089.5, $point['y'], 1); + } + +} diff --git a/tests/SwissGeo/SwissGeoTest.php b/tests/SwissGeo/SwissGeoTest.php index c8606ac..2e127fb 100644 --- a/tests/SwissGeo/SwissGeoTest.php +++ b/tests/SwissGeo/SwissGeoTest.php @@ -7,56 +7,67 @@ class SwissGeoTest extends TestCase { - public function testFindCitiesNearAddress() + public function testFindCitiesNearAddress(): void { $this->assertCount(2,SwissGeo::findCitiesNearAddress('Malters', 1.5)); $this->assertCount(1,SwissGeo::findCitiesNearAddress('Malters')); $this->assertEquals(['Malters'], SwissGeo::findCitiesNearAddress('Malters')); } - public function testFindPointByAddress() + public function testFindPointByAddress(): void { $point = SwissGeo::findPointByAddress('Malters'); $this->assertEqualsWithDelta(210318,$point['x'] ,1); } - public function testFindCitiesNearPoint() + public function testFindCitiesNearPoint(): void { $this->assertCount(2,SwissGeo::findCitiesNearPoint(210318.1875, 657089.5,1.5)); $this->assertCount(1,SwissGeo::findCitiesNearPoint(210318.1875, 657089.5)); } - public function testFindPostcodeByCity() + public function testFindCitiesNearLatLong(): void + { + $this->assertCount(2,SwissGeo::findCitiesNearLatLon(47.041442871094, 8.1898508071899,1.5)); + $this->assertCount(1,SwissGeo::findCitiesNearLatLon(47.041442871094, 8.1898508071899)); + } + + public function testFindPostcodeByCity(): void { $this->assertEquals('6102',SwissGeo::findPostcodeByCity('Malters')); } - public function testFindCityByPostcode() + public function testFindCityByPostcode(): void { $this->assertEquals('Malters',SwissGeo::findCityByPostcode('6102')); } - public function testFindCantonByPostcode() + public function testFindCantonByPostcode(): void { $this->assertEquals('LU',SwissGeo::findCantonByPostcode('6102')); } - public function testFindCantonByCity() + public function testFindCantonByCity(): void { $this->assertEquals('LU',SwissGeo::findCantonByCity('Malters')); } - public function testFindCityByPoint() + public function testFindCityByPoint(): void { $this->assertEquals('Malters',SwissGeo::findCityByPoint(210318.1875, 657089.5)); } - public function testFindPostcodeByPoint() + public function testFindCityByLatLon(): void + { + $this->assertEquals('Malters',SwissGeo::findCityByLatLon(47.041442871094, 8.1898508071899)); + } + + public function testFindPostcodeByPoint(): void { $this->assertEquals('6102',SwissGeo::findPostcodeByPoint(210318.1875, 657089.5)); } - public function testFindPossibleStreetnames() + public function testFindPossibleStreetnames(): void { $this->assertCount(1, SwissGeo::findStreetnames('Haldenhüslistrasse')); $this->assertEquals('Haldenhüslistrasse', SwissGeo::findStreetnames('Haldenhüslistrasse')['0']);