# ✈️ Карта шумового загрязнения — Прототип v0.2

Веб-приложение для визуализации шумового загрязнения от воздушных судов  
над Московской областью на основе данных Flightradar24 API.

---

## 🚀 Быстрый старт

```bash
cd tasks/flightradar24
. venv/bin/activate
pip install -r prototype/requirements.txt
cd prototype

# Сгенерировать тестовые данные (50 синтетических рейсов)
python generate_sample_data.py

# Запустить сервер
python app.py
# → http://localhost:5555
# → https://openclaw.mva154.duckdns.org/noisemap/ (через nginx)
```

С реальным API:
```bash
# Скопировать шаблон и заполнить ключи
cp .env.example .env
# FLIGHTRADAR24_API_KEY — FR24 Explorer (треки, снимки)
# YANDEX_RASP_API_KEY  — Яндекс.Расписания (табло аэропортов, стратегия Б)
python app.py
```

---

## 📁 Структура

```
prototype/
├── app.py                      # Flask backend + REST API
├── noise_model.py              # ⚙️  Модель шума (калибровочные параметры здесь)
├── fr24_client.py              # Клиент Flightradar24 API (с кэшированием)
├── generate_sample_data.py     # Генератор синтетических треков
├── fetch_airport.py            # Загрузка треков по аэропорту (стратегия А)
├── fetch_airport_offset.py     # Загрузка со смещением времени (стратегия А+)
├── fetch_svo_tracks.py         # Загрузка только SVO треков
├── fetch_tracks.py             # Загрузка треков (общий скрипт)
├── index.html                  # Фронтенд (OpenLayers + Turf.js)
├── requirements.txt
├── .env.example
└── data/
    ├── flights_SVO_2026-03-21.json       # Реальные данные SVO 21.03
    ├── flights_DME_2026-03-21.json       # Реальные данные DME 21.03
    ├── flights_VKO_2026-03-21.json       # Реальные данные VKO 21.03
    ├── flights_ZIA_2026-03-21.json       # Реальные данные ZIA 21.03
    ├── flights_SVO_2026-03-20_offset90m.json  # SVO 20.03 со смещением +1.5ч
    ├── flights_DME_2026-03-20_offset90m.json  # DME 20.03 со смещением +1.5ч
    ├── flights_VKO_2026-03-20_offset90m.json  # VKO 20.03 со смещением +1.5ч
    ├── flights_ZIA_2026-03-20_offset90m.json  # ZIA 20.03 со смещением +1.5ч
    ├── sample_flights.json               # Fallback (синтетика или последняя загрузка)
    ├── cache_SVO/                        # Кэш треков SVO
    ├── cache_DME/                        # Кэш треков DME
    ├── cache_VKO/                        # Кэш треков VKO
    ├── cache_ZIA/                        # Кэш треков ZIA
    └── cache/                            # Общий кэш API запросов
```

Сервер автоматически объединяет все файлы `flights_*.json` при старте.

---

## 🔌 REST API

| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/` | Веб-карта |
| GET | `/api/flights` | Рейсы с шумовыми данными |
| GET | `/api/noise-config` | Параметры модели шума |
| GET | `/api/airports` | Аэропорты региона |
| GET | `/api/stats` | Статистика |
| GET | `/api/usage` | Использование кредитов FR24 |
| GET | `/api/live` | Live позиции (требует API ключ) |
| GET | `/api/help` | Документация API |

### Параметры `/api/flights`

| Параметр | Тип | По умолчанию | Описание |
|----------|-----|--------------|----------|
| `limit` | int | 100 | Макс. рейсов |
| `min_alt` | int | 0 | Мин. высота (футы) |
| `max_alt` | int | 50000 | Макс. высота (футы) |
| `type` | str | all | `departure` / `arrival` / `all` |
| `airport` | str | all | `SVO` / `DME` / `VKO` / `ZIA` / `all` |
| `date_from` | str | — | Начало периода (YYYY-MM-DD) |
| `date_to` | str | — | Конец периода (YYYY-MM-DD) |

---

## 📡 Стратегии загрузки данных

### Стратегия А — Снимки позиций (текущая)

**Принцип:** делаем снимки всех самолётов над МО в фиксированные моменты времени,
затем для каждого найденного рейса загружаем полный трек и обрезаем до bbox МО.

**Скрипты:**
```bash
# Загрузить данные за дату
python fetch_airport.py SVO 2026-03-21

