I want to add an icon inside C3.js piechart slices. I want this:
This should add icons using fontawesome
d3.selectAll(".c3-chart-arcs text").each(function(d) {
// your update code here as it was in your example
d3.select(this).append('text')
.attr('font-family', 'FontAwesome')
.attr('font-size', function(d) { return '2em'} )
.text(function(d) { return '\uf118' });
});
More info - Adding FontAwesome icons to a D3 graph
Related
I'm building a number of graphs using crossfilter and dc.js. Among others, there is a row chart and an histogram (a bar chart).
What I am trying to do is to create a tooltip on the row chart which will show the histogram.
Looking at this SO-question I saw an example using d3-tip. I have made an attempt in this jsfiddle. However, I cannot see how to embed a div in the tooltip.
Any suggestion? (If using plain d3 is better, I'm ok with that.)
Snippet of code is:
function draw_row(div_id){ ...; return row_chart; }
function draw_hist(div_id){ ...; return bar_chart; }
var rate_chart = draw_row('#rate').title(function(){return'';});
dc.renderAll();
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function () {
// What to put in here???
draw_hist('#distr').render();
return "<div id='distr'>Distribution<br></div>"
});
d3.selectAll("#rate g.row")
.call(tip)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
Fun project!
Yes, as you noticed, you're not going to be able to render the chart while you're in the .html() callback - that only returns static HTML, and I don't think you can give it an element instead.
So we'll have to find a place to render after the HTML has already been generated. Luckily, d3-tip doesn't try to handle mouse events or anything like that - the code which displays the tip is right there in the code you've posted:
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
So we can wrap tip.show in a function of our own, and then render the chart into the tip once it's on the screen.
We have to watch out because mouseover will fire every time the mouse moves, and we probably don't want to replace the tip-chart until we hover over another bar. So we'll remember the id of the last bar we hovered:
var last_tip = null;
d3.selectAll("#rate g.row")
.call(tip)
.on('mouseover', function(d) {
if(d.key !== last_tip) {
tip.show(d);
draw_hist('#distr').render();
last_tip = d.key;
}
})
.on('mouseout', function(d) {
last_tip = null;
tip.hide(d);
});
Finally, d3-tip needs to know the size of the tip content in order to render in the right place. (If it accidentally renders on top of the element, this can cause horrible flickering when the mouse goes over the tip, registering mouseout on the element.)
So we'll just hard-code that, since we're hard-coding the chart size anyway. 20 extra pixels to fit the title:
.html(function (d) {
return "<div id='distr' style='min-width:300px; min-height: 320px'>Distribution<br></div>"
});
Looks pretty cool with the default translucent black style from d3-tip:
Here's the fork of your fiddle: https://jsfiddle.net/gordonwoodhull/hkx7j3r5/10/
Is there a way to put legend outside the chart area? I'm trying with bindto property, but it doesn't work (plunker)
Here is the example for amcharts
In C3 documentation there are only few positions allowed: top-left
top-right bottom-left bottom-right
You could always just re-parent it yourself:
setTimeout(function(){
var legend = d3.selectAll('.c3-legend-item');
var svg = d3.select('#legend')
.append('svg')
.attr('width', 640)
.attr('height', 100);
legend.each(function(){
svg.node().appendChild(this);
});
}, 100);
Updated plunker.
You can probably solve this using:
legend: {
show: false
}
And then adding a custom legend via C3 or D3
See http://c3js.org/samples/legend_custom.html for a working example
I want to add an image in nvd3 piechart legend instead of text.
I have this code but it changes only the text in the tooltip with the image. I want to change and the text in the legend with the same image.
var testdata = [
{
key: "<img src="+"./imgs/facebook.jpg"+">",
y: 1
},
{
key: ""<img src="+"./imgs/twitter.jpg"+">"",
y: 2
} ];
nv.addGraph(function() {
var chart = nv.models.pieChart()
.x(function(d) { return d.key})
.labelThreshold(.08)
.showLabels(true)
.color(d3.scale.category10().range())
.donut(true);
chart.pie.donutLabelsOutside(true).donut(true);
d3.select("#mypiechart")
.datum(testdata)
.transition().duration(1200)
.call(chart);
return chart;});
Any ideas?
This is not currently possible with nvd3.js. The tooltip works because the img element you have specified is being set into a div that isn't contained within the svg element. It doesn't work for the legend or chart labels because those are built using svg text elements. In order to show an image within the chart svg we'd need to use an svg image element.
We could build the svg image elements if we hack the nvd3.js code. Here's an outline of what you could do to get the legend working. You could then decide if you'd want to try something similar in the nv.models.pie code for the chart labels or if you'd just want to set chart.showLabels to false in your chart configuration.
Add a new key in your data to provide the image path:
var testdata = [
{
key: "<img src="+"./imgs/facebook.jpg"+">",
y: 1,
image_path: "./imgs/facebook.jpg"
},
{
key: "<img src="+"./imgs/twitter.jpg"+">",
y: 2,
image_path: "./imgs/twitter.jpg"
} ];
Update the nv.models.legend code to show the image:
seriesEnter.append('circle')
.style('stroke-width', 2)
.attr('class','nv-legend-symbol')
.attr('r', 5);
// Add an svg image into the legend
seriesEnter.append('image')
.attr('xlink:href', function(d) { return d.image_path} )
.attr('height',20)
.attr('width',20)
.attr('y', '-10')
.attr('x', '8');
Update the nv.models.legend code to not show the key:
// Don't add the key value into the legend text
//series.select('text').text(getKey);
Update the nv.models.legend code to consider the image width when determining the legend layout:
//seriesWidths.push(nodeTextLength + 28); // 28 is ~ the width of the circle plus some padding
//Include image width...
seriesWidths.push(nodeTextLength + 48);
I have chart type switcher (pieChart/lineChart), date selector and one svg element.
Changing the chart type or date triggers ajax request (different urls with different response data structure for pie/line) and then redraw my chart. Like this:
buildReport: function (data) {
var that = this;
// Format incoming data to chart's format
this.structure = this.formatters[this.type].call(this, data);
this.svgElem.empty(); // jQuery empty
nv.addGraph(function () {
var chart = nv.models[that.type](); // that.type - pieChart or lineChart
chart.options(that.options[that.type] || {});
d3.select(that.svgElem)
.datum(that.structure)
.transition()
.duration(500)
.call(chart);
return chart;
});
}
This function is called on chart type or date change (ajax request may cache for some conditions). Is it right to use svgElem.empty()? Or there is another way to destruct chart and draw another one?
And some additional questions:
1) How to draw legend in the center bottom of chart? Are there any options for this?
2) How to draw stacked area chart in "Expanded" state by default? I need to hide controls (showContols: false option) and draw expanded stackedArea chart
Thank you
To draw the chart with a style that is not the stacked you can use
chart.style("expand"); // for expanded
chart.style("stream"); // for stream
I am using nvd3 to draw a simple line chart with data receiving via an ajax request. It is working perfectly with the first drawing request but not on redrawing. The chart redraws by calling the same drawing function but with different data + differen max/min values.
When redrawing the chart with new data the "hover circle" does not appear, whereas the tooltip does. Furthermore when clicking on the legend of the chart and force a redraw by that the hover appears again, but the values of the yAxis are changed to these of the first drawn chart.
So far I assume that when redrawing the chart still holds the old max/min values - but only concerning the "hover" effect. The general chart looks fine so far also on redraw - the problem just faces the hover and that's it.
Sounds pretty confusing, but hopefully you will get the point.
Some code:
d3.json(queryurl, function(data2){
nv.addGraph(function(jsonData) {
if(chart){
chart.remove();
}
chart = nv.models.lineChart()
.x(function(d) { return d[0] })
.y(function(d) { return d[1] })
.color(d3.scale.category10().range());
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%x')(new Date(d))
});
chart.yAxis
.scale()
.tickFormat(d3.format(''));
chart.lines.yDomain([maxmin.max,maxmin.min]);
d3.select('#chart1 #chartsvg')
.datum(data2)
.transition().duration(600)
.call(chart);
nv.utils.windowResize(chart.update);
});
});
return chart;}
Try using .empty() on the svg element before redrawing.
I've only just started with NVD3 and D3 myself, however am doing a similar thing. What worked for me is to separate the data update function with the chart creation function. Do note the caveat below though...
I have the following to create the graph:
initGraph = function(url) {
d3.json(url, function(data) {
nv.addGraph(function() {
chart = nv.models.multiBarChart();
d3.select('#chart svg').datum(data).transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
});
};
And the following function to update it:
redrawGraph = function(url) {
d3.json(url, function(data) {
d3.select('#chart svg').datum(data).transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
});
};
I don't know if this is the recommended solution as I'm still at the "hack until it works" stage. With this, all the functions of the chart work after invocation of redrawGraph() (including axes redraw and tooltips).
Caveat: this seems to occasionally result in miscalculated ticks on recalculation: