fix(elevation): Continuous smooth IDW DEM model for realistic gradients without jumps
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user