# Загрузить со смещением (для увеличения охвата)
python fetch_airport_offset.py SVO 2026-03-21 1.5
```

**Параметры:**
- Интервал снимков: **3 часа** (00:00, 03:00, 06:00 ... 21:00 UTC)
- bbox МО: `54.0–57.0°N, 35.5–40.5°E`
- Endpoint снимков: `/historic/flight-positions/full`
- Endpoint треков: `/flight-tracks`

**Расход кредитов (1 аэропорт, 1 день):**

| Операция | Кол-во | Кредитов |
|----------|--------|----------|
| Снимки (8 шт × ~10 рейсов) | ~80 | ~80 |
| Треки (~30–45 уник. рейсов) | ~40 | ~3 000 |
| **Итого** | | **~3 100** |

**Охват:** ~10% реальных рейсов за день (большинство пролетают между снимками)

**Улучшение охвата через смещение:**
Запуск стратегии А дважды — основной (00:00, 03:00...) и со смещением +1.5ч (01:30, 04:30...)
даёт интервал 1.5ч вместо 3ч → охват ~20%, стоимость ×2.

---

### Стратегия Б — Табло → Треки (планируемая)

**Принцип:** берём полный список рейсов за день из табло аэропорта (парсинг сайта),
для каждого рейса ищем fr24_id через live API и загружаем трек над МО.

**Алгоритм:**
1. Парсим табло вылетов/прилётов с сайта (svo.aero, Яндекс.Расписания и др.) — **бесплатно**
2. По callsign ищем fr24_id через `/live/flight-positions/full?callsign=SU1234` — **1 кредит/рейс**
3. Загружаем трек `/flight-tracks?flight_id=XXX` — платим за полный трек, используем только МО-часть
4. Фильтруем точки трека по bbox МО

**Расход кредитов (1 аэропорт, 1 день):**

| Операция | Кол-во | Кредитов |
|----------|--------|----------|
| Поиск fr24_id по callsign | ~330 | ~330 |
| Треки (полный маршрут) | ~330 | ~24 000 |
| **Итого SVO за 1 день** | | **~24 500** |
| 4 аэропорта за 1 день | | **~80 000** |
| 4 аэропорта за 7 дней | | **~560 000** ⚠️ |

**Охват:** ~100% рейсов за день

**Точность трека:** 10 секунд между точками (отличная)

**Ограничения:**
- Нет API FR24 для списка рейсов по дате → нужен парсинг сайта аэропорта
- Высокая стоимость: `/flight-tracks` возвращает **весь маршрут** (~700 точек),
  временная фильтрация на стороне API **не поддерживается** (проверено)
- При промо 120k: реально покрыть **3–5 дней × 1 аэропорт** или **1 день × 4 аэропорта**

**Примечание о holding patterns:**
Небольшая доля рейсов (~5–10% прилётов) выполняет зоны ожидания над МО —
кружит перед посадкой при загруженности аэропорта или плохой погоде.
Для таких рейсов трек над МО значительно длиннее обычного.

---

### Сравнительная таблица стратегий

| Критерий | Стратегия А | Стратегия Б |
|----------|-------------|-------------|
| **Охват рейсов** | ~10–20% | ~100% |
| **Точность трека** | 10 сек | 10 сек |
| **Стоимость/день/аэропорт** | ~3 100 кредитов | ~24 500 кредитов |
| **7 дней × 4 аэропорта** | ~87 000 ✅ | ~560 000 ❌ |
| **Реализация** | Готово | Требует парсинг табло |
| **Статус** | ✅ Реализована | ⏳ Планируется |

---

## 🔊 Модель шума

### Физическая основа

Шум распространяется сферически. Уровень зависит от **реального 3D-расстояния** R до наблюдателя.
На карте отображается горизонтальный катет D (теорема Пифагора):

```
       самолёт ●
               |\
             H |  \ R  ← граница зоны (гипотенуза)
               |    \
  земля ●──────●──────● наблюдатель
              D (катет = ширина зоны на карте)

  D = √(R² − H²),  если H < R, иначе зона не видна
