Удобный прогноз северного сияния: в цветах и графиках

В начале 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 — оказался свободен, хоть в нём и нет ничего про сияние, зато легко держать в памяти и есть связь с географией регионов, где видно это необычное явление. Небольшой текст после заголовка быстро и «на пальцах» объясняет, как увидеть северное сияние и понять прогноз на странице. Клик по стрелке вниз плавно проводит к графикам.

Картинку рисовал сам (из кубиков в 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, ссылка на сайт.

© Habrahabr.ru