add dewpoint to hourly and expand to 36 hours

This commit is contained in:
Matt Walsh
2026-01-17 11:42:26 -06:00
parent 34dedb44c1
commit 320d3139c3
6 changed files with 74 additions and 29 deletions

View File

@@ -40,9 +40,10 @@ class HourlyGraph extends WeatherDisplay {
const temperature = data.map((d) => d.temperature); const temperature = data.map((d) => d.temperature);
const probabilityOfPrecipitation = data.map((d) => d.probabilityOfPrecipitation); const probabilityOfPrecipitation = data.map((d) => d.probabilityOfPrecipitation);
const skyCover = data.map((d) => d.skyCover); const skyCover = data.map((d) => d.skyCover);
const dewpoint = data.map((d) => d.dewpoint);
this.data = { this.data = {
skyCover, temperature, probabilityOfPrecipitation, temperatureUnit: data[0].temperatureUnit, skyCover, temperature, probabilityOfPrecipitation, temperatureUnit: data[0].temperatureUnit, dewpoint,
}; };
this.setStatus(STATUS.loaded); this.setStatus(STATUS.loaded);
@@ -63,12 +64,16 @@ class HourlyGraph extends WeatherDisplay {
// calculate time scale // calculate time scale
const timeScale = calcScale(0, 5, this.data.temperature.length - 1, availableWidth); const timeScale = calcScale(0, 5, this.data.temperature.length - 1, availableWidth);
const timeStep = this.data.temperature.length / 4;
const startTime = DateTime.now().startOf('hour'); const startTime = DateTime.now().startOf('hour');
document.querySelector('.x-axis .l-1').innerHTML = formatTime(startTime); let prevTime = startTime;
document.querySelector('.x-axis .l-2').innerHTML = formatTime(startTime.plus({ hour: 6 })); Array(5).fill().forEach((val, idx) => {
document.querySelector('.x-axis .l-3').innerHTML = formatTime(startTime.plus({ hour: 12 })); // track the previous label so a day of week can be added when it changes
document.querySelector('.x-axis .l-4').innerHTML = formatTime(startTime.plus({ hour: 18 })); const label = formatTime(startTime.plus({ hour: idx * timeStep }), prevTime);
document.querySelector('.x-axis .l-5').innerHTML = formatTime(startTime.plus({ hour: 24 })); prevTime = label.ts;
// write to page
document.querySelector(`.x-axis .l-${idx + 1}`).innerHTML = label.formatted;
});
// order is important last line drawn is on top // order is important last line drawn is on top
// clouds // clouds
@@ -86,11 +91,22 @@ class HourlyGraph extends WeatherDisplay {
lineWidth: 3, lineWidth: 3,
}); });
// calculate temperature scale for min and max of dewpoint and temperature
const minScale = Math.min(...this.data.dewpoint, ...this.data.temperature);
const maxScale = Math.max(...this.data.dewpoint, ...this.data.temperature);
const thirdScale = (minScale + maxScale) / 3;
const midScale1 = Math.round(minScale + thirdScale);
const midScale2 = Math.round(minScale + (thirdScale * 2));
const tempScale = calcScale(minScale, availableHeight - 10, maxScale, 10);
// dewpoint
const dewpointPath = createPath(this.data.dewpoint, timeScale, tempScale);
drawPath(dewpointPath, ctx, {
strokeStyle: 'green',
lineWidth: 3,
});
// temperature // temperature
const minTemp = Math.min(...this.data.temperature);
const maxTemp = Math.max(...this.data.temperature);
const midTemp = Math.round((minTemp + maxTemp) / 2);
const tempScale = calcScale(minTemp, availableHeight - 10, maxTemp, 10);
const tempPath = createPath(this.data.temperature, timeScale, tempScale); const tempPath = createPath(this.data.temperature, timeScale, tempScale);
drawPath(tempPath, ctx, { drawPath(tempPath, ctx, {
strokeStyle: 'red', strokeStyle: 'red',
@@ -100,15 +116,17 @@ class HourlyGraph extends WeatherDisplay {
// temperature axis labels // temperature axis labels
// limited to 3 characters, sacraficing degree character // limited to 3 characters, sacraficing degree character
const degree = String.fromCharCode(176); const degree = String.fromCharCode(176);
this.elem.querySelector('.y-axis .l-1').innerHTML = (maxTemp + degree).substring(0, 3); this.elem.querySelector('.y-axis .l-1').innerHTML = (maxScale + degree).substring(0, 3);
this.elem.querySelector('.y-axis .l-2').innerHTML = (midTemp + degree).substring(0, 3); this.elem.querySelector('.y-axis .l-2').innerHTML = (midScale2 + degree).substring(0, 3);
this.elem.querySelector('.y-axis .l-3').innerHTML = (minTemp + degree).substring(0, 3); this.elem.querySelector('.y-axis .l-3').innerHTML = (midScale1 + degree).substring(0, 3);
this.elem.querySelector('.y-axis .l-4').innerHTML = (minScale + degree).substring(0, 3);
// set the image source // set the image source
this.image.src = canvas.toDataURL(); this.image.src = canvas.toDataURL();
// change the units in the header // change the units in the header
this.elem.querySelector('.temperature').innerHTML = `Temperature ${String.fromCharCode(176)}${this.data.temperatureUnit}`; this.elem.querySelector('.temperature').innerHTML = `Temperature ${String.fromCharCode(176)}${this.data.temperatureUnit}`;
this.elem.querySelector('.dewpoint').innerHTML = `Dewpoint ${String.fromCharCode(176)}${this.data.temperatureUnit}`;
super.drawCanvas(); super.drawCanvas();
this.finishDraw(); this.finishDraw();
@@ -145,7 +163,18 @@ const drawPath = (path, ctx, options) => {
}; };
// format as 1p, 12a, etc. // format as 1p, 12a, etc.
const formatTime = (time) => time.setZone(timeZone()).toFormat('ha').slice(0, -1); const formatTime = (time, prev) => {
// if the day of the week changes, show the day of the week in the label
let format = 'ha';
if (prev.weekday !== time.weekday) format = 'ccc ha';
const ts = time.setZone(timeZone());
return {
ts,
formatted: ts.toFormat(format).slice(0, -1),
};
};
// register display // register display
registerDisplay(new HourlyGraph(4, 'hourly-graph')); registerDisplay(new HourlyGraph(4, 'hourly-graph'));

View File

@@ -75,7 +75,10 @@ class Hourly extends WeatherDisplay {
const startingHour = DateTime.local().setZone(timeZone()); const startingHour = DateTime.local().setZone(timeZone());
const lines = this.data.map((data, index) => { // shorten to 24 hours
const shortData = this.data.slice(0, 24);
const lines = shortData.map((data, index) => {
const fillValues = {}; const fillValues = {};
// hour // hour
const hour = startingHour.plus({ hours: index }); const hour = startingHour.plus({ hours: index });
@@ -102,7 +105,7 @@ class Hourly extends WeatherDisplay {
const filledRow = this.fillTemplate('hourly-row', fillValues); const filledRow = this.fillTemplate('hourly-row', fillValues);
// alter the color of the feels like column to reflect wind chill or heat index // alter the color of the feels like column to reflect wind chill or heat index
if (feelsLike < temperature) { if (data.apparentTemperature < data.temperature) {
filledRow.querySelector('.like').classList.add('wind-chill'); filledRow.querySelector('.like').classList.add('wind-chill');
} else if (feelsLike > temperature) { } else if (feelsLike > temperature) {
filledRow.querySelector('.like').classList.add('heat-index'); filledRow.querySelector('.like').classList.add('heat-index');
@@ -203,6 +206,7 @@ const parseForecast = async (data) => {
const iceAccumulation = expand(data.iceAccumulation.values); // ice icon const iceAccumulation = expand(data.iceAccumulation.values); // ice icon
const probabilityOfPrecipitation = expand(data.probabilityOfPrecipitation.values); // rain icon const probabilityOfPrecipitation = expand(data.probabilityOfPrecipitation.values); // rain icon
const snowfallAmount = expand(data.snowfallAmount.values); // snow icon const snowfallAmount = expand(data.snowfallAmount.values); // snow icon
const dewpoint = expand(data.dewpoint.values);
const icons = await determineIcon(skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed); const icons = await determineIcon(skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed);
@@ -216,6 +220,7 @@ const parseForecast = async (data) => {
probabilityOfPrecipitation: probabilityOfPrecipitation[idx], probabilityOfPrecipitation: probabilityOfPrecipitation[idx],
skyCover: skyCover[idx], skyCover: skyCover[idx],
icon: icons[idx], icon: icons[idx],
dewpoint: temperatureConverter(dewpoint[idx]),
})); }));
}; };
@@ -233,7 +238,7 @@ const determineIcon = async (skyCover, weather, iceAccumulation, probabilityOfPr
}; };
// expand a set of values with durations to an hour-by-hour array // expand a set of values with durations to an hour-by-hour array
const expand = (data, maxHours = 24) => { const expand = (data, maxHours = 36) => {
const startOfHour = DateTime.utc().startOf('hour').toMillis(); const startOfHour = DateTime.utc().startOf('hour').toMillis();
const result = []; // resulting expanded values const result = []; // resulting expanded values
data.forEach((item) => { data.forEach((item) => {

View File

@@ -11,7 +11,7 @@
right: 60px; right: 60px;
width: 360px; width: 360px;
font-family: 'Star4000 Small'; font-family: 'Star4000 Small';
font-size: 32px; font-size: 28px;
@include u.text-shadow(); @include u.text-shadow();
text-align: right; text-align: right;
@@ -23,6 +23,10 @@
color: red; color: red;
} }
.dewpoint {
color: green;
}
.cloud { .cloud {
color: lightgrey; color: lightgrey;
} }
@@ -52,32 +56,33 @@
.x-axis { .x-axis {
bottom: 0px; bottom: 0px;
left: 0px; left: 54px;
width: 640px; width: 532px;
height: 20px; height: 20px;
.label { .label {
text-align: center; text-align: center;
width: 50px; transform: translateX(-50%);
white-space: nowrap;
&.l-1 { &.l-1 {
left: 25px; left: 0px;
} }
&.l-2 { &.l-2 {
left: 158px; left: calc(532px / 4 * 1);
} }
&.l-3 { &.l-3 {
left: 291px; left: calc(532px / 4 * 2);
} }
&.l-4 { &.l-4 {
left: 424px; left: calc(532px / 4 * 3);
} }
&.l-5 { &.l-5 {
left: 557px; left: calc(532px / 4 * 4);
} }
} }
@@ -110,10 +115,14 @@
} }
&.l-2 { &.l-2 {
top: 140px; top: calc(280px / 3);
} }
&.l-3 { &.l-3 {
bottom: calc(280px / 3 - 11px);
}
&.l-4 {
bottom: 0px; bottom: 0px;
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,7 @@
<div class="main has-scroll hourly-graph"> <div class="main has-scroll hourly-graph">
<div class="top-right template "> <div class="top-right template ">
<div class="temperature">Temperature</div> <div class="temperature">Temperature</div>
<div class="dewpoint">Dewpoint</div>
<div class="cloud">Cloud %</div> <div class="cloud">Cloud %</div>
<div class="rain">Precip %</div> <div class="rain">Precip %</div>
</div> </div>
@@ -9,6 +10,7 @@
<div class="label l-1">75</div> <div class="label l-1">75</div>
<div class="label l-2">65</div> <div class="label l-2">65</div>
<div class="label l-3">55</div> <div class="label l-3">55</div>
<div class="label l-4">45</div>
</div> </div>
<div class="chart"> <div class="chart">
<img id="chart-area"></img> <img id="chart-area"></img>