UI карты: приоритет — концентрические круги + подсветка направлений

Дизайн Виктора (классика ПСО): возможная зона поиска — круг радиусом
max_distance от ТНП (ядро 0.5R / средняя 0.75R / внешняя R с градацией
плотности вероятности), а приоритетные направления ПОДСВЕЧИВАЮТСЯ
45°-секторами полного радиуса поверх кругов (топ-1 #ff4d00 op0.30,
топ-2 #ff9f1a 0.22, топ-3 #ffd166 0.18, прочие серые 0.10).
Раньше сектора были «пирогами» разного радиуса по score — зона выглядела
лоскутной. Теперь круги показывают охват, подсветка — приоритет.
This commit is contained in:
2026-09-09 19:58:05 +03:00
parent 2aa45e8932
commit b636389ee1
+75 -73
View File
@@ -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 }) => {
</Polyline>
))}
{/* Рисуем секторы или круги */}
{sectors.length > 0 ? (
sectors.map((sector, idx) => (
<Polygon
key={idx}
positions={sector.points}
pathOptions={{
color: sector.color,
fillColor: sector.color,
fillOpacity: sector.fillOpacity,
weight: 2
}}
>
<Popup>
<strong>#{idx + 1} {sector.zone.name}</strong><br/>
Направление: {sector.zone.direction}<br/>
Приоритет: {sector.zone.score}%<br/>
{sector.zone.reasoning}
</Popup>
</Polygon>
))
) : (
defaultZones.map((zone, idx) => (
<Polygon
key={idx}
positions={zone.points}
pathOptions={{
color: zone.color,
fillColor: zone.color,
fillOpacity: zone.fillOpacity,
weight: 2
}}
>
<Popup>
<strong>{zone.label}</strong><br/>
Вероятность: {zone.probability}%
</Popup>
</Polygon>
))
)}
{/* Базовый слой: концентрические круги возможной зоны поиска */}
{defaultZones.map((zone, idx) => (
<Polygon
key={`circle-${idx}`}
positions={zone.points}
pathOptions={{
color: zone.color,
fillColor: zone.color,
fillOpacity: zone.fillOpacity,
weight: 1.5,
dashArray: idx === 0 ? '6 4' : undefined
}}
>
<Popup>
<strong>{zone.label}</strong><br/>
Плотность вероятности: {zone.probability}
</Popup>
</Polygon>
))}
{/* Подсветка приоритетных направлений (поверх кругов) */}
{highlightSectors.map((sector, idx) => (
<Polygon
key={`hl-${idx}`}
positions={sector.points}
pathOptions={{
color: sector.color,
fillColor: sector.color,
fillOpacity: sector.fillOpacity,
weight: sector.weight
}}
>
<Popup>
<strong>#{idx + 1} {sector.zone.name}</strong><br/>
Направление: {sector.zone.direction}<br/>
Приоритет: {sector.zone.score}%<br/>
{sector.zone.reasoning}
</Popup>
</Polygon>
))}
{/* ТНП - красный маркер */}
<Marker