abdullmng / laravel-distance
Calculate straight-line and route-based distances between addresses or coordinates with built-in geocoding and routing
Requires
- php: ^8.2|^8.3|^8.4
- guzzlehttp/guzzle: ^7.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
README
A comprehensive Laravel package for calculating distances between locations with built-in geocoding support. Perfect for logistics, ride-sharing, delivery, and location-based applications.
Features
- 🌍 Multiple Geocoding Providers: Nominatim (OpenStreetMap), Google Maps, Mapbox, and OpenCage
- 📏 Accurate Distance Calculations: Haversine and Vincenty formulas for straight-line distances
- 🛣️ Route-Based Distances: Calculate actual driving/walking/cycling distances using road networks
- ⏱️ Travel Time Estimates: Get estimated duration for routes
- 🔄 Flexible Input: Accept addresses, coordinates, or coordinate strings
- 📦 Multiple Units: Kilometers, miles, meters, and feet
- 💾 Built-in Caching: Reduce API calls and improve performance
- 🎯 Bearing & Direction: Calculate bearing and compass direction between locations
- 🔌 Easy Integration: Laravel service provider and facade support
Requirements
- PHP 8.2, 8.3, or 8.4
- Laravel 10.x, 11.x, or 12.x
Installation
Install the package via Composer:
composer require abdullmng/laravel-distance
Publish the configuration file:
php artisan vendor:publish --tag=distance-config
Configuration
Configure your preferred geocoding and routing providers in .env:
# Geocoding Provider (nominatim, google, mapbox, opencage) GEOCODING_PROVIDER=nominatim # Routing Provider (osrm, mapbox, google) ROUTING_PROVIDER=osrm # Google Maps API Key (if using Google) GOOGLE_MAPS_API_KEY=your-api-key # Mapbox API Key (if using Mapbox) MAPBOX_API_KEY=your-api-key # OpenCage API Key (if using OpenCage) OPENCAGE_API_KEY=your-api-key # Nominatim settings (if using Nominatim) NOMINATIM_URL=https://nominatim.openstreetmap.org NOMINATIM_USER_AGENT=YourAppName/1.0 # Default distance unit (kilometers, miles, meters, feet) DISTANCE_UNIT=kilometers # Default routing mode (driving, walking, cycling) ROUTING_MODE=driving # Cache settings GEOCODING_CACHE_ENABLED=true GEOCODING_CACHE_DURATION=1440
Usage
Straight-Line Distance Calculation
use Abdullmng\Distance\Facades\Distance; // Calculate straight-line distance between two addresses $result = Distance::between( 'New York, NY', 'Los Angeles, CA' ); echo $result->distance; // Distance in default unit (km) echo $result->inMiles(); // Convert to miles echo $result->inMeters(); // Convert to meters
Route-Based Distance Calculation
// Calculate actual driving distance using road networks $route = Distance::route( 'Lagos, Nigeria', 'Abuja, Nigeria' ); echo $route->distance; // Distance in default unit (km) echo $route->duration; // Duration in seconds echo $route->formattedDuration(); // e.g., "6h 30m" echo $route->inMiles(); // Convert to miles
Comparing Straight-Line vs Route Distance
// Straight-line (as the crow flies) $straightLine = Distance::between('Lagos', 'Abuja'); echo "Straight-line: {$straightLine->inKilometers()} km\n"; // Route-based (actual driving distance) $route = Distance::route('Lagos', 'Abuja'); echo "Route: {$route->inKilometers()} km\n"; echo "Duration: {$route->formattedDuration()}\n";
Different Travel Modes
// Driving (default) $driving = Distance::route($from, $to, ['mode' => 'driving']); // Walking $walking = Distance::route($from, $to, ['mode' => 'walking']); // Cycling $cycling = Distance::route($from, $to, ['mode' => 'cycling']);
Using Coordinates
use Abdullmng\Distance\DTOs\Coordinate; // Using Coordinate objects $from = new Coordinate(40.7128, -74.0060); // New York $to = new Coordinate(34.0522, -118.2437); // Los Angeles // Straight-line distance $result = Distance::between($from, $to); // Route distance $route = Distance::route($from, $to);
Using Coordinate Strings
// Using coordinate strings $result = Distance::between( '40.7128,-74.0060', '34.0522,-118.2437' );
Specify Unit
// Calculate in miles $result = Distance::between('New York, NY', 'Los Angeles, CA', 'miles'); // Or use the unit() method $result = Distance::unit('miles')->between('New York, NY', 'Los Angeles, CA');
Geocoding
// Simple geocoding $coordinate = Distance::geocode('1600 Amphitheatre Parkway, Mountain View, CA'); echo $coordinate->latitude; echo $coordinate->longitude; echo $coordinate->formattedAddress; echo $coordinate->accuracy; // Quality score (0-1) echo $coordinate->source; // Which provider was used // Reverse geocode $address = Distance::reverse(37.4224764, -122.0842499); echo $address; // "1600 Amphitheatre Parkway, Mountain View, CA..."
Structured Geocoding (Improved Accuracy)
For better accuracy, especially in developing countries, use structured geocoding:
use Abdullmng\Distance\DTOs\StructuredAddress; $address = new StructuredAddress( houseNumber: '24', street: 'Obi Okosi Street', neighbourhood: 'Hill-Side Estate', suburb: 'Gwarimpa', city: 'Abuja', country: 'Nigeria' ); $coordinate = Distance::geocodeStructured($address); if ($coordinate->isHighAccuracy()) { echo "High quality result!\n"; echo "Coordinates: {$coordinate->latitude}, {$coordinate->longitude}\n"; }
Geocoding Quality Scores
All geocoding results include accuracy scores to help you make informed decisions:
$coordinate = Distance::geocode('Gwarimpa, Abuja, Nigeria'); if ($coordinate->isHighAccuracy()) { // accuracy >= 0.8 - Safe to use echo "High quality result\n"; } elseif ($coordinate->isLowAccuracy()) { // accuracy < 0.5 - May be inaccurate echo "Low quality - consider structured geocoding\n"; } else { // 0.5 <= accuracy < 0.8 - Moderate quality echo "Moderate quality\n"; }
Bearing and Direction
// Get bearing (0-360 degrees) $bearing = Distance::bearing('New York, NY', 'Los Angeles, CA'); echo $bearing; // e.g., 245.5 // Get compass direction $direction = Distance::direction('New York, NY', 'Los Angeles, CA'); echo $direction; // e.g., "SW"
Advanced: Vincenty Formula
For more accurate calculations (especially over long distances):
$result = Distance::betweenVincenty('New York, NY', 'Los Angeles, CA');
Working with Results
$result = Distance::between('New York, NY', 'Los Angeles, CA'); // Access properties echo $result->distance; // Distance in configured unit echo $result->unit; // Unit used echo $result->from->latitude; // Starting coordinate echo $result->to->longitude; // Ending coordinate // Convert to different units echo $result->inKilometers(); echo $result->inMiles(); echo $result->inMeters(); echo $result->inFeet(); // Get as array $array = $result->toArray();
Advanced Geocoding Features
Fallback Chain
Automatically try multiple geocoding providers until you get a high-quality result:
GEOCODING_USE_FALLBACK=true GEOCODING_MIN_ACCURACY=0.7
// config/distance.php 'use_fallback_chain' => true, 'fallback_providers' => [ 'nominatim', // Try free provider first 'opencage', // Then OpenCage 'mapbox', // Then Mapbox 'google', // Last resort (most expensive) ], 'minimum_accuracy' => 0.7,
Local Coordinate Cache
Cache frequently used addresses for instant, 100% accurate results:
// config/distance.php 'local_coordinates' => [ 'Main Warehouse Lagos' => [ 'lat' => 6.5244, 'lon' => 3.3792, 'formatted' => 'Main Warehouse, Apapa, Lagos, Nigeria' ], 'Office Abuja' => [ 'lat' => 9.0765, 'lon' => 7.3986, 'formatted' => 'Head Office, Abuja, Nigeria' ], ],
// Checks local cache first (instant, no API calls) $coordinate = Distance::geocode('Main Warehouse Lagos'); echo $coordinate->source; // 'local_cache' echo $coordinate->accuracy; // 1.0 (100%)
For more details, see GEOCODING_ACCURACY.md.
Geocoding Providers
Nominatim (OpenStreetMap) - Free
No API key required. Great for development and low-volume applications.
GEOCODING_PROVIDER=nominatim NOMINATIM_USER_AGENT=YourAppName/1.0
Google Maps - Paid
Highly accurate with extensive coverage.
GEOCODING_PROVIDER=google GOOGLE_MAPS_API_KEY=your-api-key
Mapbox - Freemium
Good balance of accuracy and pricing.
GEOCODING_PROVIDER=mapbox MAPBOX_API_KEY=your-api-key
OpenCage - Freemium
Aggregates multiple data sources.
GEOCODING_PROVIDER=opencage OPENCAGE_API_KEY=your-api-key
Use Cases
Logistics & Delivery Apps
// Calculate delivery distance $warehouse = new Coordinate(40.7128, -74.0060); $customer = '123 Main St, Brooklyn, NY'; $result = Distance::between($warehouse, $customer, 'kilometers'); if ($result->distance > 50) { // Out of delivery range }
Ride-Sharing Apps
// Find distance between driver and passenger $driver = '40.7580,-73.9855'; $passenger = '40.7489,-73.9680'; $result = Distance::between($driver, $passenger, 'miles'); $eta = $result->distance / 30; // Assuming 30 mph average speed
Store Locator
$userLocation = Distance::geocode($userAddress); $stores = Store::all(); foreach ($stores as $store) { $storeCoord = new Coordinate($store->latitude, $store->longitude); $distance = Distance::between($userLocation, $storeCoord, 'miles'); $store->distance = $distance->distance; } $nearestStores = $stores->sortBy('distance')->take(5);
Documentation
- Routing Guide - Comprehensive guide for route-based distance calculations
- Mapbox Geocoding Guide - Mapbox geocoding setup and troubleshooting
- Quick Start Guide - Get started quickly with examples
- Contributing Guide - How to contribute to this package
Testing
composer test
License
MIT License. See LICENSE for more information.
Credits
Support
For issues, questions, or contributions, please visit the GitHub repository.