Clean way to find associated values of a stacked bar graph - d3.js

As a side note, let me remark how thankful I am that there's an active D3.js community here. I've learned a lot in the few times I've posted and hope to only get better.
Let's consider the same data set that I've been using:
test_data.csv:
date,col_1,col_2
11/1/2012,1977652,1802851
12/1/2012,1128739,948687
1/1/2013,1201944,1514667
2/1/2013,1863148,1834006
3/1/2013,1314851,1906060
4/1/2013,1283943,1978702
5/1/2013,1127964,1195606
6/1/2013,1773254,977214
7/1/2013,1929574,1127450
8/1/2013,1980411,1808161
9/1/2013,1405691,1182788
10/1/2013,1336790,937890
11/1/2013,1851053,1358400
12/1/2013,1472623,1214610
1/1/2014,1155116,1757052
2/1/2014,1571611,1935038
3/1/2014,1898348,1320348
4/1/2014,1444838,1934789
5/1/2014,1235087,950194
6/1/2014,1272040,1580656
7/1/2014,980781,1680164
8/1/2014,1391291,1115999
9/1/2014,1211125,1542148
10/1/2014,1020824,1782795
11/1/2014,1685081,926612
12/1/2014,1469254,1767071
1/1/2015,1168523,935897
2/1/2015,1602610,1450541
3/1/2015,1830278,1354876
4/1/2015,1275158,1412555
5/1/2015,1560961,1839718
6/1/2015,949948,1587130
7/1/2015,1413765,1494446
8/1/2015,1166141,1305105
9/1/2015,958975,1202219
10/1/2015,902696,1023987
11/1/2015,961441,1865628
12/1/2015,1363145,1954046
1/1/2016,1862878,1470741
2/1/2016,1723891,1042760
3/1/2016,1906747,1169012
4/1/2016,1963364,1927063
5/1/2016,1899735,1936915
6/1/2016,1300369,1430697
7/1/2016,1777108,1401210
8/1/2016,1597045,1566763
9/1/2016,1558287,1140057
10/1/2016,1965665,1953595
11/1/2016,1800438,937551
12/1/2016,1689152,1221895
1/1/2017,1607824,1963282
2/1/2017,1878431,1415658
3/1/2017,1730296,1947106
4/1/2017,1956756,1696780
5/1/2017,1746673,1662892
6/1/2017,989702,1537646
7/1/2017,1098812,1592064
8/1/2017,1861973,1892987
9/1/2017,1129596,1406514
10/1/2017,1528632,1725020
11/1/2017,925850,1795575
and the .html file:
page.html:
<!DOCTYPE html>
<!-- https://bl.ocks.org/mbostock/3886208 -->
<style>
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
rect:hover {
fill:orange;
}
</style>
<script src="https://d3js.org/d3.v4.js"></script>
<body>
<div id="tooltip" class="hidden">
<p><strong>Month: </strong><span id="month"></span><p>
<p><strong>Value: </strong><span id="value"></span></p>
</div>
<script>
var margin = {top: 20, right: 20, bottom: 50, left: 40},
width = 1300 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// parse the date / time
// look at the .csv in Notepad! DO NOT LOOK AT EXCEL!
var parseDate = d3.timeParse("%m/%d/%Y");
var x = d3.scaleTime()
.range([0, width - margin.left - margin.right]);
var y = d3.scaleLinear().range([height, 0]);
var z = d3.scaleOrdinal()
.range(["#CE1126", "#00B6D0"]); // red and blue
var xAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat("%b")); // label every month
var xYearAxis = d3.axisBottom(x)
.ticks(d3.timeYear.every(1))
.tickFormat(d3.timeFormat("%Y")); // label every year
var formatNum = d3.format(",")
// load .csv file
d3.csv("test_data.csv", function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function(error, data){
if (error) throw error;
data.forEach(function(d) {
//console.log(parseDate(d.date));
d.date = parseDate(d.date);
});
var keys = data.columns.slice(1);
var barWidth = (width - margin.right- margin.left)/(data.length+1);
data.sort(function(a, b) { return b.date - a.date; });
x.domain(d3.extent( data, function(d){ return d.date }) );
var max = x.domain()[1];
var min = x.domain()[0];
var datePlusOneMonth = d3.timeDay.offset(d3.timeMonth.offset(max, 1), -1); // last day of current month: move up one month, back one day
x.domain([min,datePlusOneMonth]);
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
z.domain(keys);
// the bars
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return x(d.data.date); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", barWidth)
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x")) + barWidth / 2;
var yPosition = parseFloat(d3.select(this).attr("y")) / 2 + height / 2;
if (d[0] == 0) var value = d[1]; else var value = d[1]-d[0]; // data set values between col_1 and col_2
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#value")
.text(formatNum(value)); // return the value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#month")
.text(d3.timeFormat("%B %Y")(d.data.date)); // return the value
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
// x-axis
var axis = g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
const firstDataYear = x.domain()[0];
xYearAxis.tickValues([firstDataYear].concat(x.ticks()));
var yearAxis = g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (height + 25) + ")")
.call(xYearAxis);
axis.selectAll("g").select("text")
.attr("transform","translate(" + barWidth/2 + ",0)");
});
</script>
</body>
This does give me the desired behavior, in that I obtain the associated value with a hover:
but it was done rather messily. Namely:
if (d[0] == 0) var value = d[1]; else var value = d[1]-d[0]; // data set values between col_1 and col_2
Basically, the code says this: if the base of the bar is on the x-axis, use the height of the bar (in d[1]) in the tooltip. Otherwise, use d[1]-d[0] (for the bars in blue).
Now that I'm thinking about this, I could have just written
var value = d[1]-d[0];
but is there a way to write this so that I can read the code and specifically notice that it's referencing the col_1 and col_2 values, rather than the difference of these two d components? (Or, if this is the preferred method... I'll just stick with it.)

