尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Дашборд прогноза северного сияния для Мурманска: Kp-индекс NOAA SWPC, облачность по ярусам Open-Meteo, оценка шанса и лучшее время наблюдения этой ночью. Чистый HTML/CSS/JS без зависимостей и сборки.
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
Дашборд прогноза северного сияния для семи точек Мурманской области: текущий Kp-индекс, облачность по ярусам, оценка шанса увидеть сияние и лучшее время для наблюдения ближайшей ночью. Интерфейс на трёх языках — русском, английском и китайском: в Мурманск за сиянием едут туристы со всего мира (см. Языки). HTML, CSS и чистый JavaScript. Без фреймворков, без сборки, без зависимостей: страница — это несколько статических файлов и браузер. Необязательная серверная часть — только для уведомлений при закрытом приложении (см. ниже).查看完整 README ↗
Дашборд прогноза северного сияния для семи точек Мурманской области: текущий Kp-индекс, облачность по ярусам, оценка шанса увидеть сияние и лучшее время для наблюдения ближайшей ночью.
Интерфейс на трёх языках — русском, английском и китайском: в Мурманск за сиянием едут туристы со всего мира (см. Языки).
HTML, CSS и чистый JavaScript. Без фреймворков, без сборки, без зависимостей: страница — это несколько статических файлов и браузер. Необязательная серверная часть — только для уведомлений при закрытом приложении (см. ниже).
Двойной клик по index.html обычно работает: оба API отдают Access-Control-Allow-Origin: *. Если браузер всё же заблокирует запросы со страницы, открытой по file://, поднимите локальный сервер:
python -m http.server 8765
и откройте http://localhost:8765.
node --test tools/*.test.mjs и cd worker && npm test: тесты сайта и сервера (их же гоняет GitHub Actions).tools/budget.test.mjs: бюджет скорости. Первый заход (страница, стили, скрипты в gzip) не тяжелее заданного числа КБ и файлов.tools/golden.test.mjs: эталонные ночи из tools/golden-nights.json. Реальные случаи, где ответ известен (например, 8.10.2026: яркое сияние при Kp 0,67).node tools/lighthouse.mjs: замер скорости как с телефона на медленном мобильном интернете (медиана трёх прогонов).node tools/live-check.mjs: после публикации проверяет версию на сайте, файлы и время ответа сервера данных..githooks/pre-commit (включается командой git config --worktree core.hooksPath .githooks): перед коммитом тесты, совпадение версий страницы и кэша, нет длинных тире в новых строках.Пять вкладок: «Сейчас» — условия в выбранной точке, «Куда ехать ночью» — сравнение всех семи точек на ближайшую ночь, «Карта» — семь точек на схеме области, «Гид» — советы для туриста, «Настройки» — вид и единицы, а ниже уведомления: проверка и настройка. Вкладка запоминается и отражается в адресе (#now, #tonight, #map, #guide, #settings), так что ссылкой можно поделиться; старые ссылки #notify открывают «Настройки». На широком экране навигация сверху, на узком — внизу экрана и листается вбок; выбранная вкладка сама доезжает до середины панели.
Вкладка «Сейчас»:
| Блок | Содержимое |
|---|---|
| Шанс увидеть сияние | Низкий / средний / высокий на текущий момент, пояснение и список учтённых факторов |
| Лучшее время этой ночью | Окно наблюдения и почасовая раскладка ближайшей ночи |
| Kp-индекс | Текущее значение, шкала 0–9, время измерения |
| Облачность | Эффективная облачность, разбивка по трём ярусам |
| Погода на месте | Температура и «ощущается как», ветер с порывами и направлением, видимость, влажность; главное, что мешает: туман, снег, осадки или сильный ветер |
| Солнечный ветер | Bz, сила поля и скорость со спутников в точке L1, график Bz за 2 часа и оценка на ближайший час |
| Модель NOAA OVATION | Вероятность сияния в поле зрения и прямо над точкой на ближайшие 30–90 минут |
| Прогноз Kp | Трое суток с шагом 3 часа, текущая трёхчасовка подсвечена |
Вкладка «Куда ехать ночью»:
| Блок | Содержимое |
|---|---|
| Ответ | Лучшая точка и её окно наблюдения — либо прямое «ехать некуда» |
| Список точек | Все семь по убыванию оценки: окно, облачность, порог Kp, расстояние и время в пути |
| Когда ехать | Прогноз NOAA на 27 дней: наибольший Kp по дням, светлые ночи и яркая Луна, лучшие даты для выбранной точки |
Облачность для всех семи точек запрашивается одним запросом к Open-Meteo: API принимает список координат через запятую и возвращает массив в том же порядке. Kp и его прогноз переиспользуются с первой вкладки — это планетарные величины, одинаковые для всей области.
Данные второй вкладки грузятся при первом её открытии, а не при старте приложения, и кэшируются на тех же правилах, что остальные: три часа, пометка возраста при показе из кэша.
Если ни в одной точке условия не дотягивают до среднего, в ответе так и написано — «ехать некуда», с указанием причины. Лучшее из плохого в крупной строке не показывается, но список ниже остаётся полным: по нему видно основание для вывода.
Точка выбирается в шапке, выбор сохраняется в браузере. Координаты проверены по открытому геокодеру Open-Meteo (данные GeoNames), геомагнитная широта посчитана дипольным приближением IGRF для эпохи ~2025 (полюс 80.7° с. ш., 72.7° з. д.):
sin(φm) = sin(φ)·sin(φp) + cos(φ)·cos(φp)·cos(λ − λp)
| Точка | Широта | Долгота | Геомагн. широта | Засветка | Сияние заметно от Kp |
|---|---|---|---|---|---|
| Мурманск | 68.9678 | 33.0992 | 64,87 | сильная | 1,0 |
| Териберка | 69.1609 | 35.1453 | 64,78 | минимальная | 1,1 |
| Мончегорск | 67.9397 | 32.8739 | 63,94 | заметная | 1,5 |
| Ловозеро | 68.0056 | 35.0187 | 63,72 | слабая | 1,6 |
| Кировск | 67.6148 | 33.6727 | 63,53 | заметная | 1,7 |
| Апатиты | 67.5827 | 33.4134 | 63,53 | заметная | 1,7 |
| Кандалакша | 67.1512 | 32.4128 | 63,26 | заметная | 1,8 |
Деталь, которую видно только из этой таблицы: Териберка географически севернее Мурманска, а геомагнитно — южнее. Геомагнитная сетка наклонена, и Териберка лежит восточнее. Её преимущество перед городом не в широте, а в отсутствии засветки.
Оговорка: дипольное приближение — не строгая скорректированная геомагнитная широта (CGM), которой пользуются в авроральной науке. В этом регионе расхождение до полуградуса, то есть до четверти единицы Kp — меньше шага самих данных NOAA.
Прогноз говорит, что сияние возможно, а отметка очевидца — что оно есть. На вкладке «Сейчас» карточка «Очевидцы» показывает, сколько человек за последний час отметили сияние у выбранной точки (и сколько из них — яркое), а ниже — отметки у других точек области. Кнопка «Вижу сияние!» предлагает выбрать «слабое» или «яркое» и отправляет отметку.
Отметка анонимна: на сервер уходят только точка и сила сияния — без текста, фото и координат; адрес не хранится (см. worker/README.md), отметки живут сутки. Отметить можно, только когда у точки темно, и не чаще раза в 30 минут — страница показывает это заранее, сервер проверяет сам. Для своего места отметка ставится у ближайшей точки области. Сводка обновляется вместе с остальными данными, не чаще раза в 2 минуты; если сервер уведомлений не настроен, карточки нет.
Под «Очевидцами» — ссылки на камеры, по которым видно, есть ли сияние прямо сейчас, от ближней к дальней: Териберка (прямая трансляция, 84 км от Мурманска), Соданкюля (камера всего неба SGO), Скибутн (UiT), Кируна (IRF), Абиску (Lights over Lapland). Только ссылки, без встраивания снимков: чужие серверы не нагружаются, их условия не нарушаются, а их сбой не ломает страницу. Расстояния — в выбранных единицах, подписи — на языке страницы. Адреса проверены 26.09.2026 (js/webcams.js).
Кроме семи точек области можно добавить до пяти своих мест — например, озеро или сопку, куда вы ездите смотреть сияние. Добавляются пунктом «+ Добавить своё место…» в выборе точки, кнопкой «Добавить своё место на карте» (нажать на схему в нужном месте) или в разделе «Мои места» на вкладке «Настройки». Координаты вводятся вручную (запятая или точка; обе сразу можно вставить в поле широты), берутся с карты или кнопкой «Где я сейчас».
Для своего места считается всё то же, что для городов: облачность (оно входит в общий запрос по всем точкам), окно наблюдения, OVATION, Луна, вердикт. Пороги Kp — по геомагнитной широте, посчитанной по той же формуле, что у семи точек (geomagneticLatitude в core.js воспроизводит их значения до сотой). Засветка и дорога неизвестны — они не показываются; расстояние — от Мурманска по прямой. Принимаются места на севере: широта 60–72°, долгота 20–50° в. д. — для других широт пороги Kp не подходят.
Места хранятся только в браузере (aurora.places) и на сервер не уходят. Сервер уведомлений знает только семь точек, поэтому для своего места подписка оформляется на ближайшую из них — это сказано в подсказке. Испорченная запись в хранилище не ломает страницу: негодные места просто пропускаются.
Kp. Пороги занижены относительно привычных: вся область лежит практически под авроральным овалом, и сияние здесь возможно уже при Kp 2, тогда как в средней полосе нужен Kp 5–6.
Экваториальная граница овала опускается примерно на 2° геомагнитной широты на каждую единицу Kp, то есть овал оказывается над головой при Kp ≈ (67° − φm) / 2. Для Мурманска это ≈ 1,1, и пороги 1 / 2 / 3, подобранные под него, уже это отражают. Поэтому шкала не считается заново для каждой точки, а якорится на Мурманск и сдвигается:
сдвиг = (φm Мурманска − φm точки) / 2
порог = порог Мурманска + сдвиг
| Kp относительно порогов точки | Балл |
|---|---|
| ≥ верхнего | 3 |
| ≥ среднего | 2 |
| ≥ нижнего | 1 |
| ниже | 0 |
Практический смысл: при типичном Kp 2,67 Мурманск получает балл 2, а Кандалакша — 1. Разница почти в целую единицу Kp между крайними точками соответствует 200 км по меридиану.
Засветка в расчёт не входит намеренно: это не прогнозируемая величина, а свойство места, и смешивать её с погодой в одной формуле значило бы выдавать за прогноз то, что прогнозом не является. Она появляется в списке факторов и в пояснении к вердикту — например, для Мурманска советом отъехать на 15–20 км от огней.
Облачность. Сияние светится на высоте 100–300 км, выше любых облаков, поэтому важна только прозрачность слоя:
| Ярус | Высота | Типичные облака | Вес |
|---|---|---|---|
| Нижний | 0–2 км | Stratus, stratocumulus | 1,0 |
| Средний | 2–7 км | Altostratus, altocumulus | 0,8 |
| Верхний | 7–12 км | Cirrus, cirrostratus | 0,35 |
Ярусы сводятся в одно число по модели случайного перекрытия:
итог = 1 − (1 − low) · (1 − 0,8·mid) · (1 − 0,35·high)
Простое сложение процентов дважды учитывает перекрытие слоёв. Сравнение на 9261 сочетании с шагом 5 % показало, что сумма упирается в потолок 100 % на 57 % пространства значений и в 19 % сочетаний объявляет небо закрытым там, где модель перекрытия видит просветы. На сплошных ярусах обе формулы совпадают тождественно.
Пороги балла: ≤ 25 % — ясно (3), ≤ 50 % — переменная облачность (2), ≤ 75 % — просветы редки (1), выше — небо закрыто (0).
Луна в расчёт уровня не входит — как и засветка. Полная Луна над горизонтом заметно глушит слабое сияние, но это влияет на то, что именно человек увидит, а не на то, есть ли сияние. Луна показывается фактором в вердикте («Луна: 92%, над горизонтом»), строкой в окне наблюдения («Луна 100%, над горизонтом 19:50–23:57», «зайдёт в 03:14») и среди факторов на вкладке «Куда ехать». Пояснение «яркая Луна, слабое сияние выцветет, смотрите на север, спиной к Луне» добавляется только когда Луна действительно мешает и смотреть на небо имеет смысл.
Помеха зависит от освещённости диска и высоты: низкая Луна светит в основном вдоль горизонта и небо над головой почти не засвечивает, поэтому вклад растёт до 25° над горизонтом. Считается локально, без запросов, как высота Солнца; общий код лежит в core.js, так что сервер уведомлений может использовать те же функции.
Положение Луны — усечённая теория Меёса («Астрономические алгоритмы», гл. 47). Точность проверена по независимому источнику, астрономическому API Морской обсерватории США (данные лежат в tools/fixtures/usno-moon-2026.json, тест — tools/moon.test.mjs):
| Что сверялось | Расхождение |
|---|---|
| 50 фаз Луны за 2026 год | в среднем 1,6 мин, наибольшее 3,1 мин |
| 38 восходов и заходов в Мурманске и Кандалакше | наибольшее 1,2 мин |
| Освещённость диска | до 1–2 процентных пунктов |
| Полярные случаи: Луна не заходит (21 декабря), не восходит (11 февраля), видна 17 минут (5 июня) | совпадают |
Один нюанс из живой проверки: осенью и зимой Луна на этой широте часто вообще не поднимается над горизонтом (максимальная высота = 90° − широта + склонение, а у Мурманска это около 21°), и это верно, а не ошибка: 21 сентября 2026 года по данным обсерватории Луна «всё время под горизонтом», как и в расчёте.
Итог — произведение баллов: ≥ 6 высокий, ≥ 2 средний, иначе низкий.
Высота Солнца считается локально для координат выбранной точки, без запросов к API. С мая по июль здесь полярный день, и при любом Kp и чистом небе сияние не видно, поэтому светлое небо принудительно опускает вердикт до низкого.
Kp, прогноз Kp, солнечный ветер, OVATION и прогноз на 27 дней страница берёт сначала у сервера уведомлений (/noaa/…, см. worker/README.md): он отдаёт те же файлы в компактном виде — за обновление приходит около 60 КБ вместо 2 МБ, что заметно на мобильном интернете, — и выручает, если сеть не пускает к NOAA. Если не отвечает сервер, запрос уходит к NOAA напрямую, как раньше.
Облачность и погоду сайт берёт у Open-Meteo напрямую. Если прямой запрос не проходит — сеть или VPN не пускает к api.open-meteo.com, адрес выбрал суточный лимит Open-Meteo, — тот же запрос повторяется через сервер уведомлений (/meteo, см. worker/README.md), и до конца сеанса запросы к Open-Meteo сразу идут через него. Если Open-Meteo не отвечает и серверу, сервер берёт прогноз у резервной модели MET Norway — в карточке облачности тогда так и написано: «Модель прогноза: MET Norway». Не помог и сервер — в карточке остаётся исходная причина.
Второе мнение об облаках. После того как карточка облачности показана, страница спрашивает у сервера общую облачность модели MET Norway (/metno/cloud) и сравнивает её с ICON-EU. Модели согласны, значит под карточкой короткая строка об этом. Расходятся на 40 п.п. и больше, значит жёлтая строка «прогноз облаков сейчас ненадёжен» и отдельная строка в «Подробнее» у вердикта. Сам уровень от второго мнения не меняется: уведомления сервер считает по ICON-EU, и сайт с ними должен совпадать.
Kp описывает прошедшие три часа, поэтому о начинающейся суббуре он сообщает с опозданием. Спутники NOAA в точке L1 (1,5 млн км к Солнцу) видят солнечный ветер за 30–90 минут до того, как он дойдёт до Земли. Главное для сияния — Bz, север-юг магнитного поля ветра: когда он устойчиво южный (отрицательный), энергия ветра проходит в магнитосферу.
| Оценка | Условие |
|---|---|
| сильный южный | Bz в среднем ≤ −10 нТл за 15 минут |
| устойчиво южный | Bz в среднем ≤ −5 нТл за 20 минут |
| слабо южный | сейчас южный, но слабо или недолго |
| северный | Bz ≥ 0 |
Текущий Bz — среднее за 5 минут: поминутные отсчёты заметно дрожат. Время в пути до Земли считается по скорости ветра (1,5 млн км / скорость). Данные старше получаса за «сейчас» не выдаются — спутники иногда замолкают.
Уровень вердикта от солнечного ветра не меняется: это прогноз на час вперёд, а не текущее состояние. Но если шанс сейчас не высокий, небо открыто и темно, а Bz южный, к пояснению добавляется «солнечный ветер обещает рост активности в ближайший час». Расчёт — solarWindSummary() в core.js, общий с сервером на случай будущих ранних уведомлений.
Источники: json/rtsw/rtsw_mag_1m.json (сутки поминутных данных, около 90 КБ в сжатом виде; берётся основной поток active, а если основной спутник молчит больше получаса — самый свежий из остальных, с пометкой в карточке; ряды разных спутников не смешиваются) и products/summary/solar-wind-speed.json. Прежние адреса products/solar-wind/*.json NOAA убрала — отвечают 404.
Приходит тем же запросом к Open-Meteo, что и облачность (модель ICON-EU), и хранится вместе с ней — отдельного запроса нет. Главное условие выбирается по приоритету: туман (коды ВМО 45/48 или видимость меньше 1 км) → снег → осадки (дождь, морось, гроза) → сильный ветер (порывы от 15 м/с) → «осадков нет».
Туман облачность не показывает: модель видит ясное небо, а над головой молоко. Поэтому при тумане в вердикт добавляются фактор «Туман» и пояснение; уровень шанса не меняется. Единицы следуют настройкам: при милях ветер — в милях в час, видимость — в милях; температура — °C или °F.
Для тех, кто выбирает даты поездки. NOAA раз в неделю (по понедельникам) публикует таблицу text/27-day-outlook.txt: на каждые сутки UTC — наибольший ожидаемый Kp. Прогноз строится по вращению Солнца: области, давшие бурю, через 27 суток часто дают её снова. Точность невысокая, погода неизвестна — это ориентир, а не обещание, и карточка так и говорит.
Каждый день окрашивается по порогам Kp выбранной точки (как вердикт). В местную полночь (21:00 UTC) проверяется, будет ли ночь тёмной, и насколько светит Луна. Лучшие даты — подряд идущие дни с высоким уровнем, тёмной ночью и без яркой Луны (освещённость меньше 70%); яркая Луна не исключает день, если буря на 2 единицы Kp сильнее порога «высокого» — такая видна и так. Показываются до трёх сильнейших отрезков, по порядку дат.
Таблица загружается при открытии вкладки «Куда ехать» и не чаще раза в 6 часов; сохранённая годится 8 дней. Разбор и оценка — parseOutlook27(), outlookDays(), outlookBestRanges() в core.js.
На вкладке «Куда ехать» под прогнозом на 27 дней — как было в последние ночи у выбранной точки: для каждой ночи уровень шанса, наибольший Kp, наименьшая облачность и лучшие часы, а сверху итог («из последних 6 ночей шанс был хотя бы средним в 2 ночи»). Видно, что вы пропустили и как часто здесь бывает шанс.
Оценка задним числом — по тем же правилам, что окно наблюдения (hourLevel): Kp — измеренный NOAA по трёхчасовкам за 7 суток (products/noaa-planetary-k-index.json), облачность — та же модель ICON-EU за прошлые дни (Open-Meteo, past_days=7), высота Солнца — локально. В счёт идут только завершившиеся ночи, целиком попавшие в данные; идущая ночь — это окно наблюдения на «Сейчас». Летом, когда темноты нет, так и написано. Два небольших запроса — только при открытии вкладки, не чаще раза в час, с кэшем на три часа.
Под «Прошлыми ночами» — итог сверки прогноза с фактом. Сервер уведомлений каждый вечер записывает, какой шанс на ночь обещает прогноз для семи точек, а утром сверяет с измеренным Kp NOAA и облачностью ICON-EU за прошедшие часы — по тем же правилам, что окно наблюдения. Карточка показывает, в какой доле случаев уровень совпал, насколько часто прогноз ошибался на одну и на две ступени и что было, когда обещали высокий шанс. Пока сверено меньше пяти ночей, цифр нет — только «статистика копится». Подробности — в worker/README.md.
OVATION Prime считает по солнечному ветру вероятность увидеть сияние прямо над головой — на сетке 1°×1° по всей Земле, с упреждением 30–90 минут. Сияние светится на высоте 100–300 км и видно у северного горизонта за несколько сотен километров, поэтому кроме клетки самой точки берётся максимум в поле зрения: до 5° к северу и ±3° по долготе (на 65–70° с. ш. это около 550 × 250 км). Подпись: от 50% — «яркое сияние в поле зрения», от 30% — «вероятно», от 10% — «возможна слабая дуга у горизонта».
Облаков и светлого неба модель не учитывает — это делает вердикт, и в его уровень OVATION не входит. Файл весит около 140 КБ в сжатом виде, поэтому запрашивается не чаще раза в 15 минут (кнопка «Повторить» — всегда), а в кэш кладутся только числа по семи точкам, не сетка. Наблюдение старше полутора часов прогнозом не считается. Расчёт — ovationSummary() в core.js.
Вердикт относится к текущему моменту, а планировать поездку нужно на ночь, поэтому дашборд отдельно считает лучшее время ближайшей ночи. Новых запросов для этого не нужно — всё уже загружено:
Ночью считается ближайший непрерывный отрезок часов, когда Солнце ниже −6°. Каждому часу присваивается тот же уровень, что и вердикту: произведение баллов за Kp и облачность, с понижением до среднего, если темнота неполная (Солнце выше −12°). Окно — самый длинный непрерывный ряд часов с максимальным уровнем.
Под окном — график всей ночи, по столбцу на час: цветная полоса шанса, столбики облачности (нейтрального цвета — это не оценка, а сколько неба закрыто) и прогнозного Kp, отметка Луны, если она над горизонтом (ярче — полнее), и время. Светлые столбцы — сумерки, рамка — лучшее окно. Нажатие на час (или стрелки на клавиатуре) показывает под графиком его подробности; по умолчанию выбрано начало окна. Видно не только «когда лучше», но и почему именно тогда. Луна берётся в середине часа: если она заходит в 03:14, час 03:00–04:00 считается безлунным.
В полярный день тёмных часов не находится, и карточка честно сообщает, что темноты не будет.
Дашборд запрашивает у Open-Meteo не суммарную облачность, а три яруса по отдельности — и при проверке выяснилось, что этим полям можно верить не у всякой модели.
Исходное наблюдение: при ярусах 6 / 3 / 0 (практически чистое небо) API отдавал суммарную облачность 62 %. Это противоречие, а не округление: при любом допущении о перекрытии слоёв суммарная облачность обязана лежать между максимумом яруса и суммой ярусов.
max(ярусы) ≤ всего ≤ min(100, сумма ярусов)
Проверка двенадцати моделей на 48 часах почасового ряда (17 сентября 2026, точка 68.97 / 33.07):
| Модель | Часов с нарушением неравенства | Расхождение > 30 п.п. | Среднее расхождение |
|---|---|---|---|
best_match | 16 из 48 (33 %) | 23 % часов | 17 п.п. |
metno_seamless | 16 из 48 (33 %) | 23 % часов | 17 п.п. |
icon_eu | 0 | 0 | 2 п.п. |
ecmwf_ifs025 | 12 (25 %) | 0 | 8 п.п. |
gfs_seamless | 1 (2 %) | 0 | 1 п.п. |
Что из этого следует:
best_match для Мурманска — это MET Norway. Ответы best_match и metno_seamless совпали до единицы во всех четырёх полях и на всех 48 часах.icon_eu и gfs_seamless, а ярусы 8 / 13 / 0 разделили только knmi_seamless и dmi_seamless — сборки, которые, судя по совпадению значений до единицы, берут их из общего источника.Поэтому модель указывается явно (models=icon_eu), а не отдаётся на автовыбор. ICON-EU грубее локальной скандинавской модели — сетка 7 км против ~1 км, — но её поля самосогласованы, а для расчёта по ярусам это важнее разрешения.
Название действующей модели выведено в карточке облачности, чтобы подмена источника не прошла незаметно. Модель меняется одним значением CONFIG.weatherModel в app.js.
Страховка. Если оценка по ярусам и суммарная облачность всё же разойдутся больше чем на 30 процентных пунктов, в карточке появится предупреждение «данные об облачности противоречивы», а балл за облачность будет ограничен: высокий при недостоверных данных не ставится.
localStorage. При отказе показываются сохранённые данные приглушённо, с пометкой «данные 12 минут назад» и кнопкой «Повторить». Старше трёх часов не показываются вовсе.try/catch: в приватном режиме обращение к localStorage бросает исключение, при переполнении квоты падает запись. Кэш необязателен — без него дашборд работает как раньше.index.html разметка: вердикт, окно наблюдения, Kp, облачность, прогноз
i18n.js движок переводов: выбор языка, подстановки, множественные числа
lang/ словари интерфейса: ru.js (эталон), en.js, zh.js
styles.css тёмная «полярная» тема, адаптив до 375 px
vendor/ сторонний код без изменений: qrcode.js (MIT) для окна «Поделиться»
splash.js заставка первого визита: сияние на холсте, пропуск, переход к дашборду
map.js схема области для вкладки «Карта» (собрана tools/build-map.mjs из Natural Earth)
core.js расчётное ядро: точки, пороги Kp, облачность по ярусам, высота Солнца, уровень вердикта
app.js точка входа: запуск и регистрация service worker
js/ код приложения по темам: основа, «Сейчас», ночь, «Куда ехать», карта, уведомления,
вкладки и вердикт, «Поделиться», настройки и язык (см. js/README.md)
push.js подписка браузера на уведомления с сервера
config.js настройки развёртывания: адрес сервера уведомлений и открытый ключ
manifest.json манифест PWA
robots.txt для поисковиков: всё открыто, ссылка на карту сайта
sitemap.xml карта сайта: адрес по умолчанию и три языковые версии
sw.js service worker: кэш оболочки приложения, показ push-уведомлений
icons/ иконки 192, 512 и maskable
worker/ сервер уведомлений (Cloudflare Worker), необязательный
tools/ тесты и скрипты проверки: согласие сайта и сервера, клиент push, service worker
Почему расчёт вынесен в core.js. Его используют и страница, и сервер уведомлений. Копия формул на сервере разошлась бы с сайтом при первой же правке, и уведомление «высокий шанс» оказалось бы при открытии приложения «средним». Поэтому файл общий, а тест tools/parity.test.mjs сравнивает вердикт сайта и сервера на ~970 тыс. комбинаций Kp и облачности, для всех точек и на реальной высоте Солнца в течение всего года.
Папка tools/ не нужна странице для работы: это тесты (node --test tools/*.test.mjs) и одноразовые скрипты, которыми проверялся выбор метеомодели. Подробности — в tools/README.md.
Семь точек на схеме области, цвет — шанс на ближайшую ночь (те же данные, что на вкладке «Куда ехать»; открытие любой из двух вкладок загружает их для обеих). Нажатие на точку показывает под картой её строку из списка — окно, облачность, порог Kp, дорогу — и кнопку «Смотреть условия», которая делает её точкой наблюдения и открывает «Сейчас».
Схема — собственная, без библиотек и чужих тайлов: работает офлайн и весит около 12 КБ. Контуры области и озёр взяты из Natural Earth (общественное достояние) и собраны скриптом tools/build-map.mjs в map.js. Рамка — район семи точек, а не вся область: иначе на телефоне точки слиплись бы в пятую часть карты. Сама схема тянется по ширине, а точки — это кнопки поверх неё в CSS-пикселях: их размер не зависит от масштаба, они доступны с клавиатуры и подписаны для скринридера. Подписи Кировска и Апатитов (15 км друг от друга) разведены в разные стороны.
Облака над областью. Семь точек не отвечают на вопрос «а если отъехать на 30 км?», поэтому поверх схемы лежит слой облачности. Над рамкой карты берётся сетка 9 × 10 узлов (шаг около 30 км), и для всех узлов одним запросом к Open-Meteo приходит почасовая облачность по ярусам на сутки вперёд — та же модель ICON-EU и та же модель перекрытия ярусов, что для точек. Между узлами значения плавно смешиваются (билинейно), так что видна картина, а не квадраты; ясные места остаются прозрачными, сплошные облака закрывают схему не полностью, а береговые линии повторены поверх слоя. Ползунок листает часы от текущего на сутки вперёд, под ним — облачность числом у выбранной на карте точки. Слой можно выключить, выбор запоминается.
Сетка грузится только при открытии карты, не чаще раза в час (модель пересчитывается раз в несколько часов), и сохраняется: без сети показываются сохранённые облака с пометкой возраста, прошедшие часы отбрасываются.
При первом открытии за сеанс (новая вкладка или запуск установленного приложения) за шапкой поднимаются и гаснут пять «занавесов» сияния — зелёные, бирюзовые, фиолетовые, — заголовок переливается градиентом, а шапка, вкладки и карточки проявляются волной. Около 2 секунд, только CSS: класс intro на <html> ставит встроенный скрипт в <head> и снимает через 2,6 с.
Анимация ничего не задерживает — данные грузятся и считаются параллельно. Повторно в том же сеансе её нет, а при системной настройке «уменьшить движение» — нет вовсе (проверяется и в скрипте, и в CSS). Хранилище недоступно — страница просто открывается без анимации.
Самый первый раз на устройстве сайт открывается полноэкранной заставкой: звёздное небо, три переливающиеся ленты сияния (зелёная, бирюзовая, фиолетовая, цвета — из темы), название и регион. Около 2,8 с, затем за 0,6 с плавно растворяется в дашборд. Пропустить можно сразу: кнопкой «Пропустить» (она в фокусе, над полосой системных жестов с учётом safe-area), клавишей Esc или нажатием в любом месте; тогда переход — 0,25 с. Свернули вкладку — заставка тоже заканчивается.
aurora.splash в localStorage. При повторных визитах заставки нет вовсе — только лёгкая анимация запуска раз за сеанс (выше). Укороченная версия на 1–1,5 с повторному посетителю ничего не сообщает, а открыть прогноз он хочет сразу — поэтому её нет. В первый визит лёгкая анимация не запускается, чтобы не было двух подряд.<head> до первой отрисовки, заставка — отдельный слой поверх страницы, а запросы к NOAA и Open-Meteo уходят сразу при загрузке, параллельно с анимацией.visibility: hidden), пока заполняется данными, и проявляется одновременно с исчезновением заставки.splash.js в оболочке service worker, так что офлайн заставка тоже работает.Проверено в Chrome (телефон 375×812, DPR 3, и компьютер 1280×800): 60 кадров/с, медиана кадра 16,7 мс; при замедлении CPU в 4 раза медиана та же, дольше 50 мс — 7 кадров из ~165 (в основном при разборе скриптов на старте); CLS 0; первый запрос к данным — через 0,1 с после начала загрузки (0,4 с при замедлении).
Кнопка «Поделиться» в шапке открывает окно со ссылкой на сайт на выбранном языке (?lang=ru|en|zh, по умолчанию — язык страницы) и её QR-кодом. Действия: «Отправить…» (системное меню, где оно есть — на телефонах), «Скопировать ссылку» и «Скачать QR для печати» — PNG по 12 пикселей на модуль, чтобы распечатать для гостей отеля или туристической группы.
QR строит библиотека qrcode-generator (Kazuhiko Arase, MIT), скопированная без изменений в vendor/qrcode.js. Она подгружается при первом открытии окна, а не со страницей, и лежит в оболочке service worker — работает офлайн. Коррекция ошибок уровня M, тихая зона в 4 модуля, белый фон — код читается и в тёмной теме. Сгенерированные коды для всех трёх языков проверены декодером OpenCV: распознаются в точности как нужные ссылки.
Вкладка для туриста, статичный текст на трёх языках, пять разделов:
Советов по одежде в гиде нет намеренно — это проверяет тест.
На вкладке «Настройки» блок «Вид и единицы» (хранится в браузере одной записью aurora.settings; по умолчанию всё как было до появления настроек):
| Настройка | Варианты | Что меняет |
|---|---|---|
| Часовой пояс | Мурманск (МСК, по умолчанию) · моё устройство | все времена на сайте: окно наблюдения, прогноз Kp, восходы и заходы Луны, подпись «Обновлено в …» |
| Формат времени | 24 часа · 12 часов (AM/PM) | те же места |
| Расстояния | километры · мили | «От Мурманска 120 км» на вкладке «Куда ехать»; ветер (м/с или мили в час) и видимость |
| Температура | °C · °F | карточка «Погода на месте» |
| Автообновление | 5 · 10 · 30 минут · выкл. | период опроса NOAA и Open-Meteo и подпись в подвале; при «выкл.» данные обновляются кнопкой «Обновить» |
| Тема | тёмная (по умолчанию) · светлая · как в системе · ночное зрение | цвета всей страницы и цвет строки состояния браузера; выбор применяется до первой отрисовки, без вспышки другой темы |
| Размер текста | обычный · крупный (×1,15) · очень крупный (×1,3) | страница масштабируется целиком |
| Открывать на вкладке | где закрыл (по умолчанию) · «Сейчас» · «Куда ехать» · «Карта» | стартовая вкладка; ссылка с адресом (#tonight) важнее настройки |
| Тихие часы | выкл. · 22–08 · 23–07 · 00–06 | уведомления о сиянии в эти часы не приходят — ни от страницы, ни с сервера; время по выбранному часовому поясу; пробные уведомления приходят всегда |
| О каком шансе сообщать | только высокий (по умолчанию) · средний и высокий | порог уведомлений страницы и сервера |
| Небо скоро откроется | выкл. (по умолчанию) · вкл. | сигнал с сервера о просвете в облаках в ближайшие 3 часа, когда сияние возможно |
О чём сообщать. Блок на вкладке «Настройки»: порог — «только высокий» (по умолчанию) или «средний и высокий» — действует и на уведомления страницы, и на уведомления с сервера; уведомление приходит, когда шанс поднялся до порога или выше прежнего, не чаще раза в 3 часа. «Небо скоро откроется» (выкл. по умолчанию) — сигнал с сервера: сейчас сплошные облака, но сияние возможно, а модель обещает просвет в ближайшие 3 часа; раз за ночь. Подробности — в worker/README.md.
Тихие часы и сервер. Если включены уведомления с сервера, страница сообщает ему окно и часовой пояс (название IANA, например Europe/Moscow), и сервер сам не шлёт в это время. Сигнал не пропадает: пока «высокий» держится, подписчик остаётся подходящим и получит уведомление первым проходом после окончания тихих часов. Пропущенные не расходуют лимит отправок. Уведомления страницы в тихие часы просто пропускаются — переход уже состоялся.
Светлая тема — это переопределение переменных CSS: цвета в коде (TONE) — ссылки на переменные, поэтому перерисовка при смене темы не нужна. Контраст текста и тонов — не ниже 4,5:1 на фоне и на карточке в обеих темах, это проверяет тест.
Ночное зрение — красная тема для наблюдения. Глаза привыкают к темноте 20–30 минут, и белый экран сбивает это за секунду; палочки сетчатки, которыми мы видим в темноте, почти не чувствуют длинноволновый красный. Поэтому всё красное на почти чёрном фоне, без цветного свечения и ореолов, картинки и QR-код перекрашены фильтром, а облака на карте в этой теме тёмные — гасят экран, а не подсвечивают. Уровни шанса различаются яркостью и всегда подписаны словами. Включается одним нажатием кнопкой с глазом в шапке и так же выключается — к той теме, что была до неё. Тест проверяет, что все цвета темы красные и контраст текста не ниже 4,5:1.
Кнопка «Сбросить настройки» возвращает значения по умолчанию. Язык переключается в шапке и отдельной настройкой не дублируется. Расчёт шанса от настроек не зависит: меняются только слова, числа и время на экране. Недопустимое или испорченное значение в хранилище заменяется значением по умолчанию — страница не ломается.
Ниже блока настроек на той же вкладке — уведомления (см. следующий раздел).
Переключатель «Русский · English · 中文» стоит в шапке. Какой язык показать, решается по порядку:
?lang=en, ?lang=zh, ?lang=ru (им удобно делиться: ссылка или QR-код для гостей отеля сразу откроется на нужном языке; выбор запоминается);zh-CN, zh-TW, zh-Hans дают китайский, ru-* — русский;Что переводится: весь интерфейс, названия городов, подсказки и вердикт, тексты ошибок, диагностика уведомлений, уведомления страницы, а с сервером уведомлений — и push-уведомления при закрытом приложении. Расчёт от языка не зависит: уровень шанса при тех же Kp и облачности один и тот же на любом языке (проверяется тестом).
Как принято в языке: десятичная запятая только в русском (4,3, а не 4.3), формы множественного числа берутся из Intl.PluralRules (1 минуту, 2 минуты, 5 минут; 1 minute, 2 minutes; 5分钟), день недели и порядок слов — свои. Время везде московское (Мурманская область живёт по нему, UTC+3), 24-часовое, чтобы совпадало с расписаниями на месте.
Как добавить язык. Скопируйте lang/en.js в lang/<код>.js, переведите значения, подключите файл в index.html, sw.js (оболочка приложения) и добавьте язык в I18N_LANGS в i18n.js. Тест tools/i18n.test.mjs проверит, что ключи и подстановки {имя} совпадают с русскими, что нет пропущенного или лишнего, и что все ключи, которые называет код, существуют.
Оговорка о переводах. Английский и китайский тексты написаны без носителей языка и не вычитаны ими. Смысл и числа проверены тестами, но интонация и устоявшиеся выражения (особенно в китайской версии) стоит показать носителю: правки — только в lang/en.js и lang/zh.js, код трогать не нужно.
Под вердиктом есть кнопка «Сообщить о высоком шансе». Разрешение браузер спрашивает только по её нажатию, после согласия приходит пробное уведомление — сразу видно, что система их пропускает. Повторное нажатие выключает.
Уведомление приходит, когда вердикт выбранной точки переходит в «Высокий», а не на каждом обновлении, пока он держится. Дополнительные правила:
На вкладке «Уведомления» можно проверить, что всё работает. Пошаговая проверка показывает, где именно проблема: поддерживает ли браузер уведомления, выдано ли разрешение сайту, активен ли service worker, включены ли уведомления о высоком шансе и открыто ли приложение как установленное (на iPhone без этого не работает). Кнопка «Отправить пробное» шлёт уведомление сразу, «Через 10 секунд» — с задержкой, чтобы успеть свернуть приложение и проверить главный сценарий: придёт ли уведомление в фоне. Ниже — памятка, что делать, если не пришло, для Chrome и Edge, Windows, macOS, Android и iPhone.
Уведомления показываются через service worker — на Android обычный конструктор new Notification() не работает. Нажатие на уведомление открывает приложение или возвращает фокус уже открытому.
Ограничение. Сам сайт — статические файлы, и закрытая страница проснуться не может: эти уведомления работают, пока приложение открыто — во вкладке, в том числе фоновой, или в установленном окне. На телефоне система обычно приостанавливает свёрнутое приложение, так что там это скорее сигнал, пока экран не погас. Для уведомлений при закрытом приложении нужен сервер — см. следующий раздел. На iPhone уведомления доступны только в приложении, добавленном на экран «Домой».
Если развёрнут сервер уведомлений, на вкладке «Настройки» появляется блок с кнопкой «Включить уведомления». Сервер — Cloudflare Worker на бесплатном тарифе: раз в 5 минут он смотрит Kp (NOAA) и облачность (Open-Meteo) для точек, на которые есть подписчики, считает вердикт тем же кодом, что и сайт, и при переходе в «Высокий» шлёт push через сервис вашего браузера. Уведомление приходит, даже когда приложение закрыто.
Правила те же: при переходе в «Высокий», не чаще раза в 3 часа, ничего по неполным данным, «высокий», который уже идёт, никого не будит. Подписка привязана к выбранной точке и переезжает вместе с выбором в шапке. Пока серверные уведомления включены, страница свои не показывает, чтобы не было двух одинаковых.
Кнопка «Через 20 секунд» проверяет главный сценарий целиком: нажмите её и сразу закройте приложение совсем — уведомление придёт с сервера.
Пульс. Сервер записывает время и итог каждой проверки по расписанию, а вкладка «Настройки» показывает «сервер проверял условия 4 минуты назад». Если проверок не было больше 30 минут, там появится предупреждение, что уведомления могут не прийти, — сломавшийся cron не останется незамеченным.
Приватность. Сервер хранит только адрес подписки браузера, выбранную точку, язык уведомлений и тихие часы с часовым поясом: ни имён, ни почты, ни координат человека. Выключение уведомлений удаляет запись, недействительные подписки удаляются автоматически. Пока в config.js не указан адрес сервера, блок скрыт, а сайт работает как раньше.
Дашборд — PWA: в Chrome и Edge его можно установить с панели адреса, на Android — «Добавить на главный экран», на iOS — «Поделиться» → «На экран «Домой»». Запускается в отдельном окне без адресной строки, с тёмной темой системных панелей.
Ярлыки. Долгое нажатие на значок установленного приложения (или правый клик по нему на компьютере) предлагает три ярлыка: «Куда ехать» (/#tonight), «Карта» (/#map) и «Ночное зрение» (/?theme=night#now). Последний включает красную тему ещё до первой отрисовки и запоминает прежнюю — кнопка с глазом в шапке вернёт её; параметр после этого убирается из адреса, чтобы перезагрузка не включала тему снова.
Service worker кэширует только оболочку приложения — index.html, styles.css, app.js, манифест и иконки — по стратегии cache-first. Запросы к NOAA и Open-Meteo не кэшируются вообще и всегда идут в сеть: их свежесть обеспечивает localStorage со своей отсечкой в три часа и пометкой возраста, а два кэша с разными правилами устаревания давали бы противоречивые показания.
Без сети дашборд открывается из кэша и показывает последние сохранённые данные приглушённо, с подписью вроде «Данные 8 минут назад» на каждой карточке.
Имя кэша версионировано (aurora-v34 и дальше). При изменении файлов оболочки версия поднимается: install перезагружает файлы, минуя HTTP-кэш, activate удаляет предыдущую версию. Плата за cache-first — обновление применяется со следующего открытия приложения, а не в текущем сеансе.
hreflang="x-default"), ?lang=ru|en|zh — отдельные версии. Их связывают теги hreflang и sitemap.xml; canonical указывает на ту версию, по которой открыли страницу (его ставит applyLanguage()).icons/og-image.png 1200×630 — кадр заставки.WebApplication): название, описание, бесплатность, языки, регион.robots.txt открывает сайт целиком, кроме служебных tools/ и worker/.Согласованность всего этого (адреса, размеры картинки, переводы) проверяет tools/seo.test.mjs. Чтобы сайт быстрее попал в выдачу, его стоит добавить в Яндекс Вебмастер и Google Search Console и отправить там sitemap.xml; при изменениях страницы в sitemap.xml обновляется lastmod.
Оба API публичные, без ключей и регистрации.
Оценка ориентировочная.
© 2026 Ilya Minichev. Все права защищены. Репозиторий открыт для просмотра, но использовать, копировать, изменять и распространять код и материалы без письменного разрешения автора нельзя — подробности в LICENSE. Сторонний код (vendor/qrcode.js, MIT) и данные NOAA, Open-Meteo и Natural Earth остаются на условиях своих правообладателей.