d3, pie chart, labels outside not working properly - d3.js

I have a problem with my pie chart, as the labels are not showing on the left side. Does anyone know the issue?
http://codepen.io/user1010/pen/BQPvLL
Maybe the problem is in here:
var text=svg.selectAll('.legend')
.data(pie(dataset))
.enter()
.append("text")
.attr('class','legend')
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
.attr("dy", "20px")

You could try the following changes (applied to your original code: http://codepen.io/anon/pen/mOjaYW)
Increase the radius of labelArc
var labelArc = d3.svg.arc()
.outerRadius(radius + 30)
.innerRadius(radius + 30);
Move the main chart svg more to the right
transform: 'translate(' + w/1.5 +',' + h/2 + ')'
Align the label in the middle
.attr("text-anchor", "middle")
Adjust the size of the widget class
width:500px

Related

Mouseover event for barchart in D3

I am having trouble creating a mouseOver event for my D3 visualization for a class. I have a bar chart I created and want to make it so when I mouse over each bar, it displays a small div with the actual values of the bar inside. I have created the barchart I want and am trying integrate a section of code from one of our earlier labs in class, where we added this hover functionality to the barchart visualization but I am just not able to get anything to work.
Here is the code for my index.html with a working graph
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<div style ="float:right; padding-right:300px" id="tooltip"></div>
<script>
// set the dimensions and margins of the graph
var margin = {top: 30, right: 30, bottom: 70, left: 60},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
// Parse the Data
d3.csv("Embiid3pt.csv", function(data) {
// X axis
var x = d3.scaleBand()
.range([ 0, width ])
.domain(data.map(function(d) { return d.player; }))
.padding(0.2);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end");
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 25) + ")")
.style("text-anchor", "middle")
.text("Player Name");
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 0.7])
.range([ height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// text label for the y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Three Point Percentage");
// Bars
svg.selectAll("mybar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return x(d.player); })
.attr("y", function(d) { return y(d.percentage); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.percentage); })
.attr("fill", "#69b3a2")
})
</script>
And here is the CSV data I'm loading in:
player,percentage
Joel Embiid,0.377
Bam Adebayo,0.143
Clint Capela,0
Anthony Davis,0.26
Nikola Vucevic,0.339
Deandre Ayton,0.250
Jarrett Allen,0.189
Kristaps Porzingis,0.353
Finally, here is the section of code that we used earlier in the course to give the mouseover event to the bars of the bar chart:
let bars = chart.append('g')
.selectAll("rect")
.data(data)
.join("rect")
.attr("x", function (d) { return x(d.name); } )
.attr("y", function (d) { return y(d.value); } )
.attr("fill", function(d) { return ordinal(d.name) })
.attr("width", x.bandwidth()) //use the bandwidth returned from our X scale
.attr("height", function(d) { return height - y(+d.value); }) //full height - scaled y length
.style("opacity", 0.75)
bars //let's attach an event listener to points (all svg circles)
.on('mouseover', (event,d) => { //when mouse is over point
d3.select(event.currentTarget) //add a stroke to highlighted point
.style("stroke", "black");
d3.select('#tooltip2') // add text inside the tooltip div
.style('display', 'block') //make it visible
.html(`
<h1 class="tooltip-title">${d.name}</h1>
<div>Highway (HWY) MPG: ${d.value}</div>
`);
})
.on('mouseleave', (event) => { //when mouse isnt over point
d3.select('#tooltip2').style('display', 'none'); // hide tooltip
d3.select(event.currentTarget) //remove the stroke from point
.style("stroke", "none");
});
How do I integrate this final section of code into my index.html to get this mouseover event to work? I already created the tooltip div at the top of the index which will display the values once you mouse over.

D3 Scatterplot legend overlapping

