94 lines
2.5 KiB
PHP
94 lines
2.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Pcm\GeocodeBundle\Service;
|
|
|
|
use Pcm\GeocodeBundle\Exception\ApiErrorException;
|
|
use Pcm\GeocodeBundle\Exception\NoResultsFoundException;
|
|
use Pcm\GeocodeBundle\Model\GeoCoordinates;
|
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|
use Symfony\Contracts\HttpClient\ResponseInterface;
|
|
|
|
/**
|
|
* Find the geo-coordinates of a postcode
|
|
*/
|
|
final class Geocoder
|
|
{
|
|
private const string API_URL = "https://nominatim.openstreetmap.org/search";
|
|
|
|
public function __construct(private HttpClientInterface $client) {}
|
|
|
|
/**
|
|
* Find and return the geo-coordinates of a postcode
|
|
*
|
|
* @param string $postcode
|
|
*
|
|
* @return GeoCoordinates
|
|
*
|
|
* @throws NoResultsFoundException when no results were found for the provided postcode
|
|
* @throws ApiErrorException when the API response contains an error
|
|
*/
|
|
public function geocodePostcode(string $postcode): GeoCoordinates
|
|
{
|
|
$client = $this->createClient();
|
|
$response = $this->makeApiRequest($client, $postcode);
|
|
$data = $this->getDataFromResponse($response);
|
|
|
|
if (array_key_exists('error', $data)) {
|
|
throw new ApiErrorException($postcode, $data['error']['message']);
|
|
}
|
|
|
|
if (empty($data)) {
|
|
throw new NoResultsFoundException($postcode, "No results found.");
|
|
}
|
|
|
|
return $this->createGeoCoordinates($data);
|
|
}
|
|
|
|
private function createClient(): HttpClientInterface
|
|
{
|
|
return $this->client->withOptions([
|
|
'headers' => ["User-Agent" => "Geocode"]
|
|
]);
|
|
}
|
|
|
|
private function makeApiRequest(HttpClientInterface $client, string $postcode): ResponseInterface
|
|
{
|
|
return $client->request(
|
|
method: 'GET',
|
|
url: self::API_URL,
|
|
options: [
|
|
'query' => [
|
|
'format' => 'json',
|
|
'postalcode' => $postcode
|
|
]
|
|
]
|
|
);
|
|
}
|
|
|
|
private function getDataFromResponse(ResponseInterface $response): array
|
|
{
|
|
return $response->toArray(false);
|
|
}
|
|
|
|
private function createGeoCoordinates(array $data): GeoCoordinates
|
|
{
|
|
$lat = $this->getLatitudeFromData($data);
|
|
$long = $this->getLongitudeFromData($data);
|
|
|
|
return new GeoCoordinates($lat, $long);
|
|
}
|
|
|
|
private function getLatitudeFromData(array $data): float
|
|
{
|
|
return (float) $data[0]['lat'];
|
|
}
|
|
|
|
private function getLongitudeFromData(array $data): float
|
|
{
|
|
return (float) $data[0]['lon'];
|
|
}
|
|
}
|
|
|