Skip to content

Commit

Permalink
Merge pull request #1693 from weather-gov/mgwalker/1632-hourly-viz
Browse files Browse the repository at this point in the history
Hourly temperature visualization
  • Loading branch information
eric-gade authored Sep 6, 2024
2 parents f7fcb53 + 1a776ed commit 286e73b
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 3 deletions.
2 changes: 1 addition & 1 deletion web/themes/new_weather_theme/assets/css/styles.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/themes/new_weather_theme/assets/css/styles.css.map

Large diffs are not rendered by default.

133 changes: 133 additions & 0 deletions web/themes/new_weather_theme/assets/js/charts/hourly-temperature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/* global Chart ChartDataLabels */
(() => {
const styles = getComputedStyle(document.body);

const fontMono = styles.getPropertyValue("--font-family-mono");
const colors = {
base: styles.getPropertyValue("--color-base"),
baseLighter: styles.getPropertyValue("--color-base-lighter"),
baseLightest: styles.getPropertyValue("--color-base-lightest"),
primary: styles.getPropertyValue("--color-primary"),
primaryDark: styles.getPropertyValue("--color-primary-dark"),
primaryLight: styles.getPropertyValue("--color-primary-light"),
};

Chart.register(ChartDataLabels);

// These are applied globally to all charts. Unclear if that's okay, or if
// what we really want is to set them per-chart, but this is what I've got
// for now.
Chart.defaults.font = {
family: fontMono,
size: 12,
};

const chartContainers = Array.from(
document.querySelectorAll(".wx-hourly-temp-chart-container"),
);

for (const container of chartContainers) {
const times = JSON.parse(container.dataset.times);
const temps = JSON.parse(container.dataset.temps).map((v) =>
Number.parseInt(v, 10),
);
const feelsLike = JSON.parse(container.dataset.feelsLike).map((v) =>
Number.parseInt(v, 10),
);

// We don't need to keep a reference to the chart object. We only need the
// side-effects of creating it. This is not ideal, but it's how Chart.js
// works, so it's what we've got.
// eslint-disable-next-line no-new
new Chart(container.querySelector("canvas"), {
type: "line",
plugins: [ChartDataLabels],

options: {
animation: false,
responsive: true,
maintainAspectRatio: false,
interaction: {
intersect: false,
mode: "index",
},
plugins: {
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
autoSkip: true,
maxRotation: 0,
color: colors.base,
},
grid: {
color: times.map((v) => {
if (v === "12 AM") {
return "black";
}

const even = Number.parseInt(v, 10) % 2 === 0;
if (even) {
return colors.baseLighter;
}
return colors.baseLightest;
}),
},
},
y: {
min: Math.min(
Math.round(Math.min(...temps) / 10) * 10 - 10,
Math.round(Math.min(...feelsLike) / 10) * 10 - 10,
),
max: Math.max(
Math.round(Math.max(...temps) / 10) * 10 + 10,
Math.round(Math.max(...feelsLike) / 10) * 10 + 10,
),
ticks: {
autoSkip: true,
color: colors.base,
maxTicksLimit: 6,
callback: (v) => `${v}°`,
},
},
},
},

data: {
labels: times,
datasets: [
{
label: "Temperature",
data: temps,
datalabels: {
align: ({ dataIndex }) =>
temps[dataIndex] >= feelsLike[dataIndex] ? "top" : "bottom",
color: colors.primaryDark,
},
backgroundColor: colors.primaryDark,
borderColor: colors.primaryDark,
borderWidth: 1.5,
},
{
label: "Feels like",
data: feelsLike,
datalabels: {
align: ({ dataIndex }) =>
temps[dataIndex] >= feelsLike[dataIndex] ? "bottom" : "top",
color: colors.primary,
display: ({ dataIndex }) =>
temps[dataIndex] !== feelsLike[dataIndex],
},
borderDash: [4],
backgroundColor: colors.primaryLight,
borderColor: colors.primaryLight,
borderWidth: 1.5,
},
],
},
});
}
})();
11 changes: 11 additions & 0 deletions web/themes/new_weather_theme/assets/sass/components/variables.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@use "uswds-core" as *;

