Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

weather example #623

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
18 changes: 18 additions & 0 deletions examples/weather/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"type": "module",
"scripts": {
"build": "observable build",
"dev": "observable preview",
"deploy": "observable deploy",
"observable": "observable"
},
"dependencies": {
"@observablehq/framework": "^1.7.1"
},
"devDependencies": {
"rimraf": "^5.0.5"
},
"engines": {
"node": ">=18"
}
}
31 changes: 31 additions & 0 deletions examples/weather/src/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/src/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());
```
Loading