I made a multiple line chart visualization with d3.js. But with so many lines (each line is a column on my dataset), it's obviously unreadable. So I want a checkbox where the user can choose which line he wants to display. My problem is, in all the tutorials I see, I need to manually write the name of each column I want to display in the checkbox...is there a way to take automatically all my variables in the checkbox ?
As an example, I would like a result like this one : https://d3-graph-gallery.com/graph/line_select.html
For the moment I only have a script which displays all my variables on the graph, and I don't know what I need to add.
I leave you my code it its current state:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test pollen avec code D3 Graph Gallery</title>
<style>
body {
font: 12px sans-serif;
}
.axis path,
axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue; stroke-width: 1.5px;
}
</style>
</head>
<body>
<h1 style="margin: 2em;">Concentration pollinique au Luxembourg, 1992-2018</h1>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script>
const margin = {top: 20, right: 80, bottom: 30, left: 100},
width = 1200 - margin. left - margin. right,
height = 500 - margin.top - margin. bottom;
// formater date
const parseDate = d3. time.format("%Y-%m-%d").parse;
const x = d3.time.scale()
.range ([0, width]);
const y = d3.scale. linear()
.range ( [height, 0]);
const color = d3. scale. category10();
// axes x et y avec leur orientation
const xAxis = d3. svg.axis ()
.scale (x)
.orient ("bottom");
const yAxis = d3. svg.axis ()
.scale (y)
.orient ("left");
const line = d3.svg.line()
.interpolate ("basis")
.x(function(d) { return x(d. date); })
.y(function(d) { return y(d.concentration); });
let svg = d3.select ("body") .append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append ("g")
.attr("transform", "translate(" + margin. left + "," + margin.top + ")");
d3.csv ("pollen_tri.csv", function(error, data) {
color.domain (d3.keys (data[0]).filter(function (key) { return key !== "date"; }));
data.forEach(function(d) {
d.date = parseDate (d.date);
});
var plants = color.domain().map (function (name) {
return {
name: name,
values: data.map(function (d) {
return {date: d.date, concentration: +d [name]};
})
};
});
x.domain (d3.extent (data, function(d) { return d.date; }));
y.domain([
d3.min(plants, function(c) { return d3.min(c.values, function(v) {return v.concentration;}); }),
d3.max(plants, function(c) { return d3.max(c.values, function(v) { return v.concentration;});})
]);
svg.append("g")
.attr("class" , "x axis")
.attr("transform", "translate(0," + height + ")")
.call (xAxis);
svg.append ("g")
.attr("class", "y axis")
.call (yAxis)
.append ("text")
. attr("transform", "rotate (-90)")
.attr("y", 6)
.attr("dy", " .71em")
.style("text-anchor", "end")
.text ("Concentration (pollens/m3)");
var plant = svg.selectAll(".plant")
.data(plants)
.enter()
.append ("g")
.attr("class", "plant");
plant.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
});
</script>
</body>
</html>
Here's also an image of my current graph:
Finally, here's a sample of my csv file:
date | Ambrosia | Artemisia | Asteraceae | Alnus | Betula | Ericaceae | Carpinus | Castanea | Quercus | Chenopodium | Cupressaceae | Acer | Fraxinus | Gramineae | Fagus | Juncaceae | Aesculus | Larix | Corylus | Juglans | Umbellifereae | Ulmus | Urtica | Rumex | Populus | Pinaceae | Plantago | Platanus | Salix | Cyperaceae | Filipendula | Sambucus | Tilia |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1992-01-01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1992-01-02 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1992-01-03 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1992-01-04 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1992-01-05 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1992-01-06 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1992-01-07 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1992-01-08 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1992-01-09 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Here's an example of how to automatically create checkboxes which will show/hide the lines.
I used plain Javascript, but you could do the same thing using d3.
This example creates checkboxes (<input type="checkbox">
). To get a dropdown menu instead, use <select>
and <option>
.
There are three steps:
<path>
. In other words, it will let us look up an SVG <path>
from a plant name.The code I changed:
const mapPlantNameToSvgPath = {};
plant.append("path")
.attr("class", "line")
.attr("d", function (d) { return line(d.values); })
.style("stroke", function (d) { return color(d.name); })
//
// https://d3js.org/d3-selection/control-flow#selection_each
.each(function (d) {
const svgPath = this;
const plantName = d.name;
mapPlantNameToSvgPath[plantName] = svgPath;
})
color.domain().forEach(function (plantName) {
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
const label = document.createElement("label");
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
const checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.setAttribute("checked", "true");
checkbox.addEventListener("change", function () {
const svgPath = mapPlantNameToSvgPath[plantName];
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/visibility
if (checkbox.checked) {
svgPath.setAttribute("visibility", "visible");
} else {
svgPath.setAttribute("visibility", "hidden");
}
});
label.append(checkbox);
label.append(document.createTextNode(plantName));
document.body.append(document.createElement("br"));
document.body.append(label);
});
Screenshot (with fake data in the csv file):