Here is my plunkr code, in this position of the legend is at center, how do I position it to the right of the donut chart?
var svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2 - radius) +
',' + (height / 2 - radius) + ')');
I tried with the above code but it's not working.
Define new variable and set your legend width in pixels:
var legendWidth = 150;
Use this variable for svg element width:
var svg = d3.select('#chart')
.append('svg')
.attr('width', width + legendWidth) // <== !!!
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
Rewrite your function for legend transform attribute this way:
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var vert = i * height - offset;
return 'translate(' + width / 2 + ',' + vert + ')'; // <== move every legend item on half of width
});
Check working example here.
Checkout this version of your jsfiddle. I have corrected some calculation to show legend on right side. highlighted code change below.
// For increasing horizontal space
var width = 600;
// For arranging chart in full space
var svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 4) +
',' + (height / 2) + ')');
// For moving legend to right
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = 7 * legendRectSize;
Related
In the following code, I would like to put the donut legends outside the donut, on its right:
http://bl.ocks.org/juan-cb/1984c7f2b446fffeedde
Which line code should I change to do it?
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = -3 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d; });
Something similar to this one:
http://www.jqueryscript.net/demo/jQuery-Plugin-To-Convert-Tabular-Data-Into-Donut-Charts-Chart-js/
Edit: the solution was just a matter of changing horizontal and vertical coordinates. No need of complicated stuff.
These 2 variables:
var horz = -3 * legendRectSize; // X-axis translation.
var vert = i * height - offset; // Y-axis translation.
You could modify horz and vert formulas for translating. Like this:
var horz = 30 * legendRectSize; // Will be right side of the donut chart.
var vert = i * height - offset; // Still at the middle of the donut chart vertically.
I have this donut, and I want to make everything small, want to make a size of 70px - 70px, but do not know how to apply a scale. I have also the problem that the label "svg" also has great measures. altogether I want to have a square of 70px - 70px with the donut.
var dataset = {
datos: [(50), (50)],
};
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#38C956", "#000000"]);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);
var svg = d3.select("#donut").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.datos))
.enter().append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
http://jsfiddle.net/o11ce/7qoj49br/
the svg is 460 * 300 ... i need 70px x 70px
it would be possible that the edge is not too thick?
You want to scale, can you not just adjust the outer and inner radius ?
Updated fiddle : http://jsfiddle.net/reko91/7qoj49br/1/
Here I have created some variables :
var donutWidth = 10;
var outerRadius = 70;
And used these for both radius':
var arc = d3.svg.arc()
.innerRadius(outerRadius - donutWidth)
.outerRadius(outerRadius);
Just adjust the outerRadius for the size of the donut, and the donutWidth for the width of the donut.
var dataset = {
datos: [(50), (50)],
};
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var donutWidth = 10;
var outerRadius = 70;
var color = d3.scale.ordinal()
.range(["#38C956", "#000000"]);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(outerRadius - donutWidth)
.outerRadius(outerRadius);
var svg = d3.select("#donut").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.datos))
.enter().append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
<div id='donut'>
</div>
</body>
Just scale the group element 0.35 (70/200 where 200 is the current size).
var svg = d3.select("#donut").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ") scale(0.35)")
var dataset = {
datos: [(50), (50)],
};
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#38C956", "#000000"]);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);
var svg = d3.select("#donut").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ") scale(0.35)")
var path = svg.selectAll("path")
.data(pie(dataset.datos))
.enter().append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
<div id='donut'>
</div>
</body>
I'm trying to create a draggable element in D3 that doesn't drag beyond the page.
Like in this example:
http://jsfiddle.net/Lhwpff2r/1/
var margin = {top: 20, right: 10, bottom: 20, left: 10};
var width = 600;
var height = 600;
var zoom = d3.behavior.zoom()
.scaleExtent([0.5, 10])
.on("zoom", zoomed);
var svg = d3.select('body').append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height)
.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.right + ")")
.call(zoom);
rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
svg.append("g")
.append("rect")
.attr("class", "rectangle")
.attr('width', width)
.attr('height', height);
function zoomed() {
console.log('1: ', zoom.translate());
if (zoom.translate()[0] > 0) {
//zoom.translate([0, zoom.translate()[1]]);
//console.log('2: ', zoom.translate());
}
svg.attr("transform", "translate(" + zoom.translate() + ")scale(" + zoom.scale() + ")");
}
In other words, I don't want to see the background behind the red rectangle.
For this I'm checking whether the zoom.translate()[0] is bigger than 0 and manually setting it to 0.
This works fine, but the problem is that when I drag to the left again the zoom.translate() x value is still the original one, not the one that I manually set (unless I release the mouse click before dragging to the left).
Does anyone know how I make the zoom.translate value persist?
Thanks.
I'm drawing a simple bar chart with d3, and I have an x axis setup and have the brush setup so that I can brush to "select" a group of the bars. I would like to then drill down and scale the chart to contain only these bars. The following code works and the colors of the bars inside and touched by the brush turn the right color, but I can't make the thing zoom.
I have looked at this: http://bl.ocks.org/mbostock/1667367 and a bunch of other stuff and just cannot figure it out.
Here is a fiddle, can someone show me how to simply zoom the darn thing?
var brush;
function go3()
{
var dataset = [];
var m = 40;
var count = 500;
dataset.push(m);
for (var i = 0; i < 150; i++) { //Loop 25 times
var newNumber = Math.random() * m; //New random number (0-30)
dataset.push(newNumber); //Add new number to array
}
margin = {top: 10, right: 10, bottom: 30, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom
w = width;
h = height;
yScale = d3.scale.linear().domain([0, d3.max(dataset)]).range([0, h * .95]);
xScale = d3.scale.linear()
.range([0, w])
.domain([0, w]);
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
console.log("Max: " + d3.max(dataset));
svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
;
svg.append("rect")
.attr("stroke", "grey")
.attr("stroke-width", 1)
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.attr("fill", "#FFFFFF")
.classed("main-container", true);
svg.append("rect")
.attr("width", w)
.attr("height", h)
.attr("stroke", "grey")
.attr("stroke-width", 0)
.attr("fill", "#EEFFEE")
.attr("x", margin.left)
.attr("y", margin.top)
.classed("brushable-container", true)
;
xAxisGroup = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + (h + margin.top) + ")")
.call(xAxis)
padding=2;
rects = svg.append("g").selectAll(".brushable")
.data(dataset)
.enter()
.append("rect")
.classed("brushable", true);
brush = d3.svg.brush()
.x(xScale)
.on("brush", brushmove)
.on("brushend", brushend);
context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "brush")
.call(brush)
.selectAll('rect')
.attr('height', h);
barWidth = w / dataset.length;
console.log("Width: " + barWidth);
rects
.attr("width", 4)
.attr("height", function(d, i){ return yScale(d)})
.attr("stroke", "yellow")
.attr("stroke-width", .3)
.attr("x", function(d, i){ return (i * barWidth + margin.left) })
.attr("y", function(d, i){ return h - yScale(d) + margin.top} )
;
}
function brushend(){
var extent = brush.extent();
var min = extent[0] >= extent[1] ? extent[1] : extent[0];
var max = extent[0] >= extent[1] ? extent[0] : extent[1];
var lolobb = d3.selectAll("rect.brushable");
var lob = lolobb[0];
console.log(min + " - " + max);
var i = 0;
while( i < lob.length ){
var bbb = lob[i];
try {
var p = parseFloat(bbb.attributes.x.value);
if(min <= p && max >= p) {
d3.select(bbb).attr("fill", "#00FF00");
} else {
d3.select(bbb).attr("fill", "#000000");
}
i++;
} catch(r) {
console.log("BBB");
console.log(bbb);
console.log("Error with " + i);
console.log(typeof(bbb));
console.log(r);
}
}
console.log(min + " - " + max);
console.log(lolobb);
}
function brushmove() {
var extent = brush.extent();
}
You can do this by adding calling a zoom function on "body", you can do so by doing:
svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
;
The zooming part is:
.append("g")
.call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
Then add the zoom() function:
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
Here is a Fiddle Example. Use the scroll wheel to zoom.
I'm trying to build a basic, responsive bar chart with data loaded from a CSV. My chart isn't reading the CSV and gives me NaNs. Here's a screenshot of what my chart looks like. Here is my current code:
<script type="text/javascript">
var url = "data/data.csv",
margin = {top: 30, right: 10, bottom: 30, left: 10},
width = parseInt(d3.select('#chart').style('width'), 10),
width = width - margin.left - margin.right,
height = 200, // placeholder
barHeight = 20,
spacing = 5,
total = d3.format('n');
// scales and axes
var x = d3.scale.linear()
.range([0, width])
.domain([0, 4000]); // hard-coding this because I know the data
var y = d3.scale.ordinal();
var xAxis = d3.svg.axis()
.scale(x)
.ticks(3);
// create the chart
var chart = d3.select('#chart').append('svg')
.style('width', (width + margin.left + margin.right) + 'px')
.append('g')
.attr('transform', 'translate(' + [margin.left, margin.top] + ')');
d3.csv(url).row(function(d) {
d.total = +d.total;
d.name = +d.name;
return d;
}).get(function(err, data) {
// sort
data = _.sortBy(data, 'total').reverse();
// set y domain
y.domain(d3.range(data.length))
.rangeBands([0, data.length * barHeight]);
// set height based on data
height = y.rangeExtent()[1];
d3.select(chart.node().parentNode)
.style('height', (height + margin.top + margin.bottom) + 'px')
// render the chart
// add top and bottom axes
chart.append('g')
.attr('class', 'x axis top')
.call(xAxis.orient('top'));
chart.append('g')
.attr('class', 'x axis bottom')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis.orient('bottom'));
var bars = chart.selectAll('.bar')
.data(data)
.enter().append('g')
.attr('class', 'bar')
.attr('transform', function(d, i) { return 'translate(0,' + y(i) + ')'; });
bars.append('rect')
.attr('class', 'background')
.attr('height', y.rangeBand())
.attr('width', width);
bars.append('rect')
.attr('class', 'total')
.attr('height', y.rangeBand())
.attr('width', function(d) { return x(d.total); })
bars.append('text')
.text(function(d) { return d.name; })
.attr('class', 'name')
.attr('y', y.rangeBand() - 5)
.attr('x', spacing);
bars.append('line')
.attr('class', 'median')
.attr('x1', x(median))
.attr('x2', x(median))
.attr('y1', 1)
.attr('y2', y.rangeBand() - 1);
});
// resize
d3.select(window).on('resize', resize);
function resize() {
// update width
width = parseInt(d3.select('#chart').style('width'), 10);
width = width - margin.left - margin.right;
// resize the chart
x.range([0, width]);
d3.select(chart.node().parentNode)
.style('height', (y.rangeExtent()[1] + margin.top + margin.bottom) + 'px')
.style('width', (width + margin.left + margin.right) + 'px');
chart.selectAll('rect.background')
.attr('width', width);
chart.selectAll('rect.total')
.attr('width', function(d) { return x(d.total); });
// update axes
chart.select('.x.axis.top').call(xAxis.orient('top'));
chart.select('.x.axis.bottom').call(xAxis.orient('bottom'));
}
// highlight code blocks
hljs.initHighlighting();
</script>
Here's the data:
name, total
Brian, 1514
Frankie, 1439
Jeffery, 1615
Jerry, 685
Kenneth, 3233
Michael, 116
Roy, 817
Timothy, 2184
I can see a couple of issues with your code and data:
For the issues with the data, you can either clean up the rows by removing the spaces, eg:
name,total
Brian,1514
Frankie,1439
Jeffery,1615
Jerry,685
Kenneth,3233
Michael,116
Roy,817
Timothy,2184
Or you can reference them in your code using the [] notation, eg:
d[' total'] = +d[' total'];
d3 is adding the leading space to the attribute name for each record. When you reference the attributes as d.total, it's returning undefined and undefined coerced to a number is NaN.
Eg. data[0]['total'] is undefined but data[0][' total'] has "1514" as you'd expect. Removing the spaces from the data is the easiest way of dealing with this.
In your code, you're doing this:
d.name = +d.name;
This will translate the name value into NaN, as it's a text string beginning with an alpha character, so javascript doesn't know how to coerce it to a number.
You're also referencing a variable named median which isn't declared in your code.
I've created a jsfiddle to help: http://jsfiddle.net/BenLyall/eh1r6j2e/12/
Note: I've changed the d3.csv... line to work with an element in the DOM to store the data.
Why are you making name into Number
below code
d3.csv(url).row(function(d) {
d.total = +d.total;
d.name = +d.name;
return d;
})
remove conversion of name.Okay