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 stoplocation: Geographic coordinates (latitude and longitude)
Optional Properties
name: Human-readable name (e.g., "Central Plaza")address: Full street addressduration: Service time at the stop in minutesmetadata: 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
- Learn about Route Calculation
- Explore Map Adapters
- Check out Framework Integration Guides