I am trying to label my animated Donut using D3...Can change data and get the drop-down to work but am having trouble labeling the donuts....Here is my code
var radius = 74,
padding = 10;
var color = d3.scale.ordinal()
.range(["#aec7e8", "#ff7f0e", "#2ca02c", "#bdbdbd", "#8c564b", "#d62728", "#17becf", "#7f7f7f", "#bcbd22", "#393b79","#31a354"]);
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - 50);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
d.DataUnitID = color.domain().map(function(name) {
return {name: name, population: +d[name]};
});
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", radius * 2)
.attr("height", radius * 3.5)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 16)
.style("fill", color);
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
var svg = d3.select("body").selectAll(".pie")
.data(data)
.enter().append("svg")
.attr("class", "pie")
.attr("width", radius * 2)
.attr("height", radius * 2)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
svg.selectAll(".arc")
.data(function(d) { return pie(d.DataUnitID); })
.enter().append("path")
.attr("class", "arc")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); });
svg.append("text")
.attr("dy", ".15em")
.style("text-anchor", "middle")
.text(function(d) { return d.State; });
});
Related
I start with d3 using version 3.
I created a grouped bar chart that looks like this:
I added a drop-down list that contains all the genders (Woman (femme), Couple...).
I'm blocked because I can't highlight the bars that correspond to a certain gender when I choose from the drop-down list.
I found on the internet that I could use this code:
d3.select('#inds')
.on("change", function () {
var sect = document.getElementById("inds");
var section = sect.options[sect.selectedIndex].value;
//some code here
});
the "Section" part contains my selected gender
I would like that when I select, for example torque, that the bars corresponding to the torque remain in color and the others are in gray.
This is the all code :
source : https://bl.ocks.org/hydrosquall/7966e9c8e8414ffcd8b5
Highlight : https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 600,
height = 250 ;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#54E868", "#54CCE8", "#6395FF", "#50FFC5"]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
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 + ")");
d3.csv("../CSV/GenreOrigine.csv", function(error, data) {
if (error) throw error;
//Couples, Famille...
var genreNames = d3.keys(data[0]).filter(function(key) { return key !== "Origine"; });
data.forEach(function(d) {
//Valeur pour chacune des origines par rapport au genre
d.genres = genreNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Origine; }));
x1.domain(genreNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.genres, function(d) { return d.value; }); })]);
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", 0.3)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Nombre de personnes");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var Origine = svg.selectAll(".Origine")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Origine) + ",0)"; });
Origine.selectAll("rect")
.data(function(d) { return d.genres; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.style("fill", function(d) { return color(d.name) })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.name)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.name));
});
Origine.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
var legend = svg.selectAll(".legend")
.data(genreNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
d3.select('#inds')
.on("change", function () {
var sect = document.getElementById("inds");
var section = sect.options[sect.selectedIndex].value;
//some code here
});
});
You can select all the rect in the SVG and filter them in the function, so the code would look like:
var sect = document.getElementById('inds');
var section = sect.options[sect.selectedIndex].value;
if(section !== '') {
d3.selectAll("rect")
.attr('opacity', function(d) {
if(d.name !== section) {
return 0.2;
} else {
return 1;
}
})
} else {
d3.selectAll('rect')
.attr('opacity', 1)
}
In the example above, I changed the opacity attribute, but you can change fill with the same approach.
I'm relatively new to D3 and trying to add labels to a grouped bar chart.. With below implementation, I'm only able to see them under one group instead of both.
Below is how the data looks in the db:
category,Exceed,Fully Meets,Partially Meets,Does not meet
business,10,20,30,30
leadership,15,5,30,50
Below is the code:
var chart1 = d3.select("#svgarea2"),
margin = { top: 70, right: 0, bottom: 30, left: 40 },
width = +chart1.attr("width") - margin.left - margin.right,
height = +chart1.attr("height") - margin.top - margin.bottom,
g = chart1.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//chart background color
var bg = d3.select("g").append("svg")
// .attr("width", width + margin.right + margin.left)
.attr("width", 510 + "px")
// .attr("height", height + margin.top + margin.bottom);
.attr("height", 310 + "px");
bg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "#f8f8ff");
bg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//scale chart
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.4);
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal() //d3.schemeCategory20
// .range(["#0000ff", "#dcdcdc", "#696969", "#00008b"]);
.range(["#00008b", "#696969", "#dcdcdc", "#0000ff"]);
var columns = ['category', 'Does Not Meet', 'Partially Meets', 'Fully Meets', 'Exceed'];
var keys = columns.slice(1);
var color = d3.scaleOrdinal()
.range(["#00008b", "#696969", "#dcdcdc", "#0000ff"]);
x0.domain(data.map(function (d) { return d.category; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, 100]).nice();
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function (d) { return "translate(" + x0(d.category) + ",0)"; })
.selectAll("rect")
.data(function (d) { return keys.map(function (key) { return { key: key, value: d[key] }; }); })
.enter().append("rect")
.attr("x", function (d) { return x1(d.key); })
.attr("y", function (d) {
return y(d.value);
})
.attr("width", x1.bandwidth() - 7)
.attr("height", function (d) { return height - y(d.value); })
.attr("fill", function (d) { return z(d.key); })
.on("mousemove", function(d){
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html((d.key) + "<br>" + (d.value) + "%");
});
g.append("g")
.selectAll("g")
.data(data).enter()
.append("g")
.attr("transform", function (d) { return keys })
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.attr("x", function (d) { return x0(d.category); })
.call(d3.axisBottom(x1))
.selectAll("text")
.attr("y", 15)
.attr("x", 0)
.attr("dy", ".35em")
.attr("transform", "rotate(50)")
.style("text-anchor", "start");;
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) - 5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Employees (%)");
//chart title
g.append("text")
.attr("x", (width / 2) + 30)
.attr("y", 1 - (margin.top / 2) + 20)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-weight", "bold")
.style("text-decoration", "underline")
.attr("font-family", "sans-serif")
.text("Performance Distribution");
};
Any help is appreciated!
It will work if you create a g element for each barchart, and then add you axes, bars etc to each. See here for example:
http://blockbuilder.org/tomshanley/aa5471a3ecaf9e41283d68188aecf042
Relevant code:
var chart = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g");
chart.attr("transform", function (d) { return "translate(" + x0(d.category) + ",0)"; })
.selectAll("rect")
.data(function (d) { return keys.map(function (key) { return { key: key, value: d[key] }; }); })
.enter().append("rect")
.attr("x", function (d) { return x1(d.key); })
.attr("y", function (d) {
return y(d.value);
})
.attr("width", x1.bandwidth() - 7)
.attr("height", function (d) { return height - y(d.value); })
.attr("fill", function (d) { return z(d.key); })
//APPEND AN AXIS TO THE CHART G'S
chart.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.attr("x", function (d) { return x0(d.category); })
.call(d3.axisBottom(x1))
.selectAll("text")
.attr("y", 15)
.attr("x", 0)
.attr("dy", ".35em")
.attr("transform", "rotate(50)")
.style("text-anchor", "start");
I've been drawing a planetarium with planets and labels on it :
svg.append("circle").attr("r", 20).attr("cx", w/2)
.attr("cy", h/2).attr("class", "sun");
svg.append("text").attr("x", w/2)
.attr("y", h/2).attr("text-anchor", "middle").attr("fill", "grey").text("VICTOR HUGO");
var container = svg.append("g")
.attr("transform", "translate(" + w/2 + "," + h/2 + ")")
var orbit = container.selectAll(".orbit")
.data(dataset, key)
.enter()
.append("circle")
.attr("class", "orbit")
.attr("r", 0);
//function(d) {return (d.key * ((w/2)/151) + 20); });
var planets = container.selectAll(".planet")
.data(dataset, key)
.enter()
.append("circle")
.attr("class", "planet")
.attr("r", function(d) {return 5;})
.attr("cx", function(d) {return d.x; })
.attr("cy", function(d) {return d.y; })
.on("mouseover", function(d, i) {
stopTooltip = false
showTooltip(d);
});
d3.select("svg").on("click", function(d) {stopTooltip = true;});
var texts = container.selectAll(".text")
.data(dataset, key)
.enter()
.append("text")
.attr("class", "text")
.attr("dx", function(d) {return d.x;})
.attr("dy", ".35em")
.text(function(d) {return d.titre});
Then, I've been adding a legend and a click function, so that when you click on one or another of the legend subtitle, the dataset is updated (if you look at the tooltips, it is indeed updated).
////////////////////////Legend//////////////////////////////////
update = function(newDataset, key){
var newplanets = container.selectAll(".planet").data(newDataset, key);
newplanets.enter().append("circle");
newplanets.transition()
.duration(0)
.attr("r", function(d) {return 5;})
.attr("cx", function(d) {return d.x; })
.attr("cy", function(d) {return d.y; })
.on("mouseover", function(d, i) {
stopTooltip = false
showTooltip(d);
});
newplanets.exit().remove();
var newtexts = container.selectAll(".text").data(newDataset, key);
newtexts.enter().append("text");
newtexts.transition()
.duration(0)
.attr("class", "text")
.attr("dx", function(d) {return d.x;})
.attr("dy", ".35em")
.text(function(d) {return d.titre});
newtexts.exit().remove();
}
var legendRectSize = 7;
var legendSpacing = 20;
var VictorHugoClass = [
{ "VHClassName": '...Auteur (oeuvre)', "VHClass": r990o, "color": "#0000FF" },
{ "VHClassName": '...Auteur (expression)', "VHClass": r990e, "color": "#FBA10D" },
];
//Initiate container around Legend
var legendContainer = svg.append("g").attr("class","legendContainer")
.attr("transform", "translate(" + 30 + "," + (h - 90) + ")");
//Create title of Legend
var legendTitle = legendContainer.append('text')
.attr('x', 0)
.attr('y', legendRectSize - legendSpacing)
.attr("dy", "1em")
.attr('class', 'legendTitle')
.attr('transform', function() {
var height = legendRectSize + legendSpacing;
var offset = height * VictorHugoClass.length / 2;
var horz = -2 * legendRectSize;
var vert = -2.3 * height - offset;
return 'translate(' + horz + ',' + vert + ')';
})
.attr("fill", "black")
.text("Victor Hugo en tant que...")
.call(wrap, 200);
//Create container per circle/text pair
var legend = legendContainer
.selectAll('.legend')
.data(VictorHugoClass)
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * VictorHugoClass.length / 2;
var horz = -2 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
})
.on("click", function(d) {
update(d.VHClass, d.key);
});
//Append circles to Legend
legend.append('circle')
.attr('r', legendRectSize)
.attr('cx', 4)
.attr('cy', legendRectSize/2 - legendSpacing)
.attr("opacity", 1)
.style("fill", "white")
.style('stroke', function(d) {return d.color;});
//Append text to Legend
legend.append('text')
.attr('x', legendRectSize + legendSpacing/2)
.attr('y', legendRectSize - legendSpacing)
.attr("fill", "black")
.text(function(d) { return d.VHClassName; });
Nevertheless, the labels won't update with the planets, and some of the data of the former dataset are still remaining when the second dataset is loaded into the visualization. My Plunker is here for more details : http://plnkr.co/edit/UnhqCZhME7ymxdIbpcIj?p=preview
Thank you very much for your help in this...
Another d3 newbie question here.
I am trying to transition change a donut chart with grouped nested data. Here's what I have now.
http://bricbracs.com/test/
So when I click on a segment arc like New York it will update with data from the dept column with a nested function so I get this. I am close. I have the data grouped. I need help redrawing the donut.
http://bricbracs.com/test1/
Here is a csv file.
status,dept,city,points
temp,finance,New York,33
contract,HR,London,12
contract,HR,New York,11
casual,shop,London,43
contract,shop,Paris,51
temp,finance,London,7
contract,office,New York,61
contract,shop,London,31
temp,office,New York,16
contract,office,London,19
temp,finance,London,7
contract,office,New York,61
contract,sales,London,31
temp,finance,New York,16
contract,sales,Paris,19
Here is the d3 script. Thanks in advance.
<script>
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.values;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.style("cursor","pointer")
d3.csv("data.csv", function(error, data) {
var data = d3.nest()
.key(function(d) {
return d.city;
})
.rollup(function(d) {
return d3.sum(d, function(g) {
return g.points;
});
}).entries(data);
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.attr("stroke", "white")
.attr("stroke-width", 0.5)
.style("fill", function(d) {
return color(d.data.key);
})
.on("mouseover", function (d) {
d3.select("#tooltip")
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY + "px")
.style("opacity", .75)
.select("#value")
.text(d.value.toLocaleString())
document.getElementById("demo").innerHTML =d.data.key
})
.on("mouseout", function () {
d3.select("#tooltip")
.style("opacity", 0);
console.log("OUT")
})
.on("mousemove", function () {
d3.select("#tooltip")
.style("left", (d3.event.pageX +20) + "px")
.style("top", d3.event.pageY + "px+50")
})
.on("click", function() {
change()
});
g.append("text")
.attr("transform", function(d) {
var c = arc.centroid(d),
x = c[0],
y = c[1],l
h = Math.sqrt(x*x + y*y);
return "translate(" + arc.centroid(d) + ")";
})
//.attr("dy", "1em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data.key
})
function change() {
var data = d3.nest()
.key(function(d) {
return d.dept;
})
.rollup(function(d) {
return d3.sum(d, function(g) {
return g.points;
});
}).entries(data);
var path = svg.selectAll("path");
path = path.data(pie(data), function(d) { return d.data.key; })
path.enter().append("path").attr("fill", function(d) {return color(d.data.key); })
path.exit().remove()
path.attr("d", arc)
}
});
I'm a d3 novice trying to create a simple, two-series bar chart that transitions when different buttons are clicked. The original chart is constructed:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#d4d4d4", "#58bd5b",]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("div.d3space").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("/assets/data/data3.csv", function(error, data) {
var hourBuckets = d3.keys(data[0]).filter(function(key) { return key !== "Client"; });
data.forEach(function(d) {
d.hours = hourBuckets.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Client; }));
x1.domain(hourBuckets).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.hours, function(d) { return d.value; }); })]);
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("Hours");
var client = svg.selectAll(".client")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Client) + ",0)"; });
client.selectAll("rect")
.data(function(d) { return d.hours; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(hourBuckets.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
The csv being accessed is in the following format:
Client,Planned,Actual
ICC,25,50
RNR,50,47.5
MB,10,2.5
This chart renders as desired. The piece I am struggling with is getting this graph to transition to reflect different data when a link is clicked (link has id="fourweeks"). I have tried this onclick function:
window.onload = function() {
var a = document.getElementById("fourweeks");
var b = document.getElementById("eightweeks");
var c = document.getElementById("twelveweeks");
a.onclick = function() {
d3.csv("/assets/data/data1.csv", function(error, data) {
var hourBuckets = d3.keys(data[0]).filter(function(key) { return key !== "Client"; });
data.forEach(function(d) {
d.hours = hourBuckets.map(function(name) { return {name: name, value: +d[name]}; });
});
var client = svg.selectAll(".client")
client.selectAll("rect")
.data(function(d) { return d.hours; })
.transition()
.duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
});
}
}
...no dice. I can get this to work when creating / transitioning simple one-series bar charts that use list inputs, but not the multi-series csv ones. data2.csv is the exact same file as data1.csv, with the values adjusted slightly.
Thanks for your time reading - any advice?
First svg.selectAll(".client") returns an empty selection, because you gave these elements the class 'g' instead of 'client'.
Secondly you need to update the data of the .client-elements:
var client = svg.selectAll(".client")
.data(data);
btw. you should use selection.classed() instead of selection.attr('class')