# Интерфейс карты — документация

## Стек

| Библиотека | Версия | Назначение |
|-----------|--------|------------|
| OpenLayers | 10.3.1 | Карта, слои, треки, маркеры |
| Turf.js | 6 | Геометрия шумовых зон (buffer + difference) |

**Почему OpenLayers, а не Leaflet/MapLibre:**
- Leaflet: отказались — слабый рендеринг при большом числе объектов
- MapLibre GL: отказались — требует WebGL, заблокирован в sandbox-браузере
- OpenLayers: Canvas2D, работает везде без WebGL

---

## Слои карты (z-order снизу вверх)

```
zIndex 0   — тайлы OSM (фон)
zIndex 1–4 — шумовые зоны (zone_low → zone_critical)
             zone_low     zIndex 1 (самая широкая, снизу)
             zone_medium  zIndex 2
             zone_strong  zIndex 3
             zone_critical zIndex 4 (самая узкая, поверх)
zIndex 50  — треки рейсов
zIndex 60  — маркеры аэропортов
zIndex 70  — флажки пользователя
zIndex 80  — линейка
```

---

## Цвет треков

Градиент по высоте: `hsl(0°→120°, 100%, 48%)`

```
0 м       = hsl(0°)   = #F50000  красный
4 250 м   = hsl(60°)  = #F5F500  жёлтый
8 500+ м  = hsl(120°) = #00F500  зелёный
```

Параметр `TRACK_MAX_ALT_M = 8500` задан в `index.html` — меняй для калибровки.

---

## Шумовые зоны

Строятся через Turf.js **посегментно** — для каждого сегмента трека.
Значения `d_inner`/`d_outer` приходят с бэкенда в поле `zone_radii` каждой точки.

```javascript
// Кольцо = внешний буфер минус внутренний
outerBuf = turf.buffer(segLine, d_outer, { units: 'km', steps: 6 });
innerBuf = turf.buffer(segLine, d_inner, { units: 'km', steps: 6 });
ring     = turf.difference(outerBuf, innerBuf);
```

`steps: 6` — количество сегментов на полукруг буфера. Увеличить для более
гладких зон (медленнее), уменьшить для скорости.

---

## Фильтры (sidebar)

| Фильтр | Параметр API | Значения |
|--------|-------------|---------|
| Аэропорт | `airport` | all / SVO / DME / VKO / ZIA |
| Тип рейса | `type` | all / departure / arrival |
| Мин. высота | `min_alt` | 0–13 000 м (конвертируется в футы для API) |
| Макс. высота | `max_alt` | 100–13 000 м |
| Период | `date_from`, `date_to` | YYYY-MM-DD |

---

## Флажки

- **Добавить:** кнопка 📍 → клик на карту → флажок
- **Удалить:** клик на флажок на карте
- **Переименовать:** двойной клик на название в списке → prompt()
- **Очистить всё:** кнопка 🗑 Все
- Цвета: 6 цветов по кругу `['#e94560','#00ccff','#ffcc00','#00cc55','#ff8800','#cc44ff']`

---

## Линейка

- **Включить:** кнопка 📏 → курсор crosshair
- **Добавить точку:** клик на карту
- **Завершить:** двойной клик → автовыключение
- **Сбросить:** кнопка 🗑
- Расстояние считается по формуле **Haversine**
- Показывает итог и разбивку по сегментам

---

## Tooltip при наведении на трек

Показывает:
- Callsign
- Тип ВС
- Высота (метры)
- Уровень шума (дБ) + зона

---

## Боковая панель — детали рейса (клик)

Показывает:
- Callsign + тип рейса (🛫 Вылет / 🛬 Прилёт)
- Номер рейса, тип ВС, регистрация
- Откуда → Куда с названием города: `Сочи (AER)` → `Москва (SVO)`
- Дата полёта (из временных меток трека)
- Вход в МО / Выход из МО (время МСК, UTC+3)
- Высота (метры), скорость (км/ч)
- Уровень шума (дБ) + цветной badge

Названия городов берутся из встроенного словаря `IATA_CITIES` в `index.html` (~80 аэропортов).
Если код не найден в словаре — показывается IATA код как есть.
Словарь расширяется по мере необходимости.

---

## nginx конфигурация

```nginx
# В server block для openclaw.mva154.duckdns.org
location /noisemap/ {
    proxy_pass         http://172.19.0.2:5555/;
    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_read_timeout 30s;
}
```

**172.19.0.2** — IP контейнера OpenClaw (может меняться при перезапуске — проверить через `openclaw status`)

---

## Известные ограничения

1. **Производительность** — при >200 рейсах с шумовыми зонами браузер может тормозить.
   Зоны строятся посегментно → O(n) полигонов. Оптимизация: агрегировать сегменты одной высоты.

2. **steps: 6 в Turf.js** — буферы угловатые при малом зуме. Увеличить до 8–12 для красоты.

3. **Зоны не обновляются при зуме** — ширина зон в пикселях меняется корректно (реальные км),
   но визуально при максимальном зуме могут выглядеть разрывно между сегментами.
