Skip to content

Commit

Permalink
weather example
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock authored and Fil committed Nov 11, 2024
1 parent 324c7eb commit 2d15813
Show file tree
Hide file tree
Showing 6 changed files with 1,582 additions and 0 deletions.
5 changes: 5 additions & 0 deletions examples/weather/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
dist/
docs/.observablehq/cache/
node_modules/
yarn-error.log
3 changes: 3 additions & 0 deletions examples/weather/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Weather

This is an example Observable Framework project showing the weather. You can personalize this dashboard by editing the data loader to point to a different weather station or location.
31 changes: 31 additions & 0 deletions examples/weather/docs/data/forecast.json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// const name = "Berkeley", longitude = -122.27, latitude = 37.87;
const name = "San Francisco", longitude = -122.43, latitude = 37.788;

async function json(url) {
const response = await fetch(url);
if (!response.ok) throw new Error(`fetch failed: ${response.status}`);
return await response.json();
}

const station = await json(`https://api.weather.gov/points/${latitude},${longitude}`);
const forecast = await json(station.properties.forecast);
const forecastHourly = await json(station.properties.forecastHourly);
// const forecastGridData = await json(station.properties.forecastGridData);
// const observationStations = await json(station.properties.observationStations);
// const forecastZone = await json(station.properties.forecastZone);

console.log(
JSON.stringify(
{
name,
station,
forecast,
forecastHourly,
// forecastGridData,
// observationStations,
// forecastZone
},
null,
2
)
);
135 changes: 135 additions & 0 deletions examples/weather/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
theme: dashboard
toc: false
---

# ${data.name} weather forecast

```js
const data = FileAttachment("./data/forecast.json").json().then(revive);

function revive(object, pattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/) {
if (Array.isArray(object)) {
for (const key in object) {
revive(object[key], pattern);
}
} else if (object && typeof object === "object") {
for (const key in object) {
if (object[key] == null) continue;
if (typeof object[key] === "string" && pattern.test(object[key])) {
object[key] = new Date(object[key]);
} else {
revive(object[key], pattern);
}
}
}
return object;
}
```

<div class="grid grid-cols-4" style="grid-auto-rows: 270px;">
<div class="card" id="map"></div>
<div class="card">${
resize((width) => Plot.plot({
title: "temperature",
width,
height: 200,
nice: true,
x: {ticks: "day"},
y: {grid: true},
marks: [
Plot.link(data.forecast.properties.periods, {x1: "startTime", x2: "endTime", y: "temperature"}),
Plot.link(data.forecastHourly.properties.periods, {x1: "startTime", x2: "endTime", y: "temperature", stroke: "var(--theme-foreground-focus)"})
]
}))
}</div>
<div class="card">${
resize((width) => Plot.plot({
title: "probabilityOfPrecipitation",
width,
height: 200,
nice: true,
x: {ticks: "day"},
y: {grid: true},
marks: [
Plot.ruleY([0, 100]),
Plot.link(data.forecast.properties.periods, {x1: "startTime", x2: "endTime", y: (d) => d.probabilityOfPrecipitation.value}),
Plot.link(data.forecastHourly.properties.periods, {x1: "startTime", x2: "endTime", y: (d) => d.probabilityOfPrecipitation.value, stroke: "var(--theme-foreground-focus)"})
]
}))
}</div>
<div class="card">${
resize((width) => Plot.plot({
title: "dewpoint",
width,
height: 200,
nice: true,
x: {ticks: "day"},
y: {grid: true},
marks: [
Plot.ruleY([0]),
Plot.link(data.forecast.properties.periods, {x1: "startTime", x2: "endTime", y: (d) => d.dewpoint.value}),
Plot.link(data.forecastHourly.properties.periods, {x1: "startTime", x2: "endTime", y: (d) => d.dewpoint.value, stroke: "var(--theme-foreground-focus)"})
]
}))
}</div>
<div class="card">${
resize((width) => Plot.plot({
title: "relativeHumidity",
width,
height: 200,
nice: true,
x: {ticks: "day"},
y: {grid: true},
marks: [
Plot.ruleY([0]),
Plot.link(data.forecast.properties.periods, {x1: "startTime", x2: "endTime", y: (d) => d.relativeHumidity.value}),
Plot.link(data.forecastHourly.properties.periods, {x1: "startTime", x2: "endTime", y: (d) => d.relativeHumidity.value, stroke: "var(--theme-foreground-focus)"})
]
}))
}</div>
<div class="card">${
resize((width) => Plot.plot({
title: "windSpeed",
width,
height: 200,
nice: true,
x: {ticks: "day"},
y: {grid: true},
marks: [
Plot.ruleY([0]),
Plot.link(data.forecast.properties.periods, {x1: "startTime", x2: "endTime", y: (d) => parseWindSpeed(d.windSpeed)}),
Plot.link(data.forecastHourly.properties.periods, {x1: "startTime", x2: "endTime", y: (d) => parseWindSpeed(d.windSpeed), stroke: "var(--theme-foreground-focus)"})
]
}))
}</div>
<div class="card">${
resize((width) => Plot.plot({
title: "windDirection",
width,
height: 200,
nice: true,
x: {ticks: "day"},
y: {grid: true, label: null},
marks: [
Plot.link(data.forecast.properties.periods, {x1: "startTime", x2: "endTime", y: "windDirection"}),
Plot.link(data.forecastHourly.properties.periods, {x1: "startTime", x2: "endTime", y: "windDirection", stroke: "var(--theme-foreground-focus)"})
]
}))
}</div>
</div>

```js
function parseWindSpeed(d) {
return parseFloat(d); // e.g., "7 mph"
}
```

```js
const div = document.querySelector("#map");
const [lng, lat] = data.station.geometry.coordinates;
const map = L.map(div).setView([lat, lng], 11);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map);
L.geoJSON().addData(data.forecastHourly).addTo(map);
invalidation.then(() => map.remove());
```
14 changes: 14 additions & 0 deletions examples/weather/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"type": "module",
"scripts": {
"dev": "observable preview",
"build": "rm -rf dist && observable build",
"postinstall": "ln -sf ../../../../bin/observable-init.js node_modules/.bin/observable"
},
"dependencies": {
"@observablehq/cli": "link:../.."
},
"engines": {
"node": "^18 <18.19.0 || >=20"
}
}
Loading

0 comments on commit 2d15813

Please sign in to comment.