Public
Edited
Jan 5, 2024
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
if (!city) return md`Waiting to select a city...`;
const numDays = data.daily.time.length;
const dayIdx = 0;
let chunk = "";

for (let di = 0; di < numDays; di++) {
chunk += `<div class="day-chart">
${genDayHeader(di)}
${genDayTableV2(di)}
</div>`;
}
return html`<div class="container grid dp"> ${chunk} </div>`;
}
Insert cell
function genDayHeader(idx) {
const dt = new Date(data.daily.time[idx]);
const o = {
day: dt.toLocaleString(undefined, { day: "numeric" }),
weekday: dt.toLocaleString(undefined, { weekday: "short" }),
month: dt.toLocaleString(undefined, { month: "short" }),
max: data.daily.temperature_2m_max[idx].toFixed(0),
min: data.daily.temperature_2m_min[idx].toFixed(0)
};
return `<div class="day-header">
<h3 class="day-title">${o.weekday}, ${o.month} ${o.day}</h3>
<p class="max-min-temp">${o.max}° / ${o.min}°</p>
</div>`;
}
Insert cell
function genDayTableV2(dayIdx) {
let entries = "";
for (let hi = 0; hi < 24; hi++) {
const d = getHourlyData(dayIdx, hi);
const rainDrop = d.rainProbability > minRainProb ? rainDropSvg : "";
entries += `
<div class="${dpToClass(d.dp)} ${d.isCurrent ? "current-hour" : ""}">
<p class="time">${d.hour}</p>
<p class="dew-point">${d.dp} ${rainDrop}</p>
</div>
`;
}
return `<div class="dew-point-table">${entries}</div>`;
}
Insert cell
minRainProb = 30
Insert cell
function getHourlyData(dayIdx, hourlyIdx) {
const now = new Date();
const idxStart = dayIdx * 24;
const currHour = data.hourly.time[idxStart + hourlyIdx];
const dt = new Date(currHour);
const isCurrent =
`${dt.getDate()}-${dt.getHours()}` == `${now.getDate()}-${now.getHours()}`;
return {
hour: dt.toLocaleTimeString(undefined, { hour: "numeric" }),
dp: data.hourly.dew_point_2m[idxStart + hourlyIdx].toFixed(0),
isCurrent,
rainProbability: data.hourly.precipitation_probability[idxStart + hourlyIdx]
};
}
Insert cell
function dpToClass(dp) {
// dry comfortable alright uncomfortable miserable danger
if (dp < 50) return "dry";
if (dp < 55) return "comfortable";
if (dp < 60) return "alright";
if (dp < 65) return "uncomfortable";
if (dp < 70) return "miserable";
return "danger";
}
Insert cell
Insert cell
Insert cell
url = `https://api.open-meteo.com/v1/forecast?latitude=32.7766&longitude=-79.9309&current=temperature_2m,relative_humidity_2m,is_day,precipitation,rain&hourly=temperature_2m,dew_point_2m,precipitation_probability,wind_speed_10m,wind_direction_10m&daily=sunrise,sunset,daylight_duration&timezone=America%2FNew_York`
Insert cell
urlF = "https://api.open-meteo.com/v1/forecast?latitude=32.7766&longitude=-79.9309&current=temperature_2m,relative_humidity_2m,is_day,precipitation,rain&hourly=temperature_2m,dew_point_2m,precipitation_probability,wind_speed_10m,wind_direction_10m&daily=temperature_2m_max,temperature_2m_min,sunrise,sunset&temperature_unit=fahrenheit&precipitation_unit=inch&timezone=America%2FNew_York"
Insert cell
html`
<style>

.grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
padding: 16px;
}

.container {
width: 90%;
margin: 0 auto;
}

.dp {
font-family: 'Source Sans Pro', sans-serif;
font-weight: 200;
}


.flex-vertical {
display: flex;
flex-direction: column;
}

.day-chart {
max-width: 420px;
margin: 1em auto 1.5em;
}

.day-header {
display: flex;
align-items: center;
padding: 0;
margin-bottom: 1em;
}

.dew-point-table {
display: flex;
flex-wrap: wrap;
justify-content: center;
font-size: 1rem;
}

.dew-point-table > div {
padding: .3em .1em;
width: 15%;
text-align: center;
}

.current-hour {
position: relative;
}

.current-hour:before {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
content: '';
border: 2px solid #F7Edf0;
border-radius: 5px;
z-index: 2;
}

/* colors for dew point hours */
.miserable, .danger {
color: #F7Edf0;
}

.dry {
background-color: #3AC2F8;
}

.comfortable {
background-color: #2DC842;
}

.alright {
background-color: #F9C80E;
}

.uncomfortable {
background-color: #F86624;
}

.miserable {
background-color: #9B1D20;
}

.danger {
background-color: #671415;
}

.day-title {
margin: 0;
}

.max-min-temp {
margin: 0 0 0 .75em;
}

.time {
font-size: .75rem;
margin: 0;
}

.dew-point {
margin: 0;
font-weight: 800;
}

@media (min-width: 600px) {
.modal {
top: 108px;
}

#dew-point-charts {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
}

.day-chart {
margin: 1em auto;
}

.color-legend > div {
width: 40px;
height: 40px;
}
}



svg.rain-drop {
transform: rotate(15deg) translate(2px, 2px);
}

.color-legend {
margin-top: 1em;
display: flex;
justify-content: center;
align-items: center;
}

.color-legend > p {
margin: 0 1em;
}
.color-legend > div {
width: 30px;
height: 30px;
}

.color-legend-v2 {
margin-top: 1em;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.color-legend-v2 > div {
min-height: 80px;
}

@media (max-width: 600px) {
.color-legend-v2 {
flex-direction: column;
}

.color-legend-v2 > div {
width: 200px;
}
}



`
Insert cell
import {
openMeteoSearchCity,
openMeteoGetData
} from "@drio/open-meteo-search-for-city"
Insert cell
Insert cell
Insert cell
md`## References

1. See Mike's [post](https://observablehq.com/@mbostock/date-formatting) on date formatting.
2. Inspiration from [Stefan](https://twitter.com/stefanbfritz): https://mydewpoint.com/.
`
Insert cell
dpImage = {
return FileAttachment("ref-dp.png").image();
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more