Public
HTTP
stevekrouse-bigweather.web.val.run
August 31, 2024
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import { exponentialBackoffMiddleware } from "https://esm.town/v/stevekrouse/exponentialBackoffMiddleware";
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON";
import React from "https://esm.sh/react";
import { renderToString } from "https://esm.sh/react-dom/server";
import tailwindURL from "https://esm.town/v/stevekrouse/tailwindURL";
import { weatherGovGrid } from "https://esm.town/v/stevekrouse/weatherGovGrid";
import { DateTime } from "npm:luxon";
// const lat = "40.682632";
// const lon = "-73.974231";
// let { properties: grid } = await weatherGovGrid({
// lat,
// lon,
// });
// const brooklynGridHourly = grid.forecastHourly
const brooklynGridHourly = "https://api.weather.gov/gridpoints/OKX/35,34/forecast/hourly";
async function getHourlyWeather(day: DateTime): Promise<Period[]> {
let { properties: { periods } } = await fetchJSON(brooklynGridHourly);
return periods.filter((x) =>
DateTime.fromISO(x.startTime) >= day.set({ minute: 0, second: 0, millisecond: 0 })
&& DateTime.fromISO(x.startTime) < day.set({ hour: 24, minute: 0, second: 0, millisecond: 0 })
);
}
function formatTime(time: string) {
const hour = parseInt(time.slice(11, 13));
return hour === 12 ? `12pm` : hour < 12 ? `${hour}am` : `${hour - 12}pm`;
}
// returns an svg chart of temperature by hour
function weatherChart(periods) {
const max = periods.reduce((max, period) => Math.max(max, period.temperature), 0);
const maxIndex = periods.findIndex((x) => x.temperature === max);
const min = periods.reduce((min, period) => Math.min(min, period.temperature), max);
const minIndex = periods.findIndex((x) => x.temperature === min);
const range = (max - min) * 2;
const maxPrecipitation = periods.reduce((max, period) => Math.max(max, period.probabilityOfPrecipitation.value), 0);
const maxPrecipitationIndex = periods.findIndex((x) => x.probabilityOfPrecipitation.value === maxPrecipitation);
const heightOffset = 50;
const widthOffset = 20;
const [width, height] = [300 - widthOffset, 200 - heightOffset];
const trueHeight = height + heightOffset;
const trueWidth = width + widthOffset;
return (
<svg viewBox={`${widthOffset / -2} 0 ${trueWidth} ${trueHeight}`} width="100%">
<polyline
fill="none"
stroke="rgba(255, 104, 2, 1)"
strokeWidth="5"
strokeLinejoin="round"
strokeLinecap="round"
points={periods.map((x, i) =>
`${i * (width / (periods.length - 1))},${height - ((x.temperature - min) * (height / range))}`
)
.join("\n")}
/>
<polyline
fill="none"
stroke="rgba(0, 123, 255, 0.85)"
strokeWidth="4"
strokeLinejoin="round"
strokeLinecap="round"
points={periods.map((x, i) =>
`${i * (width / (periods.length - 1))},${height - x.probabilityOfPrecipitation.value}`
)
.join("\n")}
/>
{/* current temp */}
<text
x={0}
y={height - ((periods[0].temperature - min) * (height / range) + 30)}
textAnchor="start"
dominantBaseline="auto"
fill="rgba(255, 104, 2, 1)"
fontSize="30"
>
{periods[0].temperature}°
</text>
{/* max temp */}
{maxIndex > 1 && (
<text
x={maxIndex * (width / (periods.length - 1))}
y={height - ((max - min) * (height / range) + 10)}
textAnchor="middle"
dominantBaseline="auto"
fill="rgba(255, 104, 2, 1)"
fontSize="30"
>
{max}°
</text>
)}
{/* end temp */}
<text
x={width}
y={maxPrecipitationIndex === periods.length - 1
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
August 31, 2024
The older version, for posterity: