fix(elevation): Continuous smooth IDW DEM model for realistic gradients without jumps

This commit is contained in:
Hamza-Ayed
2026-08-21 02:34:39 +03:00
parent 398daceffb
commit ff97821fd0
+66 -14
View File
@@ -321,12 +321,14 @@ export class MapsService {
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;
const dist = Math.max(30, 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);
// Clamp physical road gradient to realistic engineering limits [-16%, +16%]
const rawSlope = (elevDiff / dist) * 100;
const slopePercent = Math.max(-16, Math.min(16, Math.round(rawSlope)));
if (slopePercent > maxInclinePercent) maxInclinePercent = slopePercent;
if (slopePercent < maxDeclinePercent) maxDeclinePercent = slopePercent;
@@ -596,20 +598,70 @@ export class MapsService {
return text;
}
private static readonly JORDAN_DEM_NODES: { lat: number; lng: number; elev: number }[] = [
// Amman Metropolitan & Balqa
{ lat: 31.9835, lng: 35.8285, elev: 1010 }, // Dabouq
{ lat: 32.0250, lng: 35.8450, elev: 1070 }, // Sweileh
{ lat: 31.9540, lng: 35.9350, elev: 850 }, // Amman Citadel
{ lat: 31.9615, lng: 35.9130, elev: 880 }, // Abdali
{ lat: 31.9580, lng: 35.8680, elev: 990 }, // 6th Circle
{ lat: 32.0390, lng: 35.7280, elev: 790 }, // Salt Center
{ lat: 31.7680, lng: 35.7250, elev: 810 }, // Mount Nebo
{ lat: 31.7160, lng: 35.7930, elev: 760 }, // Madaba
{ lat: 31.6980, lng: 35.9520, elev: 730 }, // Queen Alia Airport / Giza
// Zarqa & Eastern Basin
{ lat: 32.0608, lng: 36.0880, elev: 590 }, // Zarqa City Center
{ lat: 32.0910, lng: 36.0950, elev: 610 }, // Hashemiya / Zarqa North
{ lat: 32.1350, lng: 36.1400, elev: 640 }, // Sukhna / Zarqa Basin
{ lat: 32.3400, lng: 36.2000, elev: 700 }, // Mafraq
{ lat: 32.1800, lng: 36.8000, elev: 620 }, // Azraq
// North (Irbid, Jerash, Ajloun)
{ lat: 32.5560, lng: 35.8500, elev: 590 }, // Irbid Center
{ lat: 32.2720, lng: 35.8900, elev: 610 }, // Jerash
{ lat: 32.3320, lng: 35.7270, elev: 1120 }, // Ajloun Castle
{ lat: 32.5800, lng: 36.0100, elev: 520 }, // Ramtha
// Jordan Valley & Dead Sea Rift (-Elevations)
{ lat: 31.7200, lng: 35.5800, elev: -390 }, // Dead Sea North
{ lat: 31.5000, lng: 35.5000, elev: -430 }, // Dead Sea Central
{ lat: 32.3000, lng: 35.6000, elev: -210 }, // Deir Alla (Jordan Valley)
{ lat: 32.5000, lng: 35.6000, elev: -180 }, // North Shuna
// South (Karak, Tafila, Petra, Maan, Aqaba)
{ lat: 31.1850, lng: 35.7050, elev: 940 }, // Karak Castle
{ lat: 30.8350, lng: 35.6050, elev: 1150 }, // Tafila
{ lat: 30.6000, lng: 35.6100, elev: 1480 }, // Dana Biosphere / Shobak
{ lat: 30.3280, lng: 35.4440, elev: 1180 }, // Petra / Wadi Musa
{ lat: 30.1900, lng: 35.7300, elev: 1070 }, // Maan
{ lat: 29.9800, lng: 35.4800, elev: 1570 }, // Ras En Naqb Pass
{ lat: 29.5300, lng: 35.0050, elev: 25 }, // Aqaba Gulf
{ lat: 29.6300, lng: 35.4300, elev: 950 }, // Wadi Rum
];
/**
* Smooth, continuous topographic elevation estimation using Inverse Distance Weighting (IDW).
*/
private estimateElevation(lat: number, lng: number): number {
if (lng < 35.6 && lat < 32.2 && lat > 31.0) {
return -400 + Math.abs(lng - 35.5) * 3000;
let totalWeight = 0;
let weightedElevSum = 0;
for (const node of MapsService.JORDAN_DEM_NODES) {
const dLat = (lat - node.lat) * 111.32; // km
const dLng = (lng - node.lng) * 94.5; // km
const distSq = dLat * dLat + dLng * dLng;
if (distSq < 0.0001) {
return node.elev;
}
const weight = 1 / Math.pow(distSq + 0.25, 1.5);
totalWeight += weight;
weightedElevSum += node.elev * weight;
}
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;
return totalWeight > 0 ? Math.round(weightedElevSum / totalWeight) : 750;
}
private haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {