ws4kp/server/scripts/modules/extendedforecast.js
2020-10-29 16:44:28 -05:00

173 lines
5.6 KiB
JavaScript

// display extended forecast graphically
// technically uses the same data as the local forecast, we'll let the browser do the caching of that
/* globals WeatherDisplay, utils, STATUS, UNITS, draw, icons, navigation, luxon */
// eslint-disable-next-line no-unused-vars
class ExtendedForecast extends WeatherDisplay {
constructor(navId, elemId) {
super(navId, elemId, 'Extended Forecast');
// set timings
this.timing.totalScreens = 2;
// pre-load background image (returns promise)
this.backgroundImage = utils.image.load('images/BackGround2_1.png');
}
async getData(_weatherParameters) {
super.getData(_weatherParameters);
const weatherParameters = _weatherParameters ?? this.weatherParameters;
// request us or si units
let units = 'us';
if (navigation.units() === UNITS.metric) units = 'si';
let forecast;
try {
forecast = await utils.fetch.json(weatherParameters.forecast, {
data: {
units,
},
});
} catch (e) {
console.error('Unable to get extended forecast');
console.error(e.status, e.responseJSON);
this.setStatus(STATUS.failed);
return;
}
// we only get here if there was no error above
this.data = ExtendedForecast.parse(forecast.properties.periods);
this.screenIndex = 0;
this.setStatus(STATUS.loaded);
}
// the api provides the forecast in 12 hour increments, flatten to day increments with high and low temperatures
static parse(fullForecast) {
// create a list of days starting with today
const Days = [0, 1, 2, 3, 4, 5, 6];
const dates = Days.map((shift) => {
const date = luxon.DateTime.local().startOf('day').plus({ days: shift });
return date.toLocaleString({ weekday: 'short' });
});
// track the destination forecast index
let destIndex = 0;
const forecast = [];
fullForecast.forEach((period) => {
// create the destination object if necessary
if (!forecast[destIndex]) {
forecast.push({
dayName: '', low: undefined, high: undefined, text: undefined, icon: undefined,
});
}
// get the object to modify/populate
const fDay = forecast[destIndex];
// high temperature will always be last in the source array so it will overwrite the low values assigned below
fDay.icon = icons.getWeatherIconFromIconLink(period.icon);
fDay.text = ExtendedForecast.shortenExtendedForecastText(period.shortForecast);
fDay.dayName = dates[destIndex];
// preload the icon
utils.image.preload(fDay.icon);
if (period.isDaytime) {
// day time is the high temperature
fDay.high = period.temperature;
destIndex += 1;
} else {
// low temperature
fDay.low = period.temperature;
}
});
return forecast;
}
static shortenExtendedForecastText(long) {
let short = long;
short = short.replace(/ and /g, ' ');
short = short.replace(/Slight /g, '');
short = short.replace(/Chance /g, '');
short = short.replace(/Very /g, '');
short = short.replace(/Patchy /g, '');
short = short.replace(/Areas /g, '');
short = short.replace(/Dense /g, '');
let conditions = short.split(' ');
if (short.indexOf('then') !== -1) {
conditions = short.split(' then ');
conditions = conditions[1].split(' ');
}
let short1 = conditions[0].substr(0, 10);
let short2 = '';
if (conditions[1]) {
if (!short1.endsWith('.')) {
short2 = conditions[1].substr(0, 10);
} else {
short1 = short1.replace(/\./, '');
}
if (short2 === 'Blowing') {
short2 = '';
}
}
short = short1;
if (short2 !== '') {
short += ` ${short2}`;
}
return [short, short1, short2];
}
async drawCanvas() {
super.drawCanvas();
// determine bounds
// grab the first three or second set of three array elements
const forecast = this.data.slice(0 + 3 * this.screenIndex, 3 + this.screenIndex * 3);
const backgroundImage = await this.backgroundImage;
this.context.drawImage(backgroundImage, 0, 0);
draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2);
draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90);
draw.horizontalGradientSingle(this.context, 0, 90, 640, 399, draw.sideColor1, draw.sideColor2);
this.context.drawImage(backgroundImage, 38, 100, 174, 297, 38, 100, 174, 297);
this.context.drawImage(backgroundImage, 232, 100, 174, 297, 232, 100, 174, 297);
this.context.drawImage(backgroundImage, 426, 100, 174, 297, 426, 100, 174, 297);
draw.titleText(this.context, 'Extended', 'Forecast');
await Promise.all(forecast.map(async (Day, Index) => {
const offset = Index * 195;
draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 100 + offset, 135, Day.dayName.toUpperCase(), 2);
draw.text(this.context, 'Star4000', '24pt', '#8080FF', 85 + offset, 345, 'Lo', 2, 'center');
draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 165 + offset, 345, 'Hi', 2, 'center');
let { low } = Day;
if (low !== undefined) {
if (navigation.units() === UNITS.metric) low = utils.units.fahrenheitToCelsius(low);
draw.text(this.context, 'Star4000 Large', '24pt', '#FFFFFF', 85 + offset, 385, low, 2, 'center');
}
let { high } = Day;
if (navigation.units() === UNITS.metric) high = utils.units.fahrenheitToCelsius(high);
draw.text(this.context, 'Star4000 Large', '24pt', '#FFFFFF', 165 + offset, 385, high, 2, 'center');
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120 + offset, 270, Day.text[1], 2, 'center');
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120 + offset, 310, Day.text[2], 2, 'center');
// draw the icon
this.gifs.push(await utils.image.superGifAsync({
src: Day.icon,
auto_play: true,
canvas: this.canvas,
x: 70 + Index * 195,
y: 150,
max_height: 75,
}));
}));
this.finishDraw();
}
}