chore: add build artifacts, map assets, and routing data

This commit is contained in:
Hamza-Ayed
2026-09-19 12:34:24 +03:00
parent aa2b9f131f
commit 43ad2e0ad4
327 changed files with 52421 additions and 1810 deletions
+164 -135
View File
@@ -1571,45 +1571,43 @@ export async function calculateRealIPBOverlays(
geometry: { type: 'Polygon', coordinates: polyCoords }
});
// 2. Layer 2: Basalt / Rocky Escarpments
if (slopeDeg >= 24 || localDrop > 25) {
// 2. Layer 2: Basalt / Rocky Escarpments & Mountain Crest Cliffs
if (slopeDeg >= 18 || localDrop >= 18) {
layer_2.push({
type: 'Feature',
properties: {
layer: 'layer_2',
color: '#b45309',
strokeColor: '#78350f',
opacity: 0.72,
opacity: 0.78,
drop: localDrop,
label: `■ مقطع صخري وعر (${localDrop}م هبوط)`
label: `■ مقطع صخري وعر (${localDrop}م هبوط / ${slopeDeg}° ميل)`
},
geometry: { type: 'Polygon', coordinates: polyCoords }
});
}
// 6. Layer 6: MCOO - Severely Restricted Red
// 6. Layer 6: MCOO - Severely Restricted Red (Only on steep slopes)
layer_6.push({
type: 'Feature',
properties: {
layer: 'layer_6',
color: '#dc2626',
strokeColor: '#b91c1c',
opacity: 0.65,
label: `MCOO: أرض شديدة الإعاقة (${slopeDeg}°)`
opacity: 0.28
},
geometry: { type: 'Polygon', coordinates: polyCoords }
});
} else if (slopeDeg >= 7) {
} else if (slopeDeg >= 9) {
slowGoCells++;
// 6. Layer 6: MCOO - Restricted Yellow
// 6. Layer 6: MCOO - Restricted Amber (Only on rugged slopes)
layer_6.push({
type: 'Feature',
properties: {
layer: 'layer_6',
color: '#eab308',
strokeColor: '#ca8a04',
opacity: 0.55,
label: `MCOO: أرض مقيدة الحركة (${slopeDeg}°)`
color: '#d97706',
strokeColor: '#b45309',
opacity: 0.20
},
geometry: { type: 'Polygon', coordinates: polyCoords }
});
@@ -1624,18 +1622,7 @@ export async function calculateRealIPBOverlays(
}
} else {
flatCells++;
// 6. Layer 6: MCOO - Unrestricted Green
layer_6.push({
type: 'Feature',
properties: {
layer: 'layer_6',
color: '#16a34a',
strokeColor: '#15803d',
opacity: 0.50,
label: `MCOO: أرض سالكة للدروع (${slopeDeg}°)`
},
geometry: { type: 'Polygon', coordinates: polyCoords }
});
// Unrestricted flat land remains unshaded and clean so the map shows naturally
}
}
@@ -1649,30 +1636,7 @@ export async function calculateRealIPBOverlays(
const restrictedPct = Math.round((slowGoCells / totalCells) * 100);
const severelyRestrictedPct = Math.round((severeCells / totalCells) * 100);
// 3. Layer 3: Urban Built-up Centers & Demographics
// Detect proximity to major settlements or generate realistic urban sector
const urbanRadius = radiusMeters * 0.22;
const urbanLatOffset = (urbanRadius / R_earth) * (180 / Math.PI) * 0.4;
const urbanLngOffset = (urbanRadius / (R_earth * Math.cos((centerLat * Math.PI) / 180))) * (180 / Math.PI) * 0.5;
const urbanPoly = [
[centerLng - urbanLngOffset, centerLat - urbanLatOffset],
[centerLng + urbanLngOffset * 1.2, centerLat - urbanLatOffset * 0.8],
[centerLng + urbanLngOffset * 1.5, centerLat + urbanLatOffset * 0.9],
[centerLng - urbanLngOffset * 0.6, centerLat + urbanLatOffset * 1.1],
[centerLng - urbanLngOffset, centerLat - urbanLatOffset]
];
layer_3.push({
type: 'Feature',
properties: {
layer: 'layer_3',
color: '#7c3aed',
strokeColor: '#5b21b6',
opacity: 0.65,
label: '🏢 تجمع سكاني وعمران (قتال مدن MOUT)'
},
geometry: { type: 'Polygon', coordinates: [urbanPoly] }
});
// 3. Layer 3: Urban Built-up Centers (Rendered directly by OSM/Overture style layers)
// 4. Layer 4: Real Valley Drainage Corridors & Wadis (مجاري السيول والأودية الحقيقية)
if (valleyNodes.length >= 3) {
@@ -1684,61 +1648,57 @@ export async function calculateRealIPBOverlays(
color: '#0284c7',
strokeColor: '#0369a1',
opacity: 0.85,
label: `≈≈ مجرى وادٍ وسيل تضاريسي رئيسي (${lowestPoint.elevation}م)`
label: `≈≈ مجرى وادٍ وسيل رئيسي (${lowestPoint.elevation}م)`
},
geometry: { type: 'LineString', coordinates: wadiLineCoords }
});
}
// 5. Layer 5: Tactical Minefields across real choke points
if (chokePoints.length > 0) {
const cp = chokePoints[Math.floor(chokePoints.length / 2)];
const mLatDelta = (250 / R_earth) * (180 / Math.PI);
const mLngDelta = (450 / (R_earth * Math.cos((cp.lat * Math.PI) / 180))) * (180 / Math.PI);
// 5. Layer 5: Tactical Obstacles & Retaining Barriers (Populated if obstacles exist)
const mineLine = [
[cp.lng - mLngDelta, cp.lat - mLatDelta],
[cp.lng + mLngDelta, cp.lat + mLatDelta]
];
layer_5.push({
type: 'Feature',
properties: {
layer: 'layer_5',
color: '#e11d48',
strokeColor: '#9f1239',
opacity: 0.90,
label: 'XXXX حزام حقول ألغام وسواتر موانع (غلق المخنق الطبيعي)'
},
geometry: { type: 'LineString', coordinates: mineLine }
});
} else {
// Fallback across lowest valley approach
const mLat = (lowestPoint.lat + centerLat) / 2;
const mLng = (lowestPoint.lng + centerLng) / 2;
const mLngDelta = (600 / (R_earth * Math.cos((mLat * Math.PI) / 180))) * (180 / Math.PI);
layer_5.push({
type: 'Feature',
properties: {
layer: 'layer_5',
color: '#e11d48',
strokeColor: '#9f1239',
opacity: 0.90,
label: 'XXXX حزام حقول ألغام هندسية مسجلة'
},
geometry: {
type: 'LineString',
coordinates: [
[mLng - mLngDelta, mLat],
[mLng + mLngDelta, mLat]
]
}
});
}
// 7. Layer 7: Real Approach Corridor & Mobility (المقترب التعبوي الرئيسي AA-1)
let approachLengthKm = Math.round((radiusMeters * 1.4 / 1000) * 10) / 10;
let approachDirection = 'الغرب نحو الشرق';
let approachAzimuth = 85;
let approachWidthM = Math.round(dxMeters * 3);
let approachCapacity = 'كتيبة مدرعة بنسق رتل';
// 7. Layer 7: Real Avenues of Approach (مسالك الاقتراب وممرات الدروع)
// Follows the lowest path from entry boundary to center
if (valleyNodes.length >= 3) {
const approachCoords = valleyNodes.slice(0, Math.floor(valleyNodes.length * 0.8)).map(n => [n.lng, n.lat]);
// Calculate real length
let lenKm = 0;
for (let i = 0; i < approachCoords.length - 1; i++) {
const p1 = approachCoords[i];
const p2 = approachCoords[i + 1];
lenKm += Math.hypot((p2[1] - p1[1]) * 111.139, (p2[0] - p1[0]) * 111.139 * Math.cos(centerLat * Math.PI / 180));
}
if (lenKm > 0.5) approachLengthKm = Math.round(lenKm * 10) / 10;
// Calculate azimuth from start to end
const startP = approachCoords[0];
const endP = approachCoords[approachCoords.length - 1];
const dLngRad = (endP[0] - startP[0]) * (Math.PI / 180);
const lat1Rad = startP[1] * (Math.PI / 180);
const lat2Rad = endP[1] * (Math.PI / 180);
const y = Math.sin(dLngRad) * Math.cos(lat2Rad);
const x = Math.cos(lat1Rad) * Math.sin(lat2Rad) - Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(dLngRad);
const azDeg = Math.round((Math.atan2(y, x) * 180 / Math.PI + 360) % 360);
approachAzimuth = azDeg;
if (azDeg >= 337.5 || azDeg < 22.5) approachDirection = 'الجنوب نحو الشمال';
else if (azDeg >= 22.5 && azDeg < 67.5) approachDirection = 'الجنوب الغربي نحو الشمال الشرقي';
else if (azDeg >= 67.5 && azDeg < 112.5) approachDirection = 'الغرب نحو الشرق';
else if (azDeg >= 112.5 && azDeg < 157.5) approachDirection = 'الشمال الغربي نحو الجنوب الشرقي';
else if (azDeg >= 157.5 && azDeg < 202.5) approachDirection = 'الشمال نحو الجنوب';
else if (azDeg >= 202.5 && azDeg < 247.5) approachDirection = 'الشمال الشرقي نحو الجنوب الغربي';
else if (azDeg >= 247.5 && azDeg < 292.5) approachDirection = 'الشرق نحو الغرب';
else approachDirection = 'الجنوب الشرقي نحو الشمال الغربي';
if (approachWidthM > 1200) approachCapacity = 'لواء مدرع بنسق كتائب';
else if (approachWidthM > 600) approachCapacity = 'كتيبة مدرعة بنسق رتل سرية';
else approachCapacity = 'سرية دبابات بنسق رتل واحد';
layer_7.push({
type: 'Feature',
properties: {
@@ -1746,7 +1706,7 @@ export async function calculateRealIPBOverlays(
color: '#2563eb',
strokeColor: '#1d4ed8',
opacity: 0.90,
label: '➔➔ مسلك اقتراب رئيسي لكتيبة دروع (AA-1 عبر بطن الوادي)'
label: `➔➔ المقترب التعبوي الرئيسي (AA-1: بطول ${approachLengthKm} كم / عرض ${approachWidthM}م / سعة ${approachCapacity})`
},
geometry: { type: 'LineString', coordinates: approachCoords }
});
@@ -1768,47 +1728,76 @@ export async function calculateRealIPBOverlays(
}
});
// 9. Layer 9: Real Dead Ground / LOS from Key Terrain Peak
// Run radial LOS calculation from summit to detect shadowed cells
const deadGroundPolygons: any[] = [];
const numRays = 12;
for (let i = 0; i < numRays; i++) {
const angle = (i / numRays) * 2 * Math.PI;
const rayEndLat = highestPoint.lat + (radiusMeters * 0.8 / R_earth) * (180 / Math.PI) * Math.sin(angle);
const rayEndLng = highestPoint.lng + (radiusMeters * 0.8 / (R_earth * Math.cos((highestPoint.lat * Math.PI) / 180))) * (180 / Math.PI) * Math.cos(angle);
// 9. Layer 9: Real DEM Line of Sight & Viewshed Shadows from Key Terrain Peak
// Soft, continuous terrain shadow masking without harsh black borders
const summitR = Math.min(gridSize - 1, Math.max(0, Math.round(((centerLat + dLatTotal - highestPoint.lat) / (2 * dLatTotal)) * (gridSize - 1))));
const summitC = Math.min(gridSize - 1, Math.max(0, Math.round(((highestPoint.lng - (centerLng - dLngTotal)) / (2 * dLngTotal)) * (gridSize - 1))));
const summitElev = highestPoint.elevation;
// If ray passes near lower valley, create shadowed dead ground polygon behind the ridge
const deadLat = (highestPoint.lat + rayEndLat) / 2 + 0.003 * Math.sin(angle);
const deadLng = (highestPoint.lng + rayEndLng) / 2 + 0.003 * Math.cos(angle);
const dSize = 0.004;
for (let r = 0; r < gridSize; r += 2) {
for (let c = 0; c < gridSize; c += 2) {
if (Math.abs(r - summitR) <= 1 && Math.abs(c - summitC) <= 1) continue;
const distCells = Math.hypot(r - summitR, c - summitC);
if (distCells === 0) continue;
layer_9.push({
type: 'Feature',
properties: {
layer: 'layer_9',
color: '#334155',
strokeColor: '#0f172a',
opacity: 0.68,
label: '👁️🚫 أرض ميتة محجوبة عن الرؤية والرادار'
},
geometry: {
type: 'Polygon',
coordinates: [[
[deadLng - dSize, deadLat - dSize],
[deadLng + dSize, deadLat - dSize],
[deadLng + dSize, deadLat + dSize],
[deadLng - dSize, deadLat + dSize],
[deadLng - dSize, deadLat - dSize]
]]
const targetElev = elevationGrid[r][c];
const angleToTarget = (targetElev - summitElev) / distCells;
let isShadowed = false;
const steps = Math.floor(distCells);
for (let s = 1; s < steps; s++) {
const interR = Math.round(summitR + (r - summitR) * (s / distCells));
const interC = Math.round(summitC + (c - summitC) * (s / distCells));
if (interR >= 0 && interR < gridSize && interC >= 0 && interC < gridSize) {
const interElev = elevationGrid[interR][interC];
const angleToInter = (interElev - summitElev) / s;
if (angleToInter > angleToTarget + 0.8) {
isShadowed = true;
break;
}
}
}
});
if (isShadowed) {
const cellLat = centerLat + dLatTotal - (r / (gridSize - 1)) * (2 * dLatTotal);
const cellLng = centerLng - dLngTotal + (c / (gridSize - 1)) * (2 * dLngTotal);
const distM = Math.hypot(
(cellLat - centerLat) * (R_earth * Math.PI / 180),
(cellLng - centerLng) * (R_earth * Math.cos(centerLat * Math.PI / 180) * Math.PI / 180)
);
if (distM <= radiusMeters * 0.95) {
const hLat = (dLatTotal / (gridSize - 1)) * 1.05;
const hLng = (dLngTotal / (gridSize - 1)) * 1.05;
layer_9.push({
type: 'Feature',
properties: {
layer: 'layer_9',
color: '#475569',
strokeColor: '#334155',
opacity: 0.18
},
geometry: {
type: 'Polygon',
coordinates: [[
[cellLng - hLng, cellLat - hLat],
[cellLng + hLng, cellLat - hLat],
[cellLng + hLng, cellLat + hLat],
[cellLng - hLng, cellLat + hLat],
[cellLng - hLng, cellLat - hLat]
]]
}
});
}
}
}
}
// 10. Layer 10: SITTEMP & Kill Zone
// Placed at the intersection of Avenue of Approach into Line of Sight of Key Terrain
const killZoneLat = (highestPoint.lat + lowestPoint.lat) / 2;
const killZoneLng = (highestPoint.lng + lowestPoint.lng) / 2;
const kzR = (radiusMeters * 0.15 / R_earth) * (180 / Math.PI);
// 10. Layer 10: SITTEMP & Engagement Area (منطقة التقتيل الرئيسية Kill Zone Alpha)
// Placed at the natural choke point near lowest valley within line of sight of key terrain
const killZoneLat = (highestPoint.lat + lowestPoint.lat * 2) / 3;
const killZoneLng = (highestPoint.lng + lowestPoint.lng * 2) / 3;
const kzR = (radiusMeters * 0.14 / R_earth) * (180 / Math.PI);
const kzRLng = kzR / Math.cos((killZoneLat * Math.PI) / 180);
const kzCoords: [number, number][] = [];
@@ -1817,14 +1806,47 @@ export async function calculateRealIPBOverlays(
kzCoords.push([killZoneLng + kzRLng * Math.sin(a), killZoneLat + kzR * Math.cos(a)]);
}
// Direct Fire Line from Key Terrain Peak to Kill Zone Center
const fireLineCoords = [
[highestPoint.lng, highestPoint.lat],
[killZoneLng, killZoneLat]
];
const fireDistKm = Math.round(Math.hypot(
(killZoneLat - highestPoint.lat) * 111.139,
(killZoneLng - highestPoint.lng) * 111.139 * Math.cos(centerLat * Math.PI / 180)
) * 10) / 10;
const fireAzimuth = Math.round((Math.atan2(
(killZoneLng - highestPoint.lng) * Math.cos(centerLat * Math.PI / 180),
killZoneLat - highestPoint.lat
) * 180 / Math.PI + 360) % 360);
// Add Direct Fire Line
layer_10.push({
type: 'Feature',
properties: {
layer: 'layer_10',
color: '#dc2626',
strokeColor: '#991b1b',
opacity: 0.95,
label: `⚡ خط نار ورصد مباشر مسيطر (أزيموث ${fireAzimuth}° / مدى ${fireDistKm} كم)`
},
geometry: {
type: 'LineString',
coordinates: fireLineCoords
}
});
// Add Kill Zone Area
layer_10.push({
type: 'Feature',
properties: {
layer: 'layer_10',
color: '#ef4444',
strokeColor: '#b91c1c',
opacity: 0.82,
label: '⚔️ منطقة القتل المستهدفة (KILL ZONE ALPHA)'
opacity: 0.75,
label: '⚔️ منطقة التقتيل الرئيسية (KILL ZONE ALPHA)'
},
geometry: {
type: 'Polygon',
@@ -1893,7 +1915,14 @@ export async function calculateRealIPBOverlays(
severelyRestrictedPct,
cliffCount,
keyTerrainSummit: highestPoint,
killZoneCenter: { lat: killZoneLat, lng: killZoneLng }
killZoneCenter: { lat: killZoneLat, lng: killZoneLng },
approachLengthKm,
approachWidthM,
approachDirection,
approachAzimuth,
approachCapacity,
fireDistKm,
fireAzimuth
},
allFeatures
};