# Модель шумового загрязнения

## Версия 1.1 (текущая)

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

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

На карте отображается **горизонтальный катет D** — расстояние на земле:

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

    D = √(R² − H²),  если H < R
    D = 0,            если H ≥ R (зона не видна — самолёт выше)
```

### Следствия из модели

1. **Чем выше самолёт → тем уже зоны на карте**
2. **Зона исчезает** когда высота превышает радиус (H ≥ R)
3. **При взлёте/посадке** (H ≈ 0) зоны максимально широкие
4. **На крейсерской высоте** (H = 10–12 км) видны только самые широкие зоны

### Пример расчёта (H = 3.5 км)

| Зона | R_inner | R_outer | D_inner | D_outer | Отображение |
|------|---------|---------|---------|---------|-------------|
| Критический | 0 | 2 км | 0 | √(4−12.25) < 0 | ❌ не видна |
| Сильный | 2 км | 5 км | 0 | √(25−12.25) = **3.57 км** | ✅ круг |
| Средний | 5 км | 7 км | 3.57 км | √(49−12.25) = **6.06 км** | ✅ кольцо |
| Низкий | 7 км | 11 км | 6.06 км | √(121−12.25) = **10.43 км** | ✅ кольцо |

---

## Конфигурация зон

Файл: `noise_model.py` → переменная `NOISE_ZONES`

```python
NOISE_ZONES = [
    {
        "id":      "zone_critical",
        "label":   "Критический (R < 2 км)",
        "R_inner": 0.0,     # км — внутренняя граница сферы
        "R_outer": 2.0,     # км — внешняя граница сферы
        "color":   "#FF3333",
        "opacity": 0.55,    # прозрачность (фиксированная)
    },
    {
        "id":      "zone_strong",
        "label":   "Сильный (R 2–5 км)",
        "R_inner": 2.0,
        "R_outer": 5.0,
        "color":   "#FF8800",
        "opacity": 0.40,
    },
    {
        "id":      "zone_medium",
        "label":   "Средний (R 5–7 км)",
        "R_inner": 5.0,
        "R_outer": 7.0,
        "color":   "#FFCC00",
        "opacity": 0.28,
    },
    {
        "id":      "zone_low",
        "label":   "Низкий (R 7–11 км)",
        "R_inner": 7.0,
        "R_outer": 11.0,
        "color":   "#88DD00",
        "opacity": 0.18,
    },
]
```

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

---

## Реализация на фронтенде

### Библиотека: Turf.js

Каждая зона строится **посегментно** — для каждого сегмента трека отдельно,
с учётом высоты именно этой точки.

```javascript
// Для каждого сегмента трека:
const segLine = turf.lineString([[lon1, lat1], [lon2, lat2]]);

// Внешний буфер (кольцо снаружи)
const outerBuf = turf.buffer(segLine, d_outer, { units: 'kilometers', steps: 6 });

// Внутренний буфер (дырка внутри)
const innerBuf = turf.buffer(segLine, d_inner, { units: 'kilometers', steps: 6 });

// Кольцо = разница буферов
const ring = turf.difference(outerBuf, innerBuf);
```

Значения `d_inner` и `d_outer` для каждой точки рассчитываются на бэкенде
в `noise_model.py` → `calc_zone_radii_for_point(altitude_m)` и передаются
в поле `zone_radii` каждой точки трека.

---

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

Отдельно от зон шума — трек самолёта окрашен по высоте полёта:

```
Высота:   0 м    →   4 250 м   →   8 500+ м
Цвет:    🔴 Красный → 🟡 Жёлтый → 🟢 Зелёный
HSL:     hsl(0°)    hsl(60°)     hsl(120°)
```

```javascript
const TRACK_MAX_ALT_M = 8500;
function altToTrackColor(alt_m) {
    const t = Math.min(1.0, alt_m / TRACK_MAX_ALT_M);
    const hue = Math.round(t * 120);
    return `hsl(${hue}, 100%, 48%)`;
}
```

---

## Версия 2.0 — бэклог

Планируется учесть дополнительные факторы:

| Фактор | Влияние | Сложность |
|--------|---------|-----------|
| Тип воздушного судна | B747 громче A320 | Средняя |
| Время суток | Ночью нормы жёстче | Низкая |
| Направление ветра | Разносит шум | Высокая |
| Санитарные нормы РФ | СН 2.2.4/2.1.8.562-96 | Средняя |
| Реальные замеры | Привязка к данным | Высокая |

---

## API endpoint

Конфиг модели доступен фронтенду через:

```
GET /api/noise-config
```

Ответ:
```json
{
  "zones": [
    { "id": "zone_critical", "R_inner": 0, "R_outer": 2, "color": "#FF3333", "opacity": 0.55 },
    ...
  ],
  "altitude_bands": []
}
```

Фронтенд читает конфиг при старте и строит слои зон динамически.
