Add route optimization to improve patrol efficiency
Implement a new API endpoint that uses OSRM and the Nearest Neighbor algorithm to optimize delivery routes, calculating distances, durations, and providing an ordered list of stops. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e0b5b11c-5b75-4389-8ea9-5f3cd9332f88 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/e0b5b11c-5b75-4389-8ea9-5f3cd9332f88/N0pfy8w
This commit is contained in:
parent
efa056dd98
commit
5c22ec14f1
109
server/routes.ts
109
server/routes.ts
@ -4412,6 +4412,115 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
}
|
||||
});
|
||||
|
||||
// ============= ROUTE OPTIMIZATION API (OSRM + TSP) =============
|
||||
|
||||
app.post("/api/optimize-route", isAuthenticated, async (req: any, res) => {
|
||||
try {
|
||||
const { coordinates } = req.body;
|
||||
|
||||
// Validazione: array di coordinate [{lat, lon, id}]
|
||||
if (!Array.isArray(coordinates) || coordinates.length < 2) {
|
||||
return res.status(400).json({
|
||||
message: "Almeno 2 coordinate richieste per l'ottimizzazione"
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica formato coordinate
|
||||
for (const coord of coordinates) {
|
||||
if (!coord.lat || !coord.lon || !coord.id) {
|
||||
return res.status(400).json({
|
||||
message: "Ogni coordinata deve avere lat, lon e id"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// STEP 1: Calcola matrice distanze usando OSRM Table API
|
||||
const coordsString = coordinates.map(c => `${c.lon},${c.lat}`).join(';');
|
||||
const osrmTableUrl = `https://router.project-osrm.org/table/v1/driving/${coordsString}?annotations=distance,duration`;
|
||||
|
||||
const osrmResponse = await fetch(osrmTableUrl);
|
||||
if (!osrmResponse.ok) {
|
||||
throw new Error(`OSRM API error: ${osrmResponse.status}`);
|
||||
}
|
||||
|
||||
const osrmData = await osrmResponse.json();
|
||||
|
||||
if (osrmData.code !== 'Ok' || !osrmData.distances || !osrmData.durations) {
|
||||
throw new Error("OSRM non ha restituito dati validi");
|
||||
}
|
||||
|
||||
const distances = osrmData.distances; // Matrice NxN in metri
|
||||
const durations = osrmData.durations; // Matrice NxN in secondi
|
||||
|
||||
// STEP 2: Applica algoritmo TSP Nearest Neighbor
|
||||
// Inizia dalla prima tappa (indice 0)
|
||||
const n = coordinates.length;
|
||||
const visited = new Set<number>();
|
||||
const route: number[] = [];
|
||||
let current = 0;
|
||||
let totalDistance = 0;
|
||||
let totalDuration = 0;
|
||||
|
||||
visited.add(current);
|
||||
route.push(current);
|
||||
|
||||
// Trova sempre il vicino più vicino non visitato
|
||||
for (let i = 1; i < n; i++) {
|
||||
let nearest = -1;
|
||||
let minDistance = Infinity;
|
||||
|
||||
for (let j = 0; j < n; j++) {
|
||||
if (!visited.has(j) && distances[current][j] < minDistance) {
|
||||
minDistance = distances[current][j];
|
||||
nearest = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearest !== -1) {
|
||||
totalDistance += distances[current][nearest];
|
||||
totalDuration += durations[current][nearest];
|
||||
visited.add(nearest);
|
||||
route.push(nearest);
|
||||
current = nearest;
|
||||
}
|
||||
}
|
||||
|
||||
// Ritorna al punto di partenza (circuito chiuso)
|
||||
totalDistance += distances[current][0];
|
||||
totalDuration += durations[current][0];
|
||||
|
||||
// STEP 3: Prepara risposta
|
||||
const optimizedRoute = route.map(index => ({
|
||||
...coordinates[index],
|
||||
order: route.indexOf(index) + 1,
|
||||
}));
|
||||
|
||||
res.json({
|
||||
optimizedRoute,
|
||||
totalDistanceMeters: Math.round(totalDistance),
|
||||
totalDistanceKm: (totalDistance / 1000).toFixed(2),
|
||||
totalDurationSeconds: Math.round(totalDuration),
|
||||
totalDurationMinutes: Math.round(totalDuration / 60),
|
||||
estimatedTimeFormatted: formatDuration(totalDuration),
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error optimizing route:", error);
|
||||
res.status(500).json({ message: "Errore durante l'ottimizzazione del percorso" });
|
||||
}
|
||||
});
|
||||
|
||||
// Helper per formattare durata in ore e minuti
|
||||
function formatDuration(seconds: number): string {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}h ${minutes}m`;
|
||||
}
|
||||
return `${minutes}m`;
|
||||
}
|
||||
|
||||
const httpServer = createServer(app);
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user