diff --git a/frontend/src/components/SearchMap.jsx b/frontend/src/components/SearchMap.jsx index ccc96c1..060bd05 100644 --- a/frontend/src/components/SearchMap.jsx +++ b/frontend/src/components/SearchMap.jsx @@ -15,9 +15,9 @@ const SearchMap = ({ tnpLat, tnpLon, maxDistance, zones, waterFeatures }) => { const tnpPosition = [tnpLat || 53.9, tnpLon || 27.56]; const distance = maxDistance || 5.0; - // Функция для создания точек сектора - const createSectorPoints = (centerLat, centerLon, radiusKm, startAngle, endAngle, points = 20) => { - const coords = [[centerLat, centerLon]]; // Начинаем с центра + // Функция для создания точек дуги + const createArcPoints = (centerLat, centerLon, radiusKm, startAngle, endAngle, points = 24) => { + const coords = []; // Конвертируем км в градусы (приблизительно) const latPerKm = 1 / 111.0; @@ -34,9 +34,15 @@ const SearchMap = ({ tnpLat, tnpLon, maxDistance, zones, waterFeatures }) => { coords.push([lat, lon]); } - coords.push([centerLat, centerLon]); // Замыкаем в центре return coords; }; + + // Функция для создания точек сектора (пирог: дуга + центр) + const createSectorPoints = (centerLat, centerLon, radiusKm, startAngle, endAngle, points = 24) => { + return [[centerLat, centerLon], + ...createArcPoints(centerLat, centerLon, radiusKm, startAngle, endAngle, points), + [centerLat, centerLon]]; + }; // Направления и их углы const directionAngles = { @@ -50,45 +56,41 @@ const SearchMap = ({ tnpLat, tnpLon, maxDistance, zones, waterFeatures }) => { 'NW': 315 }; - // Создаем секторы на основе зон - const sectors = zones && zones.length > 0 ? zones.slice(0, 8).map((zone, idx) => { - const angle = directionAngles[zone.direction] || 0; - const startAngle = angle - 22.5; // ±22.5° = 45° сектор + // Подсветка приоритетных направлений: 45°-сектор ПОЛНОГО радиуса поверх кругов. + // Приоритет задаёт прозрачность (топ-зона заметнее), цвет — палитра ЦОУ. + const highlightSectors = zones && zones.length > 0 ? zones.map((zone, idx) => { + const angle = directionAngles[zone.direction]; + if (angle === undefined) return null; + const startAngle = angle - 22.5; const endAngle = angle + 22.5; - // Определяем радиус на основе score - let radius = distance; - if (zone.score >= 90) radius = distance * 1.0; - else if (zone.score >= 70) radius = distance * 0.8; - else if (zone.score >= 50) radius = distance * 0.6; - else radius = distance * 0.4; - - // Цвет на основе приоритета let color, fillOpacity; if (idx === 0) { color = '#ff4d00'; - fillOpacity = 0.35; + fillOpacity = 0.30; } else if (idx === 1) { color = '#ff9f1a'; - fillOpacity = 0.25; + fillOpacity = 0.22; } else if (idx === 2) { color = '#ffd166'; - fillOpacity = 0.2; + fillOpacity = 0.18; } else { color = '#8b949e'; - fillOpacity = 0.1; + fillOpacity = 0.10; } return { - points: createSectorPoints(tnpPosition[0], tnpPosition[1], radius, startAngle, endAngle), + points: createSectorPoints(tnpPosition[0], tnpPosition[1], distance, startAngle, endAngle), color, fillOpacity, + weight: idx < 3 ? 2 : 1, zone }; - }) : []; + }).filter(Boolean) : []; - // Если зон нет, создаем базовые концентрические круги - const createCirclePoints = (centerLat, centerLon, radiusKm, points = 40) => { + // Концентрические круги — возможная зона поиска (базовый слой). + // Классика ПСО: ядро (0.5R) — наибольшая плотность вероятности. + const createCirclePoints = (centerLat, centerLon, radiusKm, points = 48) => { const coords = []; const latPerKm = 1 / 111.0; const lonPerKm = 1 / (111.0 * Math.cos(centerLat * Math.PI / 180)); @@ -107,23 +109,23 @@ const SearchMap = ({ tnpLat, tnpLon, maxDistance, zones, waterFeatures }) => { { points: createCirclePoints(tnpPosition[0], tnpPosition[1], distance), color: '#ffd166', - fillOpacity: 0.15, - label: `Зона 3 (${distance.toFixed(1)} км)`, - probability: 10 + fillOpacity: 0.12, + label: `Внешняя зона (до ${distance.toFixed(1)} км)`, + probability: 'низкая' }, { points: createCirclePoints(tnpPosition[0], tnpPosition[1], distance * 0.75), color: '#ff9f1a', - fillOpacity: 0.2, - label: `Зона 2 (${(distance * 0.75).toFixed(1)} км)`, - probability: 20 + fillOpacity: 0.16, + label: `Средняя зона (до ${(distance * 0.75).toFixed(1)} км)`, + probability: 'средняя' }, { points: createCirclePoints(tnpPosition[0], tnpPosition[1], distance * 0.5), color: '#ff4d00', - fillOpacity: 0.3, - label: `Зона 1 (${(distance * 0.5).toFixed(1)} км)`, - probability: 70 + fillOpacity: 0.20, + label: `Ядро зоны (до ${(distance * 0.5).toFixed(1)} км)`, + probability: 'высокая' } ]; @@ -192,46 +194,46 @@ const SearchMap = ({ tnpLat, tnpLon, maxDistance, zones, waterFeatures }) => { ))} - {/* Рисуем секторы или круги */} - {sectors.length > 0 ? ( - sectors.map((sector, idx) => ( - - - #{idx + 1} {sector.zone.name}
- Направление: {sector.zone.direction}
- Приоритет: {sector.zone.score}%
- {sector.zone.reasoning} -
-
- )) - ) : ( - defaultZones.map((zone, idx) => ( - - - {zone.label}
- Вероятность: {zone.probability}% -
-
- )) - )} + {/* Базовый слой: концентрические круги возможной зоны поиска */} + {defaultZones.map((zone, idx) => ( + + + {zone.label}
+ Плотность вероятности: {zone.probability} +
+
+ ))} + + {/* Подсветка приоритетных направлений (поверх кругов) */} + {highlightSectors.map((sector, idx) => ( + + + #{idx + 1} {sector.zone.name}
+ Направление: {sector.zone.direction}
+ Приоритет: {sector.zone.score}%
+ {sector.zone.reasoning} +
+
+ ))} {/* ТНП - красный маркер */}