Here's one approach of obtaining that value:
Add class to the group that holds the bars
.attr('class', function(d) { return d.key; })
Use the class added to fetch the respective column and get the corresponding value in the mouseover function using the data object (i.e. it'll basically end up getting the value from either d.data['col_1'] or d.data['col_2']:
var value = d.data[d3.select(this.parentNode).attr('class')];
var dataAsCsv = `date,col_1,col_2
11/1/2012,1977652,1802851
12/1/2012,1128739,948687
1/1/2013,1201944,1514667
2/1/2013,1863148,1834006
3/1/2013,1314851,1906060
4/1/2013,1283943,1978702
5/1/2013,1127964,1195606
6/1/2013,1773254,977214
7/1/2013,1929574,1127450
8/1/2013,1980411,1808161
9/1/2013,1405691,1182788
10/1/2013,1336790,937890
11/1/2013,1851053,1358400
12/1/2013,1472623,1214610
1/1/2014,1155116,1757052
2/1/2014,1571611,1935038
3/1/2014,1898348,1320348
4/1/2014,1444838,1934789
5/1/2014,1235087,950194
6/1/2014,1272040,1580656
7/1/2014,980781,1680164
8/1/2014,1391291,1115999
9/1/2014,1211125,1542148
10/1/2014,1020824,1782795
11/1/2014,1685081,926612
12/1/2014,1469254,1767071
1/1/2015,1168523,935897
2/1/2015,1602610,1450541
3/1/2015,1830278,1354876
4/1/2015,1275158,1412555
5/1/2015,1560961,1839718
6/1/2015,949948,1587130
7/1/2015,1413765,1494446
8/1/2015,1166141,1305105
9/1/2015,958975,1202219
10/1/2015,902696,1023987
11/1/2015,961441,1865628
12/1/2015,1363145,1954046
1/1/2016,1862878,1470741
2/1/2016,1723891,1042760
3/1/2016,1906747,1169012
4/1/2016,1963364,1927063
5/1/2016,1899735,1936915
6/1/2016,1300369,1430697
7/1/2016,1777108,1401210
8/1/2016,1597045,1566763
9/1/2016,1558287,1140057
10/1/2016,1965665,1953595
11/1/2016,1800438,937551
12/1/2016,1689152,1221895
1/1/2017,1607824,1963282
2/1/2017,1878431,1415658
3/1/2017,1730296,1947106
4/1/2017,1956756,1696780
5/1/2017,1746673,1662892
6/1/2017,989702,1537646
7/1/2017,1098812,1592064
8/1/2017,1861973,1892987
9/1/2017,1129596,1406514
10/1/2017,1528632,1725020
11/1/2017,925850,1795575`;
var margin = {top: 20, right: 20, bottom: 50, left: 40},
width = 1300 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// parse the date / time
// look at the .csv in Notepad! DO NOT LOOK AT EXCEL!
var parseDate = d3.timeParse("%m/%d/%Y");
var x = d3.scaleTime()
.range([0, width - margin.left - margin.right]);
var y = d3.scaleLinear().range([height, 0]);
var z = d3.scaleOrdinal()
.range(["#CE1126", "#00B6D0"]); // red and blue
var xAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat("%b")); // label every month
var xYearAxis = d3.axisBottom(x)
.ticks(d3.timeYear.every(1))
.tickFormat(d3.timeFormat("%Y")); // label every year
var formatNum = d3.format(",")
var data = d3.csvParse(dataAsCsv, function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
})
data.forEach(function(d) {
//console.log(parseDate(d.date));
d.date = parseDate(d.date);
});
var keys = data.columns.slice(1);
var barWidth = (width - margin.right- margin.left)/(data.length+1);
data.sort(function(a, b) { return b.date - a.date; });
x.domain(d3.extent( data, function(d){ return d.date }) );
var max = x.domain()[1];
var min = x.domain()[0];
var datePlusOneMonth = d3.timeDay.offset(d3.timeMonth.offset(max, 1), -1); // last day of current month: move up one month, back one day
x.domain([min,datePlusOneMonth]);
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
z.domain(keys);
// the bars
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g").attr('class', function(d) { return d.key; })
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return x(d.data.date); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", barWidth)
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x")) + barWidth / 2;
var yPosition = parseFloat(d3.select(this).attr("y")) / 2 + height / 2;
var value = d.data[d3.select(this.parentNode).attr('class')];
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#value")
.text(formatNum(value)); // return the value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#month")
.text(d3.timeFormat("%B %Y")(d.data.date)); // return the value
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
// x-axis
var axis = g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
const firstDataYear = x.domain()[0]
xYearAxis.tickValues([firstDataYear].concat(x.ticks()));
var yearAxis = g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (height + 25) + ")")
.call(xYearAxis);
axis.selectAll("g").select("text")
.attr("transform","translate(" + barWidth/2 + ",0)");
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
rect:hover {
fill:orange;
}
<script src="https://d3js.org/d3.v4.js"></script>
<div id="tooltip" class="hidden">
<p><strong>Month: </strong><span id="month"></span><p>
<p><strong>Value: </strong><span id="value"></span></p>
</div>

