Каталог авиакомпаний и аэропортов: модальное окно с навигацией по маршрутной сети

23.03.2026
Каталог авиакомпаний и аэропортов: модальное окно с навигацией по маршрутной сети

Интерактивная карта на airlines.macrulez.ru обзавелась новым функциональным блоком — каталогом авиакомпаний и аэропортов. До этого момента единственным способом выбрать авиакомпанию был поиск по названию или IATA-коду в форме вверху страницы. Это удобно, когда знаешь, что ищешь. Но что если хочется просто посмотреть, какие авиакомпании летают из определённого аэропорта, или изучить маршрутную сеть конкретного перевозчика без необходимости знать его код?

Для этого и был сделан каталог.

Как открыть

В тулбаре появилась кнопка — левее переключателя режимов 2D/3D. По клику открывается модальное окно, которое накрывает карту поверх неё.

Две вкладки: авиакомпании и аэропорты

Модалка разделена на две вкладки — Авиакомпании и Аэропорты. По умолчанию открывается первая.

Алфавитная навигация

Под вкладками — строка с буквами алфавита. Нажатие на букву фильтрует список ниже: для авиакомпаний это первая буква оригинального (английского) названия, для аэропортов — первая буква города. Отдельный селектор 0-9 объединяет авиакомпании, названия которых начинаются с цифры — например, 2GO Airlines или 40-Mile Air.

Алфавитный указатель для аэропортов строится не из базы аэропортов целиком, а только из тех, из которых реально летает хотя бы одна авиакомпания. Это исключает из списка закрытые полосы, вертолётные площадки и прочие объекты без маршрутов — их в базе несколько тысяч, и они только засоряли бы навигацию.

Вкладка «Авиакомпании»

Каждая строка в списке показывает:

  • Название авиакомпании (оригинальное, без перевода)
  • Количество аэропортов в маршрутной сети
  • Количество рейсов (направлений)
  • IATA-код

Данные по количеству аэропортов и рейсов хранятся прямо в таблице airlines в PostgreSQL — при каждом обновлении маршрутов скрипт пересчитывает агрегаты и записывает в поля airports_count и routes_count. Это позволяет отдавать эти цифры без JOIN-запросов на каждый запрос к каталогу.

Раскрытие маршрутной сети авиакомпании

Клик по строке авиакомпании не переходит сразу на карту, а раскрывает детальный блок прямо под ней. В нём отображается список аэропортов вылета в алфавитном порядке. Каждая строка аэропорта вылета содержит:

  • Название города
  • IATA-код
  • Количество направлений из этого аэропорта

Под строкой вылета — набор тегов с аэропортами назначения. Каждый тег показывает город и код аэропорта.

В заголовке раскрытого блока — кнопка «На карте», которая открывает маршрутную сеть авиакомпании целиком без привязки к конкретному городу.

Навигация по клику на аэропорт

Клик на строку аэропорта вылета или на тег аэропорта назначения делает сразу три вещи:

  1. Закрывает модалку
  2. Загружает маршрутную сеть выбранной авиакомпании
  3. Выбирает кликнутый аэропорт как активный город — карта центрируется именно на нём

Это особенно полезно в 3D-режиме: камера автоматически наводится на точку и её ближайшие связи, а не показывает весь глобус с маршрутами АК.

Технически это работает через событие select-airline-with-airport, которое содержит сразу два кода — IATA авиакомпании и IATA аэропорта. В родительском компоненте сначала устанавливается код АК (это запускает загрузку маршрутов), а затем через nextTick — код города. Когда маршруты приходят и карта перестраивается, autoFitCamera уже знает выбранный город и центрируется именно на нём и его прямых соединениях.

Вкладка «Аэропорты»

Список аэропортов строится аналогично: алфавитная навигация, каждая строка показывает название города, IATA-код и количество авиакомпаний, которые летают из этого аэропорта.

Это поле — airlines_count — тоже хранится в таблице world_airport и заполняется заранее агрегирующим запросом по таблице маршрутов:

sql Copy
UPDATE "public"."world_airport" wa
SET airlines_count = sub.cnt
FROM (
  SELECT dep_iata, COUNT(DISTINCT airline_iata) AS cnt
  FROM "public"."routes"
  WHERE dep_iata IS NOT NULL AND dep_iata != ''
    AND airline_iata IS NOT NULL AND airline_iata != ''
  GROUP BY dep_iata
) sub
WHERE wa.iata_code = sub.dep_iata;

