Skip to content

Routes and Stops

Learn about the core concepts of routes and stops in the Route Optimization Map library.

Understanding Routes

A route represents a sequence of locations to visit in order. Routes are the fundamental data structure used across all packages.

Route Structure

typescript
interface Route {
  stops: Stop[];
  options?: RouteOptions;
}

Basic Route Example

typescript
const route = {
  stops: [
    {
      id: '1',
      location: { lat: 13.7563, lng: 100.5018 },
      name: 'Start Location',
    },
    {
      id: '2',
      location: { lat: 13.7467, lng: 100.5352 },
      name: 'Middle Stop',
    },
    {
      id: '3',
      location: { lat: 13.7307, lng: 100.5418 },
      name: 'End Location',
    },
  ],
};

Understanding Stops

A stop represents a single location on a route.

Stop Structure

typescript
interface Stop {
  id: string;
  location: LatLng;
  name?: string;
  address?: string;
  duration?: number; // Time spent at stop (minutes)
  metadata?: Record<string, any>;
}

interface LatLng {
  lat: number;
  lng: number;
}

Stop Properties

Required Properties

  • id: Unique identifier for the stop
  • location: Geographic coordinates (latitude and longitude)

Optional Properties

  • name: Human-readable name (e.g., "Central Plaza")
  • address: Full street address
  • duration: Service time at the stop in minutes
  • metadata: Additional custom data (delivery info, customer details, etc.)

Stop Examples

Basic Stop

typescript
const stop = {
  id: 'stop-1',
  location: { lat: 13.7563, lng: 100.5018 },
};

Detailed Stop

typescript
const deliveryStop = {
  id: 'delivery-001',
  location: { lat: 13.7563, lng: 100.5018 },
  name: 'Customer A',
  address: '123 Sukhumvit Road, Bangkok',
  duration: 15, // 15 minutes service time
  metadata: {
    orderId: 'ORD-12345',
    priority: 'high',
    items: ['Package A', 'Package B'],
    contactPhone: '+66-xxx-xxxx',
  },
};

Creating Routes

Simple Sequential Route

typescript
import { RouteMap } from '@route-optimization/vanilla';

const routeMap = new RouteMap({ apiKey: 'YOUR_API_KEY' });

const simpleRoute = {
  stops: [
    { id: '1', location: { lat: 13.7563, lng: 100.5018 }, name: 'Warehouse' },
    { id: '2', location: { lat: 13.7467, lng: 100.5352 }, name: 'Store A' },
    { id: '3', location: { lat: 13.7307, lng: 100.5418 }, name: 'Store B' },
  ],
};

await routeMap.initialize(mapElement);
routeMap.renderRoute(simpleRoute);

Multi-Day Route

typescript
const multiDayRoute = {
  stops: [
    {
      id: 'day1-1',
      location: { lat: 13.7563, lng: 100.5018 },
      name: 'Day 1 - Stop 1',
      metadata: { day: 1, sequence: 1 },
    },
    {
      id: 'day1-2',
      location: { lat: 13.7467, lng: 100.5352 },
      name: 'Day 1 - Stop 2',
      metadata: { day: 1, sequence: 2 },
    },
    {
      id: 'day2-1',
      location: { lat: 13.7307, lng: 100.5418 },
      name: 'Day 2 - Stop 1',
      metadata: { day: 2, sequence: 1 },
    },
  ],
};

Route Options

Customize how routes are displayed on the map.

RouteOptions Interface

typescript
interface RouteOptions {
  polylineOptions?: {
    strokeColor?: string;
    strokeOpacity?: number;
    strokeWeight?: number;
  };
  markerOptions?: {
    showLabels?: boolean;
    labelColor?: string;
    startMarker?: MarkerConfig;
    endMarker?: MarkerConfig;
    stopMarker?: MarkerConfig;
  };
  fitBounds?: boolean;
}

Styling Routes

typescript
const styledRoute = {
  stops: [...],
  options: {
    polylineOptions: {
      strokeColor: '#FF0000',      // Red line
      strokeOpacity: 0.8,
      strokeWeight: 6,
    },
    markerOptions: {
      showLabels: true,
      labelColor: '#FFFFFF',
      startMarker: {
        icon: '/icons/start.png',
        label: 'START',
      },
      endMarker: {
        icon: '/icons/end.png',
        label: 'END',
      },
    },
    fitBounds: true,  // Auto-zoom to show all stops
  },
};

Working with Stops

Adding Stops Dynamically

React

tsx
import { useState } from 'react';
import { RouteMapView } from '@route-optimization/react';

function DynamicRoute() {
  const [stops, setStops] = useState([{ id: '1', location: { lat: 13.7563, lng: 100.5018 } }]);

  const addStop = (location) => {
    const newStop = {
      id: `stop-${stops.length + 1}`,
      location,
      name: `Stop ${stops.length + 1}`,
    };
    setStops([...stops, newStop]);
  };

  return <RouteMapView route={{ stops }} />;
}

Vue

vue
<script setup lang="ts">
import { ref } from 'vue';
import { RouteMapView } from '@route-optimization/vue';

