gowelle / laravel-route-matrix
A Laravel wrapper package for Google Routes API (Compute Routes)
Installs: 8
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/gowelle/laravel-route-matrix
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.0
- illuminate/contracts: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^8.0|^9.0|^10.0
- pestphp/pest: ^2.0|^3.0
- pestphp/pest-plugin-laravel: ^2.0|^3.0
This package is auto-updated.
Last update: 2026-01-11 12:07:33 UTC
README
A Laravel wrapper for the Google Routes API with support for route calculation, distance matrices, and waypoint optimization.
Table of Contents
- Features
- Requirements
- Installation
- Quick Start
- Usage
- Route Matrix
- Response Objects
- Configuration
- Exception Handling
- Testing
- License
Features
- ๐ Multiple Travel Modes - Driving, walking, bicycling, two-wheeler, transit
- ๐ฆ Traffic-Aware Routing - Real-time and historical traffic data
- ๐ Flexible Waypoints - Coordinates, Place IDs, or addresses
- ๐ Distance Matrix - Calculate NรM origin-destination pairs
- ๐ฏ Find Closest - Get closest/fastest destination helpers
- โก Fluent API - Elegant, chainable method calls
- ๐งช Fully Tested - 125+ tests with Pest PHP
Requirements
- PHP 8.2+
- Laravel 10.x, 11.x, or 12.x
- Google Cloud API key with Routes API enabled
Installation
Install the package via Composer:
composer require gowelle/laravel-route-matrix
Publish the configuration file:
php artisan vendor:publish --tag=google-routes-config
Add your Google API key to your .env file:
GOOGLE_ROUTES_API_KEY=your-api-key-here
Quick Start
use Gowelle\LaravelRouteMatrix\Facades\GoogleRoutes; // Posta -> Mlimani City Mall $response = GoogleRoutes::from(['lat' => -6.8163, 'lng' => 39.2807]) ->to(['lat' => -6.7724, 'lng' => 39.2083]) ->get(); $route = $response->first(); echo "Distance: {$route->getDistanceInKilometers()} km"; echo "Duration: {$route->getFormattedDuration()}";
Usage
Basic Route Calculation
use Gowelle\LaravelRouteMatrix\Facades\GoogleRoutes; $response = GoogleRoutes::from(['lat' => -6.8163, 'lng' => 39.2807]) ->to(['lat' => -6.7724, 'lng' => 39.2083]) ->get(); // Access the first (recommended) route $route = $response->first(); echo $route->distanceMeters; // 8200 echo $route->duration; // "900s" echo $route->getDurationInSeconds(); // 900
Using Addresses
$response = GoogleRoutes::from('Julius Nyerere International Airport, Dar es Salaam') ->to('Mlimani City Mall, Dar es Salaam') ->get();
Using Place IDs
use Gowelle\LaravelRouteMatrix\ValueObjects\Waypoint; // Example with Place IDs $response = GoogleRoutes::from(Waypoint::fromPlaceId('ChIJ...' /* Posta */)) ->to(Waypoint::fromAddress('Mlimani City Mall, Dar es Salaam')) ->get();
With Intermediate Waypoints
// Posta -> Kariakoo -> Magomeni -> Mlimani City $response = GoogleRoutes::from(['lat' => -6.8163, 'lng' => 39.2807]) ->via(['lat' => -6.8235, 'lng' => 39.2695]) // Kariakoo ->via(['lat' => -6.8059, 'lng' => 39.2536]) // Magomeni ->to(['lat' => -6.7724, 'lng' => 39.2083]) ->get(); // Access individual legs foreach ($route->legs as $leg) { echo "Leg distance: {$leg->distanceMeters}m\n"; }
Travel Modes
use Gowelle\LaravelRouteMatrix\Enums\TravelMode; // Using enum $response = GoogleRoutes::from($origin) ->to($destination) ->travelMode(TravelMode::DRIVE) ->get(); // Using shortcuts $response = GoogleRoutes::from($origin) ->to($destination) ->driving() // or walking(), bicycling(), transit() ->get();
Traffic-Aware Routing
use Gowelle\LaravelRouteMatrix\Enums\RoutingPreference; $response = GoogleRoutes::from($origin) ->to($destination) ->routingPreference(RoutingPreference::TRAFFIC_AWARE_OPTIMAL) ->get(); // Or use the shortcut $response = GoogleRoutes::from($origin) ->to($destination) ->withOptimalTraffic() ->get();
Route Modifiers
$response = GoogleRoutes::from($origin) ->to($destination) ->avoidTolls() ->avoidHighways() ->avoidFerries() ->get();
Alternative Routes
$response = GoogleRoutes::from($origin) ->to($destination) ->withAlternatives() ->get(); // Get the main route $mainRoute = $response->first(); // Get alternative routes $alternatives = $response->getAlternatives(); foreach ($alternatives as $route) { echo "Alternative: {$route->getFormattedDuration()}\n"; }
Fuel-Efficient Routes
$response = GoogleRoutes::from($origin) ->to($destination) ->withFuelEfficientRoute() ->get(); $fuelEfficientRoute = $response->getFuelEfficientRoute();
Waypoint Optimization
$response = GoogleRoutes::from($origin) ->via($waypoint1) ->via($waypoint2) ->via($waypoint3) ->to($destination) ->optimizeWaypointOrder() ->get(); // Get the optimized order $optimizedOrder = $response->first()->optimizedIntermediateWaypointIndex;
Departure Time
use Carbon\Carbon; $response = GoogleRoutes::from($origin) ->to($destination) ->departureTime(Carbon::now()->addHour()) ->get(); // Or depart now $response = GoogleRoutes::from($origin) ->to($destination) ->departNow() ->get();
Extra Computations
$response = GoogleRoutes::from($origin) ->to($destination) ->withTolls() ->withFuelConsumption() ->withTrafficOnPolyline() ->get();
Custom Field Mask
Specify which fields to include in the response:
$response = GoogleRoutes::from($origin) ->to($destination) ->fields([ 'routes.duration', 'routes.distanceMeters', 'routes.polyline.encodedPolyline', 'routes.legs.steps', 'routes.viewport', ]) ->get();
High Quality Polylines
use Gowelle\LaravelRouteMatrix\Enums\PolylineEncoding; $response = GoogleRoutes::from($origin) ->to($destination) ->highQualityPolyline() ->get(); // Or use GeoJSON format $response = GoogleRoutes::from($origin) ->to($destination) ->geoJsonPolyline() ->get();
Localization
use Gowelle\LaravelRouteMatrix\Enums\Units; $response = GoogleRoutes::from($origin) ->to($destination) ->language('es-ES') ->region('ES') ->units(Units::METRIC) // or imperial() ->get();
Complete Example
use Gowelle\LaravelRouteMatrix\Facades\GoogleRoutes; use Gowelle\LaravelRouteMatrix\Enums\TravelMode; use Gowelle\LaravelRouteMatrix\Enums\RoutingPreference; use Carbon\Carbon; // Posta to Mlimani City with waypoints and options $response = GoogleRoutes::from(['lat' => -6.8163, 'lng' => 39.2807]) ->to(['lat' => -6.7724, 'lng' => 39.2083]) ->via(['lat' => -6.8235, 'lng' => 39.2695]) // Kariakoo ->travelMode(TravelMode::DRIVE) ->routingPreference(RoutingPreference::TRAFFIC_AWARE_OPTIMAL) ->avoidTolls() ->departureTime(Carbon::now()->addHour()) ->withAlternatives() ->withFuelEfficientRoute() ->language('en-US') ->metric() ->fields([ 'routes.duration', 'routes.distanceMeters', 'routes.polyline.encodedPolyline', 'routes.legs', 'routes.routeLabels', ]) ->get(); // Process the response $route = $response->first(); echo "Distance: " . $route->getDistanceInKilometers() . " km\n"; echo "Duration: " . $route->getFormattedDuration() . "\n"; echo "Polyline: " . $route->polyline?->encodedPolyline . "\n"; // Check for warnings if (!empty($route->warnings)) { foreach ($route->warnings as $warning) { echo "Warning: {$warning}\n"; } }
Route Matrix (Distance Matrix)
The Route Matrix API allows you to calculate distances and travel times between multiple origins and destinations efficiently.
One Origin to Multiple Destinations
use Gowelle\LaravelRouteMatrix\Facades\GoogleRoutes; $response = GoogleRoutes::matrix() ->addOrigin(['lat' => -6.8163, 'lng' => 39.2807]) // Posta (Your location) ->addDestination(['lat' => -6.8235, 'lng' => 39.2695]) // Kariakoo ->addDestination(['lat' => -6.7724, 'lng' => 39.2083]) // Mlimani City ->addDestination(['lat' => -6.7567, 'lng' => 39.2772]) // Masaki ->driving() ->get(); // Find the closest destination $closest = $response->getClosestDestination(0); echo "Closest: {$closest->getDistanceInKilometers()} km"; // Find the fastest destination $fastest = $response->getFastestDestination(0); echo "Fastest: {$fastest->getFormattedDuration()}";
Multiple Origins to One Destination
// Find which store/warehouse is closest to a customer $response = GoogleRoutes::matrix() ->addOrigin(['lat' => -6.8163, 'lng' => 39.2807]) // Store A (Posta) ->addOrigin(['lat' => -6.8235, 'lng' => 39.2695]) // Store B (Kariakoo) ->addOrigin(['lat' => -6.7724, 'lng' => 39.2083]) // Store C (Mlimani City) ->addDestination(['lat' => -6.7567, 'lng' => 39.2772]) // Customer (Masaki) ->driving() ->get(); $closestStore = $response->getClosestOrigin(0); echo "Ship from store at origin index: {$closestStore->originIndex}";
Many to Many (Full Matrix)
$response = GoogleRoutes::matrix() ->origins([ ['lat' => -6.8163, 'lng' => 39.2807], // Posta ['lat' => -6.8235, 'lng' => 39.2695], // Kariakoo ]) ->destinations([ ['lat' => -6.7724, 'lng' => 39.2083], // Mlimani City ['lat' => -6.7567, 'lng' => 39.2772], // Masaki ]) ->driving() ->withTraffic() ->get(); // Access specific element (origin 0 โ destination 1) $element = $response->get(0, 1); echo "Distance: {$element->getDistanceInKilometers()} km"; echo "Duration: {$element->getFormattedDuration()}"; // Convert to 2D matrix format $matrix = $response->toMatrix(); // $matrix[originIndex][destinationIndex] = RouteMatrixElement
Sorting Results
// Get all destinations sorted by distance (closest first) $sortedByDistance = $response->sortedByDistance(); // Get all destinations sorted by duration (fastest first) $sortedByDuration = $response->sortedByDuration(); // Get only elements where a route was found $validRoutes = $response->withRoutes();
RouteMatrixElement Properties
Each element in the matrix contains:
originIndex- Index of the origin (0-based)destinationIndex- Index of the destination (0-based)distanceMeters- Distance in metersduration- Duration string (e.g., "1234s")condition- Route condition (ROUTE_EXISTS, ROUTE_NOT_FOUND)
Helper methods:
routeExists()- Check if a route was foundgetDurationInSeconds()- Get duration as integergetDistanceInKilometers()- Get distance in kmgetDistanceInMiles()- Get distance in milesgetFormattedDuration()- Get human-readable duration
Real-World Example: Courier Pickup & Delivery
A courier needs to pick up packages from multiple stores and deliver to customers (some packages share the same destination):
use Gowelle\LaravelRouteMatrix\Facades\GoogleRoutes; // Courier's current location (Dar es Salaam - Posta) $courierLocation = ['lat' => -6.8160, 'lng' => 39.2803]; // Stores to pickup from $stores = [ ['id' => 'store_1', 'name' => 'Kariakoo Market', 'lat' => -6.8235, 'lng' => 39.2695], ['id' => 'store_2', 'name' => 'Mlimani City Mall', 'lat' => -6.7724, 'lng' => 39.2083], ['id' => 'store_3', 'name' => 'Slipway Shopping', 'lat' => -6.7488, 'lng' => 39.2656], ]; // Packages with destinations (some share same customer) $packages = [ ['id' => 'pkg_1', 'store_id' => 'store_1', 'customer' => 'Masaki Customer', 'lat' => -6.7567, 'lng' => 39.2772], ['id' => 'pkg_2', 'store_id' => 'store_2', 'customer' => 'Masaki Customer', 'lat' => -6.7567, 'lng' => 39.2772], ['id' => 'pkg_3', 'store_id' => 'store_2', 'customer' => 'Mikocheni Customer', 'lat' => -6.7651, 'lng' => 39.2451], ['id' => 'pkg_4', 'store_id' => 'store_3', 'customer' => 'Kinondoni Customer', 'lat' => -6.7735, 'lng' => 39.2401], ]; // Get unique destinations (consolidate packages to same location) $uniqueDestinations = collect($packages) ->unique(fn($pkg) => $pkg['lat'] . ',' . $pkg['lng']) ->values() ->all(); // STEP 1: Find optimal pickup order $pickupRoute = GoogleRoutes::from($courierLocation); foreach ($stores as $store) { $pickupRoute->via(['lat' => $store['lat'], 'lng' => $store['lng']]); } $lastStore = end($stores); $response = $pickupRoute ->to(['lat' => $lastStore['lat'], 'lng' => $lastStore['lng']]) ->optimizeWaypointOrder() ->driving() ->withTraffic() ->get(); $optimizedOrder = $response->first()->optimizedIntermediateWaypointIndex ?? []; echo "Pickup order: " . implode(' โ ', array_map(fn($i) => $stores[$i]['name'], $optimizedOrder)); // STEP 2: Calculate delivery matrix from last pickup $deliveryMatrix = GoogleRoutes::matrix() ->addOrigin(['lat' => $lastStore['lat'], 'lng' => $lastStore['lng']]) ->driving() ->withTraffic(); foreach ($uniqueDestinations as $dest) { $deliveryMatrix->addDestination(['lat' => $dest['lat'], 'lng' => $dest['lng']]); } $matrixResponse = $deliveryMatrix->get(); // Find closest delivery from last pickup $firstDelivery = $matrixResponse->getClosestDestination(0); echo "First delivery: {$uniqueDestinations[$firstDelivery->destinationIndex]['customer']}"; echo "ETA: {$firstDelivery->getFormattedDuration()}"; // STEP 3: Build full optimized route (pickups + deliveries) $allStops = array_merge( array_map(fn($s) => ['lat' => $s['lat'], 'lng' => $s['lng'], 'type' => 'pickup', 'name' => $s['name']], $stores), array_map(fn($d) => ['lat' => $d['lat'], 'lng' => $d['lng'], 'type' => 'delivery', 'name' => $d['customer']], $uniqueDestinations) ); $fullRoute = GoogleRoutes::from($courierLocation); foreach ($allStops as $stop) { $fullRoute->via(['lat' => $stop['lat'], 'lng' => $stop['lng']]); } $optimizedResponse = $fullRoute ->to($courierLocation) // Return to base ->optimizeWaypointOrder() ->driving() ->withTraffic() ->get(); $route = $optimizedResponse->first(); echo "Total distance: {$route->getDistanceInKilometers()} km"; echo "Total time: {$route->getFormattedDuration()}"; // Display optimized route $finalOrder = $route->optimizedIntermediateWaypointIndex ?? []; foreach ($finalOrder as $index => $stopIndex) { $stop = $allStops[$stopIndex]; $icon = $stop['type'] === 'pickup' ? '๐ฆ' : '๐'; echo ($index + 1) . ". {$icon} {$stop['name']}"; }
Output:
Pickup order: Kariakoo Market โ Mlimani City Mall โ Slipway Shopping
First delivery: Masaki Customer
ETA: 12 min
Total distance: 32.4 km
Total time: 1h 8m
1. ๐ฆ Kariakoo Market
2. ๐ฆ Mlimani City Mall
3. ๐ Mikocheni Customer
4. ๐ Kinondoni Customer
5. ๐ฆ Slipway Shopping
6. ๐ Masaki Customer (2 packages)
Response Objects
RoutesResponse
The main response object containing:
routes- Collection of Route objectsfallbackInfo- Information about routing fallback (if any)geocodingResults- Geocoding information for address waypoints
Route
Individual route containing:
distanceMeters- Total distance in metersduration- Duration string (e.g., "165s")polyline- Encoded polyline or GeoJSONlegs- Collection of RouteLeg objectsviewport- Map bounding boxrouteLabels- Route type labelswarnings- Route warnings
Helper methods:
getDurationInSeconds()- Get duration as integergetDistanceInKilometers()- Get distance in kmgetDistanceInMiles()- Get distance in milesgetFormattedDuration()- Get human-readable durationisDefaultRoute()- Check if default routeisFuelEfficient()- Check if fuel-efficient route
Configuration
The configuration file (config/google-routes.php) includes:
return [ 'api_key' => env('GOOGLE_ROUTES_API_KEY'), 'base_url' => env('GOOGLE_ROUTES_BASE_URL', 'https://routes.googleapis.com'), 'timeout' => env('GOOGLE_ROUTES_TIMEOUT', 30), 'defaults' => [ 'travel_mode' => env('GOOGLE_ROUTES_TRAVEL_MODE', 'DRIVE'), 'language_code' => env('GOOGLE_ROUTES_LANGUAGE', 'en-US'), 'units' => env('GOOGLE_ROUTES_UNITS', 'METRIC'), 'routing_preference' => env('GOOGLE_ROUTES_ROUTING_PREFERENCE', 'TRAFFIC_AWARE'), ], 'default_field_mask' => [ 'routes.duration', 'routes.distanceMeters', 'routes.polyline.encodedPolyline', ], ];
Exception Handling
The package throws specific exceptions:
use Gowelle\LaravelRouteMatrix\Exceptions\GoogleRoutesException; use Gowelle\LaravelRouteMatrix\Exceptions\InvalidApiKeyException; use Gowelle\LaravelRouteMatrix\Exceptions\InvalidRequestException; use Gowelle\LaravelRouteMatrix\Exceptions\NoRouteFoundException; try { $response = GoogleRoutes::from($origin) ->to($destination) ->get(); } catch (InvalidApiKeyException $e) { // API key is missing or invalid } catch (InvalidRequestException $e) { // Request parameters are invalid } catch (NoRouteFoundException $e) { // No route could be found } catch (GoogleRoutesException $e) { // Other API errors }
Testing
Run the test suite:
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security
If you discover any security-related issues, please email gowelle.john@icloud.com instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.