Related

D3 Added Circles to a MultiLine Chart - Circles on top of Circles

I'm having an issue where I'm plotting circles on a multi-line chart which has a different color for each line (circles match the colors). The catch is the way I have the function I'm writing circles on top of circles - which is an issue when I try to hide specific ones.
I want to plot circles based on then name but I'm unsure how to limit the above D3 functions to one name only - currently it plots all circles for each line.
Is there a way to use d.name to limit the plotting to one name each time?
thanks
I think d3.nest is what you want:
var names = d3.nest()
.key(function(d){ return d.name; })
.entries(data);
var data = [
{"name":"1.0E-6MHz","value":"20.0","date":"2017-08-25 21:00:00"},{"name":"1.0E-6MHz","value":"93.8","date":"2017-08-25 22:00:00"},{"name":"1.0E-6MHz","value":"86.2","date":"2017-08-25 23:00:00"},{"name":"1.0E-6MHz","value":"79.2","date":"2017-08-26 00:00:00"},{"name":"1.0E-6MHz","value":"81.7","date":"2017-08-26 01:00:00"},{"name":"1.0E-6MHz","value":"76.2","date":"2017-08-26 02:00:00"},{"name":"1.0E-6MHz","value":"86.2","date":"2017-08-26 03:00:00"},{"name":"1.0E-6MHz","value":"89.2","date":"2017-08-26 04:00:00"},{"name":"1.0E-6MHz","value":"91.9","date":"2017-08-26 05:00:00"},{"name":"1.0E-6MHz","value":"78.2","date":"2017-08-26 06:00:00"},{"name":"1.0E-6MHz","value":"86.2","date":"2017-08-26 07:00:00"},{"name":"1.0E-6MHz","value":"82.2","date":"2017-08-26 08:00:00"},{"name":"1.0E-6MHz","value":"96.2","date":"2017-08-26 09:00:00"},{"name":"1.0E-6MHz","value":"88.7","date":"2017-08-26 10:00:00"},{"name":"1.0E-6MHz","value":"92.3","date":"2017-08-26 11:00:00"},{"name":"1.0E-6MHz","value":"80.2","date":"2017-08-26 12:00:00"},{"name":"1.0E-6MHz","value":"76.2","date":"2017-08-26 13:00:00"},{"name":"1.0E-6MHz","value":"93.2","date":"2017-08-26 14:00:00"},{"name":"1.0E-6MHz","value":"89.2","date":"2017-08-26 15:00:00"},{"name":"1.0E-6MHz","value":"79.2","date":"2017-08-26 16:00:00"},{"name":"1.0E-6MHz","value":"90.2","date":"2017-08-26 17:00:00"},{"name":"1.0E-6MHz","value":"85.2","date":"2017-08-26 18:00:00"},{"name":"1.0E-6MHz","value":"86.5","date":"2017-08-26 19:00:00"},{"name":"1.0E-6MHz","value":"76.2","date":"2017-08-26 20:00:00"},{"name":"1.0E-6MHz","value":"94.5","date":"2017-08-26 21:00:00"},
{"name":"2.0E-6MHz","value":"26.2","date":"2017-08-25 21:00:00"},{"name":"2.0E-6MHz","value":"33.8","date":"2017-08-25 22:00:00"},{"name":"2.0E-6MHz","value":"26.2","date":"2017-08-25 23:00:00"},{"name":"2.0E-6MHz","value":"66.2","date":"2017-08-26 00:00:00"},{"name":"2.0E-6MHz","value":"3.7","date":"2017-08-26 01:00:00"},{"name":"2.0E-6MHz","value":"76.2","date":"2017-08-26 02:00:00"},{"name":"2.0E-6MHz","value":"36.2","date":"2017-08-26 03:00:00"},{"name":"2.0E-6MHz","value":"22.2","date":"2017-08-26 04:00:00"},{"name":"2.0E-6MHz","value":"31.6","date":"2017-08-26 05:00:00"},{"name":"2.0E-6MHz","value":"44.2","date":"2017-08-26 06:00:00"},{"name":"2.0E-6MHz","value":"7.2","date":"2017-08-26 07:00:00"},{"name":"2.0E-6MHz","value":"46.2","date":"2017-08-26 08:00:00"},{"name":"2.0E-6MHz","value":"46.2","date":"2017-08-26 09:00:00"},{"name":"2.0E-6MHz","value":"21.7","date":"2017-08-26 10:00:00"},{"name":"2.0E-6MHz","value":"22.3","date":"2017-08-26 11:00:00"},{"name":"2.0E-6MHz","value":"46.2","date":"2017-08-26 12:00:00"},{"name":"2.0E-6MHz","value":"46.2","date":"2017-08-26 13:00:00"},{"name":"2.0E-6MHz","value":"46.2","date":"2017-08-26 14:00:00"},{"name":"2.0E-6MHz","value":"46.2","date":"2017-08-26 15:00:00"},{"name":"2.0E-6MHz","value":"46.2","date":"2017-08-26 16:00:00"},{"name":"2.0E-6MHz","value":"96.2","date":"2017-08-26 17:00:00"},{"name":"2.0E-6MHz","value":"46.2","date":"2017-08-26 18:00:00"},{"name":"2.0E-6MHz","value":"33.5","date":"2017-08-26 19:00:00"},{"name":"2.0E-6MHz","value":"46.2","date":"2017-08-26 20:00:00"},{"name":"2.0E-6MHz","value":"44.5","date":"2017-08-26 21:00:00"},
{"name":"3.0E-6MHz","value":"38.2","date":"2017-08-25 21:00:00"},{"name":"3.0E-6MHz","value":"43.8","date":"2017-08-25 22:00:00"},{"name":"3.0E-6MHz","value":"56.2","date":"2017-08-25 23:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 00:00:00"},{"name":"3.0E-6MHz","value":"53.7","date":"2017-08-26 01:00:00"},{"name":"3.0E-6MHz","value":"3.2","date":"2017-08-26 02:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 03:00:00"},{"name":"3.0E-6MHz","value":"66.2","date":"2017-08-26 04:00:00"},{"name":"3.0E-6MHz","value":"37.9","date":"2017-08-26 05:00:00"},{"name":"3.0E-6MHz","value":"42.2","date":"2017-08-26 06:00:00"},{"name":"3.0E-6MHz","value":"4.2","date":"2017-08-26 07:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 08:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 09:00:00"},{"name":"3.0E-6MHz","value":"21.7","date":"2017-08-26 10:00:00"},{"name":"3.0E-6MHz","value":"22.3","date":"2017-08-26 11:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 12:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 13:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 14:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 15:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 16:00:00"},{"name":"3.0E-6MHz","value":"96.2","date":"2017-08-26 17:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 18:00:00"},{"name":"3.0E-6MHz","value":"33.5","date":"2017-08-26 19:00:00"},{"name":"3.0E-6MHz","value":"46.2","date":"2017-08-26 20:00:00"},{"name":"3.0E-6MHz","value":"34.5","date":"2017-08-26 21:00:00"},
{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-25 21:00:00"},{"name":"4.0E-6MHz","value":"53.8","date":"2017-08-25 22:00:00"},{"name":"4.0E-6MHz","value":"86.2","date":"2017-08-25 23:00:00"},{"name":"4.0E-6MHz","value":"56.2","date":"2017-08-26 00:00:00"},{"name":"4.0E-6MHz","value":"23.7","date":"2017-08-26 01:00:00"},{"name":"4.0E-6MHz","value":"16.2","date":"2017-08-26 02:00:00"},{"name":"4.0E-6MHz","value":"76.2","date":"2017-08-26 03:00:00"},{"name":"4.0E-6MHz","value":"82.2","date":"2017-08-26 04:00:00"},{"name":"4.0E-6MHz","value":"39.9","date":"2017-08-26 05:00:00"},{"name":"4.0E-6MHz","value":"6.2","date":"2017-08-26 06:00:00"},{"name":"4.0E-6MHz","value":"22.2","date":"2017-08-26 07:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 08:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 09:00:00"},{"name":"4.0E-6MHz","value":"21.7","date":"2017-08-26 10:00:00"},{"name":"4.0E-6MHz","value":"22.3","date":"2017-08-26 11:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 12:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 13:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 14:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 15:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 16:00:00"},{"name":"4.0E-6MHz","value":"96.2","date":"2017-08-26 17:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 18:00:00"},{"name":"4.0E-6MHz","value":"33.5","date":"2017-08-26 19:00:00"},{"name":"4.0E-6MHz","value":"46.2","date":"2017-08-26 20:00:00"},{"name":"4.0E-6MHz","value":"24.5","date":"2017-08-26 21:00:00"},
{"name":"5.0E-6MHz","value":"66.2","date":"2017-08-25 21:00:00"},{"name":"5.0E-6MHz","value":"63.8","date":"2017-08-25 22:00:00"},{"name":"5.0E-6MHz","value":"16.2","date":"2017-08-25 23:00:00"},{"name":"5.0E-6MHz","value":"86.2","date":"2017-08-26 00:00:00"},{"name":"5.0E-6MHz","value":"13.7","date":"2017-08-26 01:00:00"},{"name":"5.0E-6MHz","value":"36.2","date":"2017-08-26 02:00:00"},{"name":"5.0E-6MHz","value":"6.2","date":"2017-08-26 03:00:00"},{"name":"5.0E-6MHz","value":"21.2","date":"2017-08-26 04:00:00"},{"name":"5.0E-6MHz","value":"41.1","date":"2017-08-26 05:00:00"},{"name":"5.0E-6MHz","value":"86.2","date":"2017-08-26 06:00:00"},{"name":"5.0E-6MHz","value":"69.2","date":"2017-08-26 07:00:00"},{"name":"5.0E-6MHz","value":"46.2","date":"2017-08-26 08:00:00"},{"name":"5.0E-6MHz","value":"46.2","date":"2017-08-26 09:00:00"},{"name":"5.0E-6MHz","value":"21.7","date":"2017-08-26 10:00:00"},{"name":"5.0E-6MHz","value":"22.3","date":"2017-08-26 11:00:00"},{"name":"5.0E-6MHz","value":"46.2","date":"2017-08-26 12:00:00"},{"name":"5.0E-6MHz","value":"46.2","date":"2017-08-26 13:00:00"},{"name":"5.0E-6MHz","value":"46.2","date":"2017-08-26 14:00:00"},{"name":"5.0E-6MHz","value":"46.2","date":"2017-08-26 15:00:00"},{"name":"5.0E-6MHz","value":"46.2","date":"2017-08-26 16:00:00"},{"name":"5.0E-6MHz","value":"96.2","date":"2017-08-26 17:00:00"},{"name":"5.0E-6MHz","value":"46.2","date":"2017-08-26 18:00:00"},{"name":"5.0E-6MHz","value":"33.5","date":"2017-08-26 19:00:00"},{"name":"5.0E-6MHz","value":"46.2","date":"2017-08-26 20:00:00"},{"name":"5.0E-6MHz","value":"4.5","date":"2017-08-26 21:00:00"}
];
// parsing
data.forEach(function(d){
d.value = +d.value;
d.date = new Date(d.date);
})
// after this you will have 5 name keys of its values(in your case)
var names = d3.nest()
.key(function(d){ return d.name; })
.entries(data);
// console.log(names)
var chart = d3.select("#chart");
d3.select("#names")
.selectAll("button")
.data(names.map(function(d){ return d.key; }))
.enter()
.append("button")
.text(function(d){ return d; })
.on("click", redraw);
var svgWidth = 500,
svgHeight = 400,
radius = 5,
margin = { top: 30, right: 30, bottom: 30, left: 30 },
width = svgWidth - margin.right - margin.left,
height = svgHeight - margin.top - margin.bottom;
var xScale = d3.time.scale().range([0, width]),
yScale = d3.scale.linear().range([0, height]),
xAxis = d3.svg.axis().orient("bottom").scale(xScale),
yAxis = d3.svg.axis().orient("left");
var svg = chart.append("svg").attr({ width: svgWidth, height: svgHeight });
var gMain = svg.append("g").attr({
class: "gMain",
transform: "translate(" + [margin.left, margin.top] + ")"
}),
gYAxis = gMain.append("g").attr("class", "axis yaxis"),
gXAxis = gMain.append("g").attr({
class: "axis xaxis",
transform: "translate(0," + height + ")"
})
gPlot = gMain.append("g").attr({
class: "plot",
transform: "translate(0," + height + ")"
});
redraw("1.0E-6MHz", 0);
function redraw(name, index){
var points = names[index].values;
xScale.domain(d3.extent(points, function(d){ return d.date; }));
yScale.domain(d3.extent(points, function(d){ return d.value; }));
gXAxis.transition().call(xAxis);
gYAxis.transition().call(yAxis.scale(yScale.copy().range([height, 0])));
var update = gPlot.selectAll("circle").data(points),
enter = update.enter()
.append("circle")
.attr({
class: "circle",
r: radius,
fill: "steelblue",
cx: function(d){ return xScale(d.date); },
cy: function(d){ return -yScale(d.value); }
});
update.exit().remove();
update.transition()
.duration(700)
.attr({
cx: function(d){ return xScale(d.date); },
cy: function(d){ return -yScale(d.value); }
});
}
.axis path{
fill: none;
stroke: black;
stroke-width: 1;
}
.axis line{
stroke: black;
stroke-width: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="names"></div>
<div id="chart"></div>

d3 using zoom and brush on a bar chart

Hi I am trying to adapt Matthew Izanuk's Unit Bar Chart with Brush and Zoom to a randomly generated bar chart I have already created.
I get an error with the zoom function, on line 261
line 261 is
x.domain(t.rescaleX(x2).domain());
Here is the code and jsFiddle
<!DOCTYPE html>
<body>
<style>
div {
display: inline-block;
vertical-align: top;
}
#bar_chart {
border: 2px solid lightgray;
border-radius: 15px;
}
#json {
max-height: 600px;
width: 200px;
overflow: scroll;
border: 2px solid gray;
border-radius: 15px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
<div id="bar_chart">
</div>
<div id="json"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//************* generate data ************
var data = [];
var space = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var spaceLength = space.length;
makedata();
function makedata() {
var obj = {};
for (var i = 0; i < spaceLength; i++) {
obj = {};
value = Math.floor(Math.random() * 500);
rand = Math.floor(Math.random() * space.length)
name = space.charAt(rand);
obj["name"] = name;
obj["val"] = value;
data.push(obj);
space = space.slice(0, rand) + space.slice(rand + 1, space.length)
}
}
// To display json in html page
document.getElementById("json").innerHTML = "<pre>" + JSON.stringify(data, null, 4) + "</pre>";
var margin = {
top: 50,
right: 20,
bottom: 90,
left: 50
},
margin2 = {
top: 530,
right: 20,
bottom: 30,
left: 50
},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom,
height2 = 600 - margin2.top - margin2.bottom;
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var x2 = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height, 0]);
var y2 = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height2, 0]);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var svg = d3.select("#bar_chart")
// .data(data)
.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 + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var g0 = focus.append("g")
.attr("class", "focus")
.attr("transform", "translate(0,0)");
var xAxis = d3.axisBottom(x);
var xAxis2 = d3.axisBottom(x2);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
focus.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(7));
var tooltip = d3.select("#info")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var focus_group = focus.append("g");
focus_group.attr("clip-path", "url(#clip)");
var rects = focus_group.selectAll('rect')
.data(data);
//********* Bar Chart 1 ****************
var newRects1 = rects.enter();
newRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x(d.name);
})
.attr('y', function(d, i) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.attr("transform", "translate(" + 4 + ",0)")
.style('fill', 'lightblue')
.style('stroke', 'gray');
var focus_group = context.append("g");
focus_group.attr("clip-path", "url(#clip)");
var brushRects = focus_group.selectAll('rect')
.data(data);
//********* Brush Bar Chart ****************
var brushRects1 = brushRects.enter();
brushRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x2(d.name);
})
.attr('y', function(d, i) {
return y2(d.val);
})
.attr('height', function(d, i) {
return height2 - y2(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.attr("transform", "translate(" + 4 + ",0)")
.style('fill', 'lightblue')
.style('stroke', 'gray');
//append brush xAxis2
context.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
//create brush function redraw scatterplot with selection
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.selectAll(".mainBars")
.attr("x", function(d) {
return x(d.name);
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x-axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll(".mainBars")
.attr("x", function(d) {
return x(d.name);
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x-axis").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
</script>
Any help would be greatly appreciated,
thanks
As mentioned in the comments, invert only exists for continuous ranges. Your scaleBand is a ordinal scale composed of discreet values. One way I've worked around this is to simply figure which values in my domain fall in the selection:
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return;
// ignore brush-by-zoom
// get bounds of selection
var s = d3.event.selection,
nD = [];
// for each "tick" in our domain is it in selection
x2.domain().forEach((d)=>{
var pos = x2(d) + x2.bandwidth()/2;
if (pos > s[0] && pos < s[1]){
nD.push(d);
}
});
// set new domain
x.domain(nD);
// redraw
focus.selectAll(".mainBars")
// hide bars not in domain
.style("opacity", function(d){
return x.domain().indexOf(d.name) === -1 ? 0 : 100;
})
.attr("x", function(d) {
return x(d.name)+ x.bandwidth()/2 - 5;
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x.axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
Running code:
<!DOCTYPE html>
<head>
<style>
div {
display: inline-block;
vertical-align: top;
}
#bar_chart {
border: 2px solid lightgray;
border-radius: 15px;
}
#json {
max-height: 600px;
width: 200px;
overflow: scroll;
border: 2px solid gray;
border-radius: 15px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
</head>
<body>
<div id="bar_chart">
</div>
<div id="json"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//************* generate data ************
var data = [];
var space = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var spaceLength = space.length;
makedata();
function makedata() {
var obj = {};
for (var i = 0; i < spaceLength; i++) {
obj = {};
value = Math.floor(Math.random() * 500);
rand = Math.floor(Math.random() * space.length)
name = space.charAt(rand);
obj["name"] = name;
obj["val"] = value;
data.push(obj);
space = space.slice(0, rand) + space.slice(rand + 1, space.length)
}
}
// To display json in html page
document.getElementById("json").innerHTML = "<pre>" + JSON.stringify(data, null, 4) + "</pre>";
var margin = {
top: 50,
right: 20,
bottom: 90,
left: 50
},
margin2 = {
top: 530,
right: 20,
bottom: 30,
left: 50
},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom,
height2 = 600 - margin2.top - margin2.bottom;
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var x2 = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height, 0]);
var y2 = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height2, 0]);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var svg = d3.select("#bar_chart")
// .data(data)
.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 + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var xAxis = d3.axisBottom(x);
var xAxis2 = d3.axisBottom(x2);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
focus.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(7));
var tooltip = d3.select("#info")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var focus_group = focus.append("g");
focus_group.attr("clip-path", "url(#clip)");
var rects = focus_group.selectAll('rect')
.data(data);
//********* Bar Chart 1 ****************
var newRects1 = rects.enter();
newRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x(d.name) + x.bandwidth()/2;
})
.attr('y', function(d, i) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.style('fill', 'lightblue')
.style('stroke', 'gray');
var focus_group = context.append("g");
focus_group.attr("clip-path", "url(#clip)");
var brushRects = focus_group.selectAll('rect')
.data(data);
//********* Brush Bar Chart ****************
var brushRects1 = brushRects.enter();
brushRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x2(d.name);
})
.attr('y', function(d, i) {
return y2(d.val);
})
.attr('height', function(d, i) {
return height2 - y2(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.attr("transform", "translate(" + 4 + ",0)")
.style('fill', 'lightblue')
.style('stroke', 'gray');
//append brush xAxis2
context.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
//create brush function redraw scatterplot with selection
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
// get bounds of selection
var s = d3.event.selection,
nD = [];
x2.domain().forEach((d)=>{
var pos = x2(d) + x2.bandwidth()/2;
if (pos > s[0] && pos < s[1]){
nD.push(d);
}
});
x.domain(nD);
focus.selectAll(".mainBars")
.style("opacity", function(d){
return x.domain().indexOf(d.name) === -1 ? 0 : 100;
})
.attr("x", function(d) {
return x(d.name)+ x.bandwidth()/2 - 5;
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x.axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
/*
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll(".mainBars")
.attr("x", function(d) {
return x(d.name) + x.bandwidth()/2;
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x-axis").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
*/
}
</script>

Why are events from d3 in firefox not handled

Please look at http://bl.ocks.org/HoffmannP/95392bf4a37344793786 and help me find an explenation why it just doesn't work in FF but works like a charm in Chrome.
because you're using .style for width, height and x when you need to use .attr.
Having these as .styles is part of SVG 2 and not SVG 1.1 and SVG 2 is unfinished. Firefox does not yet implement this part of SVG 2, although it does implement other parts that Chrome does not.
var margin = {top: 50, right: 20, bottom: 60, left: 70};
var width = 800 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.domain([0, 4])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, 60])
.range([height, 0]);
var yVal = d3.scale.linear()
.domain([60, 0])
.range([height, 0]);
var yAxisMinor = d3.svg.axis()
.scale(y)
.ticks(13)
.tickSize(width, 0)
.orient('right');
var yAxisMajor = d3.svg.axis()
.scale(y)
.ticks(7)
.tickSize(width, 0)
.tickPadding(-(width + 5))
.tickFormat(d3.format('d'))
.orient('right');
var 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 + ')');
var xLabel = svg.append('g')
.attr('class', 'x label')
.attr('transform', 'translate(0, ' + height/2 + ') rotate(-90)')
.append('text')
.attr('text-anchor', 'middle')
.attr('dy', '-40')
.text('Prozent');
var gx = svg
.append('g').attr('class', 'x axis');
gx.append('g')
.attr('transform', 'translate(7, -15)')
.append('line')
.attr('x2', '0')
.attr('y2', height + 15);
gx.append('g')
.attr('transform', 'translate(0, -26) scale(0.15, 0.15)')
.append('path')
.attr('d', 'M0,86.6L50,0L100,86.6C50,75 50,75 0,86.6z');
var gyMinor = svg.append('g')
.attr('class', 'y axis minor')
.call(yAxisMinor);
gyMinor.selectAll('text').remove();
var gyMajor = svg.append('g')
.attr('class', 'y axis major')
.call(yAxisMajor);
gyMajor.selectAll('text')
.style('text-anchor', 'end')
.attr('dy', '7px');
var drawArea = svg.append('g')
.attr('class', 'block')
.attr('transform', 'translate(' + 20 + ', ' + height + ') scale(1, -1)');
var backBlocks = drawArea
.selectAll('rect.back')
.data([64, 64, 64, 64])
.enter()
.append('rect')
.attr('class', 'back')
.attr('width', width/5)
.attr('height', yVal)
.attr('x', function (d, i) { return x(i); });
var frontBlocks = drawArea
.selectAll('rect.front')
.data([0,0,0,0])
.enter()
.append('rect')
.attr('class', 'front')
.attr('width', width/5)
.attr('height', yVal)
.attr('x', function (d, i) { return x(i); });
var newHeight = function (d, i) {
var y = d3.event.clientY;
d3.select(frontBlocks[0][i % 4]).style('height', height + margin.bottom - y);
};
var currentActiveBlock = false;
drawArea.selectAll('rect')
.on('mouseover', function (d, i) {
d3.select(backBlocks[0][i % 4]).style('opacity', '0.5');
})
.on('mouseout', function () {
backBlocks.style('opacity', '0');
})
.on('mousedown', function (d, i) {
d3.select(backBlocks[0][i % 4]).style('opacity', '0.5');
newHeight.call(this, d, i);
currentActiveBlock = i % 4;
})
.on('mousemove', function (d, i) {
if (currentActiveBlock === false) {
return;
}
newHeight.call(this, d, currentActiveBlock);
})
.on('mouseup', function (d, i) {
d3.select(frontBlocks[0][currentActiveBlock]).style('opacity', '1');
newHeight.call(this, d, currentActiveBlock);
currentActiveBlock = false;
});
body {
font: 18px sans-serif;
}
svg {
}
.label text {
font-weight: bold;
}
.y.axis path {
display: none;
}
.x.axis path {
fill: #333;
}
.axis line {
shape-rendering: crispEdges;
stroke: #333;
stroke-width: 2px;
}
.axis.minor line {
stroke-width: 1px;
}
.axis text {
text-anchor: end;
}
.block rect {
cursor: ns-resize;
}
.block rect.back {
opacity: 0.0;
fill: #ddd;
}
}
.block rect.front {
fill: #222;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

D3 Tree Brushing

I am trying to brush a vertical tree layout.
But the selections are not working.
Please help me with respect to selection of the classed node.
I am very new to D3 and not able to understand this.
Here is my style sheet
.node {
stroke: black;
stroke-width: 1px;
}
.node .selected {
stroke: red;
}
.node text { font: 10px sans-serif; }
.link {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
.brush .extent {
fill-opacity: .1;
stroke: #fff;
shape-rendering: crispEdges;
Here is my code
var margin = {top: 140, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var i = 0;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.x, d.y]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
update(root);
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 100; });
// Declare the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter the nodes.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; });
nodeEnter.append("circle")
.attr("r", 10)
.style("fill", "#fff");
nodeEnter.append("text")
.attr("y", function(d) {
return d.children || d._children ? -18 : 18; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name; })
.style("fill-opacity", 1);
// Declare the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter the links.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", diagonal);
}
var brush = svg.append("g")
.attr("class", "brush")
.call(d3.svg.brush()
.x(d3.scale.identity().domain([0, 1000]))
.y(d3.scale.identity().domain([0, 1000]))
.on("brush",function(){
var extent =d3.event.target.extent();
console.log(extent);
svg.selectAll(".node")
.classed("selected",function(d){
// return true;
return extent[0][1] <= d.x && d.x < extent[1][0]
&& extent[0][1] <= d.y && d.y < extent[1][1];
});
})

D3.JS - how do I add gridlines to my pie chart

I have extended the pie-chart example at:
with pies that vary in radius depending on a percentage. I would like to add gridlines (circles) every 20 percent, but I can't figure out how.
here is the updated csv:
age,population,percent
<5,2704659,67
5-13,4499890,38
14-17,2159981,91
18-24,3853788,49
25-44,14106543,71
45-64,8819342,88
=65,612463,64
and here is the updated code with pie-parts of different radius:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
background: #333;
}
.arc path {
stroke: #fff;
stroke-width: 2px;
}
.arc grid {
stroke: #fff;
stroke-width: 1;
stroke-dasharray: 5,5;
}
.arc text {
fill:#fff;
font-size:12px;
font-weight:bold;
}
.arc line {
stroke: #fff;
}
</style>
<body>
<script src="d3.js"></script>
<script>
var width = 960,
height = 500,
radius = Math.min(width, height) / 2 - 10;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(function(d) { return 50 + (radius - 50) * d.data.percent / 100; })
.innerRadius(20);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
var grid = d3.svg.area.radial()
.radius(150);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.csv("data.csv", function(error, data) {
data.forEach(function(d) {
d.population = +d.population;
d.percent = d.percent;
});
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.age); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.age; });
});
</script>
First set the number of ticks:
var numTicks = 5; // Each tick is 20%
Then create the data to create the gridlines:
var sdat = [];
for (i=0; i<=numTicks; i++) {
sdat[i] = (radius/numTicks) * i;
}
And then you can use a function to create the radial gridlines, and you can call it from within the d3.csv block:
addCircleAxes = function() {
var circleAxes, i;
svg.selectAll('.circle-ticks').remove();
circleAxes = svg.selectAll('.circle-ticks')
.data(sdat)
.enter().append('svg:g')
.attr("class", "circle-ticks");
// radial tick lines
circleAxes.append("svg:circle")
.attr("r", String)
.attr("class", "circle")
.style("stroke", "#CCC")
.style("opacity", 0.5)
.style("fill", "none");
// Labels for each circle
circleAxes.append("svg:text")
.attr("text-anchor", "center")
.attr("dy", function(d) { return d - 5 })
.style("fill", "#fff")
.text(function(d,i) { return i * (100/numTicks) });
};
An example is here: http://bl.ocks.org/3994129
(Borrowed from: http://kreese.net/blog/2012/08/26/d3-js-creating-a-polar-area-diagram-radial-bar-chart/)

Resources