const stops = ref([{ id: '1', location: { lat: 13.7563, lng: 100.5018 } }]);

const addStop = (location) => {
  stops.value.push({
    id: `stop-${stops.value.length + 1}`,
    location,
    name: `Stop ${stops.value.length + 1}`,
  });
};
</script>

Vanilla JS

javascript
import { RouteMap } from '@route-optimization/vanilla';

const routeMap = new RouteMap({ apiKey: 'YOUR_API_KEY' });
let stops = [{ id: '1', location: { lat: 13.7563, lng: 100.5018 } }];

function addStop(location) {
  stops.push({
    id: `stop-${stops.length + 1}`,
    location,
    name: `Stop ${stops.length + 1}`,
  });

  routeMap.renderRoute({ stops });
}

Removing Stops

typescript
// Filter out stop by ID
const updatedStops = stops.filter((stop) => stop.id !== 'stop-2');
routeMap.renderRoute({ stops: updatedStops });

Reordering Stops

typescript
// Move first stop to end
const reorderedStops = [...stops.slice(1), stops[0]];
routeMap.renderRoute({ stops: reorderedStops });

// Sort by custom property
const sortedStops = [...stops].sort((a, b) => a.metadata.priority - b.metadata.priority);

Route Validation

Basic Validation

typescript
function validateRoute(route: Route): boolean {
  // Must have at least 2 stops
  if (!route.stops || route.stops.length < 2) {
    return false;
  }

  // All stops must have valid coordinates
  return route.stops.every((stop) => {
    const { lat, lng } = stop.location;
    return (
      typeof lat === 'number' &&
      typeof lng === 'number' &&
      lat >= -90 &&
      lat <= 90 &&
      lng >= -180 &&
      lng <= 180
    );
  });
}

Comprehensive Validation

typescript
interface ValidationResult {
  valid: boolean;
  errors: string[];
}

function validateRouteComprehensive(route: Route): ValidationResult {
  const errors: string[] = [];

  if (!route.stops || route.stops.length === 0) {
    errors.push('Route must have at least one stop');
  }

  if (route.stops && route.stops.length === 1) {
    errors.push('Route must have at least two stops');
  }

  // Check for duplicate IDs
  const ids = route.stops?.map((s) => s.id) ?? [];
  const uniqueIds = new Set(ids);
  if (ids.length !== uniqueIds.size) {
    errors.push('Stop IDs must be unique');
  }

  // Validate coordinates
  route.stops?.forEach((stop, index) => {
    const { lat, lng } = stop.location;

    if (typeof lat !== 'number' || typeof lng !== 'number') {
      errors.push(`Stop ${index}: Invalid coordinates`);
    }

    if (lat < -90 || lat > 90) {
      errors.push(`Stop ${index}: Latitude out of range (-90 to 90)`);
    }

    if (lng < -180 || lng > 180) {
      errors.push(`Stop ${index}: Longitude out of range (-180 to 180)`);
    }
  });

  return {
    valid: errors.length === 0,
    errors,
  };
}

Route Statistics

Calculate useful metrics for your routes:

typescript
import { calculateDistance, calculateDuration } from '@route-optimization/core';

function getRouteStatistics(route: Route) {
  const stops = route.stops;

  // Total service time
  const totalServiceTime = stops.reduce((sum, stop) => sum + (stop.duration ?? 0), 0);

  // Number of stops
  const stopCount = stops.length;

  // Calculate total distance (if you have optimization data)
  const totalDistance = calculateTotalDistance(stops);

  return {
    stopCount,
    totalServiceTime,
    totalDistance,
  };
}

Best Practices

1. Unique IDs

Always use unique IDs for stops:

typescript
// ✅ Good - UUID or timestamp-based
const stop = {
  id: `stop-${Date.now()}-${Math.random()}`,
  location: { lat: 13.7563, lng: 100.5018 },
};

// ❌ Bad - Non-unique
const stop = {
  id: 'stop-1', // Could conflict if reused
  location: { lat: 13.7563, lng: 100.5018 },
};

2. Coordinate Precision

Use appropriate precision (6 decimal places = ~0.11 meters):

typescript
// ✅ Good - 6 decimal places
location: { lat: 13.756300, lng: 100.501800 }

// ⚠️ Overkill - Too precise
location: { lat: 13.7563001234567, lng: 100.5018001234567 }

3. Metadata Organization

Structure metadata consistently:

typescript
const stop = {
  id: 'delivery-001',
  location: { lat: 13.7563, lng: 100.5018 },
  metadata: {
    // Order details
    order: {
      id: 'ORD-12345',
      total: 1500,
      items: 3,
    },
    // Customer details
    customer: {
      name: 'John Doe',
      phone: '+66-xxx-xxxx',
    },
    // Delivery details
    delivery: {
      priority: 'high',
      timeWindow: { start: '09:00', end: '12:00' },
    },
  },
};

4. Immutability

Treat routes as immutable when updating:

typescript
// ✅ Good - Create new array
const updatedStops = [...stops, newStop];

// ❌ Bad - Mutate original
stops.push(newStop);

Next Steps

Released under the MIT License.