Published
Edited
May 23, 2020
2 forks
Importers
Insert cell
Insert cell
rawData = {
let n = 50
let a = []
let i
for (i=0; i < n; i++) {
a.push({a: i * 100 - 50 * Math.random(), b: i * (Math.random() - 0.1)})
}
a.push({a: (i + 1) * 100, b: undefined})
a.push({a: undefined, b: 30})
a.push({a: (i + 3) * 100, b: 35})
a.push({a: (i + 4) * 100, b: 25})
a.push({a: (i + 5) * 100, b: 30})
return a
}
Insert cell
a = chart(rawData.map(d => [d.a, d.b]), {lineStroke: "grey", curve: "curveMonotoneX", lineStrokeWidth: lineStrokeWidth})
Insert cell
viewof lineStrokeWidth = slider({min: 0, max: 10, value: 2, step: 0.5, title: "lineStrokeWidth"})
Insert cell
a.help
Insert cell
chart = (data, options) => {

const unit = width / 954
const defaults = {
helpText: "data is an array of arrays: [[0, 0], [1, 5], ...]",
margin: {
left: 50 * unit + 2 * 14 * unit / Math.sqrt(unit),
top: 25 * unit,
right: 35 * unit,
bottom: 50 * unit + 14 * unit / Math.sqrt(unit),
},
width: width,
height: width / (Math.sqrt(2) + 1),
pointRadius: 15 * unit / Math.log(data.length + 2),
pointFill: "black",
pointOpacity: 1,
lineStroke: "black",
lineStrokeWidth: 2,
curve: "curveLinear",
ticklabelSize: 14 * unit / Math.sqrt(unit),
tickFontFamily: "-apple-system,system-ui,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Helvetica,Arial,sans-serif"
}
const p = Object.assign(defaults, options)

const x = d3.scaleLinear().domain(d3.extent(data, d => d[0])).nice().range([0, width])
const y = d3.scaleLinear().domain(d3.extent(data, d => d[1])).nice().range([p.height, 0])
const linePath = d3.line()
.defined(d => (!isNaN(d[0]) && !isNaN(d[1])))
.curve(d3[p.curve])
.x(d => x(d[0]))
.y(d => y(d[1]))

let svg = d3.create("svg")
.attr("viewBox", `-${p.margin.left} -${p.margin.top} ${p.width + p.margin.left + p.margin.right} ${p.height + p.margin.top + p.margin.bottom}`)
svg.append("g").attr("class", "line").append("path")
.attr("d", linePath(data))
.attr("stroke", p.lineStroke)
.attr("stroke-width", p.lineStrokeWidth)
.attr("fill", "none")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
svg.append("g").attr("class", "points").selectAll("circle").data(data).join("circle")
.attr("cx", d => x(d[0]))
.attr("cy", d => y(d[1]))
.attr("r", p.pointRadius)
.attr("fill", p.pointFill)
.attr("fill-opacity", p.pointOpacity)
.style("display", function (d, i) {
if (isNaN(d[0]) || isNaN(d[1])) {
return "none"
}
})
let xAxis = svg.append("g").attr("id", "xAxis").call(d3.axisBottom(x))
.attr("transform", `translate(0, ${p.height + p.pointRadius + p.ticklabelSize})`)
.style("font-size", p.ticklabelSize)
.style("font-family", p.tickFontFamily)

svg.append("g").attr("id", "yAxis").call(d3.axisLeft(y))
.attr("transform", `translate(${-p.pointRadius - p.ticklabelSize},0)`)
.style("font-size", p.ticklabelSize)
.style("font-family", p.tickFontFamily)
if (!data.length) {
svg.append("g").append("text").text("data is empty ¯\\_(ツ)_/¯")
.attr("x", p.width / 2)
.attr("y", p.height / 2)
.attr("text-anchor", "middle")
.style("font-family", "monospace")
.style("font-size", 2 * p.ticklabelSize)
}
const result = svg.node()
result.help = defaults
return result
}
Insert cell
Insert cell
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