11 KiB
11 KiB
Route Planning Feature with Optimization
This feature allows you to plan routes between multiple project locations using OpenRouteService, with automatic optimization to find the fastest route regardless of point addition order.
Setup
-
Get an API Key:
- Visit OpenRouteService
- Sign up for a free account
- Generate an API key
-
Configure Environment:
- Copy
.env.exampleto.env.local - Add your API key:
NEXT_PUBLIC_ORS_API_KEY=your_actual_api_key
- Copy
-
Install Dependencies:
npm install @mapbox/polyline -
Restart Development Server:
npm run dev
How to Use
Basic Routing (2 Points)
- Select Route Tool: Click the route icon in the tool panel (looks like a path)
- Add Projects: Click on project markers to add them to your route
- Calculate Route: Click "Calculate Route" to get directions
- View Results: See distance, duration, and route path on the map
Optimized Routing (3+ Points)
- Select Route Tool: Click the route icon in the tool panel
- Add Projects: Click on project markers (order doesn't matter)
- Find Optimal Route: Click "Find Optimal Route" - system automatically finds fastest path
- View Optimization Results: See which route order was selected and performance stats
Features
Core Features
- Multi-point routing: Plan routes through multiple project locations
- Visual route display: Blue dashed line shows the calculated route
- Route markers: Green start marker, red end marker
- Route information: Distance and estimated travel time
- Interactive management: Add/remove projects from route
- Map auto-fit: Automatically adjusts map view to show entire route
Optimization Features ✨
- Hybrid Optimization: Uses ORS Optimization API first, falls back to permutation testing
- Smart Fallback: Automatically switches to proven permutation method if ORS fails
- Order Detection: Clearly shows when route order was actually optimized vs unchanged
- Large Point Support: Can handle up to 50+ points with ORS API
- Performance Monitoring: Detailed logging of optimization approach and results
- Real-time Progress: Shows "Finding Optimal Route..." during calculation
Technical Implementation
Core Functions
calculateRoute()
Main function that handles both basic and optimized routing with hybrid approach:
const calculateRoute = async () => {
// For 2 points: direct calculation
if (coordinates.length === 2) {
const routeData = await calculateRouteForCoordinates(coordinates);
setRouteData({...routeData, optimized: false});
return;
}
// For 3+ points: try ORS Optimization API first
let optimizationRequest = {
jobs: coordinates.map((coord, index) => ({
id: index,
location: coord,
service: 0
})),
vehicles: [{
id: 0,
profile: 'driving-car',
// No fixed start/end for true optimization
capacity: [coordinates.length]
}],
options: { g: true }
};
try {
const optimizationResponse = await fetch('https://api.openrouteservice.org/optimization', {
method: 'POST',
headers: {
'Authorization': process.env.NEXT_PUBLIC_ORS_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(optimizationRequest)
});
const optimizationData = await optimizationResponse.json();
// Extract optimized order from ORS response
const optimizedCoordinates = extractOptimizedOrder(optimizationData, coordinates);
// Check if order actually changed
const orderChanged = detectOrderChange(coordinates, optimizedCoordinates);
if (orderChanged) {
// Use optimized order
const routeData = await calculateRouteForCoordinates(optimizedCoordinates);
setRouteData({...routeData, optimized: true, optimizationStats: {
method: 'ORS_Optimization_API',
totalJobs: coordinates.length,
duration: optimizationData.routes[0].duration,
distance: optimizationData.routes[0].distance
}});
} else {
// Fallback to permutation testing
console.log('ORS optimization did not change order, trying permutations...');
const bestRoute = await findOptimalRouteByPermutations(coordinates);
const routeData = await calculateRouteForCoordinates(bestRoute);
setRouteData({...routeData, optimized: true, optimizationStats: {
method: 'Permutation_Testing',
totalJobs: coordinates.length,
duration: routeData.summary.total_duration,
distance: routeData.summary.total_distance
}});
}
} catch (error) {
// Complete fallback to permutations
console.log('ORS optimization failed, using permutation fallback...');
const bestRoute = await findOptimalRouteByPermutations(coordinates);
const routeData = await calculateRouteForCoordinates(bestRoute);
setRouteData({...routeData, optimized: true, optimizationStats: {
method: 'Permutation_Testing',
totalJobs: coordinates.length,
duration: routeData.summary.total_duration,
distance: routeData.summary.total_distance
}});
}
};
calculateRouteForCoordinates(coordinates)
Handles individual OpenRouteService Directions API calls:
const calculateRouteForCoordinates = async (coordinates) => {
const requestBody = {
coordinates: coordinates,
format: 'geojson',
instructions: true,
geometry_simplify: false,
continue_straight: false,
roundabout_exits: true,
attributes: ['avgspeed', 'percentage']
};
const response = await fetch('https://api.openrouteservice.org/v2/directions/driving-car', {
method: 'POST',
headers: {
'Authorization': process.env.NEXT_PUBLIC_ORS_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
});
return await response.json();
};
UI Components
Dynamic Button Text
{routeProjects.length > 2 ? 'Find Optimal Route' : 'Calculate Route'}
Optimization Status Display
{routeData.optimized && (
<div className="mb-2 p-2 bg-green-50 border border-green-200 rounded">
<div className="flex items-center gap-1 font-medium">
✅ Route Optimized
</div>
<div className="mt-1">
Tested {routeData.optimizationStats.totalPermutations} routes
</div>
</div>
)}
Performance Considerations
Optimization Limits
- Maximum Points: Limited to 50 points (ORS can handle 100+ in some cases)
- Algorithm: Advanced TSP solver instead of brute-force permutations
- API Calls: Only 2 API calls (1 optimization + 1 detailed route)
- Processing Time: ~1-2 seconds for 50 points (much faster than permutation testing)
Memory Usage
- Each route response contains detailed geometry data
- Large numbers of points can consume significant memory
- Automatic cleanup of unused route data
API Integration
OpenRouteService Optimization API
{
jobs: [
{ id: 0, location: [lng, lat], service: 0 },
{ id: 1, location: [lng, lat], service: 0 }
],
vehicles: [{
id: 0,
profile: 'driving-car',
start: [lng, lat],
end: [lng, lat],
capacity: [point_count]
}],
options: { g: true }
}
Directions API Parameters
{
coordinates: [[lng, lat], [lng, lat], ...],
format: 'geojson',
instructions: true,
geometry_simplify: false,
continue_straight: false,
roundabout_exits: true,
attributes: ['avgspeed', 'percentage']
}
Response Handling
- Optimization API:
data.routes[0].steps[]for optimized order - Directions API:
data.routes[0].summaryfor route details - Fallback Path:
data.features[0].properties.segments[0] - Geometry: Supports both encoded polylines and direct coordinates
- Error Handling: Graceful fallback for failed calculations
Troubleshooting
Common Issues
"Failed to calculate route"
- Cause: Invalid API key or network issues
- Solution: Verify
NEXT_PUBLIC_ORS_API_KEYin.env.local
"Too many points for optimization"
- Cause: Selected more than 50 points
- Solution: Reduce to 50 or fewer points, or use manual routing
Optimization taking too long
- Cause: Large number of points or slow API responses
- Solution: Reduce points or wait for completion (much faster than before)
Optimization API unavailable
- Cause: ORS Optimization API temporarily unavailable
- Solution: Falls back to direct routing without optimization
Route order not optimized
- Cause: ORS Optimization API returned same order or failed
- Solution: System automatically falls back to permutation testing for guaranteed optimization
Optimization shows "Order unchanged"
- Cause: Points may already be in optimal order, or API returned original sequence
- Solution: Check browser console for detailed optimization logs
Permutation fallback activated
- Cause: ORS API unavailable or returned suboptimal results
- Solution: This is normal behavior - permutation testing ensures optimization
Debug Information
Check browser console for detailed logs:
- Coordinate parsing details
- API request/response structures
- Optimization approach used (ORS API vs permutation fallback)
- Order change detection (whether optimization actually improved the route)
- Performance timing information
- Original vs optimized coordinate sequences
File Structure
src/app/projects/map/page.js # Main map page with routing logic
src/components/ui/LeafletMap.js # Map component with route rendering
src/components/ui/mapLayers.js # Map layer configurations
Dependencies
@mapbox/polyline: For decoding route geometryleaflet: Map rendering libraryreact-leaflet: React integration for Leaflet- OpenRouteService API key (free tier available)
Future Enhancements
- Advanced Vehicle Constraints: Multiple vehicles, capacity limits, time windows
- Route Preferences: Allow users to prioritize distance vs time vs fuel efficiency
- Real-time Traffic: Integration with live traffic data
- Route History: Save and compare previously optimized routes
- Mobile Optimization: Optimize routes considering current location
- Multi-stop Services: Add service times at each location