Published
Edited
Apr 3, 2020
1 fork
Importers
Insert cell
Insert cell
inputs = ({
width: 'auto',
height: 350,
plotting: 'foxnews-wow',
title: 'Fox News',
legend: true,
sparkline: false,
titleOnHover: false,
xAxisHidden: false,
yAxisHidden: false,
twoLinesOnly: false
})
Insert cell
chart = {
const autoWidth = d3
.select('div.observablehq')
.node()
.getBoundingClientRect().width;

const width = inputs.width === 'auto' ? autoWidth : inputs.width;
const height = inputs.height;
const plotting = inputs.plotting;
const title = inputs.title;
const legend = inputs.legend;
const sparkline = inputs.sparkline;
const titleOnHover = inputs.titleOnHover;
const xAxisHidden = inputs.xAxisHidden;
const yAxisHidden = inputs.yAxisHidden;
const twoLinesOnly = inputs.twoLinesOnly;

const margin = { top: 20, right: 20, bottom: 10, left: 50 };
if (sparkline) {
margin.top = 6;
margin.right = 3;
}
if (sparkline || xAxisHidden) {
margin.bottom = 3;
}
if (sparkline || yAxisHidden) {
margin.left = 10;
}
if (legend) {
margin.right = 80;
}

// data

const lineKeys = Object.keys(data[0]).slice(1, 5);

const howToDow = {
0: 'Mon',
24: 'Tue',
48: 'Wed',
72: 'Thu',
96: 'Fri',
120: 'Sat',
144: 'Sun'
};

// ____

// const width = widthInput ? widthInput - margin.left - margin.right : totalwidth > 1040 ? 1040 - margin.left - margin.right : totalwidth - margin.left - margin.right;

// const height = heightInput ? heightInput - margin.top - margin.bottom : 340 - margin.top - margin.bottom;

const svg = d3
.create("svg")
.attr('class', 'multi-line')
.attr('id', plotting)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
// .append("g")
// .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// set the ranges
const x = d3.scaleLinear().range([margin.left, width - margin.right]);
const y = d3.scaleLinear().range([height, 0]);

// feb17_feb23 feb24_mar1 mar2_mar8 mar9_mar15
// define the 1st line
const valueline1 = d3
.line()
.curve(d3.curveCardinal.tension(0.95))
.defined(function(d) {
return d.mar2_mar8 > 0;
})
.x(d => x(d.how))
.y(d => y(d.mar2_mar8));
// define the 2nd line
const valueline2 = d3
.line()
.curve(d3.curveCardinal.tension(0.95))
.defined(function(d) {
return d.mar9_mar15 > 0;
})
.x(d => x(d.how))
.y(d => y(d.mar9_mar15));
// define the 3rd line
const valueline3 = d3
.line()
.curve(d3.curveCardinal.tension(0.95))
.defined(function(d) {
return d.mar16_mar22 > 0;
})
.x(d => x(d.how))
.y(d => y(d.mar16_mar22));
// define the 4th line
const valueline4 = d3
.line()
.curve(d3.curveCardinal.tension(0.95))
.defined(function(d) {
return d.mar23_mar29 > 0;
})
.x(d => x(d.how))
.y(d => y(d.mar23_mar29));

const minX = d3.min(data, d => d.how);
const maxX = d3.max(data, d => d.how);

const maxY = twoLinesOnly
? d3.max(data, d => Math.max(d.mar16_mar22, d.mar23_mar29))
: d3.max(data, d =>
Math.max(d.mar2_mar8, d.mar9_mar15, d.mar16_mar22, d.mar23_mar29)
);

// Scale the range of the data
x.domain([minX, maxX]);
y.domain([0, maxY]);

if (!twoLinesOnly) {
// Add the valueline path.
svg
.append("path")
.data([data])
.attr("class", "line")
.attr("stroke", "black")
.attr("stroke-opacity", 0.1)
.attr("stroke-width", 1)
.attr("d", valueline1);

// Add the valueline2 path.
svg
.append("path")
.data([data])
.attr("class", "line")
.attr("stroke", "black")
.attr("stroke-opacity", 0.2)
.attr("stroke-width", 1.2)
.attr("d", valueline2);
}

// Add the valueline3 path.
svg
.append("path")
.data([data])
.attr("class", "line")
.attr("stroke", "black")
.attr("stroke-opacity", 0.5)
.attr("stroke-width", 1.5)
.attr("d", valueline3);

// Add the valueline4 path.
svg
.append("path")
.data([data])
.attr("class", "line")
.attr("stroke", "black")
.attr("stroke-opacity", 1)
.attr("stroke-width", 2)
.attr("d", valueline4);

if (!xAxisHidden) {
// Add the X Axis
svg
.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(
d3
.axisBottom(x)
.tickValues([0, 24, 48, 72, 96, 120, 144])
.tickFormat(d => howToDow[d])
);
}

// Add the Y Axis
if (!yAxisHidden) {
if (maxY > 999) {
svg
.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ", 0)")
.call(d3.axisLeft(y).tickFormat(d3.format('.3s')));
} else {
svg
.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ", 0)")
.call(d3.axisLeft(y));
}
}

if (title) {
svg
.append("text")
.attr("x", margin.left + 4)
.attr("y", 10)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(title);
}

if (titleOnHover) {
svg
.append('rect')
.attr('class', 'overlay')
.attr('x', margin.left)
.attr('width', width - margin.right - 1)
.attr('height', height)
.style('fill', 'none')
.style('pointer-events', 'all')
.on('mouseover', () => {
svg
.append("text")
.attr("id", "title")
.attr("x", margin.left + 4)
.attr("y", 4)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data[0].network);
})
.on('mouseout', () => {
d3.select("text#title").remove();
});
}

if (legend) {
lineKeys.forEach(function(d, i) {
svg
.append("text")
.attr("x", width - 70) // space legend
.attr("y", 20 + i * 20)
.attr("class", "legend") // style the legend
.style("fill", "black")
.attr("fill-opacity", 0.25 * (i + 1))
.text(d.replace("_", " - "));
});
}

return svg.node();
}
Insert cell
Insert cell
Insert cell
data
Insert cell
Insert cell
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