В начале 2022 года я съездил в самостоятельное путешествие за северным сиянием. Это оказалось прекрасно, кроме этапа планирования. Все сайты с прогнозами «северных огней» выглядели странно и едва помогали собраться, но зато предлагали купить в пару кликов тур и ни о чем не переживать. Тур мне был не нужен, а вот хороший прогноз — да.

В конце лета я решил, что хочу написать свой небольшой опенсорс сайт, где будет просто и удобно узнать прогноз сияния самым обычным туристам вроде меня. Рассказываю, что из этого получилось.

Пестрые краски и занятные пустоты

Общая идея всех проектов с прогнозами сияния мне нравилась: это несколько цветных графиков, иногда советы, подсказки и объясняющие тексты где-то рядом. Но в тоже время реализация казалась странной, особенно две общие черты между почти любыми ресурсами: резкие, яркие цвета и внезапный английский язык.

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

Типичное оформление графиков, очень неудобно смотреть на белый текст со светло-зеленым фоном
Типичное оформление графиков, очень неудобно смотреть на белый текст со светло-зеленым фоном

Когда я только собирался в путешествие, ничего о КП-индексах мне известно не было, но в тоже время большинство сайтов сразу демонстрировали графики с непонятными цифрами и любопытными цветами: жёлтый это плохое сияние или сильное? А красный? В тоже время полезные тексты, вроде «что такое КП-индекс», лежали либо в футере, либо вовсе на отдельных страницах, до которых предстояло добраться че��ез затейливую навигацию.

Последнее место для модернизации обнаружилось прямо в моём проекте. В очередной день написания и проверки кода я заметил, что графики опустели. Более того, выяснилось, что рухнули вообще все сайты с прогнозами сияния, по крайней мере в русскоязычном сегменте. Оперативный дебаг показал, что вид записи входных данных немного изменился, поэтому из-за ненадежного кода у всех упали графики. До сих пор некоторые сайты не оправились от того «удара».

Один из ресурсов, где графики сломались, а людей, готовых это починить, не нашлось
Один из ресурсов, где графики сломались, а людей, готовых это починить, не нашлось

Итак, я решил учесть эти и некоторые другие проблемы, чтобы сделать свой сайт лучше и удобнее, а заодно утолить любопытство и выяснить, откуда все-таки берутся эти прогнозы. Вот какой вид приняло бы условное ТЗ:

  1. Три коротких секции на сайт. Первая введет человека в курс дела, объяснит, как разобраться с информацией на странице и куда вообще он попал. Вторая покажет графики с индексами. Третья предложит элементарный тест для тех, кто волнуется и хочет проверить свою готовность к путешествию. Перед своей поездкой я бы хотел пройти такую самопроверку;

  2. Цветовая лаконичность и логичность: в итоге красный график круче желтого или как?

  3. Подписи для графиков на русском языке;

  4. Надежный код, который просто так не сломается от мелких новшеств во входных данных;

  5. Простота во всём: зашёл, понял что за КП-индексы, посмотрел графики, проверил себя, ушёл.

После обмозгования мной структуры пришло время писать код и запускать его для людей, ведь северное сияния видно уже с осени, а дело близилось к зиме.

КП-индексы — кто вы и откуда

Чтобы подгадать даты поездки, в самом любительском варианте нужно смотреть на КП-индексы, — они показывают планетарный уровень геомагнитной активности и обозначаются цифрами от 0 до 9. Хотя опытные туристы наблюдают за иными параметрами, КП-индексы лежат в основе почти всех сайтов с прогнозами, да и корреляция с яркостью, цветностью сияния замечается: чем выше индекс, тем интенсивнее солнечная активность и, как следствие, выше вероятность увидеть что-то захватывающее.

Радовало, что от сайта к сайту отображались одинаковые данные, а это явно намекало на их единый источник. Тем не менее, поиск сразу не заладился. Любые попытки посмотреть через DevTools происхождение данных неминуемо натыкались на месиво из кода с плагинами для WordPress и другой засохшей кашей. Гугл также не спешил помогать: по запросу «aurora forecast json» встречались только гайды, pdf-файлы и открытия британских учёных.

Хроники DevTools: уже привычные следы давно забытого дебага в консоли одного из проектов
Хроники DevTools: уже привычные следы давно забытого дебага в консоли одного из проектов

