62 lines
2.3 KiB
JavaScript
62 lines
2.3 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.calculateWalkingRoute = calculateWalkingRoute;
|
|
const spatial_1 = require("../../../utils/spatial");
|
|
const WALKING_SPEED_MPS = 5000 / 60; // 5 km/h in meters per minute
|
|
const MINUTES_PER_DOOR = 2;
|
|
/**
|
|
* Nearest-neighbor walking route algorithm.
|
|
* Starts from volunteer GPS position or cut polygon centroid.
|
|
*/
|
|
function calculateWalkingRoute(locations, startLat, startLng, cutGeojson) {
|
|
if (locations.length === 0) {
|
|
return { orderedLocations: [], totalDistanceMeters: 0, estimatedMinutes: 0 };
|
|
}
|
|
// Determine starting point
|
|
let currentLat;
|
|
let currentLng;
|
|
if (startLat !== undefined && startLng !== undefined) {
|
|
currentLat = startLat;
|
|
currentLng = startLng;
|
|
}
|
|
else if (cutGeojson) {
|
|
const polygons = (0, spatial_1.parseGeoJsonPolygon)(cutGeojson);
|
|
const centroid = (0, spatial_1.calculateCentroid)(polygons[0]);
|
|
currentLat = centroid.lat;
|
|
currentLng = centroid.lng;
|
|
}
|
|
else {
|
|
// Use first location as starting point
|
|
currentLat = locations[0].latitude;
|
|
currentLng = locations[0].longitude;
|
|
}
|
|
const remaining = [...locations];
|
|
const ordered = [];
|
|
let totalDistance = 0;
|
|
while (remaining.length > 0) {
|
|
let nearestIdx = 0;
|
|
let nearestDist = Infinity;
|
|
for (let i = 0; i < remaining.length; i++) {
|
|
const loc = remaining[i];
|
|
const dist = (0, spatial_1.haversineDistance)(currentLat, currentLng, loc.latitude, loc.longitude);
|
|
if (dist < nearestDist) {
|
|
nearestDist = dist;
|
|
nearestIdx = i;
|
|
}
|
|
}
|
|
const nearest = remaining.splice(nearestIdx, 1)[0];
|
|
ordered.push(nearest);
|
|
totalDistance += nearestDist;
|
|
currentLat = nearest.latitude;
|
|
currentLng = nearest.longitude;
|
|
}
|
|
const walkingMinutes = totalDistance / WALKING_SPEED_MPS;
|
|
const doorMinutes = ordered.length * MINUTES_PER_DOOR;
|
|
const estimatedMinutes = Math.round(walkingMinutes + doorMinutes);
|
|
return {
|
|
orderedLocations: ordered,
|
|
totalDistanceMeters: Math.round(totalDistance),
|
|
estimatedMinutes,
|
|
};
|
|
}
|
|
//# sourceMappingURL=canvass-route.service.js.map
|