From b636389ee1acfcd9a325aa35c4bc48483ee3cfb4 Mon Sep 17 00:00:00 2001 From: viktot14-ai Date: Wed, 9 Sep 2026 19:58:05 +0300 Subject: [PATCH] =?UTF-8?q?UI=20=D0=BA=D0=B0=D1=80=D1=82=D1=8B:=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=BE=D1=80=D0=B8=D1=82=D0=B5=D1=82=20=E2=80=94=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=86=D0=B5=D0=BD=D1=82=D1=80=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=B8=D0=B5=20=D0=BA=D1=80=D1=83=D0=B3=D0=B8?= =?UTF-8?q?=20+=20=D0=BF=D0=BE=D0=B4=D1=81=D0=B2=D0=B5=D1=82=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=BD=D0=B0=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Дизайн Виктора (классика ПСО): возможная зона поиска — круг радиусом max_distance от ТНП (ядро 0.5R / средняя 0.75R / внешняя R с градацией плотности вероятности), а приоритетные направления ПОДСВЕЧИВАЮТСЯ 45°-секторами полного радиуса поверх кругов (топ-1 #ff4d00 op0.30, топ-2 #ff9f1a 0.22, топ-3 #ffd166 0.18, прочие серые 0.10). Раньше сектора были «пирогами» разного радиуса по score — зона выглядела лоскутной. Теперь круги показывают охват, подсветка — приоритет. --- frontend/src/components/SearchMap.jsx | 148 +++++++++++++------------- 1 file changed, 75 insertions(+), 73 deletions(-) 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} +
+
+ ))} {/* ТНП - красный маркер */}