feat(tactical): real DEM grid slope analysis, dynamic contour lines, and topography-driven wadi/ridge features
This commit is contained in:
@@ -1,5 +1,15 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import maplibregl from 'maplibre-gl';
|
import maplibregl from 'maplibre-gl';
|
||||||
|
import mlcontour from 'maplibre-contour';
|
||||||
|
|
||||||
|
const demSource = new mlcontour.DemSource({
|
||||||
|
url: 'https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png',
|
||||||
|
encoding: 'terrarium',
|
||||||
|
maxzoom: 15,
|
||||||
|
worker: true,
|
||||||
|
cacheSize: 100,
|
||||||
|
});
|
||||||
|
demSource.setupMaplibre(maplibregl);
|
||||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||||
import {
|
import {
|
||||||
Shield,
|
Shield,
|
||||||
@@ -89,6 +99,7 @@ export const TacticalDefenseView: React.FC = () => {
|
|||||||
const [copiedReport, setCopiedReport] = useState<boolean>(false);
|
const [copiedReport, setCopiedReport] = useState<boolean>(false);
|
||||||
|
|
||||||
// Terrain Visual Layers Toggles on Map
|
// Terrain Visual Layers Toggles on Map
|
||||||
|
const [showContours, setShowContours] = useState<boolean>(true);
|
||||||
const [showPeaksAndValleys, setShowPeaksAndValleys] = useState<boolean>(true);
|
const [showPeaksAndValleys, setShowPeaksAndValleys] = useState<boolean>(true);
|
||||||
const [showSlopeSectors, setShowSlopeSectors] = useState<boolean>(true);
|
const [showSlopeSectors, setShowSlopeSectors] = useState<boolean>(true);
|
||||||
const [showNaturalObstacles, setShowNaturalObstacles] = useState<boolean>(true);
|
const [showNaturalObstacles, setShowNaturalObstacles] = useState<boolean>(true);
|
||||||
@@ -444,6 +455,86 @@ export const TacticalDefenseView: React.FC = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Dynamic On-the-Fly Contours from Elevation DEM
|
||||||
|
const contourUrl = demSource.contourProtocolUrl({
|
||||||
|
thresholds: {
|
||||||
|
10: [100, 500],
|
||||||
|
11: [50, 250],
|
||||||
|
12: [25, 100],
|
||||||
|
13: [20, 100],
|
||||||
|
14: [10, 50],
|
||||||
|
15: [10, 50],
|
||||||
|
16: [10, 50],
|
||||||
|
17: [10, 50],
|
||||||
|
18: [10, 50],
|
||||||
|
},
|
||||||
|
elevationKey: 'ele',
|
||||||
|
levelKey: 'level',
|
||||||
|
contourLayer: 'contours',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!initialMap.getSource('contour-source')) {
|
||||||
|
initialMap.addSource('contour-source', {
|
||||||
|
type: 'vector',
|
||||||
|
tiles: [contourUrl],
|
||||||
|
maxzoom: 18,
|
||||||
|
});
|
||||||
|
|
||||||
|
initialMap.addLayer({
|
||||||
|
id: 'contour-lines-minor',
|
||||||
|
type: 'line',
|
||||||
|
source: 'contour-source',
|
||||||
|
'source-layer': 'contours',
|
||||||
|
minzoom: 10,
|
||||||
|
layout: {
|
||||||
|
visibility: 'visible',
|
||||||
|
},
|
||||||
|
filter: ['==', ['get', 'level'], 0],
|
||||||
|
paint: {
|
||||||
|
'line-color': '#a86324',
|
||||||
|
'line-width': 1.1,
|
||||||
|
'line-opacity': 0.85,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
initialMap.addLayer({
|
||||||
|
id: 'contour-lines-major',
|
||||||
|
type: 'line',
|
||||||
|
source: 'contour-source',
|
||||||
|
'source-layer': 'contours',
|
||||||
|
minzoom: 9,
|
||||||
|
layout: {
|
||||||
|
visibility: 'visible',
|
||||||
|
},
|
||||||
|
filter: ['>', ['get', 'level'], 0],
|
||||||
|
paint: {
|
||||||
|
'line-color': '#703800',
|
||||||
|
'line-width': 2.0,
|
||||||
|
'line-opacity': 0.95,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
initialMap.addLayer({
|
||||||
|
id: 'contour-labels',
|
||||||
|
type: 'symbol',
|
||||||
|
source: 'contour-source',
|
||||||
|
'source-layer': 'contours',
|
||||||
|
minzoom: 12,
|
||||||
|
layout: {
|
||||||
|
visibility: 'visible',
|
||||||
|
'symbol-placement': 'line',
|
||||||
|
'text-field': ['concat', ['to-string', ['get', 'ele']], ' m'],
|
||||||
|
'text-size': 10,
|
||||||
|
'text-font': ['Noto Sans Bold', 'Open Sans Bold'],
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'text-color': '#5c2d00',
|
||||||
|
'text-halo-color': 'rgba(255, 255, 255, 0.95)',
|
||||||
|
'text-halo-width': 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Rich Spatial Feature Layers for Terrain Study (Slope, Wadis, Roads, Urban, Hazards)
|
// Rich Spatial Feature Layers for Terrain Study (Slope, Wadis, Roads, Urban, Hazards)
|
||||||
initialMap.addSource('tactical-terrain-spatial-src', {
|
initialMap.addSource('tactical-terrain-spatial-src', {
|
||||||
type: 'geojson',
|
type: 'geojson',
|
||||||
@@ -702,6 +793,16 @@ export const TacticalDefenseView: React.FC = () => {
|
|||||||
.catch(() => setTerrainLoading(false));
|
.catch(() => setTerrainLoading(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Toggle Contour Layers Visibility
|
||||||
|
useEffect(() => {
|
||||||
|
if (!map.current) return;
|
||||||
|
['contour-lines-minor', 'contour-lines-major', 'contour-labels'].forEach(layerId => {
|
||||||
|
if (map.current!.getLayer(layerId)) {
|
||||||
|
map.current!.setLayoutProperty(layerId, 'visibility', showContours ? 'visible' : 'none');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [showContours]);
|
||||||
|
|
||||||
// Sync Spatial GeoJSON Features with Active Layer Toggles
|
// Sync Spatial GeoJSON Features with Active Layer Toggles
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!map.current || !map.current.getSource('tactical-terrain-spatial-src')) return;
|
if (!map.current || !map.current.getSource('tactical-terrain-spatial-src')) return;
|
||||||
@@ -716,7 +817,7 @@ export const TacticalDefenseView: React.FC = () => {
|
|||||||
const filtered = terrainResult.spatialGeoJson.features.filter((f: any) => {
|
const filtered = terrainResult.spatialGeoJson.features.filter((f: any) => {
|
||||||
if (f.properties.category === 'peak' || f.properties.category === 'valley') return showPeaksAndValleys;
|
if (f.properties.category === 'peak' || f.properties.category === 'valley') return showPeaksAndValleys;
|
||||||
if (f.properties.layerType === 'slope-sector') return showSlopeSectors;
|
if (f.properties.layerType === 'slope-sector') return showSlopeSectors;
|
||||||
if (f.properties.layerType === 'wadi') return showNaturalObstacles;
|
if (f.properties.layerType === 'wadi' || f.properties.layerType === 'ridge') return showNaturalObstacles;
|
||||||
if (f.properties.layerType === 'road') return showRoadCorridors;
|
if (f.properties.layerType === 'road') return showRoadCorridors;
|
||||||
if (f.properties.layerType === 'urban') return showUrbanZones;
|
if (f.properties.layerType === 'urban') return showUrbanZones;
|
||||||
if (f.properties.layerType === 'hazard') return showHazardousGround;
|
if (f.properties.layerType === 'hazard') return showHazardousGround;
|
||||||
@@ -1231,6 +1332,26 @@ ${terrainResult.tacticalRecommendations.map((r, i) => `${i + 1}. ${r}`).join('\n
|
|||||||
<Sliders size={14} /> طبقات الإظهار اللوني على الخريطة:
|
<Sliders size={14} /> طبقات الإظهار اللوني على الخريطة:
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowContours(!showContours)}
|
||||||
|
style={{
|
||||||
|
background: showContours ? 'rgba(245, 158, 11, 0.2)' : 'rgba(255,255,255,0.05)',
|
||||||
|
border: showContours ? '1px solid #f59e0b' : '1px solid rgba(255,255,255,0.1)',
|
||||||
|
color: showContours ? '#f59e0b' : '#94a3b8',
|
||||||
|
padding: '5px 8px',
|
||||||
|
borderRadius: 6,
|
||||||
|
fontSize: '0.68rem',
|
||||||
|
fontWeight: 700,
|
||||||
|
cursor: 'pointer',
|
||||||
|
textAlign: 'right',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 4
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{showContours ? '✅' : '⬜'}</span>
|
||||||
|
<span>〰️ خطوط الكنتور</span>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowPeaksAndValleys(!showPeaksAndValleys)}
|
onClick={() => setShowPeaksAndValleys(!showPeaksAndValleys)}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -661,65 +661,225 @@ export async function calculateTerrainStudy(
|
|||||||
const centerElev = await sampleElevationAt(centerLat, centerLng);
|
const centerElev = await sampleElevationAt(centerLat, centerLng);
|
||||||
const areaKm2 = Math.round(Math.PI * Math.pow(radiusMeters / 1000, 2) * 10) / 10;
|
const areaKm2 = Math.round(Math.PI * Math.pow(radiusMeters / 1000, 2) * 10) / 10;
|
||||||
|
|
||||||
// Multi-point radial sampling across the sector
|
// Grid Resolution: 11x11 sampling matrix covering the bounding box
|
||||||
const samples = 24;
|
const gridSize = 11;
|
||||||
const sampledPoints: Array<{ lat: number; lng: number; elevation: number; slope: number; angleRad: number; dist: number }> = [
|
const R_earth = 6371000;
|
||||||
{ lat: centerLat, lng: centerLng, elevation: centerElev, slope: 0, angleRad: 0, dist: 0 }
|
const dLatTotal = (radiusMeters / R_earth) * (180 / Math.PI);
|
||||||
];
|
const dLngTotal = (radiusMeters / (R_earth * Math.cos((centerLat * Math.PI) / 180))) * (180 / Math.PI);
|
||||||
|
|
||||||
let maxElev = centerElev;
|
const minLat = centerLat - dLatTotal;
|
||||||
let minElev = centerElev;
|
const maxLat = centerLat + dLatTotal;
|
||||||
let highestPoint = { lat: centerLat, lng: centerLng, elevation: centerElev, label: 'أعلى قمة' };
|
const minLng = centerLng - dLngTotal;
|
||||||
let lowestPoint = { lat: centerLat, lng: centerLng, elevation: centerElev, label: 'أخفض نقطة' };
|
const maxLng = centerLng + dLngTotal;
|
||||||
|
|
||||||
for (let i = 0; i < samples; i++) {
|
// 1. Parallel sampling of all grid points
|
||||||
const angleRad = (i / samples) * 2 * Math.PI;
|
const gridCoords: Array<{ r: number; c: number; lat: number; lng: number }> = [];
|
||||||
const dist = radiusMeters * (0.35 + (i % 3) * 0.3);
|
for (let r = 0; r < gridSize; r++) {
|
||||||
const R_earth = 6371000;
|
const lat = Number((minLat + (r / (gridSize - 1)) * (maxLat - minLat)).toFixed(5));
|
||||||
const dLat = (dist / R_earth) * (180 / Math.PI) * Math.cos(angleRad);
|
for (let c = 0; c < gridSize; c++) {
|
||||||
const dLng = (dist / (R_earth * Math.cos((centerLat * Math.PI) / 180))) * (180 / Math.PI) * Math.sin(angleRad);
|
const lng = Number((minLng + (c / (gridSize - 1)) * (maxLng - minLng)).toFixed(5));
|
||||||
|
gridCoords.push({ r, c, lat, lng });
|
||||||
const sLat = Number((centerLat + dLat).toFixed(5));
|
|
||||||
const sLng = Number((centerLng + dLng).toFixed(5));
|
|
||||||
const el = await sampleElevationAt(sLat, sLng);
|
|
||||||
const slopeDeg = Math.atan(Math.abs(el - centerElev) / dist) * (180 / Math.PI);
|
|
||||||
|
|
||||||
sampledPoints.push({ lat: sLat, lng: sLng, elevation: el, slope: slopeDeg, angleRad, dist });
|
|
||||||
|
|
||||||
if (el > maxElev) {
|
|
||||||
maxElev = el;
|
|
||||||
highestPoint = { lat: sLat, lng: sLng, elevation: el, label: `أعلى قمة: ${el}م (Peak)` };
|
|
||||||
}
|
|
||||||
if (el < minElev) {
|
|
||||||
minElev = el;
|
|
||||||
lowestPoint = { lat: sLat, lng: sLng, elevation: el, label: `أخفض نقطة: ${el}م (Valley Floor)` };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const slopes = sampledPoints.slice(1).map(p => p.slope);
|
const elevationGrid: number[][] = Array(gridSize).fill(0).map(() => Array(gridSize).fill(0));
|
||||||
|
const elevPromises = gridCoords.map(async ({ r, c, lat, lng }) => {
|
||||||
|
const el = await sampleElevationAt(lat, lng);
|
||||||
|
elevationGrid[r][c] = el;
|
||||||
|
return { r, c, lat, lng, el };
|
||||||
|
});
|
||||||
|
|
||||||
|
const sampledPoints = await Promise.all(elevPromises);
|
||||||
|
|
||||||
|
// 2. Find Exact Peak and Valley
|
||||||
|
let maxElev = -9999;
|
||||||
|
let minElev = 9999;
|
||||||
|
let highestPoint = { lat: centerLat, lng: centerLng, elevation: centerElev, label: 'أعلى قمة' };
|
||||||
|
let lowestPoint = { lat: centerLat, lng: centerLng, elevation: centerElev, label: 'أخفض نقطة' };
|
||||||
|
|
||||||
|
sampledPoints.forEach(p => {
|
||||||
|
const dist = calculateDistance(centerLat, centerLng, p.lat, p.lng);
|
||||||
|
if (dist <= radiusMeters * 1.05) {
|
||||||
|
if (p.el > maxElev) {
|
||||||
|
maxElev = p.el;
|
||||||
|
highestPoint = { lat: p.lat, lng: p.lng, elevation: p.el, label: `🔺 أعلى قمة: ${p.el}م` };
|
||||||
|
}
|
||||||
|
if (p.el < minElev) {
|
||||||
|
minElev = p.el;
|
||||||
|
lowestPoint = { lat: p.lat, lng: p.lng, elevation: p.el, label: `🔻 أخفض نقطة: ${p.el}م` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const relief = maxElev - minElev;
|
const relief = maxElev - minElev;
|
||||||
const avgSlope = Math.round((slopes.reduce((a, b) => a + b, 0) / slopes.length) * 10) / 10;
|
|
||||||
const maxSlope = Math.round(Math.max(...slopes) * 10) / 10;
|
|
||||||
|
|
||||||
const flatCount = slopes.filter(s => s < 5).length;
|
// 3. Compute Local Gradient (Slope) per cell and build Topographic Slope Polygons
|
||||||
const modCount = slopes.filter(s => s >= 5 && s <= 15).length;
|
const spatialFeatures: any[] = [];
|
||||||
const steepCount = slopes.filter(s => s > 15).length;
|
const slopeValues: number[] = [];
|
||||||
|
|
||||||
const flatPct = Math.round((flatCount / slopes.length) * 100);
|
const dxMeters = (2 * radiusMeters) / (gridSize - 1);
|
||||||
const modPct = Math.round((modCount / slopes.length) * 100);
|
const dyMeters = (2 * radiusMeters) / (gridSize - 1);
|
||||||
|
|
||||||
|
// Cell-by-cell topographic classification matching contour slopes
|
||||||
|
for (let r = 0; r < gridSize - 1; r++) {
|
||||||
|
for (let c = 0; c < gridSize - 1; c++) {
|
||||||
|
const cellCenterLat = (gridCoords[r * gridSize + c].lat + gridCoords[(r + 1) * gridSize + (c + 1)].lat) / 2;
|
||||||
|
const cellCenterLng = (gridCoords[r * gridSize + c].lng + gridCoords[(r + 1) * gridSize + (c + 1)].lng) / 2;
|
||||||
|
const distFromCenter = calculateDistance(centerLat, centerLng, cellCenterLat, cellCenterLng);
|
||||||
|
|
||||||
|
if (distFromCenter > radiusMeters * 1.02) continue;
|
||||||
|
|
||||||
|
const z00 = elevationGrid[r][c];
|
||||||
|
const z01 = elevationGrid[r][c + 1];
|
||||||
|
const z10 = elevationGrid[r + 1][c];
|
||||||
|
const z11 = elevationGrid[r + 1][c + 1];
|
||||||
|
|
||||||
|
const dz_dx = ((z01 + z11) - (z00 + z10)) / (2 * dxMeters);
|
||||||
|
const dz_dy = ((z10 + z11) - (z00 + z01)) / (2 * dyMeters);
|
||||||
|
|
||||||
|
const slopeRad = Math.atan(Math.sqrt(dz_dx * dz_dx + dz_dy * dz_dy));
|
||||||
|
const slopeDeg = Math.round((slopeRad * (180 / Math.PI)) * 10) / 10;
|
||||||
|
slopeValues.push(slopeDeg);
|
||||||
|
|
||||||
|
let cat = 'go';
|
||||||
|
let catColor = '#22c55e';
|
||||||
|
let catLabel = `ممر حركة منبسط (GO) [${slopeDeg}°]`;
|
||||||
|
|
||||||
|
if (slopeDeg > 12) {
|
||||||
|
cat = 'no-go';
|
||||||
|
catColor = '#ef4444';
|
||||||
|
catLabel = `جرف ومنحدر وعر (NO-GO) [${slopeDeg}°]`;
|
||||||
|
} else if (slopeDeg >= 5) {
|
||||||
|
cat = 'slow-go';
|
||||||
|
catColor = '#eab308';
|
||||||
|
catLabel = `تضاريس متموجة (SLOW-GO) [${slopeDeg}°]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const p1 = [gridCoords[r * gridSize + c].lng, gridCoords[r * gridSize + c].lat];
|
||||||
|
const p2 = [gridCoords[r * gridSize + (c + 1)].lng, gridCoords[r * gridSize + (c + 1)].lat];
|
||||||
|
const p3 = [gridCoords[(r + 1) * gridSize + (c + 1)].lng, gridCoords[(r + 1) * gridSize + (c + 1)].lat];
|
||||||
|
const p4 = [gridCoords[(r + 1) * gridSize + c].lng, gridCoords[(r + 1) * gridSize + c].lat];
|
||||||
|
|
||||||
|
spatialFeatures.push({
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
layerType: 'slope-sector',
|
||||||
|
category: cat,
|
||||||
|
color: catColor,
|
||||||
|
title: catLabel,
|
||||||
|
slope: slopeDeg,
|
||||||
|
elevation: Math.round((z00 + z01 + z10 + z11) / 4)
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [[p1, p2, p3, p4, p1]]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slope stats
|
||||||
|
const avgSlope = slopeValues.length > 0 ? Math.round((slopeValues.reduce((a, b) => a + b, 0) / slopeValues.length) * 10) / 10 : 2.5;
|
||||||
|
const maxSlope = slopeValues.length > 0 ? Math.round(Math.max(...slopeValues) * 10) / 10 : 5.0;
|
||||||
|
|
||||||
|
const flatCount = slopeValues.filter(s => s < 5).length;
|
||||||
|
const modCount = slopeValues.filter(s => s >= 5 && s <= 12).length;
|
||||||
|
const steepCount = slopeValues.filter(s => s > 12).length;
|
||||||
|
const totalCells = Math.max(1, slopeValues.length);
|
||||||
|
|
||||||
|
const flatPct = Math.round((flatCount / totalCells) * 100);
|
||||||
|
const modPct = Math.round((modCount / totalCells) * 100);
|
||||||
const steepPct = Math.max(0, 100 - flatPct - modPct);
|
const steepPct = Math.max(0, 100 - flatPct - modPct);
|
||||||
|
|
||||||
// Sector Geolocation and Regional Characteristics
|
// 4. Trace the Natural Valley / Drainage Line (Wadi) using DEM lowest cells
|
||||||
|
const valleyPoints: [number, number][] = [];
|
||||||
|
for (let r = 0; r < gridSize; r++) {
|
||||||
|
let minRowElev = 9999;
|
||||||
|
let minColIdx = 0;
|
||||||
|
for (let c = 0; c < gridSize; c++) {
|
||||||
|
if (elevationGrid[r][c] < minRowElev) {
|
||||||
|
minRowElev = elevationGrid[r][c];
|
||||||
|
minColIdx = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pt = gridCoords[r * gridSize + minColIdx];
|
||||||
|
const dist = calculateDistance(centerLat, centerLng, pt.lat, pt.lng);
|
||||||
|
if (dist <= radiusMeters * 1.05) {
|
||||||
|
valleyPoints.push([pt.lng, pt.lat]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valleyPoints.length >= 2) {
|
||||||
|
spatialFeatures.push({
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
layerType: 'wadi',
|
||||||
|
category: 'natural-obstacle',
|
||||||
|
title: `🌊 مجرى وادٍ رئيسي ومصرف سيل طبيعي (${minElev}م)`,
|
||||||
|
color: '#06b6d4'
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: valleyPoints
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Detect and Draw Dominant Mountain Ridge (Connecting High Points)
|
||||||
|
const ridgePoints: [number, number][] = [];
|
||||||
|
for (let r = 0; r < gridSize; r++) {
|
||||||
|
let maxRowElev = -9999;
|
||||||
|
let maxColIdx = 0;
|
||||||
|
for (let c = 0; c < gridSize; c++) {
|
||||||
|
if (elevationGrid[r][c] > maxRowElev) {
|
||||||
|
maxRowElev = elevationGrid[r][c];
|
||||||
|
maxColIdx = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pt = gridCoords[r * gridSize + maxColIdx];
|
||||||
|
const dist = calculateDistance(centerLat, centerLng, pt.lat, pt.lng);
|
||||||
|
if (dist <= radiusMeters * 1.05 && maxRowElev > minElev + relief * 0.6) {
|
||||||
|
ridgePoints.push([pt.lng, pt.lat]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ridgePoints.length >= 2) {
|
||||||
|
spatialFeatures.push({
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
layerType: 'ridge',
|
||||||
|
category: 'key-terrain',
|
||||||
|
title: `⛰️ سلسلة تلال حاكمة (Dominant Ridge - ${maxElev}م)`,
|
||||||
|
color: '#38bdf8'
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: ridgePoints
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Real Regional Sector Identification
|
||||||
let sectorName = 'قطاع عمليات ميداني';
|
let sectorName = 'قطاع عمليات ميداني';
|
||||||
let terrainClassificationAr = 'هضاب وتلال متوسطة الوعورة';
|
let terrainClassificationAr = 'هضاب وتلال متوسطة الوعورة';
|
||||||
let terrainClassification = 'Rolling Hills & Ridges';
|
let terrainClassification = 'Rolling Hills & Ridges';
|
||||||
let urbanDensity: 'HIGH' | 'MEDIUM' | 'LOW' | 'SPARSE' = 'LOW';
|
let urbanDensity: 'HIGH' | 'MEDIUM' | 'LOW' | 'SPARSE' = 'MEDIUM';
|
||||||
let urbanDensityAr = 'كثافة منخفضة / ريفية';
|
let urbanDensityAr = 'كثافة متوسطة';
|
||||||
let settlements: string[] = ['تجمعات سكنية متفرقة', 'مزارع ومنشآت ريفية'];
|
let settlements: string[] = ['تجمعات سكنية وضواحي متصلة'];
|
||||||
let soilType = 'تربة طينية صخرية جافة';
|
let soilType = 'تربة طينية صخرية جافة';
|
||||||
let trafficability = 'جيدة للمركبات المدولبة والمجنزرة مع الحذر من الانزلاقات في المنحدرات';
|
let trafficability = 'جيدة للمركبات المدولبة والمجنزرة مع الحذر في مجاري السيول';
|
||||||
|
|
||||||
if (centerLat > 32.3 && centerLng < 36.0) {
|
// Geolocation Specific Context (Zarqa / Amman / Irbid / Mafraq / Dead Sea / South)
|
||||||
|
if (centerLat >= 32.05 && centerLat <= 32.20 && centerLng >= 36.00 && centerLng <= 36.18) {
|
||||||
|
sectorName = 'قاطع الزرقاء والسخنة / حوض سيل الزرقاء';
|
||||||
|
terrainClassificationAr = 'حوض وادي صدعي واسع تحيط به تلال وهضاب حاكمة ومجرى سيل مائي';
|
||||||
|
terrainClassification = 'River Valley Basin flanked by Ridge Plateaus';
|
||||||
|
urbanDensity = 'HIGH';
|
||||||
|
urbanDensityAr = 'كثافة عالية (بلدات وضواحي ممتدة)';
|
||||||
|
settlements = ['بلدة السخنة', 'خربة قنتول', 'ضاحية البستان', 'أحياء الهاشمية', 'مدينة الزرقاء'];
|
||||||
|
soilType = 'طمي رسوبي في بطن الوادي وصخور كلسية على الأكتاف';
|
||||||
|
trafficability = 'ممتازة على شبكة الطرق وبطون الأودية المنبسطة (GO) مع صعوبة صعود الجروف الصخرية';
|
||||||
|
} else if (centerLat > 32.3 && centerLng < 36.0) {
|
||||||
sectorName = 'قاطع الشمال / حوض اليرموك وإربد';
|
sectorName = 'قاطع الشمال / حوض اليرموك وإربد';
|
||||||
terrainClassificationAr = 'مرتفعات جبلية تتخللها أودية سحيقة وخوانق مائية';
|
terrainClassificationAr = 'مرتفعات جبلية تتخللها أودية سحيقة وخوانق مائية';
|
||||||
terrainClassification = 'Highland Plateaus with Deep Gorges';
|
terrainClassification = 'Highland Plateaus with Deep Gorges';
|
||||||
@@ -728,7 +888,7 @@ export async function calculateTerrainStudy(
|
|||||||
settlements = ['بلدات وضواحي إربد', 'قرى حوض اليرموك', 'مزارع ومحميات وادي الشلالة'];
|
settlements = ['بلدات وضواحي إربد', 'قرى حوض اليرموك', 'مزارع ومحميات وادي الشلالة'];
|
||||||
soilType = 'تربة زراعية خصبة وصخور كلسية';
|
soilType = 'تربة زراعية خصبة وصخور كلسية';
|
||||||
trafficability = 'صعبة على حواف الأودية - تتطلب محاور طرق رئيسية';
|
trafficability = 'صعبة على حواف الأودية - تتطلب محاور طرق رئيسية';
|
||||||
} else if (centerLat >= 31.8 && centerLat <= 32.2 && centerLng >= 35.8 && centerLng <= 36.1) {
|
} else if (centerLat >= 31.85 && centerLat <= 32.05 && centerLng >= 35.8 && centerLng <= 36.02) {
|
||||||
sectorName = 'قاطع الوسط / إقليم العاصمة عمان';
|
sectorName = 'قاطع الوسط / إقليم العاصمة عمان';
|
||||||
terrainClassificationAr = 'هضاب وكتل جبلية حضرية مكتظة بالعمران';
|
terrainClassificationAr = 'هضاب وكتل جبلية حضرية مكتظة بالعمران';
|
||||||
terrainClassification = 'Urbanized Highland Terrain';
|
terrainClassification = 'Urbanized Highland Terrain';
|
||||||
@@ -736,8 +896,8 @@ export async function calculateTerrainStudy(
|
|||||||
urbanDensityAr = 'كثافة عالية (بيئة قتال مباني MOUT)';
|
urbanDensityAr = 'كثافة عالية (بيئة قتال مباني MOUT)';
|
||||||
settlements = ['أحياء ومجمعات العاصمة عمان', 'المناطق التجارية والتقاطعات المرورية', 'مناطق صناعية متقدمة'];
|
settlements = ['أحياء ومجمعات العاصمة عمان', 'المناطق التجارية والتقاطعات المرورية', 'مناطق صناعية متقدمة'];
|
||||||
soilType = 'أرض معبدة وأرصفة إسمنتية مع تربة صخرية';
|
soilType = 'أرض معبدة وأرصفة إسمنتية مع تربة صخرية';
|
||||||
trafficability = 'ممتازة على شبكة الطرق ولكنها مقيدة بالاختناقات المرورية والمباني';
|
trafficability = 'ممتازة على شبكة الطرق ولكنها مقيدة بالمباني والاختناقات';
|
||||||
} else if (centerLng > 36.1) {
|
} else if (centerLng > 36.18) {
|
||||||
sectorName = 'قاطع البادية والشرق / المفرق والصفاوي';
|
sectorName = 'قاطع البادية والشرق / المفرق والصفاوي';
|
||||||
terrainClassificationAr = 'سهول صحراوية مفتوحة وأراضي حرات بازلتية';
|
terrainClassificationAr = 'سهول صحراوية مفتوحة وأراضي حرات بازلتية';
|
||||||
terrainClassification = 'Open Desert Steppe & Basalt Harrah';
|
terrainClassification = 'Open Desert Steppe & Basalt Harrah';
|
||||||
@@ -755,215 +915,19 @@ export async function calculateTerrainStudy(
|
|||||||
settlements = ['مزارع الأغوار الجنوبية', 'مجمعات الفنادق ومصانع البوتاس', 'قرى غور الصافي'];
|
settlements = ['مزارع الأغوار الجنوبية', 'مجمعات الفنادق ومصانع البوتاس', 'قرى غور الصافي'];
|
||||||
soilType = 'طمي نهري وسبخات ملحية وصخور رسوبية';
|
soilType = 'طمي نهري وسبخات ملحية وصخور رسوبية';
|
||||||
trafficability = 'حذرة جداً بجانب السبخات - ممتازة على طريق البحر الميت الرئيسي';
|
trafficability = 'حذرة جداً بجانب السبخات - ممتازة على طريق البحر الميت الرئيسي';
|
||||||
} else if (centerLat < 31.5 && centerLat > 30.0) {
|
|
||||||
sectorName = 'قاطع الجنوب الجبلي / مرتفعات الكرك والشوبك';
|
|
||||||
terrainClassificationAr = 'سلاسل جبلية شاهقة وتلال متصلة ذات إشراف تكتيكي';
|
|
||||||
terrainClassification = 'High Mountainous Plateaus (>1200m)';
|
|
||||||
urbanDensity = 'LOW';
|
|
||||||
urbanDensityAr = 'بلدات جبلية وقرى متفرقة';
|
|
||||||
settlements = ['مدينة الكرك التاريخية', 'قرى المزار والشوبك', 'مزارع الجبال الجنوبية'];
|
|
||||||
soilType = 'صخور جيرية صلبة وحجارة متناثرة';
|
|
||||||
trafficability = 'مقيدة بالممرات الجبلية والمنعطفات الحادة (Slow-Go)';
|
|
||||||
} else if (centerLat <= 30.0) {
|
|
||||||
sectorName = 'قاطع الجنوب الصحراوي / وادي رم والعقبة';
|
|
||||||
terrainClassificationAr = 'جبال رملية شاهقة (Monoliths) تتخللها ممرات ووديان رملية واسعة';
|
|
||||||
terrainClassification = 'Sandstone Monoliths & Desert Valleys';
|
|
||||||
urbanDensity = 'SPARSE';
|
|
||||||
urbanDensityAr = 'شبه خالية (مخيمات سياحية وتجمعات بدوية)';
|
|
||||||
settlements = ['قرية وادي رم', 'مخيمات الديسة السياحية', 'نقاط المراقبة الجنوبية'];
|
|
||||||
soilType = 'رمال ناعمة وصخور رملية ضخمة';
|
|
||||||
trafficability = 'ممتازة للآليات ذات الدفع الرباعي والمجنزرات في بطون الأودية';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine Overall Mobility Status
|
// Mobility status calculation
|
||||||
let mobilityStatus: 'GO' | 'SLOW-GO' | 'NO-GO' = 'GO';
|
let mobilityStatus: 'GO' | 'SLOW-GO' | 'NO-GO' = 'GO';
|
||||||
let mobilityStatusAr = 'حركة حرة ممتازة للآليات والدروع (Cross-Country GO)';
|
let mobilityStatusAr = 'حركة حرة ممتازة للآليات والدروع (Cross-Country GO)';
|
||||||
if (steepPct > 40 || avgSlope > 14) {
|
if (steepPct > 30 || avgSlope > 12) {
|
||||||
mobilityStatus = 'NO-GO';
|
mobilityStatus = 'NO-GO';
|
||||||
mobilityStatusAr = 'مانع تضاريسي - حركة مقيدة وشديدة الصعوبة (NO-GO)';
|
mobilityStatusAr = 'مانع تضاريسي - حركة مقيدة وشديدة الصعوبة (NO-GO)';
|
||||||
} else if (steepPct > 15 || modPct > 40 || avgSlope > 7) {
|
} else if (steepPct > 10 || modPct > 35 || avgSlope > 6) {
|
||||||
mobilityStatus = 'SLOW-GO';
|
mobilityStatus = 'SLOW-GO';
|
||||||
mobilityStatusAr = 'حركة بطيئة تتطلب مسارب وتجهيزاً هندسياً (SLOW-GO)';
|
mobilityStatusAr = 'حركة بطيئة تتطلب مسارب وتجهيزاً هندسياً (SLOW-GO)';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate Spatial Tactical Features for Map Visualization
|
|
||||||
const spatialFeatures: any[] = [];
|
|
||||||
const R_earth = 6371000;
|
|
||||||
|
|
||||||
// 1. Highest Point Feature
|
|
||||||
spatialFeatures.push({
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
layerType: 'point',
|
|
||||||
category: 'peak',
|
|
||||||
title: `🔺 أعلى قمة (${highestPoint.elevation}م)`,
|
|
||||||
color: '#38bdf8',
|
|
||||||
icon: 'mountain'
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Point',
|
|
||||||
coordinates: [highestPoint.lng, highestPoint.lat]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. Lowest Point Feature
|
|
||||||
spatialFeatures.push({
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
layerType: 'point',
|
|
||||||
category: 'valley',
|
|
||||||
title: `🔻 أخفض نقطة (${lowestPoint.elevation}م)`,
|
|
||||||
color: '#f43f5e',
|
|
||||||
icon: 'arrow-down'
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Point',
|
|
||||||
coordinates: [lowestPoint.lng, lowestPoint.lat]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 3. Slope Mobility Sectors (Go / Slow-Go / No-Go Polygons)
|
|
||||||
const numSectors = 8;
|
|
||||||
for (let s = 0; s < numSectors; s++) {
|
|
||||||
const angle1 = (s / numSectors) * 2 * Math.PI;
|
|
||||||
const angle2 = ((s + 1) / numSectors) * 2 * Math.PI;
|
|
||||||
const midAngle = (angle1 + angle2) / 2;
|
|
||||||
|
|
||||||
const pIdx = 1 + Math.floor((s / numSectors) * (samples - 1));
|
|
||||||
const sampleSlope = sampledPoints[pIdx] ? sampledPoints[pIdx].slope : 5;
|
|
||||||
|
|
||||||
let cat = 'go';
|
|
||||||
let catColor = '#22c55e'; // Green
|
|
||||||
let catLabel = 'ممر حركة منبسط (GO)';
|
|
||||||
if (sampleSlope > 15 || (steepPct > 25 && s % 3 === 0)) {
|
|
||||||
cat = 'no-go';
|
|
||||||
catColor = '#ef4444'; // Red
|
|
||||||
catLabel = 'مانع تضاريسي وجروف وعرة (NO-GO)';
|
|
||||||
} else if (sampleSlope >= 5 || s % 2 === 0) {
|
|
||||||
cat = 'slow-go';
|
|
||||||
catColor = '#eab308'; // Yellow
|
|
||||||
catLabel = 'أراضٍ متموجة (SLOW-GO)';
|
|
||||||
}
|
|
||||||
|
|
||||||
const r1 = radiusMeters * 0.95;
|
|
||||||
const dLat1 = (r1 / R_earth) * (180 / Math.PI) * Math.cos(angle1);
|
|
||||||
const dLng1 = (r1 / (R_earth * Math.cos((centerLat * Math.PI) / 180))) * (180 / Math.PI) * Math.sin(angle1);
|
|
||||||
|
|
||||||
const dLat2 = (r1 / R_earth) * (180 / Math.PI) * Math.cos(angle2);
|
|
||||||
const dLng2 = (r1 / (R_earth * Math.cos((centerLat * Math.PI) / 180))) * (180 / Math.PI) * Math.sin(angle2);
|
|
||||||
|
|
||||||
spatialFeatures.push({
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
layerType: 'slope-sector',
|
|
||||||
category: cat,
|
|
||||||
color: catColor,
|
|
||||||
title: catLabel,
|
|
||||||
slope: Math.round(sampleSlope)
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [[
|
|
||||||
[centerLng, centerLat],
|
|
||||||
[centerLng + dLng1, centerLat + dLat1],
|
|
||||||
[centerLng + dLng2, centerLat + dLat2],
|
|
||||||
[centerLng, centerLat]
|
|
||||||
]]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Natural Wadis & Drainage Lines (Cyan LineStrings connecting low points)
|
|
||||||
const wadiCoords: [number, number][] = [
|
|
||||||
[lowestPoint.lng, lowestPoint.lat],
|
|
||||||
[centerLng + (lowestPoint.lng - centerLng) * 0.5 + 0.005, centerLat + (lowestPoint.lat - centerLat) * 0.5 - 0.004],
|
|
||||||
[centerLng - 0.012, centerLat - 0.015],
|
|
||||||
[centerLng - 0.022, centerLat - 0.028]
|
|
||||||
];
|
|
||||||
spatialFeatures.push({
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
layerType: 'wadi',
|
|
||||||
category: 'natural-obstacle',
|
|
||||||
title: '🌊 مجرى وادٍ رئيسي وسيل طبيعي (Wadi Defile)',
|
|
||||||
color: '#06b6d4'
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'LineString',
|
|
||||||
coordinates: wadiCoords
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 5. Main Road Transportation Corridors (Amber LineStrings)
|
|
||||||
const roadCoords: [number, number][] = [
|
|
||||||
[centerLng - 0.025, centerLat + 0.02],
|
|
||||||
[centerLng - 0.008, centerLat + 0.006],
|
|
||||||
[centerLng + 0.01, centerLat - 0.008],
|
|
||||||
[centerLng + 0.026, centerLat - 0.022]
|
|
||||||
];
|
|
||||||
spatialFeatures.push({
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
layerType: 'road',
|
|
||||||
category: 'infrastructure',
|
|
||||||
title: '🛣️ محور طريق رئيسي معبد (Main Supply Route - MSR)',
|
|
||||||
color: '#f59e0b'
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'LineString',
|
|
||||||
coordinates: roadCoords
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 6. Urbanized Settlements Polygon (Purple Polygon)
|
|
||||||
const uR = radiusMeters * 0.28;
|
|
||||||
const uLat = centerLat + 0.006;
|
|
||||||
const uLng = centerLng + 0.008;
|
|
||||||
const urbanBoxCoords: [number, number][] = [
|
|
||||||
[uLng - 0.008, uLat - 0.006],
|
|
||||||
[uLng + 0.008, uLat - 0.006],
|
|
||||||
[uLng + 0.01, uLat + 0.007],
|
|
||||||
[uLng - 0.007, uLat + 0.008],
|
|
||||||
[uLng - 0.008, uLat - 0.006]
|
|
||||||
];
|
|
||||||
spatialFeatures.push({
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
layerType: 'urban',
|
|
||||||
category: 'urban-area',
|
|
||||||
title: `🏙️ منطقة مأهولة وعمران (${urbanDensityAr})`,
|
|
||||||
color: '#a855f7'
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [urbanBoxCoords]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 7. Hazardous Ground / Basalt / Difficult Rocks Zone (Dark Red Polygon)
|
|
||||||
const hLat = centerLat - 0.008;
|
|
||||||
const hLng = centerLng - 0.01;
|
|
||||||
const hazardCoords: [number, number][] = [
|
|
||||||
[hLng - 0.006, hLat - 0.005],
|
|
||||||
[hLng + 0.007, hLat - 0.004],
|
|
||||||
[hLng + 0.006, hLat + 0.006],
|
|
||||||
[hLng - 0.005, hLat + 0.005],
|
|
||||||
[hLng - 0.006, hLat - 0.005]
|
|
||||||
];
|
|
||||||
spatialFeatures.push({
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
layerType: 'hazard',
|
|
||||||
category: 'difficult-terrain',
|
|
||||||
title: '🪨 أرض صخرية وعرة / حرة بازلتية (Difficult Terrain)',
|
|
||||||
color: '#dc2626'
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [hazardCoords]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
centerLat,
|
centerLat,
|
||||||
centerLng,
|
centerLng,
|
||||||
@@ -993,73 +957,71 @@ export async function calculateTerrainStudy(
|
|||||||
},
|
},
|
||||||
naturalObstacles: [
|
naturalObstacles: [
|
||||||
{
|
{
|
||||||
name: 'المنحدرات والجروف التضاريسية الحادة',
|
name: 'مجاري الوديان والسيول الطبيعية (Drainage Defiles)',
|
||||||
type: 'انحدار طبوغرافي',
|
type: 'مصارف مائية وأودية',
|
||||||
description: `تشكل المناطق ذات الانحدار الشديد (${steepPct}%) مانعاً طبيعياً يحد من تقدم الدروع ويجبرها على الالتفاف.`,
|
description: `بطن الوادي عند منسوب ${minElev}م يشكل مصرفاً مائياً رئيسياً، يوفر ممراً محجوباً ولكن قد يعيق المناورة عند هطول الأمطار.`,
|
||||||
impact: 'إعاقة محاور الهجوم المعادي وتوجيهه نحو الخناق المجهزة بالنيران'
|
impact: 'محور تسلل مناسب للمشاة ومقيد للآليات الثقيلة عند تشكل الأوحال'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'الأودية والشعاب ومجاري السيول الطبيعية',
|
name: 'التلال والقمم الحاكمة (Dominant Ridges)',
|
||||||
type: 'أودية ومصارف مائية',
|
|
||||||
description: 'بطون الأودية تمثل ممرات حركة محجوبة عن خط النظر ولكنها عرضة للكمائن والسيول الفجائية.',
|
|
||||||
impact: 'تصلح كمسارب تسلل للمشاة ولكنها خطرة للآليات الثقيلة'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'القمم والمرتفعات الحاكمة المجاورة',
|
|
||||||
type: 'أرض مسيطرة (Key Terrain)',
|
type: 'أرض مسيطرة (Key Terrain)',
|
||||||
description: `أعلى قمة في القطاع بارتفاع ${maxElev}م توفر إشرافاً نارياً واستطلاعياً على عمق يصل إلى ${Math.round(radiusMeters / 500)} كم.`,
|
description: `المرتفع الحاكم بارتفاع ${maxElev}م يشرف بشكل كامل على بطن الوادي ومحاور التحرك على مسافة ${radiusMeters / 1000} كم.`,
|
||||||
impact: 'وجوب احتلالها بنقاط مراقبة وأسلحة إسناد مضادة للدروع'
|
impact: 'موقع استراتيجي لتمركز أسلحة الإسناد ونقاط الملاحظة والاستطلاع'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'المنحدرات التضاريسية المحيطة',
|
||||||
|
type: 'انحدار طبوغرافي',
|
||||||
|
description: steepPct > 0
|
||||||
|
? `يوجد ${steepPct}% من مساحة القطاع ذات انحدار وعر (>12°) تشكل موانع طبيعية تعيق صعود الآليات.`
|
||||||
|
: `القطاع يتسم بانحدارات لطيفة إلى متوسطة (أقصى انحدار ${maxSlope}°)، ولا توجد جروف شاهقة مانعة لحركة الدروع.`,
|
||||||
|
impact: steepPct > 0 ? 'إجبار القوات المهاجمة على استخدام الممرات المعبدة' : 'حرية مناورة واسعة للدروع في مختلف الاتجاهات'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
manMadeObstacles: [
|
manMadeObstacles: [
|
||||||
{
|
{
|
||||||
name: 'شبكة الطرق والمحاور المعبدة',
|
name: 'محاور الطرق الرئيسية وخطوط الإمداد (MSR)',
|
||||||
type: 'محاور حركة سريعة',
|
type: 'بنية تحتية للمواصلات',
|
||||||
description: 'الطرق الرئيسية المارة بالقطاع تتيح سرعة إعادة انتشار القوات والإمداد اللوجستي.',
|
description: 'الطرق المعبدة المارة بالوادي تتيح تدفقاً سريعاً للأرتال وإعادة التزود اللوجستي.',
|
||||||
tacticalNote: 'تعتبر أهدافاً حيوية يجب تأمينها بنقاط سيطرة وتفتيش وغلق سريع'
|
tacticalNote: 'وجوب إعداد خطط قطع وغلق هندسي سريع للتحكم بحركة العدو'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'خطوط نقل الطاقة وأبراج الاتصالات',
|
name: 'المنشآت والكتل العمرانية',
|
||||||
type: 'بنية تحتية صناعية',
|
type: 'موانع مبنية وسواتر خرسانية',
|
||||||
description: 'أبراج الضغط العالي والاتصالات تشكل عوائق أمام الطيران المنخفض ومواقع محتملة لمحطات الإعاقة اللاسلكية.',
|
description: `البلدات والتجمعات السكانية (${settlements.slice(0, 3).join('، ')}) توفر سواتر ممتازة ضد النيران والشظايا.`,
|
||||||
tacticalNote: 'تجنب هبوط المروحيات بالقرب من خطوط الضغط العالي'
|
tacticalNote: 'تتطلب عمليات تطهير متقدمة وحماية للمدنيين'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'المباني والمنشآت الخرسانية الممتدة',
|
name: 'أبراج وشبكات نقل الطاقة والاتصالات',
|
||||||
type: 'موانع مبنية',
|
type: 'عوائق صناعية بارزة',
|
||||||
description: 'المباني المتفرقة توفر سواتر خرسانية ممتازة ضد الشظايا ونيران الأسلحة الخفيفة.',
|
description: 'خطوط الضغط العالي والاتصالات تشكل خطراً على الطيران المنخفض ومواقع محتملة للتشويش.',
|
||||||
tacticalNote: 'تتطلب تطهيراً هندسياً لضمان خلوها من العبوات والأفخاخ'
|
tacticalNote: 'تجنب تحديد مهابط الطيران بالقرب من مسارات خطوط الطاقة'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
urbanAndDemographics: {
|
urbanAndDemographics: {
|
||||||
density: urbanDensity,
|
density: urbanDensity,
|
||||||
densityAr: urbanDensityAr,
|
densityAr: urbanDensityAr,
|
||||||
settlements,
|
settlements,
|
||||||
moutComplexity: urbanDensity === 'HIGH' ? 'معقدة جداً وتتطلب تدريب قتال مباني متخصص' : 'بسيطة إلى متوسطة وتقتصر على أطراف القرى',
|
moutComplexity: urbanDensity === 'HIGH' ? 'بيئة قتال مباني حضرية متقدمة (MOUT)' : 'بيئة قتال ريفية مفتوحة تتخللها مزارع متفرقة',
|
||||||
collateralDamageRisk: urbanDensity === 'HIGH' ? 'عالية جداً وتتطلب ذخائر موجهة وقواعد اشتباك صارمة' : 'منخفضة مع مراعاة حماية الأعيان المدنية'
|
collateralDamageRisk: urbanDensity === 'HIGH' ? 'عالية وتتطلب مراعاة قواعد الاشتباك الدقيقة' : 'منخفضة مع حماية الممتلكات المدنية'
|
||||||
},
|
},
|
||||||
landCoverAndCover: {
|
landCoverAndCover: {
|
||||||
soilType,
|
soilType,
|
||||||
trafficability,
|
trafficability,
|
||||||
concealmentRating: steepPct > 20 || urbanDensity === 'HIGH' ? 'ممتازة خلف التموجات والمباني' : 'متوسطة إلى مكشوفة في الأراضي المفتوحة',
|
concealmentRating: urbanDensity === 'HIGH' || relief > 100 ? 'ممتازة خلف التلال والكتل الخرسانية' : 'متوسطة إلى مكشوفة في الأراضي المفتوحة',
|
||||||
airObservationExposure: urbanDensity === 'HIGH' ? 'مختلطة (مباني تحجب الرؤية الجوية)' : 'عالية للرصد الجوي والمسيرات نهاراً'
|
airObservationExposure: urbanDensity === 'HIGH' ? 'مختلطة (المباني توفر إخفاء جزئياً)' : 'عالية للرصد الجوي والمسيرات نهاراً'
|
||||||
},
|
},
|
||||||
oakocAssessment: {
|
oakocAssessment: {
|
||||||
obstacles: `القطاع يحتوي على ${steepPct}% أراضٍ شديدة الوعورة تشكل موانع طبيعية، مع وجود محاور طرق رئيسية تشكل ممرات حركة سريعة.`,
|
obstacles: `القطاع يتضمن مجرى وادٍ رئيسي عند منسوب ${minElev}م مع انحدارات تصل إلى ${maxSlope}°، مما يشكل محاور حركة طبيعية محددة.`,
|
||||||
avenuesOfApproach: 'محاور الاقتراب الرئيسية تتبع بطون الهضاب وشبكة الطرق الفرعية، مما يسهل رصدها وتغطيتها بالمدفعية ومضادات الدروع.',
|
avenuesOfApproach: 'محور الاقتراب الرئيسي يتبع بطن الوادي وشبكة الطرق الرئيسية، مع إمكانية الالتفاف عبر الهضاب المنبسطة.',
|
||||||
keyTerrain: `المرتفع ${maxElev}م يشكل الأرض الحاكمة (Decisive Terrain) والسيطرة عليه تمنح تفوقاً نارياً حاسماً.`,
|
keyTerrain: `القمة الحاكمة بارتفاع ${maxElev}م توفر ميزة كشف وهيمنة نارية حاسمة على كامل القطاع.`,
|
||||||
observationAndFieldsOfFire: `حقول الرماية مكشوفة ومثالية في قطاعات (${flatPct}%) المنبسطة، ومقيدة في قطاعات الأودية والعمران.`,
|
observationAndFieldsOfFire: `حقول الرماية مكشوفة ومثالية بنسبة ${flatPct}% في الأراضي المنبسطة، ومحجوبة داخل بطون الأودية.`,
|
||||||
coverAndConcealment: 'السواتر الطبيعية متوفرة على المنحدرات المعاكسة (Reverse Slopes) وفي الجيوب المنخفضة.'
|
coverAndConcealment: 'تتوفر السواتر التضاريسية في المنحدرات الخلفية (Reverse Slopes) وداخل التجمعات العمرانية.'
|
||||||
},
|
},
|
||||||
tacticalRecommendations: [
|
tacticalRecommendations: [
|
||||||
`تمركز نقاط المراقبة والرادار على المرتفع الحاكم بارتفاع ${maxElev}م لتأمين الإنذار المبكر.`,
|
`نشر مرصد استطلاع رئيسي ورادار كشف أرضي على المرتفع الحاكم (${maxElev}م) لتأمين إنذار مبكر ومراقبة بطن الوادي.`,
|
||||||
'استغلال المنحدرات الشديدة كمانع طبيعي وزرع حقول الألغام الدفاعية في الممرات الإجبارية والخناق.',
|
`تجهيز نقاط كمائن وموانع هندسية وحقول ألغام موجهة في مخانق بطن الوادي (${minElev}م) لصد أي تقدم مدرع.`,
|
||||||
'تأمين محاور الطرق المعبدة وتجهيز خطط غلق هندسي فورية لقطع تقدم أرتال العدو السريعة.',
|
`تأمين الطرق الرئيسية والمحاور الحيوية بنقاط سيطرة وحراسة ثابتة لمنع عمليات الالتفاف السريعة.`,
|
||||||
'نشر وحدات المشاة الآلية في المنحدرات الخلفية لحمايتها من الرصد والنار المباشرة (Defilade).',
|
`استغلال المناطق المنبسطة (${flatPct}%) لإنشاء مهابط المروحيات (HLZ) ومناطق الإخلاء الطبي والتجمع اللوجستي.`
|
||||||
'تخصيص مهابط المروحيات والإخلاء في المناطق المستوية ذات الانحدار الأقل من 5 درجات.'
|
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user