Спустя время подметил, что под графиками один владелец сайта оставил сухой референс «NOAA». Поиск выявил, что аббревиатура расшифровывается как «Национальное управление океанических и атмосферных исследований США» — то место, откуда явно тянулись все следы. Любопытно, что никто не указывал ссылки на конкретные разделы с данными. Вероятно, чтобы каждый прошел по дебрям запутанного американского сайта и ощутил все прелести этого путешествия самостоятельно.

Стартовая страница подсайта NOAA, посвящённого космической погоде
Стартовая страница подсайта NOAA, посвящённого космической погоде

Мне открылся суровый мир американского государственного п��ртала, где странности поджидали повсюду. Например, для получения трёхдневного прогноза американцы указали заветную ссылку, а для того же самого, но на 27 дней, такую ссылку не добавили, ищи как хочешь. Или ещё: прогнозов на три дня два, один называется «3-day forecast», а другой «3-day geomagnetic forecast», причем они почти идентичные. Что выбрать? Оказалось, что хотя логичнее отдать предпочтение «3-day geomagnetic forecast», он обновляется не так оперативно, как первый вариант, в среднем на день позже, поэтому выбрал первый — «3-day forecast».

Сами же данные, наоборот, содержались в небольших и вполне удобных таблицах, где в случае трёхдневного прогноза указали время, дату и КП-индекс на нужные часы. Сверху обычно содержится небольшой бриф по индексам за какое-то время, а снизу пояснения по таблице, но это не пригодилось в рамках моего ресурса.

Таблица с прогнозами КП-индексов на три дня на сайте NOAA SWPC
Таблица с прогнозами КП-индексов на три дня на сайте NOAA SWPC

Несколько поблуждав по американскому сайту, я всё же нашёл нужную страницу. Она оказалась простейшим списком со ссылками на любой вкус: для получения картинок, параметров Солнца и другого. Жаль, что найти её оказалось не так элементарно. При этом в NOAA SWPC предоставляют данные бесплатно, а отключают раздачу только если запросов непомерно много. Это я проверил случайно из-за живой перезагрузки по ходу написания кода, решилось временной сменой браузера.

Заключительная странность обнаружилась в том, что на найденной странице по пути «json» отсутствовали нужные мне прогнозы в этом формате. Все они оказались через ссылку ниже, — и это был «text».

Страница с прогнозами и другими данными от NOAA SWPC
Страница с прогнозами и другими данными от NOAA SWPC

Оформление страницы

Для сайта я выбрал простое и лаконичное название «Севернее». Порадовало, что этот домен — severnee.ru — оказался свободен, хоть в нём и нет ничего про сияние, зато легко держать в памяти и есть связь с географией регионов, где видно это необычное явление (UPD: на момент 2024 года сайт переехал на GitHub Pages и теперь живёт тут). Небольшой текст после заголовка быстро и «на пальцах» объясняет, как увидеть северное сияние и понять прогноз на странице. Клик по стрелке вниз плавно проводит к графикам.

Картинку рисовал сам (из кубиков в Paint) по аналогии с формой и цветами яркого сияния, а ещё не смог удержаться и наложил на неё анимацию в лице transform: rotate, чтобы она спокойно вращалась. Возможно, однажды уберу, но этот эффект меня завораживает, да и выглядит необычно. Кнопка «Подробнее» сейчас ведёт на сторонний сайт, это для тех, кто хочет лучше разобраться в теме. Сначала думал сделать свою контентную страничку, но потом решил сосредоточиться на прогнозе.

Начало страницы
Начало страницы

Основную часть разделил на четыре графика, три из которых представляют прогноз с интервалом в три часа, а последний общий на 27 дней. Вся работа над проектом сосредоточилась именно здесь: как над кодом для вывода данных, так и над посильным дизайном для их удобного просмотра.

Для графиков выбрал бесплатную библиотеку Chart.js, причём не сразу заметил, что к такому решению прибегнул не только я. Видимо, популярно и просто, хотя на некоторых ресурсах заверстали вывод индексов с помощью таблиц, но готовое решение посчитал красивее и проще. Под капотом Chart.js рендерит графики в тегах canvas.

Основной сложностью с этой библиотекой для меня выступила настройка конфигов, на основе которых формируется облик, цвета и всё остальное. Документация хорошая, но немало аспектов пришлось гуглить, причём нередко я обнаруживал, что какие-то места настроить вообще нельзя. Зато бесплатно.

График с прогнозами КП-индексов на 27 дней
График с прогнозами КП-индексов на 27 дней

