Vanilla JavaScript Integration Guide
Learn how to integrate route optimization and mapping into vanilla JavaScript applications using @route-optimization/vanilla.
Installation
bash
npm install @route-optimization/vanilla @route-optimization/core
# or
pnpm add @route-optimization/vanilla @route-optimization/core
# or
yarn add @route-optimization/vanilla @route-optimization/coreCore Concepts
The Vanilla package provides classes with no framework dependencies:
RouteMap- Main class for map lifecycle and route managementMapRenderer- Utility class for rendering and customizationRouteOptimizer- Class for route calculation and optimization
Basic Map Rendering
Simple Route Map
javascript
import { RouteMap } from '@route-optimization/vanilla';
// Get map container
const mapElement = document.getElementById('map');
// Create route map instance
const routeMap = new RouteMap({
apiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
center: { lat: 13.7563, lng: 100.5018 }, // Bangkok
zoom: 12,
});
// Initialize map
routeMap
.initialize(mapElement)
.then(() => {
console.log('Map ready!');
// Render a route
routeMap.renderRoute({
stops: [
{
id: '1',
location: { lat: 13.7563, lng: 100.5018 },
name: 'Start Location',
},
{
id: '2',
location: { lat: 13.7467, lng: 100.5352 },
name: 'End Location',
},
],
});
})
.catch((error) => {
console.error('Map initialization failed:', error);
});With Event Listeners
javascript
const routeMap = new RouteMap({
apiKey: 'YOUR_API_KEY',
onError: (error) => {
console.error('Map error:', error);
showErrorMessage(error.message);
},
});
// Button event listeners
document.getElementById('show-route').addEventListener('click', () => {
if (routeMap.isReady()) {
routeMap.renderRoute(currentRoute);
}
});
document.getElementById('clear-route').addEventListener('click', () => {
routeMap.clearRoute();
});
// Clean up when leaving page
window.addEventListener('beforeunload', () => {
routeMap.destroy();
});Map Controls
Center and Zoom
javascript
const routeMap = new RouteMap({ apiKey: 'YOUR_API_KEY' });
await routeMap.initialize(mapElement);
// Set center
routeMap.setCenter({ lat: 13.7563, lng: 100.5018 });
// Set zoom
routeMap.setZoom(15);
// Get current center
const center = routeMap.getCenter();
console.log('Current center:', center);
// Get current zoom
const zoom = routeMap.getZoom();
console.log('Current zoom:', zoom);Bounds and Fit
javascript
// Fit map to show all stops
const bounds = {
north: 13.8,
south: 13.7,
east: 100.6,
west: 100.4,
};
routeMap.fitBounds(bounds);
// Get current bounds
const currentBounds = routeMap.getBounds();
console.log('Map bounds:', currentBounds);Route Optimization
Basic Optimization
javascript
import { RouteOptimizer } from '@route-optimization/vanilla';
// Create optimizer instance
const optimizer = new RouteOptimizer({
useMockMode: true, // Use mock mode for development
});
// Set up event listeners
optimizer.on('status-change', (status) => {
console.log('Status:', status);
updateStatusUI(status);
});
optimizer.on('success', (response) => {
console.log('Optimization complete:', response);
displayResults(response);
});
optimizer.on('error', (error) => {
console.error('Optimization failed:', error);
showError(error.message);
});
// Initialize
await optimizer.initialize();
// Optimize routes
const request = {
shipments: [
{
id: 'shipment-1',
deliveryLocation: { latitude: 13.7563, longitude: 100.5018 },
},
{
id: 'shipment-2',
deliveryLocation: { latitude: 13.7467, longitude: 100.5352 },
},
],
vehicles: [
{
id: 'vehicle-1',
startLocation: { latitude: 13.7563, longitude: 100.5018 },
},
],
};
const response = await optimizer.optimize(request);
if (response.success) {
console.log('Routes:', response.routes);
}Production Mode with Google Cloud
javascript
const optimizer = new RouteOptimizer({
useMockMode: false,
projectId: 'your-google-cloud-project',
credentials: {
client_email: 'service-account@project.iam.gserviceaccount.com',
private_key: process.env.GOOGLE_PRIVATE_KEY,
},
});Progress Tracking
javascript
// Progress bar element
const progressBar = document.getElementById('progress-bar');
const progressText = document.getElementById('progress-text');
optimizer.on('progress', ({ progress, step }) => {
progressBar.style.width = `${progress}%`;
progressText.textContent = step;
});
optimizer.on('start', () => {
progressBar.style.width = '0%';
progressText.textContent = 'Starting...';
});
// Manual progress updates
async function optimizeWithProgress() {
optimizer.updateProgress(0, 'Initializing...');
// Simulate progress
setTimeout(() => {
optimizer.updateProgress(25, 'Calculating distances...');
}, 500);
const response = await optimizer.optimize(request);
optimizer.updateProgress(75, 'Optimizing routes...');
if (response.success) {
optimizer.updateProgress(100, 'Complete!');
}
}Complete Example
Combining map rendering with route optimization:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Route Optimization</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
display: flex;
height: 100vh;
}
.sidebar {
width: 300px;
padding: 20px;
background: #f5f5f5;
overflow-y: auto;
}
.map-container {
flex: 1;
}
#map {
width: 100%;
height: 100%;
}
.location-item {
padding: 10px;
margin: 5px 0;
background: white;
border-radius: 4px;
}
button {
width: 100%;
padding: 12px;
margin: 10px 0;
background: #4caf50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.progress {
width: 100%;
height: 24px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
margin: 10px 0;
}
.progress-bar {
height: 100%;
background: #4caf50;
transition: width 0.3s ease;
width: 0%;
}
.success {
padding: 10px;
background: #e8f5e9;
color: #2e7d32;
border-radius: 4px;
margin: 10px 0;
display: none;
}
</style>
</head>
<body>
<div class="sidebar">
<h2>Route Optimization</h2>
<div id="locations"></div>
<button id="optimize-btn">Optimize Route</button>
<div class="progress">
<div id="progress-bar" class="progress-bar"></div>
</div>
<p id="progress-text"></p>
<div id="success-msg" class="success">✓ Route optimized successfully!</div>
<div id="results"></div>
</div>
<div class="map-container">
<div id="map"></div>
</div>
<script type="module">
import { RouteMap, RouteOptimizer } from '@route-optimization/vanilla';
// Sample locations
const locations = [
{ lat: 13.7563, lng: 100.5018, name: 'Bangkok' },
{ lat: 13.7467, lng: 100.5352, name: 'Chatuchak' },
{ lat: 13.7307, lng: 100.5418, name: 'Victory Monument' },
];
// Render locations list
const locationsEl = document.getElementById('locations');
locations.forEach((loc) => {
const div = document.createElement('div');
div.className = 'location-item';
div.textContent = loc.name;
locationsEl.appendChild(div);
});
// Initialize map
const mapElement = document.getElementById('map');
const routeMap = new RouteMap({
apiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
center: { lat: 13.7563, lng: 100.5018 },
zoom: 12,
onError: (error) => {
console.error('Map error:', error);
alert('Map error: ' + error.message);
},
});
await routeMap.initialize(mapElement);
// Initialize optimizer
const optimizer = new RouteOptimizer({
useMockMode: true,
});
// Progress tracking
const progressBar = document.getElementById('progress-bar');
const progressText = document.getElementById('progress-text');
const successMsg = document.getElementById('success-msg');
const optimizeBtn = document.getElementById('optimize-btn');
const resultsEl = document.getElementById('results');
optimizer.on('status-change', (status) => {
if (status === 'optimizing') {
optimizeBtn.disabled = true;
successMsg.style.display = 'none';
} else if (status === 'success') {
optimizeBtn.disabled = false;
successMsg.style.display = 'block';
} else {
optimizeBtn.disabled = false;
}
});
optimizer.on('progress', ({ progress, step }) => {
progressBar.style.width = `${progress}%`;
progressText.textContent = step;
});
optimizer.on('success', (response) => {
// Display results
resultsEl.innerHTML = `
<h3>Results</h3>
<p>Routes: ${response.routes?.length ?? 0}</p>
<p>Total Distance: ${response.metrics?.totalDistance ?? 0} km</p>
<p>Total Duration: ${response.metrics?.totalDuration ?? 0} min</p>
`;
// Render on map
if (response.routes?.[0]) {
const route = {
stops:
response.routes[0].stops?.map((stop, i) => ({
id: stop.shipmentId || `stop-${i}`,
location: {
lat: stop.location?.latitude ?? 0,
lng: stop.location?.longitude ?? 0,
},
name: locations[i]?.name ?? `Stop ${i + 1}`,
})) ?? [],
};
routeMap.renderRoute(route);
}
});
optimizer.on('error', (error) => {
alert('Optimization failed: ' + error.message);
progressBar.style.width = '0%';
progressText.textContent = '';
});
// Initialize optimizer
await optimizer.initialize();
// Optimize button handler
optimizeBtn.addEventListener('click', async () => {
const request = {
shipments: locations.map((loc, i) => ({
id: `shipment-${i}`,
deliveryLocation: { latitude: loc.lat, longitude: loc.lng },
})),
vehicles: [
{
id: 'vehicle-1',
startLocation: {
latitude: locations[0].lat,
longitude: locations[0].lng,
},
},
],
};
optimizer.updateProgress(0, 'Starting...');
try {
await optimizer.optimize(request);
} catch (error) {
console.error('Optimization error:', error);
}
});
// Cleanup on page unload
window.addEventListener('beforeunload', () => {
routeMap.destroy();
optimizer.destroy();
});
</script>
</body>
</html>TypeScript Support
The package is written in TypeScript and includes full type definitions:
typescript
import type {
Route,
Stop,
MapConfig,
OptimizationRequest,
OptimizationResponse,
} from '@route-optimization/core';
import type {
RouteMapConfig,
MapRendererConfig,
OptimizationStatus,
RouteOptimizerEvents,
} from '@route-optimization/vanilla';
// Typed map instance
const routeMap: RouteMap = new RouteMap(config);
// Typed optimizer
const optimizer: RouteOptimizer = new RouteOptimizer(config);Event-Driven Architecture
The Vanilla package uses events for communication:
RouteOptimizer Events
javascript
// Status changes
optimizer.on('status-change', (status) => {
// 'idle' | 'initializing' | 'optimizing' | 'success' | 'error'
});
// Progress updates
optimizer.on('progress', ({ progress, step }) => {
// progress: 0-100
// step: string description
});
// Optimization started
optimizer.on('start', () => {
console.log('Optimization started');
});
// Optimization succeeded
optimizer.on('success', (response) => {
console.log('Success:', response);
});
// Optimization failed
optimizer.on('error', (error) => {
console.error('Error:', error);
});
// State reset
optimizer.on('reset', () => {
console.log('State reset');
});
// Remove listener
const handler = (status) => console.log(status);
optimizer.on('status-change', handler);
optimizer.off('status-change', handler);Best Practices
1. Error Handling
Always handle errors properly:
javascript
routeMap
.initialize(mapElement)
.then(() => {
// Map ready
})
.catch((error) => {
console.error('Failed to initialize:', error);
showErrorMessage(error.message);
});2. Cleanup
Clean up resources when done:
javascript
// When changing pages or routes
routeMap.destroy();
optimizer.destroy();
// In Single Page Apps
window.addEventListener('popstate', () => {
routeMap.destroy();
});3. State Management
Check state before operations:
javascript
if (routeMap.isReady()) {
routeMap.renderRoute(route);
}
if (optimizer.isSuccess) {
const data = optimizer.data;
// Use optimization data
}4. Memory Management
Remove event listeners when not needed:
javascript
function setupListeners() {
const handler = (response) => {
// Handle response
};
optimizer.on('success', handler);
// Later, remove listener
return () => {
optimizer.off('success', handler);
};
}
const cleanup = setupListeners();
// When done
cleanup();Module Bundlers
Webpack
javascript
import { RouteMap, RouteOptimizer } from '@route-optimization/vanilla';Vite
javascript
import { RouteMap } from '@route-optimization/vanilla';CDN (Browser)
html
<script type="module">
import { RouteMap } from 'https://cdn.skypack.dev/@route-optimization/vanilla';
</script>Next Steps
- Learn about React Integration
- Explore Route Calculation in depth
- Check out API Reference