diff --git a/README.md b/README.md
index 31b250196..45f1f47b5 100644
--- a/README.md
+++ b/README.md
@@ -134,7 +134,7 @@ Provider | Package | Features | Stats
[GraphHopper](https://github.com/geocoder-php/graphhopper-provider) | `geocoder-php/graphhopper-provider` | address, reverse
[Website](https://docs.graphhopper.com/#tag/Geocoding-API) | [](https://packagist.org/packages/geocoder-php/graphhopper-provider)
[](https://packagist.org/packages/geocoder-php/graphhopper-provider)
[Here](https://github.com/geocoder-php/here-provider) | `geocoder-php/here-provider` | address, reverse
[Website](https://developer.here.com/documentation/geocoder/topics/quick-start-geocode.html) | [](https://packagist.org/packages/geocoder-php/here-provider)
[](https://packagist.org/packages/geocoder-php/here-provider)
[LocationIQ](https://github.com/geocoder-php/locationiq-provider) | `geocoder-php/locationiq-provider` | address, reverse
[Website](https://locationiq.org/) | [](https://packagist.org/packages/geocoder-php/locationiq-provider)
[](https://packagist.org/packages/geocoder-php/locationiq-provider)
-[Mapbox](https://github.com/geocoder-php/mapbox-provider) | `geocoder-php/mapbox-provider` | address, reverse
[Website](https://www.mapbox.com/geocoding/) | [](https://packagist.org/packages/geocoder-php/mapbox-provider)
[](https://packagist.org/packages/geocoder-php/mapbox-provider)
+[Mapbox](https://github.com/geocoder-php/mapbox-provider) | `geocoder-php/mapbox-provider` | address, reverse
[Website](https://docs.mapbox.com/api/search/geocoding/) | [](https://packagist.org/packages/geocoder-php/mapbox-provider)
[](https://packagist.org/packages/geocoder-php/mapbox-provider)
[MapQuest](https://github.com/geocoder-php/mapquest-provider) | `geocoder-php/mapquest-provider` | address, reverse
[Website](http://developer.mapquest.com/web/products/dev-services/geocoding-ws) | [](https://packagist.org/packages/geocoder-php/mapquest-provider)
[](https://packagist.org/packages/geocoder-php/mapquest-provider)
[MapTiler](https://github.com/geocoder-php/maptiler-provider) | `geocoder-php/maptiler-provider` | address, reverse
[Website](https://www.maptiler.com/cloud/geocoding/) | [](https://packagist.org/packages/geocoder-php/maptiler-provider)
[](https://packagist.org/packages/geocoder-php/maptiler-provider)
[Nominatim](https://github.com/geocoder-php/nominatim-provider)
(OpenStreetMap) | `geocoder-php/nominatim-provider` | address, reverse
[Website](http://wiki.openstreetmap.org/wiki/Nominatim) | [](https://packagist.org/packages/geocoder-php/nominatim-provider)
[](https://packagist.org/packages/geocoder-php/nominatim-provider)
diff --git a/src/Provider/IP2Location/IP2Location.php b/src/Provider/IP2Location/IP2Location.php
index 111401003..28972ce1c 100644
--- a/src/Provider/IP2Location/IP2Location.php
+++ b/src/Provider/IP2Location/IP2Location.php
@@ -102,9 +102,8 @@ private function executeQuery(string $url): AddressCollection
throw new InvalidCredentials('Insufficient credits to use IP2Location service.');
} elseif (preg_match('/invalid ip address/i', $data['response'])) {
throw new UnsupportedOperation('Invalid IP address.');
- } else {
- throw new UnsupportedOperation(sprintf('Unexpected error: %s.', $data['response']));
}
+ throw new UnsupportedOperation(sprintf('Unexpected error: %s.', $data['response']));
}
if (isset($data['region_name'])) {
diff --git a/src/Provider/MapQuest/MapQuest.php b/src/Provider/MapQuest/MapQuest.php
index 4c433467e..d3dae33db 100644
--- a/src/Provider/MapQuest/MapQuest.php
+++ b/src/Provider/MapQuest/MapQuest.php
@@ -157,11 +157,10 @@ public function geocodeQuery(GeocodeQuery $query): Collection
$params = $this->addOptionsForGetQuery($params, $options);
return $this->executeGetQuery(static::GEOCODE_ENDPOINT, $params);
- } else {
- $params = $this->addOptionsForPostQuery($params, $options);
-
- return $this->executePostQuery(static::GEOCODE_ENDPOINT, $params);
}
+ $params = $this->addOptionsForPostQuery($params, $options);
+
+ return $this->executePostQuery(static::GEOCODE_ENDPOINT, $params);
}
public function reverseQuery(ReverseQuery $query): Collection
diff --git a/src/Provider/Mapbox/CHANGELOG.md b/src/Provider/Mapbox/CHANGELOG.md
index 214529e6a..dd0fbf7ce 100644
--- a/src/Provider/Mapbox/CHANGELOG.md
+++ b/src/Provider/Mapbox/CHANGELOG.md
@@ -2,6 +2,42 @@
The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release.
+## 2.0.0
+
+### Added
+
+- Support for the Mapbox Geocoding API v6 (`/search/geocode/v6/forward` and `/search/geocode/v6/reverse`)
+- v6 Structured Input: forward queries built from typed address fields via query data — `address_line1`, `address_number`, `street`, `block`, `place`, `region`, `postcode`, `locality`, `neighborhood`. When any of these is present, the query text is not sent.
+- `Mapbox::TYPE_STREET` (new v6 type, added to `Mapbox::TYPES`)
+- `Mapbox::TYPE_BLOCK` (Japan) and `Mapbox::TYPE_SECONDARY_ADDRESS` (US) constants (not part of `TYPES`: the API only accepts them in the corresponding regional context)
+- `Mapbox::STRUCTURED_INPUT_FIELDS` constant
+- Query data support: `autocomplete` (replaces `fuzzy_match`), `proximity` (`"lon,lat"` or `"ip"`), `worldview`
+- New 4th constructor argument `bool $permanent` (v6 `permanent=true` result storage)
+- `MapboxAddress::getMatchCode()` / `withMatchCode()` — v6 Smart Address Match object
+- `MapboxAddress::getMatchConfidence()` / `withMatchConfidence()` — `exact`, `high`, `medium` or `low`
+- `MapboxAddress::getAccuracy()` / `withAccuracy()` — `rooftop`, `parcel`, `point`, `interpolated`, `approximate` or `intersection`
+
+### Removed
+
+- Support for the Mapbox Geocoding API v5 (`/geocoding/v5/...`) — pin to `geocoder-php/mapbox-provider:^1.5` to stay on v5
+- `Mapbox::GEOCODE_ENDPOINT_URL_SSL` and `Mapbox::REVERSE_ENDPOINT_URL_SSL` constants (replaced by `Mapbox::FORWARD_ENDPOINT_URL` / `Mapbox::REVERSE_ENDPOINT_URL`)
+- `Mapbox::GEOCODING_MODE_PLACES`, `Mapbox::GEOCODING_MODE_PLACES_PERMANENT` and `Mapbox::GEOCODING_MODES` constants (v6 has no geocoding modes; use the `permanent` constructor argument)
+- `Mapbox::TYPE_POI` and `Mapbox::TYPE_POI_LANDMARK` constants (v6 does not geocode POIs; passing them as `location_type` now yields a 422 error from the API)
+- `fuzzy_match` query data (v6 has no fuzzy/typo matching; use `autocomplete` for prefix-style matching)
+
+### Changed
+
+- The 4th constructor argument is now `bool $permanent = false` (was `string $geocodingMode`); passing a mode string changes behavior or errors
+- `Mapbox::TYPES` no longer contains the POI types and now contains `street`
+- Forward requests send the search text as the `q` query parameter instead of in the URL path
+- Reverse requests send `longitude` / `latitude` query parameters (was `{lon},{lat}` in the path)
+- `MapboxAddress::getId()` returns the v6 `mapbox_id` value (format changed, e.g. `dXJu...` instead of `address.123`)
+- `MapboxAddress::getResultType()` values follow the v6 `feature_type` vocabulary (`street` added, `poi`/`poi.landmark` gone)
+- Reverse geocoding with a `limit` greater than 1 requires a single `location_type` value (v6 API constraint: multiple types + limit returns a 422 error)
+- Forward `limit` is capped at 10 by the v6 API (higher values are silently clamped)
+- Invalid access tokens are now answered with HTTP 401 by the API (v5 used 403); both map to `Geocoder\Exception\InvalidCredentials`
+- Geocoded data reflects the v6 dataset: coordinates, results and admin-level codes may differ from v5
+
## 1.5.0
### Added
diff --git a/src/Provider/Mapbox/Mapbox.php b/src/Provider/Mapbox/Mapbox.php
index 8d78d7da5..2b8a34221 100644
--- a/src/Provider/Mapbox/Mapbox.php
+++ b/src/Provider/Mapbox/Mapbox.php
@@ -13,7 +13,6 @@
namespace Geocoder\Provider\Mapbox;
use Geocoder\Collection;
-use Geocoder\Exception\InvalidArgument;
use Geocoder\Exception\InvalidServerResponse;
use Geocoder\Exception\UnsupportedOperation;
use Geocoder\Http\Provider\AbstractHttpProvider;
@@ -30,30 +29,12 @@ final class Mapbox extends AbstractHttpProvider implements Provider
/**
* @var string
*/
- public const GEOCODE_ENDPOINT_URL_SSL = 'https://api.mapbox.com/geocoding/v5/%s/%s.json';
+ public const FORWARD_ENDPOINT_URL = 'https://api.mapbox.com/search/geocode/v6/forward';
/**
* @var string
*/
- public const REVERSE_ENDPOINT_URL_SSL = 'https://api.mapbox.com/geocoding/v5/%s/%F,%F.json';
-
- /**
- * @var string
- */
- public const GEOCODING_MODE_PLACES = 'mapbox.places';
-
- /**
- * @var string
- */
- public const GEOCODING_MODE_PLACES_PERMANENT = 'mapbox.places-permanent';
-
- /**
- * @var string[]
- */
- public const GEOCODING_MODES = [
- self::GEOCODING_MODE_PLACES,
- self::GEOCODING_MODE_PLACES_PERMANENT,
- ];
+ public const REVERSE_ENDPOINT_URL = 'https://api.mapbox.com/search/geocode/v6/reverse';
/**
* @var string
@@ -98,12 +79,23 @@ final class Mapbox extends AbstractHttpProvider implements Provider
/**
* @var string
*/
- public const TYPE_POI = 'poi';
+ public const TYPE_STREET = 'street';
/**
+ * Japanese addresses only. The API accepts this type only for Japan-context requests
+ * (e.g. with country=jp and language=ja), so it is intentionally not part of TYPES.
+ *
* @var string
*/
- public const TYPE_POI_LANDMARK = 'poi.landmark';
+ public const TYPE_BLOCK = 'block';
+
+ /**
+ * Sub-unit, suite, or lot within a parent address (US only). The API accepts this type
+ * only for US-context requests, so it is intentionally not part of TYPES.
+ *
+ * @var string
+ */
+ public const TYPE_SECONDARY_ADDRESS = 'secondary_address';
/**
* @var string[]
@@ -116,9 +108,25 @@ final class Mapbox extends AbstractHttpProvider implements Provider
self::TYPE_PLACE,
self::TYPE_LOCALITY,
self::TYPE_NEIGHBORHOOD,
+ self::TYPE_STREET,
self::TYPE_ADDRESS,
- self::TYPE_POI,
- self::TYPE_POI_LANDMARK,
+ ];
+
+ /**
+ * Query data keys that trigger the v6 Structured Input mode (the query text is not sent).
+ *
+ * @var string[]
+ */
+ public const STRUCTURED_INPUT_FIELDS = [
+ 'address_line1',
+ 'address_number',
+ 'street',
+ 'block',
+ 'place',
+ 'region',
+ 'postcode',
+ 'locality',
+ 'neighborhood',
];
/**
@@ -137,91 +145,143 @@ final class Mapbox extends AbstractHttpProvider implements Provider
private $country;
/**
- * @var string
+ * @var bool
*/
- private $geocodingMode;
+ private $permanent;
/**
* @param ClientInterface $client An HTTP adapter
* @param string $accessToken Your Mapbox access token
+ * @param string|null $country Restrict results to one or more ISO 3166 alpha-2 countries (comma separated)
+ * @param bool $permanent Store results permanently (v6 `permanent` parameter)
*/
public function __construct(
ClientInterface $client,
string $accessToken,
?string $country = null,
- string $geocodingMode = self::GEOCODING_MODE_PLACES,
+ bool $permanent = false,
) {
parent::__construct($client);
- if (!in_array($geocodingMode, self::GEOCODING_MODES)) {
- throw new InvalidArgument('The Mapbox geocoding mode should be either mapbox.places or mapbox.places-permanent.');
- }
-
$this->accessToken = $accessToken;
$this->country = $country;
- $this->geocodingMode = $geocodingMode;
+ $this->permanent = $permanent;
}
public function geocodeQuery(GeocodeQuery $query): Collection
{
- // Mapbox API returns invalid data if IP address given
- // This API doesn't handle IPs
+ // The Mapbox API does not geocode raw IP addresses
if (filter_var($query->getText(), FILTER_VALIDATE_IP)) {
throw new UnsupportedOperation('The Mapbox provider does not support IP addresses, only street addresses.');
}
- $url = sprintf(self::GEOCODE_ENDPOINT_URL_SSL, $this->geocodingMode, rawurlencode($query->getText()));
+ $parameters = [];
+
+ // v6 Structured Input: typed fields replace the `q` search text
+ $structured = $this->structuredInputFields($query);
+ if ([] !== $structured) {
+ foreach (self::STRUCTURED_INPUT_FIELDS as $field) {
+ if (isset($structured[$field])) {
+ $parameters[$field] = $structured[$field];
+ }
+ }
+ } else {
+ $parameters['q'] = $query->getText();
+ }
- $urlParameters = [];
if ($query->getBounds()) {
// Format is "minLon,minLat,maxLon,maxLat"
- $urlParameters['bbox'] = sprintf(
+ $parameters['bbox'] = sprintf(
'%s,%s,%s,%s',
- $query->getBounds()->getWest(),
- $query->getBounds()->getSouth(),
- $query->getBounds()->getEast(),
- $query->getBounds()->getNorth()
+ $this->formatCoordinate($query->getBounds()->getWest()),
+ $this->formatCoordinate($query->getBounds()->getSouth()),
+ $this->formatCoordinate($query->getBounds()->getEast()),
+ $this->formatCoordinate($query->getBounds()->getNorth())
);
}
- if (null !== $locationType = $query->getData('location_type')) {
- $urlParameters['types'] = is_array($locationType) ? implode(',', $locationType) : $locationType;
- } else {
- $urlParameters['types'] = self::DEFAULT_TYPE;
+ $parameters['types'] = $this->locationTypes($query);
+
+ if (null !== $autocomplete = $query->getData('autocomplete')) {
+ $parameters['autocomplete'] = $autocomplete ? 'true' : 'false';
+ } elseif ([] !== $structured) {
+ // Mapbox recommends autocomplete=false for structured input
+ $parameters['autocomplete'] = 'false';
}
- if (null !== $fuzzyMatch = $query->getData('fuzzy_match')) {
- $urlParameters['fuzzyMatch'] = $fuzzyMatch ? 'true' : 'false';
+ if (null !== $proximity = $query->getData('proximity')) {
+ $parameters['proximity'] = $proximity;
}
- if (count($urlParameters) > 0) {
- $url .= '?'.http_build_query($urlParameters);
+ if (null !== $worldview = $query->getData('worldview')) {
+ $parameters['worldview'] = $worldview;
}
- return $this->fetchUrl($url, $query->getLimit(), $query->getLocale(), $query->getData('country', $this->country));
+ return $this->fetchUrl(
+ self::FORWARD_ENDPOINT_URL,
+ $query->getLimit(),
+ $query->getLocale(),
+ $query->getData('country', $this->country),
+ $parameters
+ );
}
public function reverseQuery(ReverseQuery $query): Collection
{
$coordinate = $query->getCoordinates();
- $url = sprintf(
- self::REVERSE_ENDPOINT_URL_SSL,
- $this->geocodingMode,
- $coordinate->getLongitude(),
- $coordinate->getLatitude()
+ $parameters = [
+ 'longitude' => $this->formatCoordinate($coordinate->getLongitude()),
+ 'latitude' => $this->formatCoordinate($coordinate->getLatitude()),
+ ];
+
+ $parameters['types'] = $this->locationTypes($query);
+
+ return $this->fetchUrl(
+ self::REVERSE_ENDPOINT_URL,
+ $query->getLimit(),
+ $query->getLocale(),
+ $query->getData('country', $this->country),
+ $parameters
);
+ }
- if (null !== $locationType = $query->getData('location_type')) {
- $urlParameters['types'] = is_array($locationType) ? implode(',', $locationType) : $locationType;
- } else {
- $urlParameters['types'] = self::DEFAULT_TYPE;
+ /**
+ * @return array
+ */
+ private function structuredInputFields(GeocodeQuery $query): array
+ {
+ $fields = [];
+
+ foreach (self::STRUCTURED_INPUT_FIELDS as $field) {
+ $value = $query->getData($field);
+ if (null !== $value && '' !== $value) {
+ $fields[$field] = $value;
+ }
}
- if (count($urlParameters) > 0) {
- $url .= '?'.http_build_query($urlParameters);
+ return $fields;
+ }
+
+ /**
+ * @param GeocodeQuery|ReverseQuery $query
+ */
+ private function locationTypes($query): string
+ {
+ if (null !== $locationType = $query->getData('location_type')) {
+ return is_array($locationType) ? implode(',', $locationType) : $locationType;
}
- return $this->fetchUrl($url, $query->getLimit(), $query->getLocale(), $query->getData('country', $this->country));
+ return self::DEFAULT_TYPE;
+ }
+
+ /**
+ * Format a coordinate with the shortest decimal representation that round-trips the
+ * value exactly (a plain (string) cast truncates to the 14 significant digits of the
+ * default `precision` ini setting, which would change the request URL).
+ */
+ private function formatCoordinate(float $value): string
+ {
+ return (string) json_encode($value);
}
public function getName(): string
@@ -230,25 +290,35 @@ public function getName(): string
}
/**
- * @return string query with extra params
+ * @param array $parameters
*/
- private function buildQuery(string $url, int $limit, ?string $locale = null, ?string $country = null): string
+ private function buildQuery(string $url, int $limit, ?string $locale, ?string $country, array $parameters): string
{
- $parameters = array_filter([
- 'country' => $country,
- 'language' => $locale,
- 'limit' => $limit,
- 'access_token' => $this->accessToken,
- ]);
+ if (null !== $country) {
+ $parameters['country'] = $country;
+ }
- $separator = parse_url($url, PHP_URL_QUERY) ? '&' : '?';
+ if (null !== $locale) {
+ $parameters['language'] = $locale;
+ }
+
+ $parameters['limit'] = $limit;
+
+ if ($this->permanent) {
+ $parameters['permanent'] = 'true';
+ }
+
+ $parameters['access_token'] = $this->accessToken;
- return $url.$separator.http_build_query($parameters);
+ return $url.'?'.http_build_query($parameters);
}
- private function fetchUrl(string $url, int $limit, ?string $locale = null, ?string $country = null): AddressCollection
+ /**
+ * @param array $parameters
+ */
+ private function fetchUrl(string $url, int $limit, ?string $locale, ?string $country, array $parameters): AddressCollection
{
- $url = $this->buildQuery($url, $limit, $locale, $country);
+ $url = $this->buildQuery($url, $limit, $locale, $country, $parameters);
$content = $this->getUrlContents($url);
$json = $this->validateResponse($url, $content);
@@ -259,103 +329,126 @@ private function fetchUrl(string $url, int $limit, ?string $locale = null, ?stri
$results = [];
foreach ($json['features'] as $result) {
- if (!array_key_exists('context', $result)) {
+ $results[] = $this->parseFeature($result);
+
+ if (count($results) >= $limit) {
break;
}
+ }
- $builder = new AddressBuilder($this->getName());
- $this->parseCoordinates($builder, $result);
+ return new AddressCollection($results);
+ }
- // set official Mapbox place id
- if (isset($result['id'])) {
- $builder->setValue('id', $result['id']);
- }
+ /**
+ * @param array $result
+ */
+ private function parseFeature(array $result): MapboxAddress
+ {
+ $properties = $result['properties'] ?? [];
+ $context = $properties['context'] ?? [];
- // set official Mapbox place id
- if (isset($result['text'])) {
- $builder->setValue('street_name', $result['text']);
- }
+ $builder = new AddressBuilder($this->getName());
+ $this->parseCoordinates($builder, $result);
- // update address components
- foreach ($result['context'] as $component) {
- $this->updateAddressComponent($builder, $component['id'], $component);
+ // update address components (context is an object keyed by feature type in v6)
+ foreach ($context as $type => $component) {
+ if (is_array($component)) {
+ $this->updateAddressComponent($builder, (string) $type, $component);
}
+ }
- /** @var MapboxAddress $address */
- $address = $builder->build(MapboxAddress::class);
- $address = $address->withId($builder->getValue('id'));
- if (isset($result['address'])) {
- $address = $address->withStreetNumber($result['address']);
- }
- if (isset($result['place_type'])) {
- $address = $address->withResultType($result['place_type']);
- }
- if (isset($result['place_name'])) {
- $address = $address->withFormattedAddress($result['place_name']);
- }
- $address = $address->withStreetName($builder->getValue('street_name'));
- $address = $address->withNeighborhood($builder->getValue('neighborhood'));
- $results[] = $address;
+ /** @var MapboxAddress $address */
+ $address = $builder->build(MapboxAddress::class);
- if (count($results) >= $limit) {
- break;
+ if (isset($properties['mapbox_id'])) {
+ $address = $address->withId((string) $properties['mapbox_id']);
+ } elseif (isset($result['id'])) {
+ $address = $address->withId((string) $result['id']);
+ }
+
+ // street name without the house number (v5 semantics)
+ $streetName = $context['address']['street_name'] ?? $context['street']['name'] ?? $properties['name'] ?? null;
+ if (null !== $streetName) {
+ $address = $address->withStreetName((string) $streetName);
+ }
+
+ $streetNumber = $context['address']['address_number'] ?? null;
+ if (null !== $streetNumber) {
+ $address = $address->withStreetNumber((string) $streetNumber);
+ }
+
+ if (isset($properties['feature_type'])) {
+ $address = $address->withResultType([(string) $properties['feature_type']]);
+ }
+
+ if (isset($properties['full_address'])) {
+ $address = $address->withFormattedAddress((string) $properties['full_address']);
+ }
+
+ $neighborhood = $context['neighborhood']['name'] ?? null;
+ if (null !== $neighborhood && '' !== $neighborhood) {
+ $address = $address->withNeighborhood((string) $neighborhood);
+ }
+
+ if (isset($properties['match_code']) && is_array($properties['match_code'])) {
+ $address = $address->withMatchCode($properties['match_code']);
+ $confidence = $properties['match_code']['confidence'] ?? null;
+ if (null !== $confidence) {
+ $address = $address->withMatchConfidence((string) $confidence);
}
}
- return new AddressCollection($results);
+ if (isset($properties['coordinates']['accuracy'])) {
+ $address = $address->withAccuracy((string) $properties['coordinates']['accuracy']);
+ }
+
+ return $address;
}
/**
* Update current resultSet with given key/value.
*
- * @param string $type Component type
- * @param array $value Component value
+ * @param array $component
*/
- private function updateAddressComponent(AddressBuilder $builder, string $type, array $value): void
+ private function updateAddressComponent(AddressBuilder $builder, string $type, array $component): void
{
- $typeParts = explode('.', $type);
- $type = reset($typeParts);
+ if (!isset($component['name']) || '' === $component['name']) {
+ return;
+ }
switch ($type) {
case 'postcode':
- $builder->setPostalCode($value['text']);
+ $builder->setPostalCode($component['name']);
break;
case 'locality':
- $builder->setLocality($value['text']);
+ $builder->setLocality($component['name']);
break;
case 'country':
- $builder->setCountry($value['text']);
- if (isset($value['short_code'])) {
- $builder->setCountryCode(strtoupper($value['short_code']));
+ $builder->setCountry($component['name']);
+ if (isset($component['country_code'])) {
+ $builder->setCountryCode(strtoupper((string) $component['country_code']));
}
break;
- case 'neighborhood':
- $builder->setValue($type, $value['text']);
-
- break;
-
case 'place':
- $builder->addAdminLevel(1, $value['text']);
- $builder->setLocality($value['text']);
+ $builder->addAdminLevel(1, $component['name']);
+ $builder->setLocality($component['name']);
break;
case 'region':
- $code = null;
- if (!empty($value['short_code']) && preg_match('/[A-z]{2}-/', $value['short_code'])) {
- $code = preg_replace('/[A-z]{2}-/', '', $value['short_code']);
- }
- $builder->addAdminLevel(2, $value['text'], $code);
+ $code = isset($component['region_code']) ? (string) $component['region_code'] : null;
+ $builder->addAdminLevel(2, $component['name'], $code);
break;
default:
+ // address, street, district, neighborhood: handled (or intentionally ignored) elsewhere
}
}
diff --git a/src/Provider/Mapbox/Model/MapboxAddress.php b/src/Provider/Mapbox/Model/MapboxAddress.php
index b767f3249..cfb51ad08 100644
--- a/src/Provider/Mapbox/Model/MapboxAddress.php
+++ b/src/Provider/Mapbox/Model/MapboxAddress.php
@@ -46,6 +46,21 @@ final class MapboxAddress extends Address
*/
private $neighborhood;
+ /**
+ * @var array|null
+ */
+ private $matchCode;
+
+ /**
+ * @var string|null
+ */
+ private $matchConfidence;
+
+ /**
+ * @var string|null
+ */
+ private $accuracy;
+
public function withId(?string $id = null): self
{
$new = clone $this;
@@ -132,4 +147,49 @@ public function withNeighborhood(?string $neighborhood = null): self
return $new;
}
+
+ /**
+ * @return array|null
+ */
+ public function getMatchCode(): ?array
+ {
+ return $this->matchCode;
+ }
+
+ /**
+ * @param array|null $matchCode
+ */
+ public function withMatchCode(?array $matchCode = null): self
+ {
+ $new = clone $this;
+ $new->matchCode = $matchCode;
+
+ return $new;
+ }
+
+ public function getMatchConfidence(): ?string
+ {
+ return $this->matchConfidence;
+ }
+
+ public function withMatchConfidence(?string $matchConfidence = null): self
+ {
+ $new = clone $this;
+ $new->matchConfidence = $matchConfidence;
+
+ return $new;
+ }
+
+ public function getAccuracy(): ?string
+ {
+ return $this->accuracy;
+ }
+
+ public function withAccuracy(?string $accuracy = null): self
+ {
+ $new = clone $this;
+ $new->accuracy = $accuracy;
+
+ return $new;
+ }
}
diff --git a/src/Provider/Mapbox/Readme.md b/src/Provider/Mapbox/Readme.md
index ebfbe29e6..d9815823d 100644
--- a/src/Provider/Mapbox/Readme.md
+++ b/src/Provider/Mapbox/Readme.md
@@ -20,6 +20,46 @@ composer require geocoder-php/mapbox-provider
A valid `Access Token` is required for Mapbox.
+### Usage
+
+This provider targets the **Mapbox Geocoding API v6**. If you rely on the v5 API, pin
+`geocoder-php/mapbox-provider` to `^1.5`.
+
+```php
+$provider = new Mapbox($client, $accessToken);
+// optional: country filter (ISO 3166 alpha-2) and permanent result storage
+$provider = new Mapbox($client, $accessToken, 'US', true);
+
+// Forward geocoding
+$provider->geocodeQuery(GeocodeQuery::create('149 9th St, San Francisco, CA 94103'));
+
+// Forward geocoding with options (query data)
+$query = GeocodeQuery::create('washi')
+ ->withData('autocomplete', true) // prefix-style matching (on by default)
+ ->withData('location_type', Mapbox::TYPE_STREET) // or an array of Mapbox::TYPE_* constants
+ ->withData('proximity', '-120.09,47.60') // or 'ip'
+ ->withData('worldview', 'us')
+ ->withData('country', 'US');
+
+// v6 Structured Input: typed fields replace the free-text query (the text is not sent)
+$query = GeocodeQuery::create('2595 Lucky John Dr, Park City, UT 84060')
+ ->withData('address_number', '2595')
+ ->withData('street', 'Lucky John Dr')
+ ->withData('place', 'Park City')
+ ->withData('region', 'UT')
+ ->withData('postcode', '84060');
+// Any of address_line1, address_number, street, block, place, region, postcode,
+// locality, neighborhood triggers Structured Input; autocomplete defaults to false.
+
+// Reverse geocoding
+$provider->reverseQuery(ReverseQuery::fromCoordinates(48.8631507, 2.388911));
+```
+
+Results are `Geocoder\Provider\Mapbox\Model\MapboxAddress` instances. Besides the regular
+address data they expose `getId()`, `getStreetName()`, `getStreetNumber()`,
+`getResultType()`, `getFormattedAddress()`, `getNeighborhood()`,
+`getMatchCode()` / `getMatchConfidence()` (v6 Smart Address Match) and `getAccuracy()`.
+
### Contribute
Contributions are very welcome! Send a pull request to the [main repository](https://github.com/geocoder-php/Geocoder) or
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_06551ee827f3b9635793737514ec6b9d368075c4 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_06551ee827f3b9635793737514ec6b9d368075c4
new file mode 100644
index 000000000..64d3405cb
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_06551ee827f3b9635793737514ec6b9d368075c4
@@ -0,0 +1 @@
+s:2020:"{"type":"FeatureCollection","features":[{"type":"Feature","id":"dXJuOm1ieGFkcjozOGY3OTg1OC1jNzY0LTQ4ZGUtYTFmMC04NjJjOTM1ZWViMjc","geometry":{"type":"Point","coordinates":[-111.507008,40.6716]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjozOGY3OTg1OC1jNzY0LTQ4ZGUtYTFmMC04NjJjOTM1ZWViMjc","feature_type":"address","full_address":"2595 Lucky John Drive, Park City, Utah 84060, United States","name":"2595 Lucky John Drive","name_preferred":"2595 Lucky John Drive","coordinates":{"longitude":-111.507008,"latitude":40.6716,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":40.6716,"longitude":-111.506529}]},"place_formatted":"Park City, Utah 84060, United States","match_code":{"address_number":"matched","street":"matched","postcode":"matched","place":"matched","region":"matched","locality":"not_applicable","country":"inferred","confidence":"exact"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjozOGY3OTg1OC1jNzY0LTQ4ZGUtYTFmMC04NjJjOTM1ZWViMjc","address_number":"2595","street_name":"Lucky John Drive","name":"2595 Lucky John Drive"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6MzhmNzk4NTgtYzc2NC00OGRlLWExZjAtODYyYzkzNWVlYjI3","name":"Lucky John Drive"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpITktzN0E","name":"Park Meadows"},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpFUjNPN0E","name":"84060"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpEdjlvN0E","name":"Park City","wikidata_id":"Q482993"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpBVlRHN0E","name":"Summit County","wikidata_id":"Q484563"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpCa1Rz","name":"Utah","wikidata_id":"Q829","region_code":"UT","region_code_full":"US-UT"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}}],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_20d856dd6d6ae671b181193de4e52f7fb651fd97 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_20d856dd6d6ae671b181193de4e52f7fb651fd97
deleted file mode 100644
index 8a7277674..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_20d856dd6d6ae671b181193de4e52f7fb651fd97
+++ /dev/null
@@ -1 +0,0 @@
-s:967:"{"type":"FeatureCollection","query":["princ"],"features":[{"id":"place.4899176537126140","type":"Feature","place_type":["place"],"relevance":1,"properties":{"wikidata":"Q235"},"text_it":"Principato di Monaco","language_it":"it","place_name_it":"Principato di Monaco, Principato di Monaco","text":"Principato di Monaco","language":"it","place_name":"Principato di Monaco, Principato di Monaco","bbox":[7.408977,43.724643,7.440014,43.751895],"center":[7.41974,43.73125],"geometry":{"type":"Point","coordinates":[7.41974,43.73125]},"context":[{"id":"country.3435803554126140","wikidata":"Q235","text_it":"Principato di Monaco","language_it":"it","text":"Principato di Monaco","language":"it"}]}],"attribution":"NOTICE: © 2020 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained. POI(s) provided by Foursquare."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_27f7da637a8e576c59a3128575847a7abc2d13ba b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_27f7da637a8e576c59a3128575847a7abc2d13ba
new file mode 100644
index 000000000..867689ad1
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_27f7da637a8e576c59a3128575847a7abc2d13ba
@@ -0,0 +1 @@
+s:285:"{"type":"FeatureCollection","features":[],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_338a05e2ceeb9a75e55774746945065683127870 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_338a05e2ceeb9a75e55774746945065683127870
new file mode 100644
index 000000000..4f1aed58f
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_338a05e2ceeb9a75e55774746945065683127870
@@ -0,0 +1 @@
+s:5571:"{"type":"FeatureCollection","features":[{"type":"Feature","id":"dXJuOm1ieHBsYzpBUVRz","geometry":{"type":"Point","coordinates":[-120.094293,48.025005]},"properties":{"mapbox_id":"dXJuOm1ieHBsYzpBUVRz","feature_type":"region","full_address":"Washington, United States","name":"Washington","name_preferred":"Washington","coordinates":{"longitude":-120.094293,"latitude":48.025005},"place_formatted":"United States","bbox":[-124.861672,45.543637,-116.916072,49.002595],"context":{"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpBUVRz","name":"Washington","region_code":"WA","region_code_full":"US-WA","wikidata_id":"Q1223"}}}},{"type":"Feature","id":"dXJuOm1ieHBsYzpLU3RzN0E","geometry":{"type":"Point","coordinates":[-120.482925,46.59803]},"properties":{"mapbox_id":"dXJuOm1ieHBsYzpLU3RzN0E","feature_type":"neighborhood","full_address":"Washington Park, Yakima, Washington, United States","name":"Washington Park","name_preferred":"Washington Park","coordinates":{"longitude":-120.482925,"latitude":46.59803},"place_formatted":"Yakima, Washington, United States","bbox":[-120.494858,46.592164,-120.481383,46.601423],"context":{"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpFN1B1N0E","name":"98901"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpGZE1JN0E","name":"Yakima","wikidata_id":"Q499203","alternate":{"mapbox_id":"dXJuOm1ieHBsYzpFKy9vN0E","name":"Union Gap"}},"district":{"mapbox_id":"dXJuOm1ieHBsYzpBWVdHN0E","name":"Yakima County","wikidata_id":"Q156629"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpBUVRz","name":"Washington","wikidata_id":"Q1223","region_code":"WA","region_code_full":"US-WA"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpLU3RzN0E","name":"Washington Park"}}}},{"type":"Feature","id":"dXJuOm1ieHBsYzpMcDhzN0E","geometry":{"type":"Point","coordinates":[-123.04517,48.07898]},"properties":{"mapbox_id":"dXJuOm1ieHBsYzpMcDhzN0E","feature_type":"neighborhood","full_address":"Washington Harbor, Sequim, Washington, United States","name":"Washington Harbor","name_preferred":"Washington Harbor","coordinates":{"longitude":-123.04517,"latitude":48.07898},"place_formatted":"Sequim, Washington, United States","bbox":[-123.051913,48.07448,-123.038438,48.083482],"context":{"place":{"mapbox_id":"dXJuOm1ieHBsYzpFY2RJN0E","name":"Sequim","wikidata_id":"Q1514115"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpRTWJz","name":"Clallam County","wikidata_id":"Q156306"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpBUVRz","name":"Washington","wikidata_id":"Q1223","region_code":"WA","region_code_full":"US-WA"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpMcDhzN0E","name":"Washington Harbor"}}}},{"type":"Feature","id":"dXJuOm1ieGFkci1zdHI6ZGNjNjFmZjctZDkwZi00N2U5LTg2OGMtNTMyMWFlZWYyZmY5","geometry":{"type":"Point","coordinates":[-123.381214,48.444226]},"properties":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6ZGNjNjFmZjctZDkwZi00N2U5LTg2OGMtNTMyMWFlZWYyZmY5","feature_type":"street","full_address":"Washington Avenue, Victoria, British Columbia V9A 1P7, Canada","name":"Washington Avenue","name_preferred":"Washington Avenue","coordinates":{"longitude":-123.381214,"latitude":48.444226},"place_formatted":"Victoria, British Columbia V9A 1P7, Canada","context":{"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6ZGNjNjFmZjctZDkwZi00N2U5LTg2OGMtNTMyMWFlZWYyZmY5","name":"Washington Avenue"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpNQ3du","name":"Burnside"},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpBYVlQYmlj","name":"V9A 1P7"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpCMTVJSnc","name":"Victoria","wikidata_id":"Q2132"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpBcVlu","name":"Capital Regional District","wikidata_id":"Q550219"},"region":{"mapbox_id":"dXJuOm1ieHBsYzo1Q2M","name":"British Columbia","wikidata_id":"Q1973","region_code":"BC","region_code_full":"CA-BC"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJaWM","name":"Canada","wikidata_id":"Q16","country_code":"CA","country_code_alpha_3":"CAN"}}}},{"type":"Feature","id":"address.7607992201284362","geometry":{"type":"Point","coordinates":[-117.039767,46.930253]},"properties":{"mapbox_id":"address.7607992201284362","feature_type":"street","full_address":"Washington Highway 6, Palouse, Washington 99161, United States","name":"Washington Highway 6","name_preferred":"Washington Highway 6","coordinates":{"longitude":-117.039767,"latitude":46.930253},"place_formatted":"Palouse, Washington 99161, United States","context":{"street":{"mapbox_id":"address.7607992201284362","name":"Washington Highway 6"},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpFOEtPN0E","name":"99161"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpEdkVvN0E","name":"Palouse"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpBWHpHN0E","name":"Whitman County","wikidata_id":"Q484153"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpBUVRz","name":"Washington","wikidata_id":"Q1223","region_code":"WA","region_code_full":"US-WA"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}}],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_436e84f1a3531213304e67e5bca76e7550349a2a b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_436e84f1a3531213304e67e5bca76e7550349a2a
deleted file mode 100644
index 46df4575c..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_436e84f1a3531213304e67e5bca76e7550349a2a
+++ /dev/null
@@ -1 +0,0 @@
-s:331:"{"type":"FeatureCollection","query":["jsajhgsdkfjhsfkjhaldkadjaslgldasd"],"features":[],"attribution":"NOTICE: © 2017 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_5c71bf49f4586edeacc48e9b5d1ab218054d423d b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_5c71bf49f4586edeacc48e9b5d1ab218054d423d
new file mode 100644
index 000000000..86e710744
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_5c71bf49f4586edeacc48e9b5d1ab218054d423d
@@ -0,0 +1 @@
+s:10311:"{"type":"FeatureCollection","features":[{"type":"Feature","id":"dXJuOm1ieGFkcjo2YjE5MDRmMi1lYjBkLTRjZDQtODViZC1mNmUzZmI1ZTNiZWE","geometry":{"type":"Point","coordinates":[-0.12767,51.50354]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjo2YjE5MDRmMi1lYjBkLTRjZDQtODViZC1mNmUzZmI1ZTNiZWE","feature_type":"address","full_address":"10 Downing Street, City of Westminster, London, SW1A 2AA, United Kingdom","name":"10 Downing Street","name_preferred":"10 Downing Street","coordinates":{"longitude":-0.12767,"latitude":51.50354,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":51.503257,"longitude":-0.12772}]},"place_formatted":"City of Westminster, London, SW1A 2AA, United Kingdom","match_code":{"address_number":"matched","street":"matched","postcode":"unmatched","place":"matched","region":"not_applicable","locality":"unmatched","country":"inferred","confidence":"low"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjo2YjE5MDRmMi1lYjBkLTRjZDQtODViZC1mNmUzZmI1ZTNiZWE","address_number":"10","street_name":"Downing Street","name":"10 Downing Street"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6NmIxOTA0ZjItZWIwZC00Y2Q0LTg1YmQtZjZlM2ZiNWUzYmVh","name":"Downing Street"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpBeFlNVHc","name":"Whitehall","translations":{"en":{"language":"en","name":"Whitehall"}}},"postcode":{"mapbox_id":"postcode.8654501461090586","name":"SW1A 2AA"},"locality":{"mapbox_id":"dXJuOm1ieHBsYzpDM3RLVHc","name":"City of Westminster","wikidata_id":"Q179351","translations":{"en":{"language":"en","name":"City of Westminster"}}},"place":{"mapbox_id":"dXJuOm1ieHBsYzphaWhQ","name":"London","wikidata_id":"Q84","translations":{"en":{"language":"en","name":"London"}}},"district":{"mapbox_id":"dXJuOm1ieHBsYzpDUVpQ","name":"Greater London","wikidata_id":"Q23306","translations":{"en":{"language":"en","name":"Greater London"}}},"region":{"mapbox_id":"dXJuOm1ieHBsYzpKRTg","name":"England","wikidata_id":"Q21","region_code":"ENG","region_code_full":"GB-ENG","translations":{"en":{"language":"en","name":"England"}}},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazg","name":"United Kingdom","wikidata_id":"Q145","country_code":"GB","country_code_alpha_3":"GBR","translations":{"en":{"language":"en","name":"United Kingdom"}}}}}},{"type":"Feature","id":"dXJuOm1ieGFkcjpjZDYxNzhjMS02NDhhLTQ4OWItYTA2ZS1mYTkzZTExZmU2NDA","geometry":{"type":"Point","coordinates":[0.066,51.51288]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjpjZDYxNzhjMS02NDhhLTQ4OWItYTA2ZS1mYTkzZTExZmU2NDA","feature_type":"address","full_address":"10 Downings, London Borough of Newham, London, E6 6WP, United Kingdom","name":"10 Downings","name_preferred":"10 Downings","coordinates":{"longitude":0.066,"latitude":51.51288,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":51.512731,"longitude":0.065978}]},"place_formatted":"London Borough of Newham, London, E6 6WP, United Kingdom","match_code":{"address_number":"matched","street":"matched","postcode":"unmatched","place":"matched","region":"not_applicable","locality":"unmatched","country":"inferred","confidence":"low"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjpjZDYxNzhjMS02NDhhLTQ4OWItYTA2ZS1mYTkzZTExZmU2NDA","address_number":"10","street_name":"Downings","name":"10 Downings"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6Y2Q2MTc4YzEtNjQ4YS00ODliLWEwNmUtZmE5M2UxMWZlNjQw","name":"Downings"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzoyRXhQ","name":"East Ham","translations":{"en":{"language":"en","name":"East Ham"}}},"postcode":{"mapbox_id":"postcode.5087212628037556","name":"E6 6WP"},"locality":{"mapbox_id":"dXJuOm1ieHBsYzpCN1JxVHc","name":"London Borough of Newham","wikidata_id":"Q208139","translations":{"en":{"language":"en","name":"London Borough of Newham"}}},"place":{"mapbox_id":"dXJuOm1ieHBsYzphaWhQ","name":"London","wikidata_id":"Q84","translations":{"en":{"language":"en","name":"London"}}},"district":{"mapbox_id":"dXJuOm1ieHBsYzpDUVpQ","name":"Greater London","wikidata_id":"Q23306","translations":{"en":{"language":"en","name":"Greater London"}}},"region":{"mapbox_id":"dXJuOm1ieHBsYzpKRTg","name":"England","wikidata_id":"Q21","region_code":"ENG","region_code_full":"GB-ENG","translations":{"en":{"language":"en","name":"England"}}},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazg","name":"United Kingdom","wikidata_id":"Q145","country_code":"GB","country_code_alpha_3":"GBR","translations":{"en":{"language":"en","name":"United Kingdom"}}}}}},{"type":"Feature","id":"dXJuOm1ieGFkcjo2MWJmMDJlMC04Yzk5LTRjMjItYjAwYS1jOTYxMWNmYmRmNmE","geometry":{"type":"Point","coordinates":[0.12269,52.20363]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjo2MWJmMDJlMC04Yzk5LTRjMjItYjAwYS1jOTYxMWNmYmRmNmE","feature_type":"address","full_address":"10 Downing Street, Cambridge, CB2 3DS, United Kingdom","name":"10 Downing Street","name_preferred":"10 Downing Street","coordinates":{"longitude":0.12269,"latitude":52.20363,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":52.203383,"longitude":0.122846}]},"place_formatted":"Cambridge, CB2 3DS, United Kingdom","match_code":{"address_number":"matched","street":"matched","postcode":"unmatched","place":"unmatched","region":"not_applicable","locality":"not_applicable","country":"inferred","confidence":"low"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjo2MWJmMDJlMC04Yzk5LTRjMjItYjAwYS1jOTYxMWNmYmRmNmE","address_number":"10","street_name":"Downing Street","name":"10 Downing Street"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6NjFiZjAyZTAtOGM5OS00YzIyLWIwMGEtYzk2MTFjZmJkZjZh","name":"Downing Street"},"postcode":{"mapbox_id":"postcode.5786147553339014","name":"CB2 3DS"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpJZ2hQ","name":"Cambridge","wikidata_id":"Q350","translations":{"en":{"language":"en","name":"Cambridge"}}},"district":{"mapbox_id":"dXJuOm1ieHBsYzpBd1pQ","name":"Cambridgeshire","wikidata_id":"Q23112","translations":{"en":{"language":"en","name":"Cambridgeshire"}}},"region":{"mapbox_id":"dXJuOm1ieHBsYzpKRTg","name":"England","wikidata_id":"Q21","region_code":"ENG","region_code_full":"GB-ENG","translations":{"en":{"language":"en","name":"England"}}},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazg","name":"United Kingdom","wikidata_id":"Q145","country_code":"GB","country_code_alpha_3":"GBR","translations":{"en":{"language":"en","name":"United Kingdom"}}}}}},{"type":"Feature","id":"dXJuOm1ieGFkcjo5MTRjM2RjMy04M2E2LTQzNTgtYjg0YS0xYzk4YWRlNGQ2NGU","geometry":{"type":"Point","coordinates":[-0.79926,51.21331]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjo5MTRjM2RjMy04M2E2LTQzNTgtYjg0YS0xYzk4YWRlNGQ2NGU","feature_type":"address","full_address":"10 Downing Street, Farnham, GU9 7PB, United Kingdom","name":"10 Downing Street","name_preferred":"10 Downing Street","coordinates":{"longitude":-0.79926,"latitude":51.21331,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":51.213331,"longitude":-0.79914}]},"place_formatted":"Farnham, GU9 7PB, United Kingdom","match_code":{"address_number":"matched","street":"matched","postcode":"unmatched","place":"unmatched","region":"not_applicable","locality":"not_applicable","country":"inferred","confidence":"low"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjo5MTRjM2RjMy04M2E2LTQzNTgtYjg0YS0xYzk4YWRlNGQ2NGU","address_number":"10","street_name":"Downing Street","name":"10 Downing Street"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6OTE0YzNkYzMtODNhNi00MzU4LWI4NGEtMWM5OGFkZTRkNjRl","name":"Downing Street"},"postcode":{"mapbox_id":"postcode.5586436697038858","name":"GU9 7PB"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpQc2hQ","name":"Farnham","wikidata_id":"Q643160","translations":{"en":{"language":"en","name":"Farnham"}}},"district":{"mapbox_id":"dXJuOm1ieHBsYzpFcVpQ","name":"Surrey","wikidata_id":"Q23276","translations":{"en":{"language":"en","name":"Surrey"}}},"region":{"mapbox_id":"dXJuOm1ieHBsYzpKRTg","name":"England","wikidata_id":"Q21","region_code":"ENG","region_code_full":"GB-ENG","translations":{"en":{"language":"en","name":"England"}}},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazg","name":"United Kingdom","wikidata_id":"Q145","country_code":"GB","country_code_alpha_3":"GBR","translations":{"en":{"language":"en","name":"United Kingdom"}}}}}},{"type":"Feature","id":"dXJuOm1ieGFkcjphYjdkZjY0MC03OTA5LTRjZTktOWEyOC01NGQ3MjIwN2IxMWE","geometry":{"type":"Point","coordinates":[-1.20259,53.00619]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjphYjdkZjY0MC03OTA5LTRjZTktOWEyOC01NGQ3MjIwN2IxMWE","feature_type":"address","full_address":"10 Downing Street, Nottingham, NG6 8JU, United Kingdom","name":"10 Downing Street","name_preferred":"10 Downing Street","coordinates":{"longitude":-1.20259,"latitude":53.00619,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":53.0062,"longitude":-1.202407}]},"place_formatted":"Nottingham, NG6 8JU, United Kingdom","match_code":{"address_number":"matched","street":"matched","postcode":"unmatched","place":"unmatched","region":"not_applicable","locality":"not_applicable","country":"inferred","confidence":"low"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjphYjdkZjY0MC03OTA5LTRjZTktOWEyOC01NGQ3MjIwN2IxMWE","address_number":"10","street_name":"Downing Street","name":"10 Downing Street"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6YWI3ZGY2NDAtNzkwOS00Y2U5LTlhMjgtNTRkNzIyMDdiMTFh","name":"Downing Street"},"postcode":{"mapbox_id":"postcode.5551195545198608","name":"NG6 8JU"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpmVWhQ","name":"Nottingham","wikidata_id":"Q41262","translations":{"en":{"language":"en","name":"Nottingham"}}},"district":{"mapbox_id":"dXJuOm1ieHBsYzpCS1pQ","name":"City of Nottingham","wikidata_id":"Q21885994","translations":{"en":{"language":"en","name":"City of Nottingham"}}},"region":{"mapbox_id":"dXJuOm1ieHBsYzpKRTg","name":"England","wikidata_id":"Q21","region_code":"ENG","region_code_full":"GB-ENG","translations":{"en":{"language":"en","name":"England"}}},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazg","name":"United Kingdom","wikidata_id":"Q145","country_code":"GB","country_code_alpha_3":"GBR","translations":{"en":{"language":"en","name":"United Kingdom"}}}}}}],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_5e373c440fdae88e9ec714d42d85489c9986ab7c b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_5e373c440fdae88e9ec714d42d85489c9986ab7c
deleted file mode 100644
index 0bcf04c1b..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_5e373c440fdae88e9ec714d42d85489c9986ab7c
+++ /dev/null
@@ -1 +0,0 @@
-s:4034:"{"type":"FeatureCollection","query":["wahsington"],"features":[{"id":"address.3196918775570688","type":"Feature","place_type":["address"],"relevance":0.95,"properties":{"accuracy":"street"},"text":"Washington Street","place_name":"Washington Street, Sumas, Washington 98295, United States","center":[-122.2718746,49.0017164],"geometry":{"type":"Point","coordinates":[-122.2718746,49.0017164]},"context":[{"id":"postcode.1103001428069280","text":"98295"},{"id":"place.10997180196070370","wikidata":"Q1510124","text":"Sumas"},{"id":"region.9713796497246050","wikidata":"Q1223","short_code":"US-WA","text":"Washington"},{"id":"country.19678805456372290","wikidata":"Q30","short_code":"us","text":"United States"}]},{"id":"address.1946746068053092","type":"Feature","place_type":["address"],"relevance":0.95,"properties":{"accuracy":"street"},"text":"Washington Avenue","place_name":"Washington Avenue, Victoria, British Columbia V9A 1L5, Canada","matching_text":"Washington Ave E","matching_place_name":"Washington Ave E, Victoria, British Columbia V9A 1L5, Canada","center":[-123.380986,48.4440317],"geometry":{"type":"Point","coordinates":[-123.380986,48.4440317]},"context":[{"id":"neighborhood.11535041776736560","text":"Burnside"},{"id":"postcode.7635888694089700","text":"V9A 1L5"},{"id":"place.7877779600899450","wikidata":"Q2132","text":"Victoria"},{"id":"region.9984400673322020","wikidata":"Q1974","short_code":"CA-BC","text":"British Columbia"},{"id":"country.10278600750587150","wikidata":"Q16","short_code":"ca","text":"Canada"}]},{"id":"address.499813899464900","type":"Feature","place_type":["address"],"relevance":0.95,"properties":{"accuracy":"street"},"text":"Washington Highway 6","place_name":"Washington Highway 6, Palouse, Washington 99161, United States","center":[-117.0397671,46.9302529],"geometry":{"type":"Point","coordinates":[-117.0397671,46.9302529]},"context":[{"id":"postcode.12946567993911350","text":"99161"},{"id":"place.13856508424544360","text":"Palouse"},{"id":"region.9713796497246050","wikidata":"Q1223","short_code":"US-WA","text":"Washington"},{"id":"country.19678805456372290","wikidata":"Q30","short_code":"us","text":"United States"}]},{"id":"address.8713060929842858","type":"Feature","place_type":["address"],"relevance":0.95,"properties":{"accuracy":"street"},"text":"Down River Road","place_name":"Down River Road, Lewiston, Washington 83501, United States","matching_text":"Washington Highway 128","matching_place_name":"Washington Highway 128, Lewiston, Washington 83501, United States","center":[-117.0397913,46.4312588],"geometry":{"type":"Point","coordinates":[-117.0397913,46.4312588]},"context":[{"id":"neighborhood.278688","text":"Transfer"},{"id":"postcode.11154056676467040","text":"83501"},{"id":"place.9371029782746440","wikidata":"Q505539","text":"Lewiston"},{"id":"region.9713796497246050","wikidata":"Q1223","short_code":"US-WA","text":"Washington"},{"id":"country.19678805456372290","wikidata":"Q30","short_code":"us","text":"United States"}]},{"id":"address.8337368900196888","type":"Feature","place_type":["address"],"relevance":0.95,"properties":{"accuracy":"street"},"text":"South Washington Street","place_name":"South Washington Street, Moscow, Idaho 83843, United States","matching_text":"Washington","matching_place_name":"Washington, Moscow, Idaho 83843, United States","center":[-116.9997431,46.7299485],"geometry":{"type":"Point","coordinates":[-116.9997431,46.7299485]},"context":[{"id":"postcode.10023543810932580","text":"83843"},{"id":"place.19031168784618380","wikidata":"Q499927","text":"Moscow"},{"id":"region.9862000087605220","wikidata":"Q1221","short_code":"US-ID","text":"Idaho"},{"id":"country.19678805456372290","wikidata":"Q30","short_code":"us","text":"United States"}]}],"attribution":"NOTICE: © 2020 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained. POI(s) provided by Foursquare."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_5e53474a50aa75267953e6d90b2e3472021efbf2 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_5e53474a50aa75267953e6d90b2e3472021efbf2
new file mode 100644
index 000000000..3de4f2cbd
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_5e53474a50aa75267953e6d90b2e3472021efbf2
@@ -0,0 +1 @@
+s:11512:"{"type":"FeatureCollection","features":[{"type":"Feature","id":"dXJuOm1ieGFkcjo3Njg3YjZmNy01YmZkLTQzMjItOGMzOS02OGE1NDhmZWYwM2U","geometry":{"type":"Point","coordinates":[-122.413709,37.7757]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjo3Njg3YjZmNy01YmZkLTQzMjItOGMzOS02OGE1NDhmZWYwM2U","feature_type":"address","full_address":"149 9th Street, San Francisco, California 94103, United States","name":"149 9th Street","name_preferred":"149 9th Street","coordinates":{"longitude":-122.413709,"latitude":37.7757,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":37.775569,"longitude":-122.413874}]},"place_formatted":"San Francisco, California 94103, United States","match_code":{"address_number":"matched","street":"matched","postcode":"matched","place":"matched","region":"matched","locality":"not_applicable","country":"inferred","confidence":"exact"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjo3Njg3YjZmNy01YmZkLTQzMjItOGMzOS02OGE1NDhmZWYwM2U","address_number":"149","street_name":"9th Street","name":"149 9th Street"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6NzY4N2I2ZjctNWJmZC00MzIyLThjMzktNjhhNTQ4ZmVmMDNl","name":"9th Street"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpJOWtzN0E","name":"South of Market","alternate":{"mapbox_id":"dXJuOm1ieHBsYzpCdi9zN0E","name":"Civic Center","wikidata_id":"Q267874"}},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpFcDlPN0E","name":"94103"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpFVzBJN0E","name":"San Francisco","wikidata_id":"Q62"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpBVG1HN0E","name":"San Francisco County","wikidata_id":"Q62"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpCbVRz","name":"California","wikidata_id":"Q99","region_code":"CA","region_code_full":"US-CA"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}},{"type":"Feature","id":"dXJuOm1ieGFkci1pdHA6ZXlKeElqb2lNVFE1SURsMGFDQlRkQ3dnVTJGdUlFWnlZVzVqYVhOamJ5d2dRMEVnT1RReE1ETWlMQ0owZVhCbGN5STZJbUZrWkhKbGMzTWlMQ0pzYVcxcGRDSTZJalVpTENKcGJtTnNkV1JsWDIxaGRHTm9YMlJsZEdGcGJDSTZJblJ5ZFdVaUxDSnRiMlJsSWpvaVptOXlkMkZ5WkNJc0luWTJkSGx3WlhNaU9pSmhaR1J5WlhOekluMDox","geometry":{"type":"Point","coordinates":[-122.360907,37.932889]},"properties":{"mapbox_id":"dXJuOm1ieGFkci1pdHA6ZXlKeElqb2lNVFE1SURsMGFDQlRkQ3dnVTJGdUlFWnlZVzVqYVhOamJ5d2dRMEVnT1RReE1ETWlMQ0owZVhCbGN5STZJbUZrWkhKbGMzTWlMQ0pzYVcxcGRDSTZJalVpTENKcGJtTnNkV1JsWDIxaGRHTm9YMlJsZEdGcGJDSTZJblJ5ZFdVaUxDSnRiMlJsSWpvaVptOXlkMkZ5WkNJc0luWTJkSGx3WlhNaU9pSmhaR1J5WlhOekluMDox","feature_type":"address","full_address":"149 9th Street, Richmond, California 94804, United States","name":"149 9th Street","name_preferred":"149 9th Street","coordinates":{"longitude":-122.360907,"latitude":37.932889,"accuracy":"interpolated","routable_points":[{"name":"default","latitude":37.932889,"longitude":-122.360907}]},"place_formatted":"Richmond, California 94804, United States","match_code":{"address_number":"plausible","street":"matched","postcode":"unmatched","place":"unmatched","region":"matched","locality":"not_applicable","country":"inferred","confidence":"low"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkci1pdHA6ZXlKeElqb2lNVFE1SURsMGFDQlRkQ3dnVTJGdUlFWnlZVzVqYVhOamJ5d2dRMEVnT1RReE1ETWlMQ0owZVhCbGN5STZJbUZrWkhKbGMzTWlMQ0pzYVcxcGRDSTZJalVpTENKcGJtTnNkV1JsWDIxaGRHTm9YMlJsZEdGcGJDSTZJblJ5ZFdVaUxDSnRiMlJsSWpvaVptOXlkMkZ5WkNJc0luWTJkSGx3WlhNaU9pSmhaR1J5WlhOekluMDox","address_number":"149","street_name":"9th Street","name":"149 9th Street"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHItaXRwOmV5SnhJam9pTVRRNUlEbDBhQ0JUZEN3Z1UyRnVJRVp5WVc1amFYTmpieXdnUTBFZ09UUXhNRE1pTENKMGVYQmxjeUk2SW1Ga1pISmxjM01pTENKc2FXMXBkQ0k2SWpVaUxDSnBibU5zZFdSbFgyMWhkR05vWDJSbGRHRnBiQ0k2SW5SeWRXVWlMQ0p0YjJSbElqb2labTl5ZDJGeVpDSXNJblkyZEhsd1pYTWlPaUpoWkdSeVpYTnpJbjA6MQ","name":"9th Street"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpFdGNNN0E","name":"Iron Triangle","wikidata_id":"Q6072867"},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpFcjl1N0E","name":"94804"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpGZnhvN0E","name":"Richmond","wikidata_id":"Q495377"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpUa2Jz","name":"Contra Costa County","wikidata_id":"Q108058"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpCbVRz","name":"California","wikidata_id":"Q99","region_code":"CA","region_code_full":"US-CA"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}},{"type":"Feature","id":"dXJuOm1ieGFkci1pdHA6ZXlKeElqb2lNVFE1SURsMGFDQlRkQ3dnVTJGdUlFWnlZVzVqYVhOamJ5d2dRMEVnT1RReE1ETWlMQ0owZVhCbGN5STZJbUZrWkhKbGMzTWlMQ0pzYVcxcGRDSTZJalVpTENKcGJtTnNkV1JsWDIxaGRHTm9YMlJsZEdGcGJDSTZJblJ5ZFdVaUxDSnRiMlJsSWpvaVptOXlkMkZ5WkNJc0luWTJkSGx3WlhNaU9pSmhaR1J5WlhOekluMDoy","geometry":{"type":"Point","coordinates":[-122.239871,38.097967]},"properties":{"mapbox_id":"dXJuOm1ieGFkci1pdHA6ZXlKeElqb2lNVFE1SURsMGFDQlRkQ3dnVTJGdUlFWnlZVzVqYVhOamJ5d2dRMEVnT1RReE1ETWlMQ0owZVhCbGN5STZJbUZrWkhKbGMzTWlMQ0pzYVcxcGRDSTZJalVpTENKcGJtTnNkV1JsWDIxaGRHTm9YMlJsZEdGcGJDSTZJblJ5ZFdVaUxDSnRiMlJsSWpvaVptOXlkMkZ5WkNJc0luWTJkSGx3WlhNaU9pSmhaR1J5WlhOekluMDoy","feature_type":"address","full_address":"149 9th Street, Vallejo, California 94590, United States","name":"149 9th Street","name_preferred":"149 9th Street","coordinates":{"longitude":-122.239871,"latitude":38.097967,"accuracy":"interpolated","routable_points":[{"name":"default","latitude":38.097967,"longitude":-122.239871}]},"place_formatted":"Vallejo, California 94590, United States","match_code":{"address_number":"plausible","street":"matched","postcode":"unmatched","place":"unmatched","region":"matched","locality":"not_applicable","country":"inferred","confidence":"low"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkci1pdHA6ZXlKeElqb2lNVFE1SURsMGFDQlRkQ3dnVTJGdUlFWnlZVzVqYVhOamJ5d2dRMEVnT1RReE1ETWlMQ0owZVhCbGN5STZJbUZrWkhKbGMzTWlMQ0pzYVcxcGRDSTZJalVpTENKcGJtTnNkV1JsWDIxaGRHTm9YMlJsZEdGcGJDSTZJblJ5ZFdVaUxDSnRiMlJsSWpvaVptOXlkMkZ5WkNJc0luWTJkSGx3WlhNaU9pSmhaR1J5WlhOekluMDoy","address_number":"149","street_name":"9th Street","name":"149 9th Street"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHItaXRwOmV5SnhJam9pTVRRNUlEbDBhQ0JUZEN3Z1UyRnVJRVp5WVc1amFYTmpieXdnUTBFZ09UUXhNRE1pTENKMGVYQmxjeUk2SW1Ga1pISmxjM01pTENKc2FXMXBkQ0k2SWpVaUxDSnBibU5zZFdSbFgyMWhkR05vWDJSbGRHRnBiQ0k2SW5SeWRXVWlMQ0p0YjJSbElqb2labTl5ZDJGeVpDSXNJblkyZEhsd1pYTWlPaUpoWkdSeVpYTnpJbjA6Mg","name":"9th Street"},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpFcmp1N0E","name":"94590"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpGQTFJN0E","name":"Vallejo","wikidata_id":"Q208445"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpBVWtHN0E","name":"Solano County","wikidata_id":"Q108083"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpCbVRz","name":"California","wikidata_id":"Q99","region_code":"CA","region_code_full":"US-CA"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}},{"type":"Feature","id":"dXJuOm1ieGFkci1pdHA6ZXlKeElqb2lNVFE1SURsMGFDQlRkQ3dnVTJGdUlFWnlZVzVqYVhOamJ5d2dRMEVnT1RReE1ETWlMQ0owZVhCbGN5STZJbUZrWkhKbGMzTWlMQ0pzYVcxcGRDSTZJalVpTENKcGJtTnNkV1JsWDIxaGRHTm9YMlJsZEdGcGJDSTZJblJ5ZFdVaUxDSnRiMlJsSWpvaVptOXlkMkZ5WkNJc0luWTJkSGx3WlhNaU9pSmhaR1J5WlhOekluMDoz","geometry":{"type":"Point","coordinates":[-122.266226,37.798081]},"properties":{"mapbox_id":"dXJuOm1ieGFkci1pdHA6ZXlKeElqb2lNVFE1SURsMGFDQlRkQ3dnVTJGdUlFWnlZVzVqYVhOamJ5d2dRMEVnT1RReE1ETWlMQ0owZVhCbGN5STZJbUZrWkhKbGMzTWlMQ0pzYVcxcGRDSTZJalVpTENKcGJtTnNkV1JsWDIxaGRHTm9YMlJsZEdGcGJDSTZJblJ5ZFdVaUxDSnRiMlJsSWpvaVptOXlkMkZ5WkNJc0luWTJkSGx3WlhNaU9pSmhaR1J5WlhOekluMDoz","feature_type":"address","full_address":"149 9th Street, Oakland, California 94607, United States","name":"149 9th Street","name_preferred":"149 9th Street","coordinates":{"longitude":-122.266226,"latitude":37.798081,"accuracy":"interpolated","routable_points":[{"name":"default","latitude":37.798081,"longitude":-122.266226}]},"place_formatted":"Oakland, California 94607, United States","match_code":{"address_number":"plausible","street":"matched","postcode":"unmatched","place":"unmatched","region":"matched","locality":"not_applicable","country":"inferred","confidence":"low"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkci1pdHA6ZXlKeElqb2lNVFE1SURsMGFDQlRkQ3dnVTJGdUlFWnlZVzVqYVhOamJ5d2dRMEVnT1RReE1ETWlMQ0owZVhCbGN5STZJbUZrWkhKbGMzTWlMQ0pzYVcxcGRDSTZJalVpTENKcGJtTnNkV1JsWDIxaGRHTm9YMlJsZEdGcGJDSTZJblJ5ZFdVaUxDSnRiMlJsSWpvaVptOXlkMkZ5WkNJc0luWTJkSGx3WlhNaU9pSmhaR1J5WlhOekluMDoz","address_number":"149","street_name":"9th Street","name":"149 9th Street"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHItaXRwOmV5SnhJam9pTVRRNUlEbDBhQ0JUZEN3Z1UyRnVJRVp5WVc1amFYTmpieXdnUTBFZ09UUXhNRE1pTENKMGVYQmxjeUk2SW1Ga1pISmxjM01pTENKc2FXMXBkQ0k2SWpVaUxDSnBibU5zZFdSbFgyMWhkR05vWDJSbGRHRnBiQ0k2SW5SeWRXVWlMQ0p0YjJSbElqb2labTl5ZDJGeVpDSXNJblkyZEhsd1pYTWlPaUpoWkdSeVpYTnpJbjA6Mw","name":"9th Street"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpCc3RzN0E","name":"Chinatown","wikidata_id":"Q5100154"},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpFcnF1N0E","name":"94607"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpGZnZvN0E","name":"Oakland","wikidata_id":"Q17042"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpBMGJz","name":"Alameda County","wikidata_id":"Q107146"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpCbVRz","name":"California","wikidata_id":"Q99","region_code":"CA","region_code_full":"US-CA"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}},{"type":"Feature","id":"dXJuOm1ieGFkcjpjNmYwM2Y1MC0zM2MwLTQ1ZmItODYxNi1lOTAwYWM3YjBhM2E","geometry":{"type":"Point","coordinates":[-122.361262,37.929928]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjpjNmYwM2Y1MC0zM2MwLTQ1ZmItODYxNi1lOTAwYWM3YjBhM2E","feature_type":"address","full_address":"149 South 9th Street, Richmond, California 94804, United States","name":"149 South 9th Street","name_preferred":"149 South 9th Street","coordinates":{"longitude":-122.361262,"latitude":37.929928,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":37.929927,"longitude":-122.361032}]},"place_formatted":"Richmond, California 94804, United States","match_code":{"address_number":"matched","street":"unmatched","postcode":"unmatched","place":"unmatched","region":"matched","locality":"not_applicable","country":"inferred","confidence":"low"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjpjNmYwM2Y1MC0zM2MwLTQ1ZmItODYxNi1lOTAwYWM3YjBhM2E","address_number":"149","street_name":"South 9th Street","name":"149 South 9th Street"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6YzZmMDNmNTAtMzNjMC00NWZiLTg2MTYtZTkwMGFjN2IwYTNh","name":"South 9th Street"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpJYndNN0E","name":"Santa Fe"},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpFcjl1N0E","name":"94804"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpGZnhvN0E","name":"Richmond","wikidata_id":"Q495377"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpUa2Jz","name":"Contra Costa County","wikidata_id":"Q108058"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpCbVRz","name":"California","wikidata_id":"Q99","region_code":"CA","region_code_full":"US-CA"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}}],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_61c96e256b090c50a0a9a247c3808fb84b79f55c b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_61c96e256b090c50a0a9a247c3808fb84b79f55c
deleted file mode 100644
index 3be44cc8c..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_61c96e256b090c50a0a9a247c3808fb84b79f55c
+++ /dev/null
@@ -1 +0,0 @@
-s:5251:"{"type":"FeatureCollection","query":["10","downing","st","london","uk"],"features":[{"id":"address.2844060426507784","type":"Feature","place_type":["address"],"relevance":0.9666666666666666,"properties":{},"text_en":"Downing Street","place_name_en":"10 Downing Street, St. James's, London, SW1A 2AT, United Kingdom","text":"Downing Street","place_name":"10 Downing Street, St. James's, London, SW1A 2AT, United Kingdom","center":[-0.126126,51.503157],"geometry":{"type":"Point","coordinates":[-0.126126,51.503157]},"address":"10","context":[{"id":"postcode.11318645777473120","text_en":"SW1A 2AT","text":"SW1A 2AT"},{"id":"locality.15910638793964150","wikidata":null,"text_en":"St. James's","text":"St. James's"},{"id":"place.8500383470913397","wikidata":"Q84","text_en":"London","language_en":"en","text":"London","language":"en"},{"id":"district.9655698107976620","wikidata":"Q23306","text_en":"Greater London","language_en":"en","text":"Greater London","language":"en"},{"id":"region.3185","short_code":"GB-ENG","wikidata":"Q21","text_en":"England","language_en":"en","text":"England","language":"en"},{"id":"country.358","short_code":"gb","wikidata":"Q145","text_en":"United Kingdom","language_en":"en","text":"United Kingdom","language":"en"}]},{"id":"address.4852922328439012","type":"Feature","place_type":["address"],"relevance":0.3,"properties":{},"text_en":"Rue Downing","place_name_en":"10 Rue Downing, Grande-Digue, New Brunswick, Canada","text":"Rue Downing","place_name":"10 Rue Downing, Grande-Digue, New Brunswick, Canada","matching_text":"Downing Street","matching_place_name":"10 Downing Street, Grande-Digue, New Brunswick, Canada","center":[-64.529877,46.335725],"geometry":{"type":"Point","coordinates":[-64.529877,46.335725]},"address":"10","context":[{"id":"place.18359628229490950","wikidata":"Q2406204","text_en":"Grande-Digue","language_en":"en","text":"Grande-Digue","language":"en"},{"id":"region.218977","short_code":"CA-NB","wikidata":"Q1965","text_en":"New Brunswick","language_en":"en","text":"New Brunswick","language":"en"},{"id":"country.3179","short_code":"ca","wikidata":"Q16","text_en":"Canada","language_en":"en","text":"Canada","language":"en"}]},{"id":"address.2778496043278880","type":"Feature","place_type":["address"],"relevance":0.3,"properties":{},"text_en":"Downing Street","place_name_en":"10 Downing Street, Fredericton, New Brunswick E3A 4M6, Canada","text":"Downing Street","place_name":"10 Downing Street, Fredericton, New Brunswick E3A 4M6, Canada","center":[-66.585237,45.979891],"geometry":{"type":"Point","coordinates":[-66.585237,45.979891]},"address":"10","context":[{"id":"postcode.13170795948825840","text_en":"E3A 4M6","text":"E3A 4M6"},{"id":"place.12649774315478860","wikidata":"Q2138","text_en":"Fredericton","language_en":"en","text":"Fredericton","language":"en"},{"id":"region.218977","short_code":"CA-NB","wikidata":"Q1965","text_en":"New Brunswick","language_en":"en","text":"New Brunswick","language":"en"},{"id":"country.3179","short_code":"ca","wikidata":"Q16","text_en":"Canada","language_en":"en","text":"Canada","language":"en"}]},{"id":"address.2232726546138078","type":"Feature","place_type":["address"],"relevance":0.3,"properties":{},"text_en":"Downing Street","place_name_en":"10 Downing Street, Hamilton, Ontario L0R 0A0, Canada","text":"Downing Street","place_name":"10 Downing Street, Hamilton, Ontario L0R 0A0, Canada","center":[-79.810597,43.116989],"geometry":{"type":"Point","coordinates":[-79.810597,43.116989],"interpolated":true},"address":"10","context":[{"id":"postcode.19608462426414870","text_en":"L0R 0A0","text":"L0R 0A0"},{"id":"place.14129018412865700","wikidata":"Q133116","text_en":"Hamilton","language_en":"en","text":"Hamilton","language":"en"},{"id":"region.3721","short_code":"CA-ON","wikidata":"Q1904","text_en":"Ontario","language_en":"en","text":"Ontario","language":"en"},{"id":"country.3179","short_code":"ca","wikidata":"Q16","text_en":"Canada","language_en":"en","text":"Canada","language":"en"}]},{"id":"address.4850220298517218","type":"Feature","place_type":["address"],"relevance":0.3,"properties":{},"text_en":"Downing Street","place_name_en":"10 Downing Street, Halifax, Nova Scotia B3M 2G3, Canada","text":"Downing Street","place_name":"10 Downing Street, Halifax, Nova Scotia B3M 2G3, Canada","center":[-63.669482,44.685001],"geometry":{"type":"Point","coordinates":[-63.669482,44.685001],"interpolated":true},"address":"10","context":[{"id":"neighborhood.11574814505474780","text_en":"Rockingham","text":"Rockingham"},{"id":"postcode.13399737010185770","text_en":"B3M 2G3","text":"B3M 2G3"},{"id":"place.17790518833069190","wikidata":"Q2141","text_en":"Halifax","language_en":"en","text":"Halifax","language":"en"},{"id":"region.218945","short_code":"CA-NS","wikidata":"Q1952","text_en":"Nova Scotia","language_en":"en","text":"Nova Scotia","language":"en"},{"id":"country.3179","short_code":"ca","wikidata":"Q16","text_en":"Canada","language_en":"en","text":"Canada","language":"en"}]}],"attribution":"NOTICE: © 2017 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_798771ac350f81cfa1f5e499d3a6d7a4df71525f b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_798771ac350f81cfa1f5e499d3a6d7a4df71525f
new file mode 100644
index 000000000..b2896a0d6
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_798771ac350f81cfa1f5e499d3a6d7a4df71525f
@@ -0,0 +1 @@
+s:2219:"{"type":"FeatureCollection","features":[{"type":"Feature","id":"dXJuOm1ieGFkcjo1N2NhYTE3Mi03MGEyLTQ5YzYtYjk2ZS1jMDhkYjRkNGE5Zjc","geometry":{"type":"Point","coordinates":[-77.036948,38.900488]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjo1N2NhYTE3Mi03MGEyLTQ5YzYtYjk2ZS1jMDhkYjRkNGE5Zjc","feature_type":"address","full_address":"800 16th Street Northwest, Washington, District of Columbia 20006, United States","name":"800 16th Street Northwest","name_preferred":"800 16th Street Northwest","coordinates":{"longitude":-77.036948,"latitude":38.900488,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":38.900488,"longitude":-77.036768}]},"place_formatted":"Washington, District of Columbia 20006, United States","context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjo1N2NhYTE3Mi03MGEyLTQ5YzYtYjk2ZS1jMDhkYjRkNGE5Zjc","address_number":"800","street_name":"16th Street Northwest","name":"800 16th Street Northwest"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6NTdjYWExNzItNzBhMi00OWM2LWI5NmUtYzA4ZGI0ZDRhOWY3","name":"16th Street Northwest"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpDb1BzN0E","name":"Dupont Circle","alternate":{"mapbox_id":"dXJuOm1ieHBsYzpCK0xzN0E","name":"Connecticut Avenue","translations":{"en":{"language":"en","name":"Connecticut Avenue"}}},"translations":{"en":{"language":"en","name":"Dupont Circle"}}},"postcode":{"mapbox_id":"postcode.6693630501763360","name":"20006"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpGSmlvN0E","name":"Washington","wikidata_id":"Q61","translations":{"en":{"language":"en","name":"Washington"}}},"region":{"mapbox_id":"dXJuOm1ieHBsYzpCUVRz","name":"District of Columbia","wikidata_id":"Q3551781","region_code":"DC","region_code_full":"US-DC","translations":{"en":{"language":"en","name":"District of Columbia"}}},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA","translations":{"en":{"language":"en","name":"United States"}}}}}}],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_7b8b47952b295102acd4cb1bc0a9536c26c7bc9b b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_7b8b47952b295102acd4cb1bc0a9536c26c7bc9b
new file mode 100644
index 000000000..5fac39b0b
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_7b8b47952b295102acd4cb1bc0a9536c26c7bc9b
@@ -0,0 +1 @@
+s:6626:"{"type":"FeatureCollection","features":[{"type":"Feature","id":"dXJuOm1ieGFkcjpjM2M2NWFjNC1hMTExLTQ1NDItOGIxNi05ZDg1Y2I4YjYzMDc","geometry":{"type":"Point","coordinates":[2.388886,48.863134]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjpjM2M2NWFjNC1hMTExLTQ1NDItOGIxNi05ZDg1Y2I4YjYzMDc","feature_type":"address","full_address":"12 Avenue Gambetta, 75020 Paris, France","name":"12 Avenue Gambetta","name_preferred":"12 Avenue Gambetta","coordinates":{"longitude":2.388886,"latitude":48.863134,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":48.863236,"longitude":2.388829}]},"place_formatted":"75020 Paris, France","context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjpjM2M2NWFjNC1hMTExLTQ1NDItOGIxNi05ZDg1Y2I4YjYzMDc","address_number":"12","street_name":"Avenue Gambetta","name":"12 Avenue Gambetta"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6YzNjNjVhYzQtYTExMS00NTQyLThiMTYtOWQ4NWNiOGI2MzA3","name":"Avenue Gambetta"},"postcode":{"mapbox_id":"postcode.3248070486542928","name":"75020"},"locality":{"mapbox_id":"dXJuOm1ieHBsYzpDRXBO","name":"20th arrondissement","wikidata_id":"Q210720"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpEYVJO","name":"Paris","wikidata_id":"Q90","short_code":"FR-75"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpFc1JO","name":"Île-de-France","wikidata_id":"Q13917","region_code":"IDF","region_code_full":"FR-IDF"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazA","name":"France","wikidata_id":"Q142","country_code":"FR","country_code_alpha_3":"FRA"}}}},{"type":"Feature","id":"dXJuOm1ieGFkcjozZjgzZmVjMi02N2UzLTRiMzctOGY4Yi01ZDVlNTNkMDgzODQ","geometry":{"type":"Point","coordinates":[2.388742,48.863306]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjozZjgzZmVjMi02N2UzLTRiMzctOGY4Yi01ZDVlNTNkMDgzODQ","feature_type":"address","full_address":"1 Avenue Gambetta, 75020 Paris, France","name":"1 Avenue Gambetta","name_preferred":"1 Avenue Gambetta","coordinates":{"longitude":2.388742,"latitude":48.863306,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":48.863226,"longitude":2.388787}]},"place_formatted":"75020 Paris, France","context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjozZjgzZmVjMi02N2UzLTRiMzctOGY4Yi01ZDVlNTNkMDgzODQ","address_number":"1","street_name":"Avenue Gambetta","name":"1 Avenue Gambetta"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6M2Y4M2ZlYzItNjdlMy00YjM3LThmOGItNWQ1ZTUzZDA4Mzg0","name":"Avenue Gambetta"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpCU25NVFE","name":"Menilmontant","alternate":{"mapbox_id":"dXJuOm1ieHBsYzpCU3NNVFE","name":"Père-Lachaise","wikidata_id":"Q3413289"}},"postcode":{"mapbox_id":"postcode.3248070486542928","name":"75020"},"locality":{"mapbox_id":"dXJuOm1ieHBsYzpDRXBO","name":"20th arrondissement","wikidata_id":"Q210720"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpEYVJO","name":"Paris","wikidata_id":"Q90","short_code":"FR-75"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpFc1JO","name":"Île-de-France","wikidata_id":"Q13917","region_code":"IDF","region_code_full":"FR-IDF"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazA","name":"France","wikidata_id":"Q142","country_code":"FR","country_code_alpha_3":"FRA"}}}},{"type":"Feature","id":"dXJuOm1ieGFkcjphNmFhMTZmOS1jZWE3LTQ0ZTAtOGYwYS1lMjc1NDVhMTdiN2I","geometry":{"type":"Point","coordinates":[2.38854,48.863297]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjphNmFhMTZmOS1jZWE3LTQ0ZTAtOGYwYS1lMjc1NDVhMTdiN2I","feature_type":"address","full_address":"13 Place Auguste Métivier, 75020 Paris, France","name":"13 Place Auguste Métivier","name_preferred":"13 Place Auguste Métivier","coordinates":{"longitude":2.38854,"latitude":48.863297,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":48.863242,"longitude":2.388245}]},"place_formatted":"75020 Paris, France","context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjphNmFhMTZmOS1jZWE3LTQ0ZTAtOGYwYS1lMjc1NDVhMTdiN2I","address_number":"13","street_name":"Place Auguste Métivier","name":"13 Place Auguste Métivier"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6YTZhYTE2ZjktY2VhNy00NGUwLThmMGEtZTI3NTQ1YTE3Yjdi","name":"Place Auguste Métivier"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpCU25NVFE","name":"Menilmontant","alternate":{"mapbox_id":"dXJuOm1ieHBsYzpCU3NNVFE","name":"Père-Lachaise","wikidata_id":"Q3413289"}},"postcode":{"mapbox_id":"postcode.8604773807997748","name":"75020"},"locality":{"mapbox_id":"dXJuOm1ieHBsYzpDRXBO","name":"20th arrondissement","wikidata_id":"Q210720"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpEYVJO","name":"Paris","wikidata_id":"Q90","short_code":"FR-75"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpFc1JO","name":"Île-de-France","wikidata_id":"Q13917","region_code":"IDF","region_code_full":"FR-IDF"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazA","name":"France","wikidata_id":"Q142","country_code":"FR","country_code_alpha_3":"FRA"}}}},{"type":"Feature","id":"dXJuOm1ieGFkcjpjYzVkYzQ3MS1lNGJiLTRiOTItYTU1MC1lZTg4ZTFjOWUzMmE","geometry":{"type":"Point","coordinates":[2.388546,48.863359]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjpjYzVkYzQ3MS1lNGJiLTRiOTItYTU1MC1lZTg4ZTFjOWUzMmE","feature_type":"address","full_address":"2 Rue Des Amandiers, 75020 Paris, France","name":"2 Rue Des Amandiers","name_preferred":"2 Rue Des Amandiers","coordinates":{"longitude":2.388546,"latitude":48.863359,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":48.863389,"longitude":2.388492}]},"place_formatted":"75020 Paris, France","context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjpjYzVkYzQ3MS1lNGJiLTRiOTItYTU1MC1lZTg4ZTFjOWUzMmE","address_number":"2","street_name":"Rue Des Amandiers","name":"2 Rue Des Amandiers"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6Y2M1ZGM0NzEtZTRiYi00YjkyLWE1NTAtZWU4OGUxYzllMzJh","name":"Rue Des Amandiers"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpCU25NVFE","name":"Menilmontant","alternate":{"mapbox_id":"dXJuOm1ieHBsYzpCU3NNVFE","name":"Père-Lachaise","wikidata_id":"Q3413289"}},"postcode":{"mapbox_id":"postcode.5685635047513804","name":"75020"},"locality":{"mapbox_id":"dXJuOm1ieHBsYzpDRXBO","name":"20th arrondissement","wikidata_id":"Q210720"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpEYVJO","name":"Paris","wikidata_id":"Q90","short_code":"FR-75"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpFc1JO","name":"Île-de-France","wikidata_id":"Q13917","region_code":"IDF","region_code_full":"FR-IDF"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazA","name":"France","wikidata_id":"Q142","country_code":"FR","country_code_alpha_3":"FRA"}}}}],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_9e3e5555aca6117bf14dbd86c37ed23cbece335d b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_9e3e5555aca6117bf14dbd86c37ed23cbece335d
new file mode 100644
index 000000000..3c6af5dd0
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_9e3e5555aca6117bf14dbd86c37ed23cbece335d
@@ -0,0 +1 @@
+s:1325:"{"type":"FeatureCollection","features":[{"type":"Feature","id":"address.858151070459380","geometry":{"type":"Point","coordinates":[-122.949686,47.629081]},"properties":{"mapbox_id":"address.858151070459380","feature_type":"street","full_address":"Yashi Road, Brinnon, Washington 98320, United States","name":"Yashi Road","name_preferred":"Yashi Road","coordinates":{"longitude":-122.949686,"latitude":47.629081},"place_formatted":"Brinnon, Washington 98320, United States","context":{"street":{"mapbox_id":"address.858151070459380","name":"Yashi Road"},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpFNDVPN0E","name":"98320"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpBa2JJN0E","name":"Brinnon","wikidata_id":"Q612805"},"district":{"mapbox_id":"dXJuOm1ieHBsYzpyMGJz","name":"Jefferson County","wikidata_id":"Q384737"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpBUVRz","name":"Washington","wikidata_id":"Q1223","region_code":"WA","region_code_full":"US-WA"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}}],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_bbef3c8c03f148c252a89f0a36e1872e67c9a761 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_bbef3c8c03f148c252a89f0a36e1872e67c9a761
deleted file mode 100644
index 816e76110..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_bbef3c8c03f148c252a89f0a36e1872e67c9a761
+++ /dev/null
@@ -1 +0,0 @@
-s:2309:"{"type":"FeatureCollection","query":[2.388911,48.863151],"features":[{"id":"address.1085979616","type":"Feature","place_type":["address"],"relevance":1,"properties":{},"text":"Avenue Gambetta","place_name":"8 Avenue Gambetta, 75020 Paris, France","center":[2.388781,48.863109],"geometry":{"type":"Point","coordinates":[2.388781,48.863109]},"address":"8","context":[{"id":"postcode.11865225190696130","text":"75020"},{"id":"place.228080","short_code":"FR-75","wikidata":"Q90","text":"Paris"},{"id":"country.3148","wikidata":"Q142","short_code":"fr","text":"France"}]},{"id":"address.1085979616","type":"Feature","place_type":["address"],"relevance":1,"properties":{},"text":"Avenue Gambetta","place_name":"10 Avenue Gambetta, 75020 Paris, France","center":[2.389073,48.863181],"geometry":{"type":"Point","coordinates":[2.389073,48.863181]},"address":"10","context":[{"id":"postcode.11865225190696130","text":"75020"},{"id":"place.228080","short_code":"FR-75","wikidata":"Q90","text":"Paris"},{"id":"country.3148","wikidata":"Q142","short_code":"fr","text":"France"}]},{"id":"address.1085979616","type":"Feature","place_type":["address"],"relevance":1,"properties":{},"text":"Avenue Gambetta","place_name":"1 Avenue Gambetta, 75020 Paris, France","center":[2.388736,48.863305],"geometry":{"type":"Point","coordinates":[2.388736,48.863305]},"address":"1","context":[{"id":"postcode.11865225190696130","text":"75020"},{"id":"place.228080","short_code":"FR-75","wikidata":"Q90","text":"Paris"},{"id":"country.3148","wikidata":"Q142","short_code":"fr","text":"France"}]},{"id":"address.1085979616","type":"Feature","place_type":["address"],"relevance":1,"properties":{},"text":"Avenue Gambetta","place_name":"12 Avenue Gambetta, 75020 Paris, France","center":[2.389194,48.863214],"geometry":{"type":"Point","coordinates":[2.389194,48.863214]},"address":"12","context":[{"id":"postcode.11865225190696130","text":"75020"},{"id":"place.228080","short_code":"FR-75","wikidata":"Q90","text":"Paris"},{"id":"country.3148","wikidata":"Q142","short_code":"fr","text":"France"}]}],"attribution":"NOTICE: © 2017 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_c2fd1618bb5f29f1998244add688fc1668ec2416 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_c2fd1618bb5f29f1998244add688fc1668ec2416
deleted file mode 100644
index dfa6e6864..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_c2fd1618bb5f29f1998244add688fc1668ec2416
+++ /dev/null
@@ -1 +0,0 @@
-s:530:"{"type":"FeatureCollection","query":[0,0],"features":[{"id":"address.262530399228832","type":"Feature","place_type":["address"],"relevance":1,"properties":{},"text":"South St","place_name":"206 South St, ","center":[0,0],"geometry":{"type":"Point","coordinates":[0,0]},"address":"206"}],"attribution":"NOTICE: © 2017 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_c97f9d6eb99ff44a7e13d0dc8eb54c3fd8f6971b b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_c97f9d6eb99ff44a7e13d0dc8eb54c3fd8f6971b
deleted file mode 100644
index fa170aee9..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_c97f9d6eb99ff44a7e13d0dc8eb54c3fd8f6971b
+++ /dev/null
@@ -1 +0,0 @@
-s:3470:"{"type":"FeatureCollection","query":[-77.036991,38.900206],"features":[{"id":"address.717957002859378","type":"Feature","place_type":["address"],"relevance":1,"properties":{},"text_en":"H Street Northwest","place_name_en":"1615 H Street Northwest, Washington, District of Columbia 20006, United States","text":"H Street Northwest","place_name":"1615 H Street Northwest, Washington, District of Columbia 20006, United States","center":[-77.037527,38.900583],"geometry":{"type":"Point","coordinates":[-77.037527,38.900583]},"address":"1615","context":[{"id":"neighborhood.291451","text_en":"Downtown","text":"Downtown"},{"id":"postcode.10186570277652840","text_en":"20006","text":"20006"},{"id":"place.7673410831246050","wikidata":"Q61","text_en":"Washington","language_en":"en","text":"Washington","language":"en"},{"id":"region.3403","short_code":"US-DC","wikidata":"Q61","text_en":"District of Columbia","language_en":"en","text":"District of Columbia","language":"en"},{"id":"country.3145","wikidata":"Q30","short_code":"us","text_en":"United States","language_en":"en","text":"United States","language":"en"}]},{"id":"address.8110859437053248","type":"Feature","place_type":["address"],"relevance":1,"properties":{},"text_en":"16th Street Northwest","place_name_en":"800 16th Street Northwest, Washington, District of Columbia 20006, United States","text":"16th Street Northwest","place_name":"800 16th Street Northwest, Washington, District of Columbia 20006, United States","center":[-77.036959,38.900533],"geometry":{"type":"Point","coordinates":[-77.036959,38.900533]},"address":"800","context":[{"id":"neighborhood.291451","text_en":"Downtown","text":"Downtown"},{"id":"postcode.10186570277652840","text_en":"20006","text":"20006"},{"id":"place.7673410831246050","wikidata":"Q61","text_en":"Washington","language_en":"en","text":"Washington","language":"en"},{"id":"region.3403","short_code":"US-DC","wikidata":"Q61","text_en":"District of Columbia","language_en":"en","text":"District of Columbia","language":"en"},{"id":"country.3145","wikidata":"Q30","short_code":"us","text_en":"United States","language_en":"en","text":"United States","language":"en"}]},{"id":"address.45528587931684","type":"Feature","place_type":["address"],"relevance":1,"properties":{},"text_en":"Jackson Place Northwest","place_name_en":"748 Jackson Place Northwest, Washington, District of Columbia 20062, United States","text":"Jackson Place Northwest","place_name":"748 Jackson Place Northwest, Washington, District of Columbia 20062, United States","center":[-77.038199,38.900034],"geometry":{"type":"Point","coordinates":[-77.038199,38.900034]},"address":"748","context":[{"id":"neighborhood.291451","text_en":"Downtown","text":"Downtown"},{"id":"postcode.10886799999681030","text_en":"20062","text":"20062"},{"id":"place.7673410831246050","wikidata":"Q61","text_en":"Washington","language_en":"en","text":"Washington","language":"en"},{"id":"region.3403","short_code":"US-DC","wikidata":"Q61","text_en":"District of Columbia","language_en":"en","text":"District of Columbia","language":"en"},{"id":"country.3145","wikidata":"Q30","short_code":"us","text_en":"United States","language_en":"en","text":"United States","language":"en"}]}],"attribution":"NOTICE: © 2017 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_d72ced1f54dff6389745d11b002a772451804618 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_d72ced1f54dff6389745d11b002a772451804618
deleted file mode 100644
index 6f03b3ead..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_d72ced1f54dff6389745d11b002a772451804618
+++ /dev/null
@@ -1 +0,0 @@
-s:339:"{"type":"FeatureCollection","query":["wahsington"],"features":[],"attribution":"NOTICE: © 2020 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained. POI(s) provided by Foursquare."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_d73a2016dbd70a152bffadf02f4f04d7e4d6a0b7 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_d73a2016dbd70a152bffadf02f4f04d7e4d6a0b7
new file mode 100644
index 000000000..1cbcbe74c
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_d73a2016dbd70a152bffadf02f4f04d7e4d6a0b7
@@ -0,0 +1 @@
+s:1516:"{"type":"FeatureCollection","features":[{"type":"Feature","id":"dXJuOm1ieHBsYzpoMmhQ","geometry":{"type":"Point","coordinates":[-0.83069,51.724422]},"properties":{"mapbox_id":"dXJuOm1ieHBsYzpoMmhQ","feature_type":"place","full_address":"Princes Risborough, Buckinghamshire, Inghilterra, Regno Unito","name":"Princes Risborough","name_preferred":"Princes Risborough","coordinates":{"longitude":-0.83069,"latitude":51.724422},"place_formatted":"Buckinghamshire, Inghilterra, Regno Unito","bbox":[-0.912802,51.680448,-0.770371,51.756426],"context":{"district":{"mapbox_id":"dXJuOm1ieHBsYzpBcVpQ","name":"Buckinghamshire","wikidata_id":"Q21694636","translations":{"it":{"language":"fr","name":"Buckinghamshire"}}},"region":{"mapbox_id":"dXJuOm1ieHBsYzpKRTg","name":"Inghilterra","wikidata_id":"Q21","region_code":"ENG","region_code_full":"GB-ENG","translations":{"it":{"language":"it","name":"Inghilterra"}}},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJazg","name":"Regno Unito","wikidata_id":"Q145","country_code":"GB","country_code_alpha_3":"GBR","translations":{"it":{"language":"it","name":"Regno Unito"}}},"place":{"mapbox_id":"dXJuOm1ieHBsYzpoMmhQ","name":"Princes Risborough","translations":{"it":{"language":"it","name":"Princes Risborough"}},"wikidata_id":"Q2568545"}}}}],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_eb032a2644f68b47a4076ea601305eaf8ba72a31 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_eb032a2644f68b47a4076ea601305eaf8ba72a31
deleted file mode 100644
index 6dde633d7..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_eb032a2644f68b47a4076ea601305eaf8ba72a31
+++ /dev/null
@@ -1 +0,0 @@
-s:3926:"{"type":"FeatureCollection","query":["149","9th","st","san","francisco","ca","94103"],"features":[{"id":"address.3071152063251042","type":"Feature","place_type":["address"],"relevance":0.875,"properties":{},"text":"9th Street","place_name":"149 9th Street, San Francisco, California 94103, United States","center":[-122.41362,37.77572],"geometry":{"type":"Point","coordinates":[-122.41362,37.77572]},"address":"149","context":[{"id":"neighborhood.293577","text":"South of Market"},{"id":"postcode.13892342768265050","text":"94103"},{"id":"place.15734669613361910","wikidata":"Q62","text":"San Francisco"},{"id":"region.3591","short_code":"US-CA","wikidata":"Q99","text":"California"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]},{"id":"address.2787669249858206","type":"Feature","place_type":["address"],"relevance":0.25,"properties":{},"text":"9th Street","place_name":"149 9th Street, Greenport, New York 11944, United States","center":[-72.370864,41.099811],"geometry":{"type":"Point","coordinates":[-72.370864,41.099811],"interpolated":true},"address":"149","context":[{"id":"neighborhood.278108","text":"Stirling"},{"id":"postcode.5709909550190040","text":"11944"},{"id":"place.10410131002602850","wikidata":"Q94652","text":"Greenport"},{"id":"region.3866","short_code":"US-NY","wikidata":"Q1384","text":"New York"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]},{"id":"address.1299539993830358","type":"Feature","place_type":["address"],"relevance":0.25,"properties":{},"text":"East 9th Street","place_name":"149 East 9th Street, Erie, Pennsylvania 16503, United States","matching_text":"9th Street","matching_place_name":"149 9th Street, Erie, Pennsylvania 16503, United States","center":[-80.069022,42.131794],"geometry":{"type":"Point","coordinates":[-80.069022,42.131794],"interpolated":true},"address":"149","context":[{"id":"postcode.11103612841590770","text":"16503"},{"id":"place.10004124579967850","wikidata":"Q234061","text":"Erie"},{"id":"region.3337","short_code":"US-PA","wikidata":"Q1400","text":"Pennsylvania"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]},{"id":"address.7335913800968804","type":"Feature","place_type":["address"],"relevance":0.25,"properties":{},"text":"West 9th Street","place_name":"149 West 9th Street, Erie, Pennsylvania 16501, United States","matching_text":"9th Street","matching_place_name":"149 9th Street, Erie, Pennsylvania 16501, United States","center":[-80.086582,42.125322],"geometry":{"type":"Point","coordinates":[-80.086582,42.125322],"interpolated":true},"address":"149","context":[{"id":"neighborhood.270119","text":"Dock Junction"},{"id":"postcode.11009455618026990","text":"16501"},{"id":"place.10004124579967850","wikidata":"Q234061","text":"Erie"},{"id":"region.3337","short_code":"US-PA","wikidata":"Q1400","text":"Pennsylvania"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]},{"id":"address.8377599986498868","type":"Feature","place_type":["address"],"relevance":0.25,"properties":{},"text":"Ninth Street","place_name":"149 Ninth Street, Nashua, New Hampshire 03060, United States","center":[-71.481591,42.753499],"geometry":{"type":"Point","coordinates":[-71.481591,42.753499],"interpolated":true,"omitted":true},"address":"149","context":[{"id":"neighborhood.287645","text":"Downtown"},{"id":"postcode.17825302421333580","text":"03060"},{"id":"place.6791107671778960","wikidata":"Q48274","text":"Nashua"},{"id":"region.211885","short_code":"US-NH","wikidata":"Q759","text":"New Hampshire"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]}],"attribution":"NOTICE: © 2017 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_f212d067263ee5a25cce4e79ee710e3a4af3fc5e b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_f212d067263ee5a25cce4e79ee710e3a4af3fc5e
deleted file mode 100644
index c88d497a0..000000000
--- a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_f212d067263ee5a25cce4e79ee710e3a4af3fc5e
+++ /dev/null
@@ -1 +0,0 @@
-s:4352:"{"type":"FeatureCollection","query":["116th","st","broadway","new","york","ny","10027","united","states"],"features":[{"id":"address.2431617896783536","type":"Feature","place_type":["address"],"relevance":0.6666666666666666,"properties":{},"text":"116th Street","place_name":"116th Street, Queens, New York, New York 11356, United States","center":[-73.851157,40.786596],"geometry":{"type":"Point","coordinates":[-73.851157,40.786596]},"context":[{"id":"neighborhood.2104291","text":"College Point"},{"id":"locality.6306827455006740","wikidata":"Q18424","text":"Queens"},{"id":"postcode.11196253910231740","text":"11356"},{"id":"place.15278078705964500","wikidata":"Q60","text":"New York"},{"id":"region.3866","short_code":"US-NY","wikidata":"Q1384","text":"New York"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]},{"id":"address.4427827672964774","type":"Feature","place_type":["address"],"relevance":0.5833333333333333,"properties":{},"text":"East 116th Street","place_name":"East 116th Street, Manhattan, New York, New York 10035, United States","matching_text":"116th Street","matching_place_name":"116th Street, Manhattan, New York, New York 10035, United States","center":[-73.938724,40.797571],"geometry":{"type":"Point","coordinates":[-73.938724,40.797571]},"context":[{"id":"neighborhood.2100588","text":"East Harlem"},{"id":"locality.12696928000137850","wikidata":"Q11299","text":"Manhattan"},{"id":"postcode.11255904502577980","text":"10035"},{"id":"place.15278078705964500","wikidata":"Q60","text":"New York"},{"id":"region.3866","short_code":"US-NY","wikidata":"Q1384","text":"New York"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]},{"id":"address.2741753979749180","type":"Feature","place_type":["address"],"relevance":0.5833333333333333,"properties":{},"text":"West 116th Street","place_name":"West 116th Street, Manhattan, New York, New York 10026, United States","matching_text":"116th Street","matching_place_name":"116th Street, Manhattan, New York, New York 10026, United States","center":[-73.956278,40.804979],"geometry":{"type":"Point","coordinates":[-73.956278,40.804979]},"context":[{"id":"neighborhood.2102863","text":"Harlem"},{"id":"locality.12696928000137850","wikidata":"Q11299","text":"Manhattan"},{"id":"postcode.13768369279157190","text":"10026"},{"id":"place.15278078705964500","wikidata":"Q60","text":"New York"},{"id":"region.3866","short_code":"US-NY","wikidata":"Q1384","text":"New York"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]},{"id":"address.2201553461107342","type":"Feature","place_type":["address"],"relevance":0.5833333333333333,"properties":{},"text":"116th Street","place_name":"116th Street, Queens, New York, New York 11419, United States","matching_place_name":"116th Street, Queens, NY, New York 11419, United States","center":[-73.827854,40.687554],"geometry":{"type":"Point","coordinates":[-73.827854,40.687554]},"context":[{"id":"neighborhood.297765","text":"Richmond Hill"},{"id":"locality.6306827455006740","wikidata":"Q18424","text":"Queens"},{"id":"postcode.453327290579340","text":"11419"},{"id":"place.15278078705964500","wikidata":"Q60","text":"New York"},{"id":"region.3866","short_code":"US-NY","wikidata":"Q1384","text":"New York"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]},{"id":"address.3561887178367494","type":"Feature","place_type":["address"],"relevance":0.42857142857142855,"properties":{},"text":"South West Street","place_name":"South West Street, Mount Vernon, New York 10550, United States","matching_text":"St","matching_place_name":"St, Mount Vernon, New York 10550, United States","center":[-73.850347,40.912431],"geometry":{"type":"Point","coordinates":[-73.850347,40.912431]},"context":[{"id":"postcode.15487211573224510","text":"10550"},{"id":"place.9087657435224330","wikidata":"Q835831","text":"Mount Vernon"},{"id":"region.3866","short_code":"US-NY","wikidata":"Q1384","text":"New York"},{"id":"country.3145","short_code":"us","wikidata":"Q30","text":"United States"}]}],"attribution":"NOTICE: © 2017 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_f42b62632d0fa8733ae9eda5e6cb3c9091935380 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_f42b62632d0fa8733ae9eda5e6cb3c9091935380
new file mode 100644
index 000000000..867689ad1
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_f42b62632d0fa8733ae9eda5e6cb3c9091935380
@@ -0,0 +1 @@
+s:285:"{"type":"FeatureCollection","features":[],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_fee388586d54f2150af453c6e033e452d85e46a8 b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_fee388586d54f2150af453c6e033e452d85e46a8
new file mode 100644
index 000000000..383d14da5
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/.cached_responses/api.mapbox.com_fee388586d54f2150af453c6e033e452d85e46a8
@@ -0,0 +1 @@
+s:3898:"{"type":"FeatureCollection","features":[{"type":"Feature","id":"dXJuOm1ieGFkcjoxMDg4MTIxNi1mZTUwLTQyM2QtOWNjYS00ODQxYjNjMmFkNTA","geometry":{"type":"Point","coordinates":[-77.036574,38.897684]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjoxMDg4MTIxNi1mZTUwLTQyM2QtOWNjYS00ODQxYjNjMmFkNTA","feature_type":"address","full_address":"1600 Pennsylvania Avenue Northwest, Washington, District of Columbia 20500, United States","name":"1600 Pennsylvania Avenue Northwest","name_preferred":"1600 Pennsylvania Avenue Northwest","coordinates":{"longitude":-77.036574,"latitude":38.897684,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":38.897876,"longitude":-77.036544}]},"place_formatted":"Washington, District of Columbia 20500, United States","match_code":{"address_number":"matched","street":"matched","postcode":"matched","place":"matched","region":"matched","locality":"not_applicable","country":"inferred","confidence":"exact"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjoxMDg4MTIxNi1mZTUwLTQyM2QtOWNjYS00ODQxYjNjMmFkNTA","address_number":"1600","street_name":"Pennsylvania Avenue Northwest","name":"1600 Pennsylvania Avenue Northwest"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6MTA4ODEyMTYtZmU1MC00MjNkLTljY2EtNDg0MWIzYzJhZDUw","name":"Pennsylvania Avenue Northwest"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpHYUVzN0E","name":"National Mall"},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpHVnFPN0E","name":"20500"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpGSmlvN0E","name":"Washington","wikidata_id":"Q61"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpCUVRz","name":"District of Columbia","wikidata_id":"Q3551781","region_code":"DC","region_code_full":"US-DC"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}},{"type":"Feature","id":"dXJuOm1ieGFkcjpmOTYwMzc4OC01MWE4LTQ3NzQtOWVhMi1lM2ZkZGY0NWQ0OWI","geometry":{"type":"Point","coordinates":[-76.981937,38.87922]},"properties":{"mapbox_id":"dXJuOm1ieGFkcjpmOTYwMzc4OC01MWE4LTQ3NzQtOWVhMi1lM2ZkZGY0NWQ0OWI","feature_type":"address","full_address":"1600 Pennsylvania Avenue Southeast, Washington, District of Columbia 20003, United States","name":"1600 Pennsylvania Avenue Southeast","name_preferred":"1600 Pennsylvania Avenue Southeast","coordinates":{"longitude":-76.981937,"latitude":38.87922,"accuracy":"rooftop","routable_points":[{"name":"default","latitude":38.879037,"longitude":-76.98206}]},"place_formatted":"Washington, District of Columbia 20003, United States","match_code":{"address_number":"matched","street":"unmatched","postcode":"unmatched","place":"matched","region":"matched","locality":"not_applicable","country":"inferred","confidence":"medium"},"context":{"address":{"mapbox_id":"dXJuOm1ieGFkcjpmOTYwMzc4OC01MWE4LTQ3NzQtOWVhMi1lM2ZkZGY0NWQ0OWI","address_number":"1600","street_name":"Pennsylvania Avenue Southeast","name":"1600 Pennsylvania Avenue Southeast"},"street":{"mapbox_id":"dXJuOm1ieGFkci1zdHI6Zjk2MDM3ODgtNTFhOC00Nzc0LTllYTItZTNmZGRmNDVkNDli","name":"Pennsylvania Avenue Southeast"},"neighborhood":{"mapbox_id":"dXJuOm1ieHBsYzpCVnVzN0E","name":"Capitol Hill","alternate":{"mapbox_id":"dXJuOm1ieHBsYzpFVzhzN0E","name":"Hill East"}},"postcode":{"mapbox_id":"dXJuOm1ieHBsYzpBNTZ1N0E","name":"20003"},"place":{"mapbox_id":"dXJuOm1ieHBsYzpGSmlvN0E","name":"Washington","wikidata_id":"Q61"},"region":{"mapbox_id":"dXJuOm1ieHBsYzpCUVRz","name":"District of Columbia","wikidata_id":"Q3551781","region_code":"DC","region_code_full":"US-DC"},"country":{"mapbox_id":"dXJuOm1ieHBsYzpJdXc","name":"United States","wikidata_id":"Q30","country_code":"US","country_code_alpha_3":"USA"}}}}],"attribution":"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."}";
\ No newline at end of file
diff --git a/src/Provider/Mapbox/Tests/MapboxAddressTest.php b/src/Provider/Mapbox/Tests/MapboxAddressTest.php
new file mode 100644
index 000000000..9f3f87153
--- /dev/null
+++ b/src/Provider/Mapbox/Tests/MapboxAddressTest.php
@@ -0,0 +1,60 @@
+build(MapboxAddress::class);
+
+ $this->assertNull($address->getMatchCode());
+
+ $matchCode = ['street' => 'matched', 'confidence' => 'exact'];
+ $address = $address->withMatchCode($matchCode);
+
+ $this->assertSame($matchCode, $address->getMatchCode());
+ $this->assertNull($address->withMatchCode($matchCode)->withMatchCode()->getMatchCode());
+ }
+
+ public function testMatchConfidence(): void
+ {
+ /** @var MapboxAddress $address */
+ $address = (new AddressBuilder('mapbox'))->build(MapboxAddress::class);
+
+ $this->assertNull($address->getMatchConfidence());
+
+ $address = $address->withMatchConfidence('exact');
+
+ $this->assertSame('exact', $address->getMatchConfidence());
+ $this->assertNull($address->withMatchConfidence('exact')->withMatchConfidence()->getMatchConfidence());
+ }
+
+ public function testAccuracy(): void
+ {
+ /** @var MapboxAddress $address */
+ $address = (new AddressBuilder('mapbox'))->build(MapboxAddress::class);
+
+ $this->assertNull($address->getAccuracy());
+
+ $address = $address->withAccuracy('rooftop');
+
+ $this->assertSame('rooftop', $address->getAccuracy());
+ $this->assertNull($address->withAccuracy('rooftop')->withAccuracy()->getAccuracy());
+ }
+}
diff --git a/src/Provider/Mapbox/Tests/MapboxTest.php b/src/Provider/Mapbox/Tests/MapboxTest.php
index 82668e672..ce351a79c 100644
--- a/src/Provider/Mapbox/Tests/MapboxTest.php
+++ b/src/Provider/Mapbox/Tests/MapboxTest.php
@@ -12,6 +12,7 @@
namespace Geocoder\Provider\Mapbox\Tests;
+use Geocoder\Exception\InvalidServerResponse;
use Geocoder\IntegrationTest\BaseTestCase;
use Geocoder\Model\AddressCollection;
use Geocoder\Model\Bounds;
@@ -19,6 +20,7 @@
use Geocoder\Provider\Mapbox\Model\MapboxAddress;
use Geocoder\Query\GeocodeQuery;
use Geocoder\Query\ReverseQuery;
+use Psr\Http\Message\RequestInterface;
class MapboxTest extends BaseTestCase
{
@@ -72,28 +74,204 @@ public function testGeocodeWithQuotaExceeded(): void
$provider->geocodeQuery(GeocodeQuery::create('10 avenue Gambetta, Paris, France'));
}
- public function testGeocodePlaceWithNoCountryShortCode(): void
+ public function testGeocodeWithInvalidApiKey(): void
{
- $provider = new Mapbox($this->getHttpClient($_SERVER['MAPBOX_GEOCODING_KEY']), $_SERVER['MAPBOX_GEOCODING_KEY']);
+ // The v6 API answers 401 for an invalid token (v5 answered 403).
+ // Both status codes map to InvalidCredentials in AbstractHttpProvider.
+ $this->expectException(\Geocoder\Exception\InvalidCredentials::class);
- $query = GeocodeQuery::create('princ'); // Principato di Monaco
- $query = $query->withLocale('it');
- $query = $query->withBounds(new Bounds(
- 35.82809688193029,
- -11.36323261153737,
- 59.05992036364424,
- 34.33947713277206
- ));
- $query = $query->withLimit(1);
- $query = $query->withData('location_type', [
- Mapbox::TYPE_PLACE,
- Mapbox::TYPE_LOCALITY,
- Mapbox::TYPE_NEIGHBORHOOD,
- Mapbox::TYPE_POI,
- Mapbox::TYPE_POI_LANDMARK,
- ]);
+ $provider = new Mapbox($this->getMockedHttpClient('', 401), 'api_key');
+ $provider->geocodeQuery(GeocodeQuery::create('10 avenue Gambetta, Paris, France'));
+ }
- $results = $provider->geocodeQuery($query);
+ public function testReverseWithInvalidResponse(): void
+ {
+ $this->expectException(InvalidServerResponse::class);
+
+ $provider = new Mapbox($this->getMockedHttpClient(), 'access_token');
+ $provider->reverseQuery(ReverseQuery::fromCoordinates(1, 2));
+ }
+
+ private function assertRequestUrl(string $expectedUrl, callable $request): void
+ {
+ $uri = '';
+ $client = $this->getMockedHttpClientCallback(function (RequestInterface $request) use (&$uri) {
+ $uri = (string) $request->getUri();
+ });
+
+ try {
+ $request($client);
+ } catch (InvalidServerResponse $e) {
+ // the mocked client answers an empty body; we only assert the request
+ }
+
+ $this->assertSame($expectedUrl, $uri);
+ }
+
+ public function testForwardGeocodeUrl(): void
+ {
+ $this->assertRequestUrl(
+ 'https://api.mapbox.com/search/geocode/v6/forward'
+ .'?q=10+Downing+St%2C+London'
+ .'&types=address'
+ .'&limit=5'
+ .'&access_token=access_token',
+ static function ($client) {
+ (new Mapbox($client, 'access_token'))->geocodeQuery(GeocodeQuery::create('10 Downing St, London'));
+ }
+ );
+ }
+
+ public function testForwardGeocodeUrlWithAllOptions(): void
+ {
+ $this->assertRequestUrl(
+ 'https://api.mapbox.com/search/geocode/v6/forward'
+ .'?q=wahsington'
+ .'&bbox=-124.83609163%2C45.54372254%2C-116.91742984%2C49.00243912'
+ .'&types=address%2Cstreet'
+ .'&autocomplete=true'
+ .'&proximity=-120.09%2C47.60'
+ .'&worldview=us'
+ .'&country=US'
+ .'&language=en'
+ .'&limit=10'
+ .'&permanent=true'
+ .'&access_token=access_token',
+ static function ($client) {
+ $provider = new Mapbox($client, 'access_token', 'US', true);
+ $query = GeocodeQuery::create('wahsington')
+ ->withLocale('en')
+ ->withBounds(new Bounds(45.54372254, -124.83609163, 49.00243912, -116.91742984))
+ ->withLimit(10)
+ ->withData('location_type', [Mapbox::TYPE_ADDRESS, Mapbox::TYPE_STREET])
+ ->withData('autocomplete', true)
+ ->withData('proximity', '-120.09,47.60')
+ ->withData('worldview', 'us');
+ $provider->geocodeQuery($query);
+ }
+ );
+ }
+
+ public function testForwardGeocodeUrlWithStructuredInput(): void
+ {
+ // Structured Input: the query text is NOT sent; fields use their v6 names.
+ // autocomplete defaults to false for structured input (Mapbox guidance).
+ $this->assertRequestUrl(
+ 'https://api.mapbox.com/search/geocode/v6/forward'
+ .'?address_number=2595'
+ .'&street=Lucky+John+Dr'
+ .'&place=Park+City'
+ .'®ion=UT'
+ .'&postcode=84060'
+ .'&types=address'
+ .'&autocomplete=false'
+ .'&country=US'
+ .'&limit=5'
+ .'&access_token=access_token',
+ static function ($client) {
+ $provider = new Mapbox($client, 'access_token');
+ $query = GeocodeQuery::create('2595 Lucky John Dr, Park City, UT 84060')
+ ->withData('address_number', '2595')
+ ->withData('street', 'Lucky John Dr')
+ ->withData('place', 'Park City')
+ ->withData('region', 'UT')
+ ->withData('postcode', '84060')
+ ->withData('country', 'US');
+ $provider->geocodeQuery($query);
+ }
+ );
+ }
+
+ public function testForwardGeocodeUrlWithStructuredInputAndExplicitAutocomplete(): void
+ {
+ // an explicit autocomplete value overrides the structured-input default
+ $this->assertRequestUrl(
+ 'https://api.mapbox.com/search/geocode/v6/forward'
+ .'?street=9th+Street'
+ .'&types=address'
+ .'&autocomplete=true'
+ .'&limit=5'
+ .'&access_token=access_token',
+ static function ($client) {
+ $provider = new Mapbox($client, 'access_token');
+ $query = GeocodeQuery::create('9th Street')
+ ->withData('street', '9th Street')
+ ->withData('autocomplete', true);
+ $provider->geocodeQuery($query);
+ }
+ );
+ }
+
+ public function testReverseGeocodeUrl(): void
+ {
+ $this->assertRequestUrl(
+ 'https://api.mapbox.com/search/geocode/v6/reverse'
+ .'?longitude=2.388911'
+ .'&latitude=48.8631507'
+ .'&types=address'
+ .'&limit=5'
+ .'&access_token=access_token',
+ static function ($client) {
+ (new Mapbox($client, 'access_token'))->reverseQuery(ReverseQuery::fromCoordinates(48.8631507, 2.388911));
+ }
+ );
+ }
+
+ public function testReverseGeocodeUrlWithLocationTypeAndCountry(): void
+ {
+ $this->assertRequestUrl(
+ 'https://api.mapbox.com/search/geocode/v6/reverse'
+ .'?longitude=2.388911'
+ .'&latitude=48.8631507'
+ .'&types=address%2Cpostcode'
+ .'&country=FR'
+ .'&limit=2'
+ .'&access_token=access_token',
+ static function ($client) {
+ $provider = new Mapbox($client, 'access_token');
+ $query = ReverseQuery::fromCoordinates(48.8631507, 2.388911)
+ ->withData('location_type', [Mapbox::TYPE_ADDRESS, Mapbox::TYPE_POSTCODE])
+ ->withData('country', 'FR')
+ ->withLimit(2);
+ $provider->reverseQuery($query);
+ }
+ );
+ }
+
+ public function testParseForwardAddressFeature(): void
+ {
+ $json = <<<'JSON'
+ {
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "id": "dXJuOm1ieGFkcjo3Njg3YjZmNy01YmZkLTQzMjItOGMzOS02OGE1NDhmZWYwM2U",
+ "geometry": { "type": "Point", "coordinates": [-122.413709, 37.7757] },
+ "properties": {
+ "mapbox_id": "dXJuOm1ieGFkcjo3Njg3YjZmNy01YmZkLTQzMjItOGMzOS02OGE1NDhmZWYwM2U",
+ "feature_type": "address",
+ "name": "149 9th Street",
+ "full_address": "149 9th Street, San Francisco, California 94103, United States",
+ "coordinates": { "longitude": -122.413709, "latitude": 37.7757, "accuracy": "rooftop" },
+ "match_code": { "address_number": "matched", "street": "matched", "confidence": "exact" },
+ "context": {
+ "address": { "address_number": "149", "street_name": "9th Street", "name": "149 9th Street" },
+ "street": { "name": "9th Street" },
+ "neighborhood": { "name": "South of Market" },
+ "postcode": { "name": "94103" },
+ "place": { "name": "San Francisco" },
+ "region": { "name": "California", "region_code": "CA", "region_code_full": "US-CA" },
+ "country": { "name": "United States", "country_code": "US", "country_code_alpha_3": "USA" }
+ }
+ }
+ }
+ ]
+ }
+ JSON;
+
+ $provider = new Mapbox($this->getMockedHttpClient($json), 'access_token');
+ $results = $provider->geocodeQuery(GeocodeQuery::create('149 9th St, San Francisco, CA 94103'));
$this->assertInstanceOf(AddressCollection::class, $results);
$this->assertCount(1, $results);
@@ -101,18 +279,181 @@ public function testGeocodePlaceWithNoCountryShortCode(): void
/** @var MapboxAddress $result */
$result = $results->first();
$this->assertInstanceOf(MapboxAddress::class, $result);
- $this->assertEqualsWithDelta(43.73125, $result->getCoordinates()->getLatitude(), 0.001);
- $this->assertEqualsWithDelta(7.41974, $result->getCoordinates()->getLongitude(), 0.001);
- $this->assertEquals('Principato di Monaco', $result->getStreetName());
- $this->assertEquals('Principato di Monaco', $result->getCountry()->getName());
- $this->assertEquals('place.4899176537126140', $result->getId());
+ $this->assertEqualsWithDelta(37.7757, $result->getCoordinates()->getLatitude(), 0.00001);
+ $this->assertEqualsWithDelta(-122.413709, $result->getCoordinates()->getLongitude(), 0.00001);
+ $this->assertEquals('dXJuOm1ieGFkcjo3Njg3YjZmNy01YmZkLTQzMjItOGMzOS02OGE1NDhmZWYwM2U', $result->getId());
+ $this->assertEquals('9th Street', $result->getStreetName());
+ $this->assertEquals('149', $result->getStreetNumber());
+ $this->assertEquals('94103', $result->getPostalCode());
+ $this->assertEquals('San Francisco', $result->getLocality());
+ $this->assertCount(2, $result->getAdminLevels());
+ $this->assertEquals('San Francisco', $result->getAdminLevels()->get(1)->getName());
+ $this->assertEquals('California', $result->getAdminLevels()->get(2)->getName());
+ $this->assertEquals('CA', $result->getAdminLevels()->get(2)->getCode());
+ $this->assertEquals('United States', $result->getCountry()->getName());
+ $this->assertEquals('US', $result->getCountry()->getCode());
+ $this->assertEquals('149 9th Street, San Francisco, California 94103, United States', $result->getFormattedAddress());
+ $this->assertEquals('South of Market', $result->getNeighborhood());
+ $this->assertEquals(['address'], $result->getResultType());
+ $this->assertEquals('exact', $result->getMatchConfidence());
+ $this->assertEquals('matched', $result->getMatchCode()['street']);
+ $this->assertEquals('rooftop', $result->getAccuracy());
+ $this->assertNull($result->getTimezone());
+ }
- // not provided
- $this->assertNull($result->getCountry()->getCode());
+ public function testParseForwardPlaceFeature(): void
+ {
+ // place feature: no context.address; name is the place name; region code may be 3 letters
+ $json = <<<'JSON'
+ {
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "id": "dXJuOm1ieHBsYzpoMmhQ",
+ "geometry": { "type": "Point", "coordinates": [-0.83069, 51.724422] },
+ "properties": {
+ "mapbox_id": "dXJuOm1ieHBsYzpoMmhQ",
+ "feature_type": "place",
+ "name": "Princes Risborough",
+ "full_address": "Princes Risborough, Buckinghamshire, Inghilterra, Regno Unito",
+ "coordinates": { "longitude": -0.83069, "latitude": 51.724422 },
+ "context": {
+ "place": { "name": "Princes Risborough" },
+ "region": { "name": "Inghilterra", "region_code": "ENG" },
+ "country": { "name": "Regno Unito", "country_code": "GB" }
+ }
+ }
+ }
+ ]
+ }
+ JSON;
+
+ $provider = new Mapbox($this->getMockedHttpClient($json), 'access_token');
+ $results = $provider->geocodeQuery(GeocodeQuery::create('princ'));
+
+ $this->assertCount(1, $results);
+
+ /** @var MapboxAddress $result */
+ $result = $results->first();
+ $this->assertEquals('dXJuOm1ieHBsYzpoMmhQ', $result->getId());
+ $this->assertEquals('Princes Risborough', $result->getStreetName());
+ $this->assertNull($result->getStreetNumber());
$this->assertNull($result->getPostalCode());
- $this->assertNull($result->getLocality());
- $this->assertNull($result->getTimezone());
+ $this->assertEquals('Princes Risborough', $result->getLocality());
+ $this->assertCount(2, $result->getAdminLevels());
+ $this->assertEquals('Princes Risborough', $result->getAdminLevels()->get(1)->getName());
+ $this->assertEquals('Inghilterra', $result->getAdminLevels()->get(2)->getName());
+ $this->assertEquals('ENG', $result->getAdminLevels()->get(2)->getCode());
+ $this->assertEquals('Regno Unito', $result->getCountry()->getName());
+ $this->assertEquals('GB', $result->getCountry()->getCode());
+ $this->assertEquals('Princes Risborough, Buckinghamshire, Inghilterra, Regno Unito', $result->getFormattedAddress());
+ $this->assertNull($result->getNeighborhood());
+ $this->assertEquals(['place'], $result->getResultType());
+ $this->assertNull($result->getMatchConfidence());
+ $this->assertNull($result->getMatchCode());
+ $this->assertNull($result->getAccuracy());
+ }
+
+ public function testParseForwardStreetFeature(): void
+ {
+ // v6 street feature: street name comes from context.street
+ $json = <<<'JSON'
+ {
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "id": "address.7607992201284362",
+ "geometry": { "type": "Point", "coordinates": [-117.039767, 46.930253] },
+ "properties": {
+ "mapbox_id": "address.7607992201284362",
+ "feature_type": "street",
+ "name": "Washington Highway 6",
+ "full_address": "Washington Highway 6, Palouse, Washington 99161, United States",
+ "coordinates": { "longitude": -117.039767, "latitude": 46.930253 },
+ "context": {
+ "street": { "name": "Washington Highway 6" },
+ "postcode": { "name": "99161" },
+ "place": { "name": "Palouse" },
+ "region": { "name": "Washington", "region_code": "WA" },
+ "country": { "name": "United States", "country_code": "US" }
+ }
+ }
+ }
+ ]
+ }
+ JSON;
+
+ $provider = new Mapbox($this->getMockedHttpClient($json), 'access_token');
+ $results = $provider->geocodeQuery(GeocodeQuery::create('Washington Highway 6'));
+
+ $this->assertCount(1, $results);
+
+ /** @var MapboxAddress $result */
+ $result = $results->first();
+ $this->assertEquals('address.7607992201284362', $result->getId());
+ $this->assertEquals('Washington Highway 6', $result->getStreetName());
$this->assertNull($result->getStreetNumber());
+ $this->assertEquals('99161', $result->getPostalCode());
+ $this->assertEquals('Palouse', $result->getLocality());
+ $this->assertEquals('Washington', $result->getAdminLevels()->get(2)->getName());
+ $this->assertEquals('WA', $result->getAdminLevels()->get(2)->getCode());
+ $this->assertEquals(['street'], $result->getResultType());
+ }
+
+ public function testParseFeatureWithoutContext(): void
+ {
+ // features without a context object must still parse (name + coordinates)
+ $json = <<<'JSON'
+ {
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "id": "abc123",
+ "geometry": { "type": "Point", "coordinates": [2.35, 48.85] },
+ "properties": {
+ "mapbox_id": "abc123",
+ "feature_type": "place",
+ "name": "Some Place",
+ "coordinates": { "longitude": 2.35, "latitude": 48.85 }
+ }
+ }
+ ]
+ }
+ JSON;
+
+ $provider = new Mapbox($this->getMockedHttpClient($json), 'access_token');
+ $results = $provider->geocodeQuery(GeocodeQuery::create('Some Place'));
+
+ $this->assertCount(1, $results);
+
+ /** @var MapboxAddress $result */
+ $result = $results->first();
+ $this->assertEquals('Some Place', $result->getStreetName());
+ $this->assertEquals('abc123', $result->getId());
+ $this->assertEqualsWithDelta(48.85, $result->getCoordinates()->getLatitude(), 0.00001);
+ $this->assertEqualsWithDelta(2.35, $result->getCoordinates()->getLongitude(), 0.00001);
+ }
+
+ public function testParseEmptyFeatures(): void
+ {
+ $json = '{"type":"FeatureCollection","features":[]}';
+
+ $provider = new Mapbox($this->getMockedHttpClient($json), 'access_token');
+ $results = $provider->geocodeQuery(GeocodeQuery::create('jsajhgsdkfjhsfkjhaldkadjaslgldasd'));
+
+ $this->assertInstanceOf(AddressCollection::class, $results);
+ $this->assertEmpty($results);
+ }
+
+ public function testParseInvalidJson(): void
+ {
+ $this->expectException(InvalidServerResponse::class);
+
+ $provider = new Mapbox($this->getMockedHttpClient('{invalid json'), 'access_token');
+ $provider->geocodeQuery(GeocodeQuery::create('10 Downing St, London'));
}
public function testGeocodeWithRealAddress(): void
@@ -130,30 +471,56 @@ public function testGeocodeWithRealAddress(): void
/** @var MapboxAddress $result */
$result = $results->first();
$this->assertInstanceOf(MapboxAddress::class, $result);
- $this->assertEqualsWithDelta(37.77572, $result->getCoordinates()->getLatitude(), 0.001);
- $this->assertEqualsWithDelta(-122.41362, $result->getCoordinates()->getLongitude(), 0.001);
- $this->assertNull($result->getBounds());
- $this->assertEquals(149, $result->getStreetNumber());
+ $this->assertEqualsWithDelta(37.7757, $result->getCoordinates()->getLatitude(), 0.001);
+ $this->assertEqualsWithDelta(-122.413709, $result->getCoordinates()->getLongitude(), 0.001);
$this->assertEquals('9th Street', $result->getStreetName());
- $this->assertEquals(94103, $result->getPostalCode());
+ $this->assertEquals('149', $result->getStreetNumber());
+ $this->assertEquals('94103', $result->getPostalCode());
$this->assertEquals('San Francisco', $result->getLocality());
+ $this->assertCount(2, $result->getAdminLevels());
$this->assertEquals('California', $result->getAdminLevels()->get(2)->getName());
$this->assertEquals('CA', $result->getAdminLevels()->get(2)->getCode());
- $this->assertEquals('San Francisco', $result->getAdminLevels()->get(1)->getName());
$this->assertEquals('United States', $result->getCountry()->getName());
$this->assertEquals('US', $result->getCountry()->getCode());
- $this->assertEquals('address.3071152063251042', $result->getId());
+ $this->assertEquals('149 9th Street, San Francisco, California 94103, United States', $result->getFormattedAddress());
+ $this->assertEquals('South of Market', $result->getNeighborhood());
+ $this->assertEquals(['address'], $result->getResultType());
+ $this->assertEquals('exact', $result->getMatchConfidence());
+ $this->assertEquals('rooftop', $result->getAccuracy());
// not provided
$this->assertNull($result->getTimezone());
}
- public function testReverse(): void
+ public function testGeocodeWithRealAddressInDc(): void
{
- $this->expectException(\Geocoder\Exception\InvalidServerResponse::class);
+ if (!isset($_SERVER['MAPBOX_GEOCODING_KEY'])) {
+ $this->markTestSkipped('You need to configure the MAPBOX_GEOCODING_KEY value in phpunit.xml');
+ }
- $provider = new Mapbox($this->getMockedHttpClient(), 'access_token');
- $provider->reverseQuery(ReverseQuery::fromCoordinates(1, 2));
+ $provider = new Mapbox($this->getHttpClient($_SERVER['MAPBOX_GEOCODING_KEY']), $_SERVER['MAPBOX_GEOCODING_KEY']);
+ $results = $provider->geocodeQuery(GeocodeQuery::create('1600 Pennsylvania Avenue NW, Washington, DC 20500'));
+
+ $this->assertInstanceOf(AddressCollection::class, $results);
+ $this->assertCount(2, $results);
+
+ /** @var MapboxAddress $result */
+ $result = $results->first();
+ $this->assertInstanceOf(MapboxAddress::class, $result);
+ $this->assertEqualsWithDelta(38.897684, $result->getCoordinates()->getLatitude(), 0.001);
+ $this->assertEqualsWithDelta(-77.036574, $result->getCoordinates()->getLongitude(), 0.001);
+ $this->assertEquals('Pennsylvania Avenue Northwest', $result->getStreetName());
+ $this->assertEquals('1600', $result->getStreetNumber());
+ $this->assertEquals('20500', $result->getPostalCode());
+ $this->assertEquals('Washington', $result->getLocality());
+ $this->assertCount(2, $result->getAdminLevels());
+ $this->assertEquals('District of Columbia', $result->getAdminLevels()->get(2)->getName());
+ $this->assertEquals('DC', $result->getAdminLevels()->get(2)->getCode());
+ $this->assertEquals('United States', $result->getCountry()->getName());
+ $this->assertEquals('US', $result->getCountry()->getCode());
+ $this->assertEquals('1600 Pennsylvania Avenue Northwest, Washington, District of Columbia 20500, United States', $result->getFormattedAddress());
+ $this->assertEquals('exact', $result->getMatchConfidence());
+ $this->assertEquals('rooftop', $result->getAccuracy());
}
public function testReverseWithRealCoordinates(): void
@@ -171,26 +538,25 @@ public function testReverseWithRealCoordinates(): void
/** @var MapboxAddress $result */
$result = $results->first();
$this->assertInstanceOf(MapboxAddress::class, $result);
- $this->assertEquals(8, $result->getStreetNumber());
+ $this->assertEquals('12', $result->getStreetNumber());
$this->assertEquals('Avenue Gambetta', $result->getStreetName());
- $this->assertEquals(75020, $result->getPostalCode());
+ $this->assertEquals('75020', $result->getPostalCode());
$this->assertEquals('Paris', $result->getLocality());
- $this->assertCount(1, $result->getAdminLevels());
+ $this->assertCount(2, $result->getAdminLevels());
$this->assertEquals('Paris', $result->getAdminLevels()->get(1)->getName());
+ $this->assertEquals('Île-de-France', $result->getAdminLevels()->get(2)->getName());
+ $this->assertEquals('IDF', $result->getAdminLevels()->get(2)->getCode());
$this->assertEquals('France', $result->getCountry()->getName());
$this->assertEquals('FR', $result->getCountry()->getCode());
- $this->assertEquals('address.1085979616', $result->getId());
- }
+ $this->assertEquals('12 Avenue Gambetta, 75020 Paris, France', $result->getFormattedAddress());
+ $this->assertEquals('rooftop', $result->getAccuracy());
- public function testGeocodeWithInvalidApiKey(): void
- {
- $this->expectException(\Geocoder\Exception\InvalidCredentials::class);
-
- $provider = new Mapbox($this->getMockedHttpClient('', 403), 'api_key');
- $provider->geocodeQuery(GeocodeQuery::create('10 avenue Gambetta, Paris, France'));
+ // not provided by reverse geocoding
+ $this->assertNull($result->getMatchConfidence());
+ $this->assertNull($result->getNeighborhood());
}
- public function testGeocodeWithRealValidApiKey(): void
+ public function testGeocodePlaceWithLocale(): void
{
if (!isset($_SERVER['MAPBOX_GEOCODING_KEY'])) {
$this->markTestSkipped('You need to configure the MAPBOX_GEOCODING_KEY value in phpunit.xml');
@@ -198,31 +564,48 @@ public function testGeocodeWithRealValidApiKey(): void
$provider = new Mapbox($this->getHttpClient($_SERVER['MAPBOX_GEOCODING_KEY']), $_SERVER['MAPBOX_GEOCODING_KEY']);
- $results = $provider->geocodeQuery(GeocodeQuery::create('116th St & Broadway, New York, NY 10027, United States'));
+ $query = GeocodeQuery::create('princ');
+ $query = $query->withLocale('it');
+ $query = $query->withBounds(new Bounds(
+ 35.82809688193029,
+ -11.36323261153737,
+ 59.05992036364424,
+ 34.33947713277206
+ ));
+ $query = $query->withLimit(1);
+ $query = $query->withData('location_type', [
+ Mapbox::TYPE_PLACE,
+ Mapbox::TYPE_LOCALITY,
+ Mapbox::TYPE_NEIGHBORHOOD,
+ ]);
+
+ $results = $provider->geocodeQuery($query);
$this->assertInstanceOf(AddressCollection::class, $results);
- $this->assertCount(5, $results);
+ $this->assertCount(1, $results);
/** @var MapboxAddress $result */
$result = $results->first();
$this->assertInstanceOf(MapboxAddress::class, $result);
- $this->assertEquals('116th Street', $result->getStreetName());
- $this->assertEquals(11356, $result->getPostalCode());
- $this->assertCount(2, $result->getAdminLevels());
- $this->assertEquals('United States', $result->getCountry()->getName());
- $this->assertEquals('US', $result->getCountry()->getCode());
- $this->assertEquals('address.2431617896783536', $result->getId());
- $this->assertNotNull($result->getCoordinates()->getLatitude());
- $this->assertNotNull($result->getCoordinates()->getLongitude());
- $this->assertEqualsWithDelta(40.786596, $result->getCoordinates()->getLatitude(), 0.001);
- $this->assertEqualsWithDelta(-73.851157, $result->getCoordinates()->getLongitude(), 0.001);
- $this->assertEquals('New York', $result->getLocality());
+ $this->assertEqualsWithDelta(51.724422, $result->getCoordinates()->getLatitude(), 0.001);
+ $this->assertEqualsWithDelta(-0.83069, $result->getCoordinates()->getLongitude(), 0.001);
+ $this->assertEquals('Princes Risborough', $result->getStreetName());
+ $this->assertEquals(['place'], $result->getResultType());
+ $this->assertEquals('Princes Risborough', $result->getLocality());
$this->assertCount(2, $result->getAdminLevels());
- $this->assertEquals('New York', $result->getAdminLevels()->get(1)->getName());
- $this->assertEquals('NY', $result->getAdminLevels()->get(2)->getCode());
+ $this->assertEquals('Princes Risborough', $result->getAdminLevels()->get(1)->getName());
+ $this->assertEquals('Inghilterra', $result->getAdminLevels()->get(2)->getName());
+ $this->assertEquals('ENG', $result->getAdminLevels()->get(2)->getCode());
+ $this->assertEquals('Regno Unito', $result->getCountry()->getName());
+ $this->assertEquals('GB', $result->getCountry()->getCode());
+
+ // not provided
+ $this->assertNull($result->getPostalCode());
+ $this->assertNull($result->getStreetNumber());
+ $this->assertNull($result->getMatchConfidence());
}
- public function testGeocodeWithFuzzyMatch(): void
+ public function testGeocodeWithAutocompleteEnabled(): void
{
if (!isset($_SERVER['MAPBOX_GEOCODING_KEY'])) {
$this->markTestSkipped('You need to configure the MAPBOX_GEOCODING_KEY value in phpunit.xml');
@@ -230,21 +613,35 @@ public function testGeocodeWithFuzzyMatch(): void
$provider = new Mapbox($this->getHttpClient($_SERVER['MAPBOX_GEOCODING_KEY']), $_SERVER['MAPBOX_GEOCODING_KEY']);
- $query = GeocodeQuery::create('wahsington'); // Washington
- $query = $query->withData('fuzzy_match', true);
+ $query = GeocodeQuery::create('washi');
+ $query = $query->withData('autocomplete', true);
$query = $query->withBounds(new Bounds(
45.54372254,
-124.83609163,
49.00243912,
-116.91742984
));
+ $query = $query->withData('location_type', [
+ Mapbox::TYPE_REGION,
+ Mapbox::TYPE_NEIGHBORHOOD,
+ Mapbox::TYPE_STREET,
+ Mapbox::TYPE_PLACE,
+ Mapbox::TYPE_LOCALITY,
+ ]);
$results = $provider->geocodeQuery($query);
$this->assertInstanceOf(AddressCollection::class, $results);
$this->assertCount(5, $results);
+
+ /** @var MapboxAddress $first */
+ $first = $results->first();
+ $this->assertEquals(['region'], $first->getResultType());
+ $this->assertEquals('Washington', $first->getStreetName());
+ $this->assertEquals('Washington', $first->getAdminLevels()->get(2)->getName());
+ $this->assertEquals('WA', $first->getAdminLevels()->get(2)->getCode());
}
- public function testGeocodeWithoutFuzzyMatch(): void
+ public function testGeocodeWithAutocompleteDisabled(): void
{
if (!isset($_SERVER['MAPBOX_GEOCODING_KEY'])) {
$this->markTestSkipped('You need to configure the MAPBOX_GEOCODING_KEY value in phpunit.xml');
@@ -252,17 +649,70 @@ public function testGeocodeWithoutFuzzyMatch(): void
$provider = new Mapbox($this->getHttpClient($_SERVER['MAPBOX_GEOCODING_KEY']), $_SERVER['MAPBOX_GEOCODING_KEY']);
- $query = GeocodeQuery::create('wahsington'); // Washington
- $query = $query->withData('fuzzy_match', false);
+ $query = GeocodeQuery::create('washi');
+ $query = $query->withData('autocomplete', false);
$query = $query->withBounds(new Bounds(
45.54372254,
-124.83609163,
49.00243912,
-116.91742984
));
+ $query = $query->withData('location_type', [
+ Mapbox::TYPE_REGION,
+ Mapbox::TYPE_NEIGHBORHOOD,
+ Mapbox::TYPE_STREET,
+ Mapbox::TYPE_PLACE,
+ Mapbox::TYPE_LOCALITY,
+ ]);
$results = $provider->geocodeQuery($query);
$this->assertInstanceOf(AddressCollection::class, $results);
- $this->assertEmpty($results);
+ $this->assertCount(1, $results);
+
+ /** @var MapboxAddress $first */
+ $first = $results->first();
+ $this->assertEquals(['street'], $first->getResultType());
+ $this->assertEquals('Yashi Road', $first->getStreetName());
+ }
+
+ public function testGeocodeWithStructuredInput(): void
+ {
+ if (!isset($_SERVER['MAPBOX_GEOCODING_KEY'])) {
+ $this->markTestSkipped('You need to configure the MAPBOX_GEOCODING_KEY value in phpunit.xml');
+ }
+
+ $provider = new Mapbox($this->getHttpClient($_SERVER['MAPBOX_GEOCODING_KEY']), $_SERVER['MAPBOX_GEOCODING_KEY']);
+
+ $query = GeocodeQuery::create('2595 Lucky John Dr, Park City, UT 84060');
+ $query = $query->withData('address_number', '2595');
+ $query = $query->withData('street', 'Lucky John Dr');
+ $query = $query->withData('place', 'Park City');
+ $query = $query->withData('region', 'UT');
+ $query = $query->withData('postcode', '84060');
+
+ $results = $provider->geocodeQuery($query);
+
+ $this->assertInstanceOf(AddressCollection::class, $results);
+ $this->assertCount(1, $results);
+
+ /** @var MapboxAddress $result */
+ $result = $results->first();
+ $this->assertInstanceOf(MapboxAddress::class, $result);
+ $this->assertEqualsWithDelta(40.6716, $result->getCoordinates()->getLatitude(), 0.001);
+ $this->assertEqualsWithDelta(-111.507008, $result->getCoordinates()->getLongitude(), 0.001);
+ $this->assertEquals('Lucky John Drive', $result->getStreetName());
+ $this->assertEquals('2595', $result->getStreetNumber());
+ $this->assertEquals('84060', $result->getPostalCode());
+ $this->assertEquals('Park City', $result->getLocality());
+ $this->assertCount(2, $result->getAdminLevels());
+ $this->assertEquals('Park City', $result->getAdminLevels()->get(1)->getName());
+ $this->assertEquals('Utah', $result->getAdminLevels()->get(2)->getName());
+ $this->assertEquals('UT', $result->getAdminLevels()->get(2)->getCode());
+ $this->assertEquals('United States', $result->getCountry()->getName());
+ $this->assertEquals('US', $result->getCountry()->getCode());
+ $this->assertEquals('2595 Lucky John Drive, Park City, Utah 84060, United States', $result->getFormattedAddress());
+ $this->assertEquals('Park Meadows', $result->getNeighborhood());
+ $this->assertEquals('exact', $result->getMatchConfidence());
+ $this->assertEquals('rooftop', $result->getAccuracy());
}
}