Мне всегда казалось странным, что чем выше значение КП-индексов, тем более пёстрый цвет шкал выбирали на других проектах. Почему жёлтый и красный, если северное сияние обычно зелёное? Поэтому для низких индексов я окрасил столбцы в сероватый цвет — на самом деле, слабое сияние очень похоже на обычные облака. Чем выше индексы, тем более насыщенным будет зелёный, а при очень высоких значениях шкалы примут оттенки фиолетового, которые появляются и на самом деле.

Подписи к графикам наконец сделал на русском — теперь никакого контраста. Chart.js также позволяет вывести подсказку при наведении на графики, что показалось очень удобным, особенно для мобильной версии. В полной мере это ощущается в почасовых прогнозах, где можно вести курсором или пальцем вдоль линии данных и наблюдать, как КП-индексы меняются со временем.

Почасовой график с подсказкой на сегодня
Почасовой график с подсказкой на сегодня

В конце страницы добавил простейший тест для самопроверки, где можно выбрать либо «да», либо «нет». По его результатам в конце будут советы. Когда планируешь путешествие самостоятельно, часто возникает ощущение, что определенно что-то забыто. Такой тест как раз поможет проверить самые базовые пункты в подготовке, при соблюдении которых всё точно получится. Увидеть северное сияние на самом деле очень просто, и этот тест призван поддержать сомневающихся в своих силах.

Тест для самопроверки
Тест для самопроверки

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

Немного кода под капотом

Пожалуй, главная часть — как всё это работает на JS. Обработку прогнозов NOAA можно разделить на три общих этапа: сперва fetch-запрос, затем подготовка данных к передаче графикам и наконец сам рендеринг графиков. Работа с прогнозами на три дня и на 27 дней немного различается, но первая часть общая, кроме URL-адреса:

  try {
    let response = await fetch('https://services.swpc.noaa.gov/text/3-day-forecast.txt') // Запрос трехдневного прогноза
    let textForecast = await response.text() // Ответ в текстовом формате
    let resultForecast = threeDayTablesFiller(textForecast) // Обработка прогнозов

    daysChartBuilder(resultForecast) // Рендер графиков
  } catch (error) {
    console.log(`Ошибка в получении трёхдневного прогноза: ${error}`)
  }

Помимо прогноза КП-индексов в ответе NOAA есть и другие данные, например, краткий бриф с самыми высокими индексами за некоторый промежуток времени. Поэтому во второй части — той, где подготавливаются данные, — сперва я вырезал нужную мне информацию из строки по-простому: от одного индекса до другого. На тот момент я не сомневался, что текущая запись прогнозов существуют ещё с незапамятных времён и столько же лет не поменяется, а те события с крушением графиков на всех сайтах ещё не успели произойти. Вид портала NOAA SWPC лишь подкреплял эти заблуждения.

Через пару дней я обнаружил, что NOAA иногда добавляет справа от КП-индексов такие записи, как «G1», что указывает на геомагнитную бурю в это время. Если КП-индексы ниже пяти, то таких «добавок» нет. Из-за этого длина строки увеличивалась, а цикл, который вытаскивал нужную информацию, съезжал на несколько символов, поэтому в графики попадало не то, что нужно. Но тогда я всё равно посчитал, что это просто особенность, и лишь добавил проверку на эти «G1», чтобы в случае их появления чистить строку от ненужных мне данных.

Это была ошибка, — американцы не противились пробовать новенькое, и скоро они отказались от целых значений КП-индексов и добавили им точность до сотых. Строка снова длиннее — в графиках снова не то. Правда, в тот день, как я писал выше, упали графики у всех сайтов с прогнозами северного сияния, видимо, не я один убеждал себя в консервативности NOAA. После этого решил сделать гибкий поиск начала и конца нужных данных в таблице через RegExp.

const startOfForecastTable = new RegExp('(00-03UT)', 'g') // Находит начало таблицы с KP-индексами
const endOfForecastTable = new RegExp('\n\nRationale', 'g') // Находит конец таблицы
const extraSignatures = new RegExp('[(]G[1-5][)]', 'g') // Находит лишние подписи, как «(G1)»

const indexOfStart = data.search(startOfForecastTable)
const indexOfEnd = data.search(endOfForecastTable)
let crudeForecast = data.slice(indexOfStart, indexOfEnd) // Вырезаем таблицу из response.text()

