Skip to content

Route Calculation and Optimization

This guide explains how to use the route calculation and optimization features in the Core package.

Overview

The RouteCalculator service integrates with Google Route Optimization API to solve Vehicle Routing Problems (VRP). It supports:

  • ✅ Multi-vehicle route optimization
  • ✅ Capacity constraints (soft/hard limits)
  • ✅ Cost optimization (distance, duration, penalties)
  • ✅ Both old and new Google API formats
  • ✅ Mock mode for development/testing
  • ✅ Comprehensive error handling

Installation

bash
npm install @route-optimization/core

Quick Start

Basic Usage (Mock Mode)

typescript
import { RouteCalculator } from '@route-optimization/core';

// Create calculator instance (mock mode for testing)
const calculator = new RouteCalculator({
  useMockMode: true,
  debug: true,
});

// Define optimization request
const request = {
  shipments: [
    {
      id: 'shipment-1',
      deliveryLocation: { latitude: 13.7563, longitude: 100.5018 },
    },
    {
      id: 'shipment-2',
      deliveryLocation: { latitude: 13.7467, longitude: 100.5342 },
    },
  ],
  vehicles: [
    {
      id: 'vehicle-1',
      startLocation: { latitude: 13.7563, longitude: 100.5018 },
    },
  ],
};

// Optimize routes
const response = await calculator.optimizeRoutes(request);

if (response.success) {
  console.log('Optimized routes:', response.routes);
  console.log('Metrics:', response.metrics);
  console.log('Statistics:', response.statistics);
}

Production Usage (Google API)

For production use, you need to authenticate with Google Cloud. See the Authentication Guide for detailed setup instructions.

Using API Key (Simpler)

typescript
import { RouteCalculator } from '@route-optimization/core';

const calculator = new RouteCalculator({
  projectId: 'your-project-id',
  apiKey: 'YOUR_API_KEY',
  debug: true,
});

await calculator.initialize();

Using Service Account (More Secure)

typescript
import { RouteCalculator } from '@route-optimization/core';

const calculator = new RouteCalculator({
  projectId: 'your-project-id',
  credentialsPath: './service-account-key.json',
  debug: true,
});

await calculator.initialize();

// Optimize routes
const response = await calculator.optimizeRoutes(request);

Configuration

RouteCalculatorConfig

typescript
interface RouteCalculatorConfig {
  /** Google Cloud project ID */
  projectId?: string;

  /** Path to service account key file */
  credentialsPath?: string;

  /** Service account key JSON object */
  credentials?: object;

  /** Use mock mode (for testing without API calls) */
  useMockMode?: boolean;

  /** Default search mode */
  defaultSearchMode?: 'RETURN_FAST' | 'CONSUME_ALL_AVAILABLE_TIME';

  /** Default timeout (e.g., "30s") */
  defaultTimeout?: string;

  /** Enable debug logging */
  debug?: boolean;
}

Request Format

Shipments

typescript
// Old Format (Backward Compatible)
const shipment = {
  id: 'shipment-1',
  pickupLocation: { latitude: 13.7563, longitude: 100.5018 },
  deliveryLocation: { latitude: 13.7467, longitude: 100.5342 },
  demands: [{ type: 'weight', value: 10 }],
  label: 'Package #1',
};

// New Format (Google API Standard)
const shipment = {
  id: 'shipment-1',
  pickups: [
    {
      location: { latitude: 13.7563, longitude: 100.5018 },
      duration: '300s',
    },
  ],
  deliveries: [
    {
      location: { latitude: 13.7467, longitude: 100.5342 },
      duration: '300s',
    },
  ],
  demands: [{ type: 'weight', value: 10 }],
};

Vehicles

typescript
const vehicle = {
  id: 'vehicle-1',
  startLocation: { latitude: 13.7563, longitude: 100.5018 },
  endLocation: { latitude: 13.7563, longitude: 100.5018 }, // Optional

  // Load limits with soft/hard constraints
  loadLimits: {
    weight: {
      softMaxLoad: 100, // Soft limit (generates penalty)
      hardMaxLoad: 150, // Hard limit (cannot exceed)
      costPerUnitAboveSoftMax: 50, // Penalty cost
    },
  },

  // Cost configuration
  fixedCost: 1000, // Fixed cost per vehicle
  costPerKilometer: 5, // Cost per km
  costPerHour: 50, // Cost per hour

  // Travel mode
  travelMode: 'DRIVING', // DRIVING, WALKING, BICYCLING, TRANSIT

  // Time constraints
  maxRouteDuration: '28800s', // 8 hours max
};

Optimization Options

typescript
const request = {
  shipments: [...],
  vehicles: [...],

  // Global cost per hour
  globalDurationCostPerHour: 10,

  // Search mode
  searchMode: 'RETURN_FAST', // or 'CONSUME_ALL_AVAILABLE_TIME'

  // Timeout
  timeout: '30s',

  // Traffic consideration
  considerTraffic: true,
};

Response Format

typescript
interface OptimizationResponse {
  success: boolean;

  // Optimized routes
  routes?: Array<{
    vehicleId: string;
    shipmentIds: string[];
    stops: number;
    totalDistance?: number; // meters
    totalDuration?: string; // e.g., "3600s"
    cost?: number;
  }>;

  // Overall metrics
  metrics?: {
    totalCost: number;
    totalDistance?: number;
    totalDuration?: string;
    usedVehicles: number;
    skippedShipments: number;
    optimizationDuration?: string;
  };

  // Statistics
  statistics?: {
    averageStopsPerVehicle: number;
    maxStopsInRoute: number;
    minStopsInRoute: number;
    utilizationRate: number;
  };