Аэропорты без авиакомпаний (airlines_count IS NULL) в списке не отображаются.

Список авиакомпаний аэропорта

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

Данные для этого блока загружаются по запросу: вызывается эндпоинт /api/airlines/routes/airport/:iata, который возвращает список АК с агрегатами. Закрыть раскрытый список можно кнопкой в заголовке.

Изменения на бэкенде

Для поддержки каталога потребовалось несколько изменений в API.

Новые поля в базе данных:

sql Copy
-- В таблице airlines
ALTER TABLE "public"."airlines"
  ADD COLUMN airports_count INTEGER DEFAULT NULL,
  ADD COLUMN routes_count   INTEGER DEFAULT NULL;

-- В таблице world_airport
ALTER TABLE "public"."world_airport"
  ADD COLUMN airlines_count INTEGER DEFAULT NULL;

Изменения в API аэропортов (GET /api/airlines/airports):

  • Добавлен параметр initial — фильтр по первой букве COALESCE(NULLIF(municipality, ''), name). Это гарантирует, что буква в указателе совпадает с первой буквой отображаемого названия
  • Параметр iata_only=true фильтрует только аэропорты с непустым IATA-кодом
  • GET /api/airlines/airports/initials теперь тоже поддерживает iata_only и группирует по той же формуле с приоритетом города над техническим названием аэропорта

Изменения в API маршрутов (GET /api/airlines/routes/stats/airlines):

  • В SELECT добавлены a.airports_count и a.routes_count из таблицы airlines через уже существующий JOIN
  • Обновлён ключ кеша — routes-stats-airlines-v2 — чтобы инвалидировать старые ответы после изменения структуры

Остальные эндпоинты (/api/airlines, /api/airports/iata/:code и т.д.) уже использовали SELECT *, поэтому новые поля появились в ответах автоматически.

Загрузчик во время запросов

При загрузке маршрутной сети после выбора авиакомпании (не только из каталога, но и из любого другого места) теперь показывается полноэкранная шторка-загрузчик. Она затемняет карту с лёгким blur-эффектом и показывает спиннер с текстом. При первичной загрузке данных приложения (список авиакомпаний и аэропортов РФ) шторка более плотная, при загрузке маршрутов — чуть прозрачнее.

Это заменило старую строку статуса под полем поиска, которая терялась визуально.

Дизайн

Текущий дизайн модалки — технический: белый фон, стандартная типографика, синие акценты. Никакой тематики авиации, никаких доработок под мобильные устройства. Это сознательное решение: сначала функционал, потом внешний вид.

В следующей итерации планируется переработка дизайна в соответствии с общей стилистикой приложения — тёмная тема, согласованность с картой и информационными панелями.

Читать далее

23.03.2026

Переход на модели Airport/Company и кеширование данных на клиенте

Обновил клиентскую архитектуру карты: перешёл с «сырых» API-объектов на модели Airport и Company, а также внедрил многоуровневое кеширование данных. В статье разобрал, как это упростило код, ускорило интерфейс и позволило безопасно кешировать каталог авиакомпаний/аэропортов с автоматической инвалидиацией по версии данных (newest_update). Есть практические фрагменты кода: нормализация моделей, lazy enrich и версионирование кеша.

Метки
vue3typescriptfrontend-architecturedata-modelingcachingperformance
24.03.2026

Камера в URL: синхронизация, восстановление и конвертация между 2D и 3D

Реализовал запись положения камеры в URL, восстановление вида при открытии ссылки и конвертацию координат камеры между плоской картой и глобусом. Разобрал несколько нетривиальных проблем с таймингом инициализации и вынес всю логику в отдельный Pinia-стор.

Метки
three.jsvuetypescriptcamerarouting
24.03.2026

Stronghold: генератор паролей прямо в браузере

На toolz.macrulez.ru появился новый модуль — Stronghold. Это генератор криптографически стойких паролей с полным контролем над набором символов, длиной, количеством вариантов и дополнительными ограничениями. Все операции выполняются в браузере — никакие данные не покидают устройство. Поддерживает латиницу, кириллицу и спецсимволы, показывает оценку надёжности в битах энтропии и позволяет сохранить результат в текстовый файл.

Метки
паролибезопасностьгенераторвебмастерtoolz