feat(tactical): high-resolution 21x21 DEM terrain study, bilinear interpolation, cliff/rock barrier detection, and 4-tier slope classification
This commit is contained in:
@@ -551,6 +551,7 @@ export const TacticalDefenseView: React.FC = () => {
|
||||
'fill-opacity': [
|
||||
'match',
|
||||
['get', 'layerType'],
|
||||
'cliff', 0.50,
|
||||
'urban', 0.28,
|
||||
'hazard', 0.32,
|
||||
'slope-sector', 0.18,
|
||||
@@ -566,8 +567,18 @@ export const TacticalDefenseView: React.FC = () => {
|
||||
filter: ['==', '$type', 'Polygon'],
|
||||
paint: {
|
||||
'line-color': ['get', 'color'],
|
||||
'line-width': 2,
|
||||
'line-dasharray': [3, 2]
|
||||
'line-width': [
|
||||
'match',
|
||||
['get', 'layerType'],
|
||||
'cliff', 3.5,
|
||||
2
|
||||
],
|
||||
'line-dasharray': [
|
||||
'match',
|
||||
['get', 'layerType'],
|
||||
'cliff', ['literal', [2, 1]],
|
||||
['literal', [3, 2]]
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -581,6 +592,7 @@ export const TacticalDefenseView: React.FC = () => {
|
||||
'line-width': [
|
||||
'match',
|
||||
['get', 'layerType'],
|
||||
'cliff', 4.5,
|
||||
'road', 4.5,
|
||||
'wadi', 3.5,
|
||||
3
|
||||
@@ -588,6 +600,7 @@ export const TacticalDefenseView: React.FC = () => {
|
||||
'line-dasharray': [
|
||||
'match',
|
||||
['get', 'layerType'],
|
||||
'cliff', ['literal', [2, 1]],
|
||||
'road', ['literal', [1, 0]],
|
||||
'wadi', ['literal', [2, 1.5]],
|
||||
['literal', [1, 0]]
|
||||
@@ -817,10 +830,10 @@ export const TacticalDefenseView: React.FC = () => {
|
||||
const filtered = terrainResult.spatialGeoJson.features.filter((f: any) => {
|
||||
if (f.properties.category === 'peak' || f.properties.category === 'valley') return showPeaksAndValleys;
|
||||
if (f.properties.layerType === 'slope-sector') return showSlopeSectors;
|
||||
if (f.properties.layerType === 'wadi' || f.properties.layerType === 'ridge') return showNaturalObstacles;
|
||||
if (f.properties.layerType === 'wadi' || f.properties.layerType === 'ridge' || f.properties.layerType === 'cliff') return showNaturalObstacles;
|
||||
if (f.properties.layerType === 'road') return showRoadCorridors;
|
||||
if (f.properties.layerType === 'urban') return showUrbanZones;
|
||||
if (f.properties.layerType === 'hazard') return showHazardousGround;
|
||||
if (f.properties.layerType === 'hazard' || f.properties.layerType === 'cliff') return showHazardousGround;
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -1652,24 +1665,72 @@ ${terrainResult.tacticalRecommendations.map((r, i) => `${i + 1}. ${r}`).join('\n
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Slope Distribution Bar */}
|
||||
{/* Slope Distribution Bar (4 Military Tiers) */}
|
||||
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8 }}>
|
||||
<div style={{ fontSize: '0.72rem', fontWeight: 700, color: '#cbd5e1', marginBottom: 6, display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>توزيع درجات الانحدار (Slope Analysis):</span>
|
||||
<span style={{ color: '#94a3b8' }}>متوسط: {terrainResult.avgSlopeDegrees}°</span>
|
||||
</div>
|
||||
<div style={{ height: 10, width: '100%', display: 'flex', borderRadius: 5, overflow: 'hidden', marginBottom: 6 }}>
|
||||
<div style={{ width: `${terrainResult.slopeDistribution.flatPercentage}%`, background: '#22c55e' }} title={`منبسط: ${terrainResult.slopeDistribution.flatPercentage}%`} />
|
||||
<div style={{ width: `${terrainResult.slopeDistribution.moderatePercentage}%`, background: '#eab308' }} title={`متوسط: ${terrainResult.slopeDistribution.moderatePercentage}%`} />
|
||||
<div style={{ width: `${terrainResult.slopeDistribution.steepPercentage}%`, background: '#ef4444' }} title={`شديد الوعورة: ${terrainResult.slopeDistribution.steepPercentage}%`} />
|
||||
<div style={{ width: `${terrainResult.slopeDistribution.flatPercentage}%`, background: '#22c55e' }} title={`منبسط (<7°): ${terrainResult.slopeDistribution.flatPercentage}%`} />
|
||||
<div style={{ width: `${terrainResult.slopeDistribution.moderatePercentage}%`, background: '#eab308' }} title={`تضاريس متموجة (7-15°): ${terrainResult.slopeDistribution.moderatePercentage}%`} />
|
||||
<div style={{ width: `${terrainResult.slopeDistribution.steepPercentage}%`, background: '#f97316' }} title={`انحدار وعر (15-25°): ${terrainResult.slopeDistribution.steepPercentage}%`} />
|
||||
<div style={{ width: `${terrainResult.slopeDistribution.cliffPercentage}%`, background: '#ef4444' }} title={`قواطع وجروف صخرية (>25°): ${terrainResult.slopeDistribution.cliffPercentage}%`} />
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.68rem', color: '#94a3b8' }}>
|
||||
<span style={{ color: '#4ade80' }}>■ منبسط (<5°): {terrainResult.slopeDistribution.flatPercentage}%</span>
|
||||
<span style={{ color: '#facc15' }}>■ متوسط (5-15°): {terrainResult.slopeDistribution.moderatePercentage}%</span>
|
||||
<span style={{ color: '#f87171' }}>■ وعر (>15°): {terrainResult.slopeDistribution.steepPercentage}%</span>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '3px 8px', fontSize: '0.66rem', color: '#94a3b8' }}>
|
||||
<span style={{ color: '#4ade80' }}>■ منبسط (<7°): {terrainResult.slopeDistribution.flatPercentage}%</span>
|
||||
<span style={{ color: '#facc15' }}>■ متموج (7-15°): {terrainResult.slopeDistribution.moderatePercentage}%</span>
|
||||
<span style={{ color: '#fb923c' }}>■ وعر (15-25°): {terrainResult.slopeDistribution.steepPercentage}%</span>
|
||||
<span style={{ color: '#f87171' }}>■ جروف صخرية (>25°): {terrainResult.slopeDistribution.cliffPercentage}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Detected Cliffs & Natural Rock Barriers */}
|
||||
{terrainResult.cliffs && terrainResult.cliffs.length > 0 && (
|
||||
<div style={{ background: 'rgba(239, 68, 68, 0.08)', padding: 10, borderRadius: 8, border: '1px solid rgba(239, 68, 68, 0.35)' }}>
|
||||
<div style={{ fontSize: '0.76rem', fontWeight: 800, color: '#f87171', marginBottom: 6, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
|
||||
<AlertTriangle size={14} /> 🧗♂️ القواطع والجروف الصخرية المرصودة ({terrainResult.cliffsCount})
|
||||
</span>
|
||||
<span style={{ fontSize: '0.62rem', background: '#ef4444', color: '#fff', padding: '1px 6px', borderRadius: 10, fontWeight: 700 }}>مانع NO-GO</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 5, maxHeight: 160, overflowY: 'auto' }}>
|
||||
{terrainResult.cliffs.map((cliff, cIdx) => (
|
||||
<div
|
||||
key={cIdx}
|
||||
onClick={() => {
|
||||
if (map.current) {
|
||||
map.current.flyTo({ center: [cliff.lng, cliff.lat], zoom: 15.5, pitch: 55 });
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
background: 'rgba(0,0,0,0.3)',
|
||||
padding: '6px 8px',
|
||||
borderRadius: 6,
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
border: '1px solid rgba(239, 68, 68, 0.2)'
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontSize: '0.72rem', fontWeight: 700, color: '#fca5a5' }}>
|
||||
قاطع #{cIdx + 1}: سقوط {cliff.dropMeters}م (ميل {cliff.slopeDegrees}°)
|
||||
</div>
|
||||
<div style={{ fontSize: '0.62rem', color: '#94a3b8', fontFamily: 'monospace' }}>
|
||||
{cliff.lat}, {cliff.lng}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: '0.65rem', color: '#f87171', fontWeight: 800, background: 'rgba(239,68,68,0.15)', padding: '2px 6px', borderRadius: 4 }}>
|
||||
🔍 تركيز
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* SECTION 1: NATURAL OBSTACLES */}
|
||||
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(239, 68, 68, 0.2)' }}>
|
||||
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f87171', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
|
||||
Reference in New Issue
Block a user