  // Skipped shipments (if any)
  skippedShipments?: Array<{
    id: string;
    reason?: string;
  }>;

  // Error (if failed)
  error?: string;
}

Examples

Multi-Vehicle Optimization

typescript
const request = {
  shipments: Array.from({ length: 100 }, (_, i) => ({
    id: `shipment-${i + 1}`,
    deliveryLocation: {
      latitude: 13.75 + Math.random() * 0.1,
      longitude: 100.5 + Math.random() * 0.1,
    },
    demands: [{ type: 'stops', value: 1 }],
  })),

  vehicles: Array.from({ length: 20 }, (_, i) => ({
    id: `vehicle-${i + 1}`,
    startLocation: { latitude: 13.7563, longitude: 100.5018 },
    loadLimits: {
      stops: {
        softMaxLoad: 5,
        hardMaxLoad: 10,
        costPerUnitAboveSoftMax: 500,
      },
    },
  })),

  globalDurationCostPerHour: 10,
};

const response = await calculator.optimizeRoutes(request);

console.log(`Total cost: ${response.metrics?.totalCost}`);
console.log(`Used vehicles: ${response.metrics?.usedVehicles} / 20`);
console.log(`Average stops per vehicle: ${response.statistics?.averageStopsPerVehicle}`);

With Pickup and Delivery

typescript
const request = {
  shipments: [
    {
      id: 'shipment-1',
      pickups: [
        {
          location: { latitude: 13.7563, longitude: 100.5018 },
          duration: '300s',
          timeWindows: [
            {
              startTime: '2024-01-01T08:00:00Z',
              endTime: '2024-01-01T10:00:00Z',
            },
          ],
        },
      ],
      deliveries: [
        {
          location: { latitude: 13.7467, longitude: 100.5342 },
          duration: '300s',
          timeWindows: [
            {
              startTime: '2024-01-01T14:00:00Z',
              endTime: '2024-01-01T16:00:00Z',
            },
          ],
        },
      ],
    },
  ],
  vehicles: [
    {
      id: 'vehicle-1',
      startLocation: { latitude: 13.7563, longitude: 100.5018 },
    },
  ],
};

Custom Search Mode

typescript
// Fast optimization (good for quick results)
const fastRequest = {
  ...request,
  searchMode: 'RETURN_FAST',
  timeout: '10s',
};

// Thorough optimization (better quality)
const thoroughRequest = {
  ...request,
  searchMode: 'CONSUME_ALL_AVAILABLE_TIME',
  timeout: '120s',
};

Error Handling

typescript
const response = await calculator.optimizeRoutes(request);

if (!response.success) {
  console.error('Optimization failed:', response.error);

  // Check specific error types
  if (response.error?.includes('not enabled')) {
    console.log('Enable Route Optimization API in Google Cloud Console');
  } else if (response.error?.includes('billing')) {
    console.log('Enable billing or use mock mode');
  } else if (response.error?.includes('UNAUTHENTICATED')) {
    console.log('Check your service account credentials');
  }
}

Mock Mode vs Production

Mock Mode (Development)

Pros:

  • No Google Cloud account needed
  • No API costs
  • Fast testing
  • Predictable results

Cons:

  • Simple round-robin distribution
  • Not optimized routes
  • No actual distance/duration calculation

Production Mode (Google API)

Pros:

  • Real optimization algorithms
  • Accurate distance/duration
  • Handles complex constraints
  • Traffic consideration

Cons:

  • Requires Google Cloud setup
  • API usage costs
  • Network dependency

Google Cloud Setup

  1. Create Google Cloud Project

    https://console.cloud.google.com
  2. Enable Route Optimization API

    https://console.cloud.google.com/apis/library/cloudoptimization.googleapis.com
  3. Create Service Account

    • Go to IAM & Admin > Service Accounts
    • Create new service account
    • Grant "Cloud Optimization AI Admin" role
    • Create JSON key
  4. Enable Billing

    • Required for API usage
    • Set up billing account
    • Monitor usage in Cloud Console

Best Practices

1. Use Mock Mode for Development

typescript
const isDevelopment = process.env.NODE_ENV === 'development';

const calculator = new RouteCalculator({
  useMockMode: isDevelopment,
  projectId: process.env.GOOGLE_CLOUD_PROJECT_ID,
  credentialsPath: process.env.GOOGLE_APPLICATION_CREDENTIALS,
});

2. Handle Errors Gracefully

typescript
try {
  const response = await calculator.optimizeRoutes(request);

  if (response.success) {
    // Use optimized routes
  } else {
    // Fall back to simple routing
    console.warn('Optimization failed, using fallback');
  }
} catch (error) {
  console.error('Unexpected error:', error);
}

3. Monitor Skipped Shipments

typescript
if (response.skippedShipments && response.skippedShipments.length > 0) {
  console.warn(`${response.skippedShipments.length} shipments could not be assigned`);
  response.skippedShipments.forEach((s) => {
    console.log(`- ${s.id}: ${s.reason}`);
  });
}

4. Update Configuration Dynamically

typescript
// Start with mock mode
calculator.updateConfig({ useMockMode: true });

// Switch to production when ready
calculator.updateConfig({
  useMockMode: false,
  projectId: 'production-project',
});

// Re-initialize for new config
await calculator.initialize();

Performance Tips

  1. Batch Requests: Optimize multiple routes in single request
  2. Use Fast Mode: For real-time scenarios, use RETURN_FAST
  3. Set Timeouts: Prevent long-running optimizations
  4. Cache Results: Store optimization results when possible
  5. Monitor Costs: Track API usage in Google Cloud Console

Next Steps

Released under the MIT License.