I have a scatterplot that works fine, but the legend I add to it is overlapping the chart. My current approach is to make the chart DIV be 70% of the width and have the legend take up the remaining 30%. For some reason, the legend isn't showing up on the screen, even though the HTML is there.
This is the link to my initial problem: http://jsfiddle.net/chp5a09e/373/
Here is the link to what I'm currently trying: http://jsfiddle.net/chp5a09e/372/
var legend = d3.select("#legend").append("svg")
.attr("width", $("#legend").width())
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
legend.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 12)
.attr("width", 12)
.attr("height", 12)
.style("fill", function(d) {
return color(d);
})
.on("click", function(d) {
d3.selectAll(".symbol").style("opacity", 1)
if (clicked !== d) {
d3.selectAll(".symbol")
.filter(function(e) {
return e.items[columns.indexOf("Channel")] !== d;
})
.style("opacity", 0.1)
clicked = d
} else {
clicked = ""
}
});
legend.append("text")
.attr("x", width - 16)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
HTML is there
Only group (g) elements are there, and they are never visible themselves. In your original code
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
...
legend.append("rect")
the legend here is a selection of multiple g.legend elements, and thus a rect gets appended to each of them, as well as gets access to the datum bound to the parent g. However in you new code
var legend = d3.select("#legend").append("svg")
...
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
legend.selectAll(".legend")
.data(color.domain())
.enter().append("g")
...;
legend.append("rect")
The legend here refers only to the single g element that contains your whole legend. Your legend.selectAll(".legend") isn't saved into variable, so while inside the chain you set class and transform attributes properly, you don't use it to get rect appended to it -- again, legend at that point refers to the outter single g container.
Potential solution:
var legendCnt = d3.select("#legend").append("svg")
.attr("width", $("#legend").width())
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var legend = legendCnt.selectAll(".legend")
.data(color.domain())
...
You'll notice you'll need to remove or lower x attribute for text and rect, since 0 is now at the beginning of the legend instead of chart

d3 pie labels aren't in right place, centroids are way off for some reason

I'm building a d3 pie chart with labels on the centroids just like so many examples. I've checked my code against many of those examples but can't figure out where or how my centroids are being calculated. The labels appear to be arranged around the origin of the svg and not the center of the chart like I'd expect. I feel like there's a grouping issue since the text is added but it's not grouped with the arc. Unfortunately, I have no idea how to fix it.
var arcs = svg.datum(domain)
.selectAll('path')
.data(pie);
arcs.enter()
.append('path')
.attr('fill', function(d, i){
return color[i];
})
.attr('d', arc)
.each(function(d) {
this._current = d;
}) // store the initial angles
.attr('transform', 'translate(' + outerRadius + ", " + outerRadius + ')');
arcs.enter()
.append("text")
.attr("transform", function(d) {
console.log(arc.centroid(d));
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d,i) {
return data[i].playerName+": "+data[i].playerScore;
});
Here's a fiddle with the complete code.
You need to also move the text over to adjust for the radius of the chart:
arcs.enter()
.append("text")
.attr("transform", function(d) {
console.log(arc.centroid(d));
return "translate(" + arc.centroid(d) + ")translate(" + outerRadius + ", " + outerRadius + ")";
})
.attr("text-anchor", "middle")
.text(function(d,i) {
return data[i].playerName+": "+data[i].playerScore;
});
This will shift everything over to match the locations of the arcs' centroids.

Fill the inside of a pie donut chart d3

I am trying to create the image above using d3
http://jsfiddle.net/Spwizard/LBzx7/1/
var dataset = {
hddrives: [20301672, 9408258, 2147483, 21474836, 35622,32210000],
};
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#2DA7E2"]);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 70);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(dataset.hddrives))
.enter().append("path")
.attr("class", "arc")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc);
svg.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("class", "inside")
.text(function(d) { return '56%'; });
svg.append("text")
.attr("dy", "2em")
.style("text-anchor", "middle")
.attr("class", "data")
.text(function(d) { return 'some text'; });
Im struggling to see how to deal with the background color of the inner circle and dealing with the space left for storage
Thanks
To get a "background", you can add another circle with the respective fill colour. To deal with the free space, you can selectively set the opacity of one of the segments to 0. In your example, I've done that for the last slice:
.style("opacity", function(d, i) { return i == dataset.hddrives.length - 1 ? 0 : 1; })
Complete example (provided by OP) here.
Just append text:
svg.append("text")
.attr("text-anchor", "middle")
.attr('font-size', '20px')
.attr('y', '5')
.text(dataset.hddrives + "%");

D3.js donut chart... arc.centroid(d) is not influenced by d.innerRadius and d.outerRadius

I'm creating a donut (or Piechart) and I want to place the labels just outside the area.
I've created a fiddle http://jsfiddle.net/VeeTee/mA3V7/ for it.
arcs.append("svg:text")
.attr("transform", function(d) {
//this is where I want to make a translation to the outside border
d.innerRadius = radius;
d.outerRadius = height/2;
return "translate(" + arc.centroid(d) +")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d, i) { return d.value.toFixed(2); });
arc.centroid(d) -> is always giving the same result (and therefore the same translation)
Not sure what you mean by that it's always giving you the same result, but you can place the labels just outside the chart by multiplying the coordinates of the centroid by 1.5. The code is something like this.
.attr("transform", function(d) {
var c = arc.centroid(d);
return "translate(" + c[0]*1.5 +"," + c[1]*1.5 + ")";
})
Updated jsfiddle here.

Resources