Published
Edited
Dec 24, 2019
Insert cell
Insert cell
DOM.uid().id; // create a unique id (keep running it to see the effect)
Insert cell
data.labels
Insert cell
(data.labels[0] + "").replace(/"/g, """)
Insert cell
legend = {
const id = DOM.uid().id;
return html`<style>

.${id} {
display: flex;
min-height: 33px;
font: 10px sans-serif;
margin-left: ${margin.left}px;
}

.${id}-item {
display: flex;
align-items: center;
width: 60px;
padding-bottom: 1px;
}

.${id}-swatch {
width: 20px;
height: 20px;
margin: 0 5px 0 0;
}

</style>
<div class="${id}">${color.domain().map((name, i) => html`
<div class="${id}-item" title="${data.labels === undefined ? "" : (data.labels[i] + "").replace(/"/g, "&quot;")}">
<div class="${id}-swatch" style="background:${color(name)};"></div>
${document.createTextNode(name)}
</div>`)}
</div>`;
}
Insert cell
color = d3.scaleOrdinal(
data.conditions === undefined ? data.map(d => d.condition) : data.conditions,
data.colors === undefined ? d3.schemeCategory10 : data.colors
).unknown("red") // there is no unknown data, that's why we don't see redness
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80).tickSizeOuter(0))// tickSizeInner(0)) can shrink inner tize size to nothing
// https://github.com/d3/d3-axis/blob/v1.0.12/README.md#axis_tickSizeOuter
.call(g => g.select(".domain").remove()) // remove the xAxis horizontal line
// .call(g => g.select(".tick:last-of-type text").append("tspan").text(data.y)) // this works too
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);

svg.append("g")
.call(grid);

const colorId = DOM.uid("color"); // https://github.com/observablehq/stdlib#DOM_uid

svg.append("linearGradient") // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient
.attr("id", colorId.id)
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", 0)
.attr("x2", width)
.selectAll("stop")
.data(data)
.join("stop")
.attr("offset", d => x(d.date) / width) // divide width is for offset ratio
.attr("stop-color", d => color(d.condition)); // use condition to map different color

svg.append("path")
.datum(data)
.attr("fill", "none") // if fill with "red", there will be a area of redness
.attr("stroke", colorId ) // https://github.com/observablehq/stdlib#DOM_uid
.attr("stroke-width", 2)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", line);
// svg.append("path")
// .datum(data)
// .attr("fill", "none")
// .attr("stroke", colorId)
// .attr("stroke-width", 2)
// .attr("stroke-linejoin", "round")
// .attr("stroke-linecap", "round")
// .attr("d", line2);

return svg.node();
}
Insert cell
colorId = DOM.uid("color");
Insert cell
// see how to use stop offset
html`
<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="myGradient" gradientTransform="rotate(90)">
<stop offset="10%" stop-color="gold" />
<stop offset="90%" stop-color="red" />
</linearGradient>
</defs>
<!-- using my linear gradient -->
<circle cx="5" cy="5" r="4" fill="url('#myGradient')" />
</svg>
`
Insert cell
html`
<svg viewBox="0 0 420 200" xmlns="http://www.w3.org/2000/svg">
<radialGradient id="gradient1" gradientUnits="userSpaceOnUse"
cx="100" cy="100" r="100" fx="100" fy="100">
<stop offset="0%" stop-color="darkblue" />
<stop offset="50%" stop-color="skyblue" />
<stop offset="100%" stop-color="darkblue" />
</radialGradient>
<radialGradient id="gradient2" gradientUnits="userSpaceOnUse"
cx="100" cy="100" r="100" fx="100" fy="100"
gradientTransform="skewX(20) translate(-35, 0)">
<stop offset="0%" stop-color="darkblue" />
<stop offset="50%" stop-color="skyblue" />
<stop offset="100%" stop-color="darkblue" />
</radialGradient>

<rect x="0" y="0" width="200" height="200" fill="url(#gradient1)" />
<rect x="0" y="0" width="200" height="200" fill="url(#gradient2)" style="transform: translateX(220px);" />
</svg>
`
Insert cell
FileAttachment("FCM.txt").text()
Insert cell
data = {
const parseDate = d3.utcParse("%Y-%m-%d %H:%M");
return Object.assign(d3.csvParse(await FileAttachment("FCM.txt").text(), ({valid, tmpf, skyc1}) => {
return tmpf === "M" ? null : { // if null, it won't be included into data
date: parseDate(valid),
value: +tmpf,
condition: skyc1
};
}), {
y: " °F",
conditions: ["CLR", "FEW", "SCT", "BKN", "OVC", "VV "],
labels: ["Clear", "Few clouds", "Scattered clouds", "Broken clouds", "Overcast", "Indefinite ceiling (vertical visibility)"],
colors: ["deepskyblue", "lightskyblue", "lightblue", "#aaaaaa", "#666666", "#666666"]
});
}
Insert cell
line = d3.line()
.curve(d3.curveStep) // without it, it is just normal smooth line; with it, it seems to be steps
.x(d => x(d.date))
.y(d => y(d.value))
Insert cell
line2 = d3.line()
.curve(d3.curveStep)
.x(d => x(d.date))
.y(d => y(d.value * 2))
Insert cell
x = d3.scaleUtc()
.domain(d3.extent(data, d => d.date))
.rangeRound([margin.left, width - margin.right])
Insert cell
y = d3.scaleLinear()
.domain(d3.extent(data, d => d.value)).nice()
.rangeRound([height - margin.bottom, margin.top])
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").append("tspan").text(data.y))
Insert cell
x.ticks()
Insert cell
y.ticks()
Insert cell
grid = g => g
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.1)
.call(g => g.append("g")
.selectAll("line")
.data(x.ticks())
.join("line")
.attr("x1", d => 0.5 + x(d))
.attr("x2", d => 0.5 + x(d))
.attr("y1", margin.top)
.attr("y2", height - margin.bottom))
.call(g => g.append("g")
.selectAll("line")
.data(y.ticks())
.join("line")
.attr("y1", d => 0.5 + y(d))
.attr("y2", d => 0.5 + y(d))
.attr("x1", margin.left)
.attr("x2", width - margin.right));
Insert cell
margin = ({top: 20, right: 20, bottom: 30, left: 40})
Insert cell
height = 500
Insert cell
d3 = require("d3@5")
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