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

v1.1.1 2026-01-11 11:59 UTC

This package is auto-updated.

Last update: 2026-01-11 12:07:33 UTC


README

Latest Version on Packagist GitHub Tests Action Status Total Downloads

A Laravel wrapper for the Google Routes API with support for route calculation, distance matrices, and waypoint optimization.

Table of Contents

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 meters
  • duration - Duration string (e.g., "1234s")
  • condition - Route condition (ROUTE_EXISTS, ROUTE_NOT_FOUND)

Helper methods:

  • routeExists() - Check if a route was found
  • getDurationInSeconds() - Get duration as integer
  • getDistanceInKilometers() - Get distance in km
  • getDistanceInMiles() - Get distance in miles
  • getFormattedDuration() - 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 objects
  • fallbackInfo - Information about routing fallback (if any)
  • geocodingResults - Geocoding information for address waypoints

Route

Individual route containing:

  • distanceMeters - Total distance in meters
  • duration - Duration string (e.g., "165s")
  • polyline - Encoded polyline or GeoJSON
  • legs - Collection of RouteLeg objects
  • viewport - Map bounding box
  • routeLabels - Route type labels
  • warnings - Route warnings

Helper methods:

  • getDurationInSeconds() - Get duration as integer
  • getDistanceInKilometers() - Get distance in km
  • getDistanceInMiles() - Get distance in miles
  • getFormattedDuration() - Get human-readable duration
  • isDefaultRoute() - Check if default route
  • isFuelEfficient() - 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.