feat: Implement comprehensive Contacts Management System with API and UI integration
- Added new `contacts` and `project_contacts` database tables for managing contacts. - Created API endpoints for CRUD operations on contacts and linking them to projects. - Developed UI components including `ContactForm` and `ProjectContactSelector`. - Integrated navigation and translations for Polish language support. - Documented usage, features, and future enhancements for the contacts system. feat: Introduce DOCX Template System for generating documents from templates - Enabled creation and management of DOCX templates with placeholders for project data. - Documented the process for creating templates, uploading, and generating documents. - Included detailed information on available variables and custom data fields. - Implemented troubleshooting guidelines for common issues related to template generation. feat: Add Radicale CardDAV Sync Integration for automatic contact synchronization - Implemented automatic syncing of contacts to a Radicale server on create/update/delete actions. - Documented setup instructions, including environment variable configuration and initial sync script. - Provided troubleshooting steps for common sync issues and error codes. feat: Develop Route Planning Feature with Optimization using OpenRouteService API - Integrated multi-point routing and automatic optimization for project locations. - Documented setup, usage, and technical implementation details for route planning. - Included performance considerations and troubleshooting for common routing issues. chore: Remove unnecessary files and scripts from the codebase - Deleted temporary, debug-related, and test-specific files that are not needed in production. - Reviewed and ensured core application code and essential documentation remain intact.
This commit is contained in:
518
docs/features/ROUTE_PLANNING.md
Normal file
518
docs/features/ROUTE_PLANNING.md
Normal file
@@ -0,0 +1,518 @@
|
||||
# Route Planning Feature with Optimization
|
||||
|
||||
This feature allows you to plan routes between multiple project locations using OpenRouteService API, with automatic optimization to find the fastest route regardless of point addition order.
|
||||
|
||||
## 🌟 Overview
|
||||
|
||||
The route planning system integrates with your project map to help optimize field visits. It supports:
|
||||
- Multi-point routing through project locations
|
||||
- Automatic route optimization for 3+ points
|
||||
- Visual route display on the map
|
||||
- Distance and time estimation
|
||||
- Hybrid optimization approach (API + permutation testing)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Setup
|
||||
|
||||
1. **Get an API Key**:
|
||||
- Visit [OpenRouteService](https://openrouteservice.org/)
|
||||
- Sign up for a free account
|
||||
- Generate an API key
|
||||
|
||||
2. **Configure Environment**:
|
||||
- Copy `.env.example` to `.env.local`
|
||||
- Add your API key: `NEXT_PUBLIC_ORS_API_KEY=your_actual_api_key`
|
||||
|
||||
3. **Install Dependencies**:
|
||||
```bash
|
||||
npm install @mapbox/polyline
|
||||
```
|
||||
|
||||
4. **Restart Development Server**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## How to Use
|
||||
|
||||
### Basic Routing (2 Points)
|
||||
1. **Select Route Tool**: Click the route icon in the tool panel (looks like a path)
|
||||
2. **Add Projects**: Click on project markers to add them to your route
|
||||
3. **Calculate Route**: Click "Calculate Route" to get directions
|
||||
4. **View Results**: See distance, duration, and route path on the map
|
||||
|
||||
### Optimized Routing (3+ Points)
|
||||
1. **Select Route Tool**: Click the route icon in the tool panel
|
||||
2. **Add Projects**: Click on project markers (order doesn't matter)
|
||||
3. **Find Optimal Route**: Click "Find Optimal Route" - system automatically finds fastest path
|
||||
4. **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:
|
||||
|
||||
```javascript
|
||||
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:
|
||||
|
||||
```javascript
|
||||
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
|
||||
```javascript
|
||||
{routeProjects.length > 2 ? 'Find Optimal Route' : 'Calculate Route'}
|
||||
```
|
||||
|
||||
#### Optimization Status Display
|
||||
```javascript
|
||||
{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
|
||||
```javascript
|
||||
{
|
||||
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
|
||||
```javascript
|
||||
{
|
||||
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].summary` for 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_KEY` in `.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
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### 1. "API Key Missing" Error
|
||||
**Symptom**: Route calculation fails with authentication error
|
||||
|
||||
**Solutions**:
|
||||
- Check `.env.local` file has `NEXT_PUBLIC_ORS_API_KEY=your_key`
|
||||
- Verify no extra spaces around the key
|
||||
- Ensure development server was restarted after adding the key
|
||||
- Confirm your OpenRouteService API key is active
|
||||
|
||||
```bash
|
||||
# Verify environment variable
|
||||
echo $env:NEXT_PUBLIC_ORS_API_KEY
|
||||
# Should output your API key
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2. Route Not Displaying on Map
|
||||
**Symptom**: Calculation succeeds but no route visible
|
||||
|
||||
**Solutions**:
|
||||
- Check browser console for coordinate transformation errors
|
||||
- Verify all projects have valid coordinates in database
|
||||
- Confirm map is zoomed to appropriate level
|
||||
- Check if route layer is enabled in layer control
|
||||
|
||||
**Debug**:
|
||||
```javascript
|
||||
// Check route data in browser console
|
||||
console.log('Route GeoJSON:', routeData.geojson);
|
||||
console.log('Route bounds:', routeData.bounds);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3. Optimization Takes Too Long
|
||||
**Symptom**: "Find Optimal Route" hangs or times out for many points
|
||||
|
||||
**Current Limits**:
|
||||
- 8+ points: May take 30+ seconds
|
||||
- 10+ points: Not recommended (factorial growth)
|
||||
|
||||
**Solutions**:
|
||||
- Split route into multiple segments
|
||||
- Use manual point selection for 8+ locations
|
||||
- Consider implementing A* or genetic algorithm for large routes
|
||||
|
||||
**Permutation Growth**:
|
||||
```
|
||||
3 points = 6 routes to test
|
||||
4 points = 24 routes
|
||||
5 points = 120 routes
|
||||
6 points = 720 routes
|
||||
7 points = 5,040 routes
|
||||
8 points = 40,320 routes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 4. API Rate Limit Exceeded
|
||||
**Symptom**: Error 429 or "Too many requests"
|
||||
|
||||
**Solutions**:
|
||||
- OpenRouteService free tier: 40 requests/minute, 500/day
|
||||
- Wait 1 minute and try again
|
||||
- Consider upgrading to paid plan for higher limits
|
||||
- Implement request queuing with delays
|
||||
|
||||
```javascript
|
||||
// Add rate limiting check
|
||||
if (routeProjects.length > 5) {
|
||||
alert('Large route may hit rate limits. Consider breaking into segments.');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 5. Incorrect Route Order
|
||||
**Symptom**: Optimization doesn't select expected fastest route
|
||||
|
||||
**Causes**:
|
||||
- Road network topology (one-way streets, traffic restrictions)
|
||||
- API routing preferences (avoid highways, ferries)
|
||||
- Distance vs time optimization trade-offs
|
||||
|
||||
**Verification**:
|
||||
```javascript
|
||||
// Check all tested routes in console
|
||||
routeData.optimizationStats.testedRoutes.forEach(route => {
|
||||
console.log(`Route ${route.order}: ${route.distance}m in ${route.duration}s`);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6. Map Coordinate Transformation Errors
|
||||
**Symptom**: "Failed to transform coordinates" in console
|
||||
|
||||
**Solutions**:
|
||||
- Verify Proj4 definitions are loaded
|
||||
- Check project coordinates are in valid EPSG:2180 format
|
||||
- Confirm transformation libraries are properly initialized
|
||||
|
||||
```javascript
|
||||
// Test coordinate transformation
|
||||
import proj4 from 'proj4';
|
||||
const wgs84 = proj4('EPSG:2180', 'EPSG:4326', [x, y]);
|
||||
console.log('Transformed:', wgs84);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Performance Tips
|
||||
|
||||
1. **Batch Route Calculations**: Group nearby projects before calculating routes
|
||||
2. **Cache Routes**: Store frequently used routes in localStorage
|
||||
3. **Limit Points**: Use max 7 points for real-time optimization
|
||||
4. **Debounce Updates**: Wait for user to finish selecting points
|
||||
5. **Progressive Loading**: Calculate partial routes while building full path
|
||||
|
||||
---
|
||||
|
||||
### API Limitations
|
||||
|
||||
| Tier | Requests/Minute | Requests/Day | Cost |
|
||||
|------|----------------|--------------|------|
|
||||
| Free | 40 | 500 | $0 |
|
||||
| Starter | 300 | 10,000 | Contact ORS |
|
||||
| Business | Custom | Custom | Contact ORS |
|
||||
|
||||
**Best Practices**:
|
||||
- Avoid unnecessary recalculations
|
||||
- Implement client-side caching
|
||||
- Show loading states during API calls
|
||||
- Handle errors gracefully with user feedback
|
||||
|
||||
---
|
||||
|
||||
### Quick Reference
|
||||
|
||||
**Enable Route Planning**:
|
||||
```bash
|
||||
# 1. Get API key from openrouteservice.org
|
||||
# 2. Add to .env.local
|
||||
NEXT_PUBLIC_ORS_API_KEY=your_key_here
|
||||
# 3. Restart dev server
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**Debug Mode**:
|
||||
```javascript
|
||||
// Enable in RoutePanel.js
|
||||
const DEBUG = true;
|
||||
// Logs all tested routes and optimization stats
|
||||
```
|
||||
|
||||
**Performance Monitoring**:
|
||||
```javascript
|
||||
console.time('Route Optimization');
|
||||
await optimizeRoute();
|
||||
console.timeEnd('Route Optimization');
|
||||
// Shows exact optimization duration
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 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 geometry
|
||||
- `leaflet`: Map rendering library
|
||||
- `react-leaflet`: React integration for Leaflet
|
||||
- `proj4`: Coordinate system transformations
|
||||
- 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
|
||||
- **Advanced Optimization**: Implement A* or genetic algorithms for 8+ points
|
||||
- **Multi-Day Routes**: Break long routes into segments with overnight stops
|
||||
- **Export Options**: Export routes to GPS devices or Google Maps
|
||||
- **Cost Estimation**: Calculate fuel costs and travel expenses
|
||||
|
||||
---
|
||||
|
||||
## 📚 Additional Resources
|
||||
|
||||
- [OpenRouteService API Documentation](https://openrouteservice.org/dev/#/api-docs)
|
||||
- [Directions API Reference](https://openrouteservice.org/dev/#/api-docs/v2/directions)
|
||||
- [Polyline Encoding](https://developers.google.com/maps/documentation/utilities/polylinealgorithm)
|
||||
- [Leaflet Routing Integration](https://www.liedman.net/leaflet-routing-machine/)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: January 2025
|
||||
**Maintainer**: Panel Development Team
|
||||
Reference in New Issue
Block a user