if (crudeForecast.includes('G')) {
  crudeForecast = crudeForecast.replaceAll(extraSignatures, '    ') // Пробелы нужны, чтобы удалить элемент без сдвига
}

const resultForecast = crudeForecast
  .split(' ') // Создаём массив из таблицы для удобства
  .filter(item => item !== '')

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

const firstDayChartData = new Array()
const secondDayChartData = new Array()
const thirdDayChartData = new Array()

for (let i = 1; i < 30; i += 4) {
  firstDayChartData.push(Number(resultForecast[i]))
}

for (let j = 2; j < 31; j += 4) {
  secondDayChartData.push(Number(resultForecast[j]))
}

for (let c = 3; c < 32; c += 4) {
  thirdDayChartData.push(Number(resultForecast[c]))
}

return { firstDayChartData, secondDayChartData, thirdDayChartData } // Возвращаем готовые почасовые прогнозы на три дня для графиков

С прогнозом на 27 дней почти тоже самое, вся разница в том, что там требуется перевести на русский язык подписи для графиков, — для этого сделал импровизированный объект с английскими и русскими названиями месяцев. Методом .map() каждая дата в виде «2022 Jan 01» очищается от года и переводится на русский, а в конце преобразованный массив лейблов и массив, полный КП-индексами, передаются далее для рендеринга графиков.

const MONTHS_TRANSLATION = {
  Jan: 'Янв',
  Feb: 'Фев',
  Mar: 'Мар',
  Apr: 'Апр',
  May: 'Май',
  Jun: 'Июн',
  Jul: 'Июл',
  Aug: 'Авг',
  Sep: 'Сен',
  Oct: 'Окт',
  Nov: 'Ноя',
  Dec: 'Дек'
}

const translatedLabels = monthChartLabels.map(date => {
  const engMonth = date.slice(5, 8)
  const ruMonth = MONTHS_TRANSLATION[engMonth]

  return date.slice(5).replace(engMonth, ruMonth) // Очищаем от года, переводим название месяца на русский язык
})

return { monthChartData, translatedLabels } // Возвращаем готовый прогноз и лейблы

На последнем этапе обработанные данные в массивах разбираются на отдельные переменные, например, первый день, второй день, третий. После этого они попадают в конфиг графиков и рендерятся вызовом new Chart(). Я не стал приводить внутренности конфигов, потому что они достаточно объемные, но посмотреть их полностью можно в репозитории Гитхаба.

module.exports.daysChartBuilder = function (forecasts) {
  const { firstDayChartData, secondDayChartData, thirdDayChartData } = forecasts

  const config1 = {} // Прототип конфигов для графиков

  const config2 = Object.create(config1, {}) // Создаём экземпляры прототипа config1 и перезаписываем data

  const config3 = Object.create(config1, {})

  try { // Рендеринг графиков на основе конфигов
    new Chart(
      document.getElementById('first-day-chart'),
      config1
    )
    new Chart(
      document.getElementById('second-day-chart'),
      config2
    )
    new Chart(
      document.getElementById('third-day-chart'),
      config3
    )
  } catch (error) {
    console.log(`Ошибка рендеринга графиков на три дня: ${error}`)
  }
}

Функция для рендеринга графика на 27 дней выглядит также, разве что переменные — это КП-индексы и лейблы на русском. Весь проект собирается на Parcel, хотя сперва я подключил Webpack, но позже передумал, поскольку для такого небольшого ресурса Parcel куда проще, удобнее и лаконичнее.

Настоящее и будущее

На текущий момент сайт работает примерно два с половиной месяца. Первое время я даже не заходил в метрики, чтобы понять, пользуются ли им вообще люди, поскольку не питал иллюзий, что подниматься в поиске он будет долго. Но позже всё-таки решил проверить и не зря: оказалось, что ресурс находится среди трёх первых сайтов выдачи по запросу «прогноз северного сияния» в Яндексе, а иногда поднимается на второе и даже первое место.

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

Не сомневаюсь, что код можно улучшать и улучшать, а ещё добавить к проекту что-нибудь новое, например, продвинутый прогноз в красивой форме, такого пока никто не делал. Возможно, чуть позже вернусь к коду и все-таки что-то добавлю, но сейчас для меня главное, что он спокойно работает и, наверняка, помогает спланировать чьё-то путешествие. Проект полностью доступен на Гитхабе и любой желающий может взять за основу идею или делать вообще что угодно.

P.S. Ссылка на репозиторий GitHub.