body {
--color-base: #{color("base")};
--color-base-lighter: #{color("base-lighter")};
--color-base-lightest: #{color("base-lightest")};
--color-primary: #{color("primary")};
--color-primary-dark: #{color("primary-dark")};
--color-primary-light: #{color("primary-light")};
--font-family-mono: #{font-family("mono")};
}
1 change: 1 addition & 0 deletions web/themes/new_weather_theme/assets/sass/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
@use "components/touchpoints";
@use "components/usa-identifier";
@use "components/utilities";
@use "components/variables";
@use "components/visual-page-headings";
@use "components/weather-story";
@use "components/wfo-page";
Expand Down
22 changes: 22 additions & 0 deletions web/themes/new_weather_theme/new_weather_theme.libraries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,25 @@ afd-selector:
version: 2024-08-16
js:
assets/js/components/afd-selector.js: { preprocess: false }

chartjs:
version: 4.4.1
header: true
js:
https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js:
attributes:
integrity: sha512-CQBWl4fJHWbryGE+Pc7UAxWMUMNMWzWxF4SQo9CgkJIN1kx6djDQZjh3Y8SZ1d+6I+1zze6Z7kHXO7q3UyZAWw==
crossorigin: anonymous
referrerpolicy: no-referrer
https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.2.0/chartjs-plugin-datalabels.min.js:
attributes:
integrity: sha512-JPcRR8yFa8mmCsfrw4TNte1ZvF1e3+1SdGMslZvmrzDYxS69J7J49vkFL8u6u8PlPJK+H3voElBtUCzaXj+6ig==
crossorigin: anonymous
referrerpolicy: no-referrer

chart-hourly-temperature:
version: 2024-08-27
js:
assets/js/charts/hourly-temperature.js:
attributes:
defer: true
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
{% set message = "There was an error loading the daily forecast." | t %}
{% include '@new_weather_theme/partials/uswds-alert.html.twig' with { 'level': "error", body: message } %}
{% else %}

<div class="grid-container padding-x-0 tablet:padding-x-2">
<ol class="usa-list--unstyled">
{# Attempt to render the period(s) associated with the current day #}
Expand All @@ -22,5 +21,6 @@
{% endfor %}
</ol>
</div>

{% endif %}
</div>
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
{{ attach_library('new_weather_theme/hourly-table') }}
{{ attach_library('new_weather_theme/chartjs') }}
{{ attach_library('new_weather_theme/chart-hourly-temperature') }}

{% set times = hours | map(h => h.time) | json_encode %}
{% set temps = hours | map(h => h.temperature) | json_encode %}
{% set feelsLike = hours | map(h => h.apparentTemperature) | json_encode %}

<wx-hourly-table class="display-block position-relative margin-top-2 bg-white">
<h5 class="wx-visual-h3 font-heading-md text-normal text-primary-darker margin-top-0 margin-bottom-205">Hourly forecast</h5>

<div class="margin-bottom-205">
<span class="font-family-mono text-primary-dark">Temperature</span>
<div class="wx-hourly-temp-chart-container margin-y-105 position-relative grid-col-12" data-times="{{ times }}" data-temps="{{ temps }}" data-feels-like="{{ feelsLike }}">
<canvas>
</canvas>
</div>
<div class="display-flex flex-align-center font-mono-xs text-base">
<div class="width-3 border-bottom-2px border-primary-dark height-0 margin-right-1">
<div class="bg-primary-dark margin-left-1 radius-pill width-1 height-1" style="margin-top: -3px;"></div>
</div>
Temperature
<div class="margin-left-3 width-3 border-bottom-2px border-top-0 border-left-0 border-right-0 border-dotted border-primary-light height-0 margin-right-1">
<div class="bg-primary-light margin-left-1 radius-pill width-1 height-1" style="margin-top: -3px;"></div>
</div>
Feels like
</div>
</div>


<div class="display-flex flex-justify">
<div>
<button class="wx-scroll-button bg-gray-30 circle-5 border-0 text-white display-flex flex-align-center flex-justify-center padding-y-1px padding-x-05" data-direction="left" type="button" role="button">
Expand Down

0 comments on commit 286e73b

Please sign in to comment.