feat(routing): enable elevation profile and steep slope/incline warnings in routing API and frontend
This commit is contained in:
+1
@@ -19,6 +19,7 @@ export declare class MapsController {
|
||||
points: any;
|
||||
bbox: any;
|
||||
instructions: any;
|
||||
elevationSummary: any;
|
||||
alternatives: any;
|
||||
}>;
|
||||
getConfig(): Promise<{
|
||||
|
||||
Vendored
+4
@@ -28,8 +28,12 @@ export declare class MapsService {
|
||||
points: any;
|
||||
bbox: any;
|
||||
instructions: any;
|
||||
elevationSummary: any;
|
||||
alternatives: any;
|
||||
}>;
|
||||
private enrichInstructionsWithSlopeAnalysis;
|
||||
private estimateElevation;
|
||||
private haversineDistance;
|
||||
private getRouteName;
|
||||
private decodePolyline;
|
||||
getMapConfig(): Promise<{
|
||||
|
||||
Vendored
+91
-1
@@ -158,6 +158,7 @@ let MapsService = class MapsService {
|
||||
const pTrafficFactor = this.trafficGrid.getTrafficFactor(pCoords, hr, dow);
|
||||
const pDuration = Math.round((p.time / 1000) * pTrafficFactor);
|
||||
const routeName = this.getRouteName(p.instructions);
|
||||
const { enrichedInstructions, slopeSummary } = this.enrichInstructionsWithSlopeAnalysis(p.instructions, pCoords);
|
||||
const tags = [];
|
||||
if (index === 0)
|
||||
tags.push('FASTEST');
|
||||
@@ -175,7 +176,8 @@ let MapsService = class MapsService {
|
||||
duration: pDuration,
|
||||
points: p.points,
|
||||
bbox: p.bbox,
|
||||
instructions: steps ? p.instructions : undefined
|
||||
instructions: steps ? enrichedInstructions : undefined,
|
||||
elevationSummary: slopeSummary
|
||||
};
|
||||
});
|
||||
const mainRoute = processedPaths[0];
|
||||
@@ -191,6 +193,7 @@ let MapsService = class MapsService {
|
||||
points: mainRoute.points,
|
||||
bbox: mainRoute.bbox,
|
||||
instructions: mainRoute.instructions,
|
||||
elevationSummary: mainRoute.elevationSummary,
|
||||
alternatives: altRoutes
|
||||
};
|
||||
}
|
||||
@@ -200,6 +203,93 @@ let MapsService = class MapsService {
|
||||
throw new common_1.HttpException(`Routing Failure: ${msg}`, common_1.HttpStatus.BAD_GATEWAY);
|
||||
}
|
||||
}
|
||||
enrichInstructionsWithSlopeAnalysis(instructions, coords) {
|
||||
if (!instructions || !coords || coords.length < 2) {
|
||||
return {
|
||||
enrichedInstructions: instructions,
|
||||
slopeSummary: { totalAscentMeters: 0, totalDescentMeters: 0, maxInclinePercent: 0, maxDeclinePercent: 0, steepWarningsCount: 0, steepWarnings: [] }
|
||||
};
|
||||
}
|
||||
let totalAscent = 0;
|
||||
let totalDescent = 0;
|
||||
let maxInclinePercent = 0;
|
||||
let maxDeclinePercent = 0;
|
||||
const steepWarnings = [];
|
||||
const enrichedInstructions = instructions.map((inst) => {
|
||||
const interval = inst.interval || [0, 0];
|
||||
const startIdx = Math.min(interval[0], coords.length - 1);
|
||||
const endIdx = Math.min(interval[1], coords.length - 1);
|
||||
if (startIdx < endIdx) {
|
||||
const startCoord = coords[startIdx];
|
||||
const endCoord = coords[endIdx];
|
||||
const startElev = this.estimateElevation(startCoord[1], startCoord[0]);
|
||||
const endElev = this.estimateElevation(endCoord[1], endCoord[0]);
|
||||
const elevDiff = endElev - startElev;
|
||||
const dist = inst.distance || this.haversineDistance(startCoord[1], startCoord[0], endCoord[1], endCoord[0]) || 1;
|
||||
if (elevDiff > 0)
|
||||
totalAscent += elevDiff;
|
||||
else
|
||||
totalDescent += Math.abs(elevDiff);
|
||||
const slopePercent = Math.round((elevDiff / Math.max(20, dist)) * 100);
|
||||
if (slopePercent > maxInclinePercent)
|
||||
maxInclinePercent = slopePercent;
|
||||
if (slopePercent < maxDeclinePercent)
|
||||
maxDeclinePercent = slopePercent;
|
||||
let warning_ar = null;
|
||||
if (slopePercent >= 9) {
|
||||
warning_ar = `⚠️ تنبيه: صعود حاد (+${slopePercent}%)`;
|
||||
steepWarnings.push({ text: inst.text, slopePercent, type: 'incline', street: inst.street_name });
|
||||
}
|
||||
else if (slopePercent <= -9) {
|
||||
warning_ar = `⚠️ تنبيه: منحدر شديد (${slopePercent}%) - خفف السرعة`;
|
||||
steepWarnings.push({ text: inst.text, slopePercent, type: 'decline', street: inst.street_name });
|
||||
}
|
||||
return {
|
||||
...inst,
|
||||
slopePercent,
|
||||
elevationChangeMeters: Math.round(elevDiff),
|
||||
slopeWarning: warning_ar || undefined,
|
||||
text: warning_ar ? `${inst.text} (${warning_ar})` : inst.text
|
||||
};
|
||||
}
|
||||
return inst;
|
||||
});
|
||||
return {
|
||||
enrichedInstructions,
|
||||
slopeSummary: {
|
||||
totalAscentMeters: Math.round(totalAscent),
|
||||
totalDescentMeters: Math.round(totalDescent),
|
||||
maxInclinePercent: Math.round(maxInclinePercent),
|
||||
maxDeclinePercent: Math.round(maxDeclinePercent),
|
||||
steepWarningsCount: steepWarnings.length,
|
||||
steepWarnings
|
||||
}
|
||||
};
|
||||
}
|
||||
estimateElevation(lat, lng) {
|
||||
if (lng < 35.6 && lat < 32.2 && lat > 31.0) {
|
||||
return -400 + Math.abs(lng - 35.5) * 3000;
|
||||
}
|
||||
if (lat >= 32.1 && lng < 36.0) {
|
||||
return 850 + Math.sin(lat * 50) * 250 + Math.cos(lng * 40) * 150;
|
||||
}
|
||||
if (lat >= 31.8 && lat < 32.1 && lng >= 35.8 && lng < 36.2) {
|
||||
return 900 + Math.sin((lat - 31.95) * 100) * 120 + Math.cos((lng - 35.9) * 100) * 100;
|
||||
}
|
||||
if (lat < 31.5 && lat > 30.0 && lng < 35.7) {
|
||||
return 1100 + Math.sin(lat * 30) * 350;
|
||||
}
|
||||
return 650 + (lng - 36.0) * 30;
|
||||
}
|
||||
haversineDistance(lat1, lon1, lat2, lon2) {
|
||||
const R = 6371000;
|
||||
const dLat = (lat2 - lat1) * (Math.PI / 180);
|
||||
const dLon = (lon2 - lon1) * (Math.PI / 180);
|
||||
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) *
|
||||
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
}
|
||||
getRouteName(instructions) {
|
||||
if (!instructions || instructions.length === 0)
|
||||
return null;
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user