ws4kp/server/scripts/modules/travelforecast.mjs

164 lines
5.2 KiB
JavaScript
Raw Normal View History

2020-09-04 18:02:20 +00:00
// travel forecast display
2022-11-22 22:19:10 +00:00
import STATUS from './status.mjs';
import { json } from './utils/fetch.mjs';
import { getWeatherRegionalIconFromIconLink } from './icons.mjs';
import { DateTime } from '../vendor/auto/luxon.mjs';
2022-11-22 22:29:10 +00:00
import WeatherDisplay from './weatherdisplay.mjs';
2022-12-06 22:14:56 +00:00
import { registerDisplay } from './navigation.mjs';
2020-09-04 18:02:20 +00:00
class TravelForecast extends WeatherDisplay {
2020-09-18 16:24:45 +00:00
constructor(navId, elemId, defaultActive) {
2020-09-04 18:02:20 +00:00
// special height and width for scrolling
2022-11-22 03:50:22 +00:00
super(navId, elemId, 'Travel Forecast', defaultActive);
// set up the timing
this.timing.baseDelay = 20;
// page sizes are 4 cities, calculate the number of pages necessary plus overflow
2020-10-29 21:44:28 +00:00
const pagesFloat = TravelCities.length / 4;
2020-09-05 16:16:38 +00:00
const pages = Math.floor(pagesFloat) - 2; // first page is already displayed, last page doesn't happen
2020-10-29 21:44:28 +00:00
const extra = pages % 1;
2022-09-23 20:12:10 +00:00
const timingStep = 75 * 4;
2020-10-29 21:44:28 +00:00
this.timing.delay = [150 + timingStep];
// add additional pages
2020-10-29 21:44:28 +00:00
for (let i = 0; i < pages; i += 1) this.timing.delay.push(timingStep);
// add the extra (not exactly 4 pages portion)
2020-10-29 21:44:28 +00:00
if (extra !== 0) this.timing.delay.push(Math.round(this.extra * this.cityHeight));
// add the final 3 second delay
2020-09-06 01:01:13 +00:00
this.timing.delay.push(150);
2020-09-04 18:02:20 +00:00
}
async getData() {
2020-09-18 16:24:45 +00:00
// super checks for enabled
if (!super.getData()) return;
2020-10-29 21:44:28 +00:00
const forecastPromises = TravelCities.map(async (city) => {
2020-09-04 18:02:20 +00:00
try {
// get point then forecast
if (!city.point) throw new Error('No pre-loaded point');
2022-11-22 22:19:10 +00:00
const forecast = await json(`https://api.weather.gov/gridpoints/${city.point.wfo}/${city.point.x},${city.point.y}/forecast`);
2020-09-04 18:02:20 +00:00
// determine today or tomorrow (shift periods by 1 if tomorrow)
2020-10-29 21:44:28 +00:00
const todayShift = forecast.properties.periods[0].isDaytime ? 0 : 1;
2020-09-04 18:02:20 +00:00
// return a pared-down forecast
return {
today: todayShift === 0,
high: forecast.properties.periods[todayShift].temperature,
2020-10-29 21:44:28 +00:00
low: forecast.properties.periods[todayShift + 1].temperature,
2020-09-04 18:02:20 +00:00
name: city.Name,
2022-11-22 22:19:10 +00:00
icon: getWeatherRegionalIconFromIconLink(forecast.properties.periods[todayShift].icon),
2020-09-04 18:02:20 +00:00
};
} catch (e) {
console.error(`GetTravelWeather for ${city.Name} failed`);
2020-09-23 16:49:15 +00:00
console.error(e.status, e.responseJSON);
return { name: city.Name, error: true };
2020-09-04 18:02:20 +00:00
}
});
// wait for all forecasts
const forecasts = await Promise.all(forecastPromises);
this.data = forecasts;
// test for some data available in at least one forecast
2020-10-29 21:44:28 +00:00
const hasData = this.data.reduce((acc, forecast) => acc || forecast.high, false);
if (!hasData) {
this.setStatus(STATUS.noData);
return;
}
2020-09-17 21:34:38 +00:00
this.setStatus(STATUS.loaded);
this.drawLongCanvas();
2020-09-04 18:02:20 +00:00
}
2020-10-29 21:44:28 +00:00
async drawLongCanvas() {
2022-09-23 20:12:10 +00:00
// get the element and populate
const list = this.elem.querySelector('.travel-lines');
list.innerHTML = '';
2020-10-20 21:37:11 +00:00
2020-09-04 18:02:20 +00:00
// set up variables
const cities = this.data;
2022-09-23 20:12:10 +00:00
const lines = cities.map((city) => {
if (city.error) return false;
2022-09-23 20:12:10 +00:00
const fillValues = {};
2020-09-04 18:02:20 +00:00
2020-09-17 21:34:38 +00:00
// city name
2022-09-23 20:12:10 +00:00
fillValues.city = city;
2020-09-04 18:02:20 +00:00
2020-09-17 21:34:38 +00:00
// check for forecast data
if (city.icon) {
2022-09-23 20:12:10 +00:00
fillValues.city = city.name;
2020-09-04 18:02:20 +00:00
// get temperatures and convert if necessary
2022-12-06 22:25:28 +00:00
const { low, high } = city;
2020-09-04 18:02:20 +00:00
2020-09-17 21:34:38 +00:00
// convert to strings with no decimal
const lowString = Math.round(low).toString();
const highString = Math.round(high).toString();
2022-09-23 20:12:10 +00:00
fillValues.low = lowString;
fillValues.high = highString;
2020-09-17 21:34:38 +00:00
2022-09-23 20:12:10 +00:00
fillValues.icon = { type: 'img', src: city.icon };
2020-09-17 21:34:38 +00:00
} else {
2022-09-23 20:12:10 +00:00
fillValues.error = 'NO TRAVEL DATA AVAILABLE';
2020-09-17 21:34:38 +00:00
}
2022-09-23 20:12:10 +00:00
return this.fillTemplate('travel-row', fillValues);
}).filter((d) => d);
2022-09-23 20:12:10 +00:00
list.append(...lines);
2020-09-17 21:34:38 +00:00
}
async drawCanvas() {
// there are technically 2 canvases: the standard canvas and the extra-long canvas that contains the complete
// list of cities. The second canvas is copied into the standard canvas to create the scroll
super.drawCanvas();
// set up variables
const cities = this.data;
2020-09-04 18:02:20 +00:00
2022-09-23 20:12:10 +00:00
this.elem.querySelector('.header .title.dual .bottom').innerHTML = `For ${TravelForecast.getTravelCitiesDayName(cities)}`;
2020-09-04 18:02:20 +00:00
this.finishDraw();
2020-09-17 21:34:38 +00:00
}
async showCanvas() {
// special to travel forecast to draw the remainder of the canvas
await this.drawCanvas();
super.showCanvas();
2020-09-04 18:02:20 +00:00
}
// screen index change callback just runs the base count callback
screenIndexChange() {
this.baseCountChange(this.navBaseCount);
}
// base count change callback
baseCountChange(count) {
// calculate scroll offset and don't go past end
2022-09-23 20:12:10 +00:00
let offsetY = Math.min(this.elem.querySelector('.travel-lines').getBoundingClientRect().height - 289, (count - 150));
// don't let offset go negative
if (offsetY < 0) offsetY = 0;
// copy the scrolled portion of the canvas
2022-09-23 20:12:10 +00:00
this.elem.querySelector('.main').scrollTo(0, offsetY);
}
2020-10-29 21:44:28 +00:00
static getTravelCitiesDayName(cities) {
2020-09-04 18:02:20 +00:00
// effectively returns early on the first found date
return cities.reduce((dayName, city) => {
if (city && dayName === '') {
// today or tomorrow
2020-10-29 21:44:28 +00:00
const day = DateTime.local().plus({ days: (city.today) ? 0 : 1 });
2020-09-04 18:02:20 +00:00
// return the day
2020-10-29 21:44:28 +00:00
return day.toLocaleString({ weekday: 'long' });
2020-09-04 18:02:20 +00:00
}
return dayName;
}, '');
}
// necessary to get the lastest long canvas when scrolling
getLongCanvas() {
return this.longCanvas;
}
2020-10-29 21:44:28 +00:00
}
2022-11-22 22:19:10 +00:00
2022-12-06 22:14:56 +00:00
// register display, not active by default
registerDisplay(new TravelForecast(3, 'travel', false));