Files
pcm-geocode-bundle/src/Service/Geocoder.php
2024-08-05 14:50:26 +01:00

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'];
}
}