-
Notifications
You must be signed in to change notification settings - Fork 5
/
test.html
292 lines (209 loc) · 13.7 KB
/
test.html
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@hpcc-js/common/font-awesome/css/font-awesome.min.css">
<style>
body {
padding: 0px;
margin: 8px;
background: white;
color: black;
}
#placeholder {
position: absolute;
left: 8px;
top: 8px;
right: 8px;
bottom: 8px;
max-width: 480px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/@hpcc-js/observable-md/dist/index.full.js" type="text/javascript" charset="utf-8"></script>
<script>
var omdMod = window["@hpcc-js/observable-md"]
</script>
</head>
<body onresize="doResize()">
<div id="placeholder">
</div>
<script>
var app = new omdMod.Observable()
.target("placeholder")
.showValues(true)
.mode("ojs")
.text(`md\`# A Taste of Observable
Hello and welcome to Observable! 👋\`
md\`Observable helps you sketch with live data, prototype visualizations, connect to Web APIs, share and reuse techniques and components with a friendly community of other authors, and publish your work for the world to see.\`
md\`A notebook is made up of a series of cells, and each cell is defined by its JavaScript source code.
👈 Click the margin to the left of any cell (including this one!), to toggle it open and closed, and see, and even edit, the source that lies below.
Let’s start by loading up some data. We’re going to use weather forecast data for the purposes of this example. After getting a taste of Observable, you can use similar techniques on the data you care about.
Here, we have a cell containing a recent forecast from the weather station on Belvedere Castle in Central Park, New York City:\`
nycForecast = FileAttachment(/* "canned-forecast.json" */"https://static.observableusercontent.com/files/a033960c84e9ea9d99675223913e00af5ced5d2e833762a3cd2bdac843795ec672ac61b9f27e3a8a1719de6bdd5fa7363fc1cd05aa51c164b88a4aa170a650a0").json()
md\`👆 This is a snapshot of raw data from the National Weather Service, unprocessed and full of crufty metadata describing different attributes of the weather forecast. You can explore it yourself by clicking into the carets: \${caret()}\`
md\`Prefer live data instead? No problem. We can do that by fetching directly from \${code(\`api.weather.gov\`)}. (We’ve attached a snapshot of this data to this notebook so the notebook will continue working if the API is unavailable.)\`
fetch("https://api.weather.gov/gridpoints/OKX/33,37/forecast").then(response => response.json())
md\`Let’s drill down to the actual weather forecast.
👇 Try inspecting the data in \\\`nycForecastPeriods\\\` by clicking on \${caret()}\\\`Array(14)\\\`, and then toggle open some of the specific forecasts over the week to come.\`
nycForecastPeriods = nycForecast.properties.periods
md\`You’ll notice that they include the forecasted temperature, a description of what the weather is going to do, information about wind speed, and even something called \\\`icon\\\`.\`
md\`Let’s make that forecast easier to read by taking the next few days of weather and turning it into a table:\`
md\`### New York City weather forecast\`
table(
nycForecastPeriods.slice(0, 7).map(
({ name, temperature, icon, detailedForecast, shortForecast }) => ({
Day: md\`**\${name}**\`,
Temp: md\`**\${temperature} °F**\`,
Icon: html\`<img width=70 src="\${icon}" />\`,
Forecast: detailedForecast
})
)
)
md\`👆 In the code of the cell above, try replacing \${code(
\`Forecast: detailedForecast\`
)} with \${code(
\`Forecast: shortForecast\`
)}. Then press the blue play button \${run()} or use Shift-Return to run your change. What happens?
...
You’ll notice that the table above reacts, displaying a \\\`shortForecast\\\` instead of the \\\`detailedForecast\\\` that it contained previously.
This reactivity is the heart of what makes Observable notebooks a special environment for exploration. Whenever you change anything, or anything changes by itself, the rest of the notebook instantly updates to reflect the latest information. In technical terms, this is called reactive, or dataflow programming. But in practical terms, it means that your notebooks never get stuck in a broken state, and that you never have to refresh the page. Reactivity gives you immediate feedback when you are exploring your data, sketching ideas in code, and collaborating with others.
Once you start sketching ideas in a reactive, web-enabled notebook, it’s hard to go back to scripts, spreadsheets and web servers.\`
md\`Let’s continue by putting that same NYC forecast data into a plot. We’ll use Vega-Lite, a simple charting library, to draw the forecasted temperatures over the coming week. The daytime temperatures are marked in orange, and the forecasted nighttime temperatures in blue.\`
vl
.markCircle() // Make a plot of circles
.data(nycForecastPeriods) // Using the NYC forecast data
.encode(
vl
.x() // For the X axis
.fieldN('name') // Use "name"
.sort(null), // But don’t sort alphabetically
vl
.y() // For the Y axis
.fieldQ('temperature'), // Use "temperature"
vl
.color() // For the color
.fieldN('isDaytime') // Use "isDaytime"
)
.render() // Draw the plot
md\`👆 In the code of the cell above, try replacing \${code(
\`markCircle()\`
)} with \${code(
\`markSquare()\`
)}. Then press the blue play button \${run()} or use Shift-Return to run your change. What happens? How about \${code(
\`markPoint()\`
)} ?\`
{
const [now, _, later] = nycForecastPeriods;
const trend = later.temperature - now.temperature < 0 ? \`colder\` : \`warmer\`;
return md\`Of course, you can use data to drive prose just as easily as visualizations. For example, from the chart and table above, you can see how in New York City, it is going to be \${trend} on \${later.name} than it is \${now.name}. Click on the left margin 👈 to see how I’m using data from the forecast to generate the text for this paragraph, awkward capitalization and all.\`
}
md\`But perhaps you don't live in New York? (Sadly, I no longer do.)
Let’s make a version that:
- Allows you to pick any location in the United States.
- Uses the Weather.gov API to find the nearest weather station.
- Renders the current forecast into our table and chart.\`
md\`We need to start with a longitude and latitude pair. You could write this by hand, like: \\\`[-73.7, 40.8]\\\`, but it takes at least five seconds and another browser tab to Google for coordinates, so we’ll do something more convenient by importing a handy map widget from [an existing notebook](https://observablehq.com/@jashkenas/inputs#usaMapCoordinates). \`
viewof coordinates = usaMapCoordinates({
value: [-122.27, 37.87], // The default location: Berkeley, CA.
width: 350,
title: "Pick a location, see the weather forecast"
})
md\`Now, when you click on the map above 👆, watch how the data flows through to the coordinates data below 👇. You may notice that the left margin flashes whenever a cell receives new data and runs again.\`
coordinates
md\`Here, the \\\`coordinates\\\` flow into a request to the National Weather Service API, to find the closest weather station. (Again, a flash of the left margin.)\`
weatherStation = {
const [lng, lat] = coordinates;
return (await fetch(\`https://api.weather.gov/points/\${[lat, lng]}\`)).json()
}
md\`Looks like you picked a point near **\${place}**.\`
place = {
const {city, state} = weatherStation.properties.relativeLocation.properties;
return \`\${city}, \${state}\`;
}
md\`Now, we’ll use the \\\`weatherStation\\\`’s \\\`forecast\\\` URL to retrieve the current forecast for \${place}. This data has exactly the same shape as the NYC forecast at the top of the page. As before, click on the caret \${caret()} to explore the raw data.\`
localForecast = (await fetch(weatherStation.properties.forecast)).json()
md\`Here’s our formatted weather table:\`
md\`### Current weather forecast for \${place}\`
table(
localForecast.properties.periods.slice(0, 7).map(
({ name, temperature, icon, detailedForecast, shortForecast }) => ({
Day: md\`**\${name}**\`,
Temp: md\`**\${temperature} °F**\`,
Icon: html\`<img width=70 src="\${icon}" />\`,
Forecast: detailedForecast
})
)
)
md\`And our daytime and nighttime forecasted temperature chart:\`
md\`### Forecasted temperature chart for \${place}\`
vl
.markCircle()
.data(localForecast.properties.periods)
.encode(
vl.x().fieldN('name').sort(null),
vl.y().scale(domain).fieldQ('temperature'),
vl.color().fieldN('isDaytime')
)
.render()
md\`If you scroll back up and pick a different location, you’ll notice that the table and chart will have automatically updated within a second or two — the amount of time it takes for the National Weather Service to generate and serve your forecast.\`
md\`For a final fancy touch, let’s connect our forecast data to [this custom D3 weather visualization](/@kerryrodden/simple-custom-weather-chart-in-d3), implemented in a different notebook. Vega-Lite is great for the rapid creation of basic charts, but sometimes you want to create something custom with more visual flair. For that, D3 is an excellent choice, and Observable makes it particularly easy to repurpose and remix other peoples’ D3 visualizations. Since the external notebook also happens to also render data in the Weather.gov forecast format, that’s not going to be too hard.
We import the \\\`weatherChart\\\` from that notebook, and inject our live \\\`localForecast\\\` data into it:\`
import { weatherChart } with { localForecast as forecast } from "@kerryrodden/simple-custom-weather-chart-in-d3"
md\`And then render the chart onto the page:\`
weatherChart
md\`Even though this is code being imported from another notebook, as you change your location on the map, it reacts to changes in the forecast data in the same way as any other cell.
If you’d like to work on a notebook with someone else, you can enable link sharing, and send them the URL. Then, after tinkering with your notebook, they can click a button to [send you their comments and code suggestions](https://observablehq.com/@observablehq/suggestions-and-comments). Or, you can work together on a notebook in real time, using [Observable Teams](https://observablehq.com/teams).\`
md\`🌈 I think that’s it for our first taste of Observable!
If you’d like to learn more, about how to actually *build* notebooks like this one, we recommend starting with these introductory notebook sequences, which gently teach Observable concepts with special consideration for folks from different backgrounds, having different goals:\`
md\`
- **[Learn D3](/@d3/learn-d3)**<br> D3 is a powerful JavaScript library that helps you bring data to life using HTML, SVG, and CSS. If you already have some D3 under your belt, you'll find using D3 in Observable is a little bit different. Whether you're new to D3, Observable, or both, get your feet wet here.
- **[Observable for Jupyter Users](/@observablehq/observable-for-jupyter-users?collection=@observablehq/observable-for-jupyter-users)**<br> If you have a Python data science background, or just feel more comfortable in Python than in JavaScript, these tutorials will help ease you in.
- **[Charting with Vega-Lite](/@observablehq/vega-lite)**<br> Vega-Lite is probably the best JavaScript library for extremely rapid visual exploration of data sets. You can iterate on your Vega visualizations even faster in Observable’s reactive environment.
\`
md\`And if you don’t want to hold hands and would prefer to dive straight into the deep end, I’d recommend bookmarking the [User Manual](https://observablehq.com/@observablehq/user-manual) (everything is in there), and checking out the [Explore](https://observablehq.com/explore) page for inspiration.
Have fun! 👋\`
md\`<br><br><br><br><br><br><br><br><br>
---
## Appendix
The cells below power the cells above.\`
domain = {
const temps = localForecast.properties.periods.map(d => d.temperature);
const high = Math.max.apply(Math, temps);
const low = Math.min.apply(Math, temps);
return {domain: [Math.min(low, 0), Math.max(high, 90)]};
}
function code(s) {
return html\`<code style="white-space: nowrap;">\${s}</code>\`;
}
function caret() {
const c = caretImage.cloneNode(true);
c.style.verticalAlign = "middle";
return c;
}
caretImage = Object.assign(await FileAttachment(/* "[email protected]" */"https://static.observableusercontent.com/files/46c8df99316e9926d90ff847b4aa5cc4755ec8a1699f2d81ee6daec11e397e0bf090556bb9488461ed942a251f0ca8b4eb9b2b0281065bd5b13cac54b548ae17").image(), {width: 18})
function run() {
return svg\`<svg width="16" height="16" class="db bump" stroke-linejoin="round" fill="#3b5fc0"><path d="M11.7206 6.94335C12.2406 7.34365 12.2406 8.12786 11.7206 8.52816L5.60999 13.2321C4.95242 13.7383 4 13.2696 4 12.4397L4 3.03178C4 2.20194 4.95243 1.73318 5.60999 2.23937L11.7206 6.94335Z" stroke="#3b5fc0" stroke-width="1.6"></path></svg>\`;
}
style = html\`<style>
.table-2 table td,
.table-2 table th {
padding: 5px 10px;
}
</style>\`
import {usaMapCoordinates} from "@jashkenas/inputs"
import {table} from "@tmcw/tables/2"
import {vl} from "@vega/vega-lite-api"`)
;
doResize();
function doResize() {
if (app) {
app
.resize()
.lazyRender()
;
}
}
</script>
</body>
</html>