```

**Следствия:**
- Чем выше самолёт — тем уже зоны на карте
- При H ≥ R — зона полностью исчезает
- При H = 0 — ширина зоны = R (максимум)

**Пример для H = 3.5 км:**

| Зона | R_outer | D_outer | R_inner | D_inner | Вид |
|------|---------|---------|---------|---------|-----|
| Критический | 2 км | — (H>R) | 0 | — | ❌ |
| Сильный | 5 км | 3.57 км | 2 | 0 | ✅ круг |
| Средний | 7 км | 6.06 км | 5 | 3.57 | ✅ кольцо |
| Низкий | 11 км | 10.43 км | 7 | 6.06 | ✅ кольцо |

### Таблица зон

| Зона | R (сфера) | Цвет | Прозрачность |
|------|-----------|------|-------------|
| Критический | < 2 км | 🔴 #FF3333 | 0.55 |
| Сильный | 2–5 км | 🟠 #FF8800 | 0.40 |
| Средний | 5–7 км | 🟡 #FFCC00 | 0.28 |
| Низкий | 7–11 км | 🟢 #88DD00 | 0.18 |
| Нет шума | > 11 км | — | — |

### 🎛 Калибровка

Все параметры вынесены в начало `noise_model.py`:

```python
NOISE_ZONES = [
    {
        "id":      "zone_critical",
        "R_inner": 0.0,   # км — внутренняя граница сферы
        "R_outer": 2.0,   # км — внешняя  граница сферы ← меняй здесь
        "color":   "#FF3333",
        "opacity": 0.55,
    },
    ...
]
```

После изменения — перезапустить `python app.py`.

---

## 🗺️ Карта — функциональность

### Треки
- Цвет — **градиент по высоте**: 🔴 0 м → 🟡 4 250 м → 🟢 8 500+ м
- Hover → tooltip с параметрами точки
- Клик → детали рейса в боковой панели
- Переключатель **✈ Треки** — скрыть/показать треки (зоны остаются)
- Трек отображается **поверх** шумовых зон (zIndex 50)

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

### Шумовые зоны
- Реальные географические полигоны (Turf.js `buffer` + `difference`)
- Строятся **посегментно** с учётом высоты каждого сегмента
- Чекбоксы для включения/отключения каждой зоны

### Фильтры
- **Аэропорт**: Все / SVO / DME / VKO / ZIA
- **Тип рейса**: Все / Вылеты / Прилёты
- **Высота**: слайдеры в метрах (0–13 000 м)
- **Период**: date picker (date_from / date_to)

### Флажки (маркеры)
- Кнопка **📍 Добавить** → клик на карту → ставит флажок
- Клик на флажок → удалить
- Двойной клик на название в списке → переименовать
- Несколько флажков одновременно, разные цвета

### Линейка
- Кнопка **📏 Включить** → кликать по точкам → тянется линия
- Двойной клик → завершить (автовыключение)
- Показывает итог и разбивку по сегментам (формула Haversine)
- **🗑 Сбросить** → очистить

---

## 📊 Текущие данные

| Файл | Дата | Аэропорт | Рейсов | Точек |
|------|------|----------|--------|-------|
| flights_SVO_2026-03-21.json | 21.03 | SVO | 33 | ~6 000 |
| flights_DME_2026-03-21.json | 21.03 | DME | 15 | ~3 000 |
| flights_VKO_2026-03-21.json | 21.03 | VKO | 21 | ~4 000 |
| flights_ZIA_2026-03-21.json | 21.03 | ZIA | 1 | ~46 |
| flights_SVO_2026-03-20_offset90m.json | 20.03 +1.5ч | SVO | — | — |
| flights_DME_2026-03-20_offset90m.json | 20.03 +1.5ч | DME | — | — |
| flights_VKO_2026-03-20_offset90m.json | 20.03 +1.5ч | VKO | — | — |
| flights_ZIA_2026-03-20_offset90m.json | 20.03 +1.5ч | ZIA | — | — |

**Итого:** 147 рейсов / 29 487 точек / 2 дня / 4 аэропорта

---

## 💳 Расход кредитов FR24 API

**Тариф Explorer:** 60 000 кредитов/месяц (промо 120 000 до 31.05.2026)

На 22.03.2026 потрачено (приблизительно):
- `historic/flight-positions/light` (тесты): ~1 717
- `historic/flight-positions/full` (данные): ~5 564
- `flight-tracks` (треки): ~8 880
- **Итого: ~16 161 кредитов (~13% промо-лимита)**

---

## 🗓️ Статус и план

| Шаг | Статус | Описание |
|-----|--------|----------|
| **Шаг 0** | ✅ Готово | UI, синтетика, модель шума, линейка, флажки |
| **Шаг 1** | ✅ Готово | Sandbox проверка, исправление bounds |
| **Шаг 2** | ✅ Готово | Production данные (4 аэропорта, 2 дня, стратегия А) |
| **Шаг 3** | ✅ Готово | Стратегия Б v2: 111 новых треков SVO 21.03, итого **258 рейсов / 50 282 точки** |

### Бэклог
- [ ] Стратегия Б: парсинг табло + полный охват рейсов
- [ ] Фильтр по дате в UI (переключение между загруженными днями)
- [ ] Тепловая карта накопленного шума по регионам
- [ ] Фильтр по авиакомпаниям и типам ВС
- [ ] Учёт времени суток (ночные полёты)
- [ ] Привязка к санитарным нормам (СН 2.2.4/2.1.8.562-96)
- [ ] Экспорт зон в GeoJSON/KML
- [ ] Оптимизация производительности (много треков → тормоза браузера)
