geocoder-php/nominatim-provider
Nominatim provider for Geocoder PHP. Adds OpenStreetMap Nominatim geocoding and reverse geocoding support, converting addresses and coordinates to structured results, with configurable endpoints and HTTP client integration for Laravel/PHP apps.
Installation
composer require geocoder-php/geocoder geocoder-php/nominatim-provider
geocoder-php/geocoder is installed as the base package.Basic Usage
use Geocoder\Geocoder;
use Geocoder\Provider\Nominatim\NominatimProvider;
$geocoder = new Geocoder([
'nominatim' => [
'key' => 'YOUR_NOMINATIM_API_KEY', // Optional, but recommended
'host' => 'https://nominatim.openstreetmap.org', // Default host
],
]);
// Geocode an address
$results = $geocoder->geocode('1600 Amphitheatre Parkway, Mountain View, CA');
foreach ($results as $result) {
echo $result->getPosition()->getLatitude() . ', ' . $result->getPosition()->getLongitude();
}
// Reverse geocode coordinates
$results = $geocoder->reverseGeocode('37.4220, -122.0841');
foreach ($results as $result) {
echo $result->getStreet() . ', ' . $result->getLocality();
}
First Use Case
use Illuminate\Support\Facades\Validator;
$validator = Validator::make($request->all(), [
'address' => 'required|geocode', // Custom rule to validate geocoding
]);
Batch Geocoding Useful for processing large datasets (e.g., CSV imports):
$addresses = ['Address 1', 'Address 2', 'Address 3'];
$geocoded = [];
foreach ($addresses as $address) {
$results = $geocoder->geocode($address);
$geocoded[] = $results->first()?->getPosition();
}
Caching Results Avoid hitting Nominatim’s rate limits by caching responses:
use Illuminate\Support\Facades\Cache;
$cachedKey = 'geocode_' . md5($address);
$results = Cache::remember($cachedKey, now()->addHours(1), function () use ($geocoder, $address) {
return $geocoder->geocode($address);
});
Fallback Providers Combine Nominatim with other providers (e.g., Google Maps) for redundancy:
$geocoder = new Geocoder([
'nominatim' => [...],
'google_maps' => [...], // Requires geocoder-php/google-maps-provider
]);
$results = $geocoder->geocodeQuery('1600 Amphitheatre Parkway')->first();
Laravel Service Provider Integration Bind the geocoder to Laravel’s container for easy dependency injection:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton(Geocoder::class, function () {
return new Geocoder([
'nominatim' => [
'key' => config('services.nominatim.key'),
],
]);
});
}
Customizing Request Options
Nominatim supports query parameters like format, addressdetails, or viewbox:
$results = $geocoder->geocodeQuery('Paris')
->withOptions(['addressdetails' => 1, 'viewbox' => '1,1,2,2'])
->get();
Laravel Form Requests
Extend FormRequest to validate geocoding:
use Geocoder\Geocoder;
public function rules()
{
return [
'address' => 'required|string',
];
}
public function withValidator($validator)
{
$validator->after(function ($validator) {
$geocoder = app(Geocoder::class);
$results = $geocoder->geocode($this->address);
if ($results->isEmpty()) {
$validator->errors()->add('address', 'Invalid address.');
}
});
}
Eloquent Model Observers Automatically geocode addresses when a model is saved:
// app/Observers/AddressObserver.php
public function saved(Address $address)
{
if (empty($address->latitude)) {
$results = $geocoder->geocode($address->full_address);
$position = $results->first()?->getPosition();
$address->update([
'latitude' => $position?->getLatitude(),
'longitude' => $position?->getLongitude(),
]);
}
}
API Rate Limiting Nominatim has usage policies (see here). Implement middleware to throttle requests:
// app/Http/Middleware/GeocodeThrottle.php
public function handle($request, Closure $next)
{
$key = $request->ip() . '_geocode';
$remaining = Cache::get($key, 1000); // Default: 1000 requests/day
if ($remaining <= 0) {
abort(429, 'Nominatim rate limit exceeded.');
}
Cache::decrement($key);
return $next($request);
}
Rate Limiting
Read-Only Access
Deprecated Hosts
nominatim.openstreetmap.org for production without an API key. Use a dedicated instance (e.g., nominatim.example.com) or pay for a commercial license.Inconsistent Results
"New York, NY, USA").Timeouts
https://nominatim.openstreetmap.org/search for simple queries.https://nominatim.openstreetmap.org/reverse for reverse geocoding.Enable Debugging Enable Guzzle’s debug mode to inspect raw responses:
$geocoder = new Geocoder([
'nominatim' => [
'key' => 'YOUR_KEY',
'http_client' => new \GuzzleHttp\Client([
'debug' => true,
]),
],
]);
Validate API Responses
Nominatim returns JSON with a status field. Check for errors:
$results = $geocoder->geocode('invalid address');
if ($results->isEmpty()) {
$lastResponse = $geocoder->getProvider('nominatim')->getLastResponse();
Log::error('Nominatim error:', ['response' => $lastResponse]);
}
Common HTTP Errors
Custom Nominatim Instance Configure a private Nominatim instance (e.g., self-hosted):
$geocoder = new Geocoder([
'nominatim' => [
'host' => 'https://your-nominatim-instance.com',
'scheme' => 'https',
'port' => 443,
],
]);
Add Custom Fields
Nominatim supports addressdetails for structured data. Parse it in Laravel:
$result = $ge
How can I help you explore Laravel packages today?