nvd3.js how to properly redraw chart - d3.js

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

Related

Disable resize of brush on range chart from focus charts (dc.js, d3.js) - Solved

Please note - there is a solution for part of this problem by Gordon Woodhull in Disable brush on range chart before selecting a scale from the dropdown/on page load(dc.js,d3.js)
In addition,there is a partial solution at the end this question.
Furthermore there are two fiddles:
1) https://jsfiddle.net/dani2011/4dsjjdan/1/
2) https://jsfiddle.net/dani2011/uzg48yk7/1/ (with partial solution)
Need to disable resize of the brush on range chart (timeSlider) by dragging the line within the focus charts (bitChart,bitChart2). As Gordon Woodhull suggested (Disable brush resize (DC.js, D3.js) try to enable only pan without zoom.
Current behavior:
1)
Dragging the line on bitChart2 (focus chart) pans the brush until the borders of the timeChart. Once reaching the borders,the brush shrinks. The other focus chart (bitChart) resizes the brush of the range chart during drag of its line.
2)
When selecting a span for the brush from the dropdown only the .on('zoomed', function (chart, filter) { of bitChart2 is loaded and not the .on("zoomed"... of bitChart.
Print screens from the console:
a) Selecting scale from the dropdown
b) Dragging line on bitChart:
c) Dragging line on bitChart2:
3)
For both bitChart and bitChart2 the value of scale is 1 and the position
is 0,0:
.on('zoomed', function (chart, filter) {
//var zoom = d3.behavior.zoom()
// .translate([0, 0])
//.scale(1).scaleExtent([1, 1])
var zoom = d3.behavior.zoom()
var scale = zoom.scale(); var position = zoom.translate();
js file
The following implementations did not solve the issue:
**option 1**
bitChart.on('zoomed', function (chart, filter) {
d3.behavior.zoom().on("zoom", null);//doesn't stop zoom
//event needs svg element(tried different options),doesn't work
d3.behavior.zoom().scale(1).scaleExtent([1,1]).translate([0,0]).event(chart.select('g.stack_0')))
**option 2**
//Applied on timeslider,bitChart,bitChart2 to eliminate zoom and
//maintain pan
.zoomScale([1, 1])//dc.js
//And also
.on('zoomed', function (chart, filter) {
bitChart.zoomScale([1, 1]);
//Nothing pans with
chart.focus(chart.xOriginalDomain())
**option 3**
bitChart.on('zoomed', function (chart, filter) {
var svg = d3.select("body")
.append("svg")
.call(d3.behavior.zoom().on("zoom", function () {
svg.attr("transform", "translate(" + d3.event.translate + ")" +"
scale(" + 1 + ")")
}))
//.append("g")
**option 4**
.on('zoomed', function (chart, filter) {
var chartBody = chart.select('g.stack _0');
var path = chartBody.selectAll('path.line').data([extra_data]);
path.enter().append('path').attr({
class: 'line',
});
path.attr('transform', 'translate(0,50)');
**option 5**
bitChart.on('zoomed', function (chart, filter) {
var zoom = d3.behavior.zoom()
.scaleExtent([1, 1])
chart.select('g.stack _0').call(zoom);
zoom.scale(1);
zoom.event(chart.select('g.stack _0'));
**option 6**
bitChart.on('zoomed', function (chart, filter) {
svg.call(d3.behavior.zoom().scale(1));
**option 7**
var min_zoom = 1;
var max_zoom = 1;
var svg = d3.select("body").append("svg");
var zoom = d3.behavior.zoom().scaleExtent([min_zoom, max_zoom])
bitChart.on('zoomed', function (chart, filter) {
svg.call(zoom);
My fiddle:
https://jsfiddle.net/dani2011/4dsjjdan/1/ was forked from https://jsfiddle.net/gordonwoodhull/272xrsat/9/.
When selecting span from the dropdown and clicking on the range chart,The range chart (timeSlider) acts strange on the fiddle, but behaves as expected when run it in my environment. Please note in this fiddle that bitChart2 pans the brush as expected.The resize of the brush when reaching the edge happens in my enviroment. bitChart still resizes the brush.
A partial solution:
To enable multi focus charts on a single range chart as in https://github.com/dc-js/dc.js/blob/master/web/examples/multi-focus.html written by Gordon Woodhull.Used the focus chart which worked properly in my code (bitChart2) as the main reference chart:
bitChart2.focusCharts = function (chartlist) {
if (!arguments.length) {
return this._focusCharts;
}
this._focusCharts = chartlist; // only needed to support the getter above
this.on('filtered', function (range_chart) {
if (!range_chart.filter()) {
dc.events.trigger(function () {
chartlist.forEach(function(focus_chart) {
focus_chart.x().domain(focus_chart.xOriginalDomain());
});
});
} else chartlist.forEach(function(focus_chart) {
if (!rangesEqual(range_chart.filter(), focus_chart.filter())) {
dc.events.trigger(function () {
focus_chart.focus(range_chart.filter());
});
}
});
});
return this;
};
bitChart2.focusCharts([bitChart]);
My second fiddle:
https://jsfiddle.net/dani2011/uzg48yk7/1/ was forked from https://jsfiddle.net/gordonwoodhull/272xrsat/9/.
1) When clicking on the range chart in the fiddle it does not function properly, but works in my environment.
2) The brush does not resize at the edges of the range chart in the fiddle as it does in my environment
3) It does show in the fiddle that the whole range chart is selected when panning/clicking on the lines in the focus charts and when clicking in the range chart
4) It does show in the fiddle that after selecting the brush span from the dropdown, panning the lines in the focus charts moves the brush properly on the range chart.
5) It does show in the fiddle that dragging the brush on the range chart is possible again in no span is selected from the dropdown
Still needs to solve:
1) When reaching the ends of the range chart (timeSlider) the brush resizes
solved by updating versions to be the same as the version of the external resources in the fiddle https://jsfiddle.net/gordonwoodhull/272xrsat/9/. Thank you Gordon!
2) Before selecting a scale from the dropdown:
a) When panning /translating the line of the focus charts(bitChart,bitChart2) the brush resizes
b) It is possible again to drag the brush on the range chart
Any help would be appreciated !

Add graph in dc.js tooltip

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/

Formatting data in the axis

I am implementing a stacked chart using nvd3. One problem I am having is formatting the x data with the x-axis label
The x-axis data will in the form of year.week. But, I need the data to be shown at label as year | week. How can I format this data as I want to?
nv.addGraph(function() {
var chart = nv.models.multiBarChart()
//If 'false', every single x-axis tick label will be rendered
.reduceXTicks(false)
//Angle to rotate x-axis labels.
.rotateLabels(0)
//Allow user to switch between 'Grouped' and 'Stacked' mode
.showControls(false)
//Distance between each group of bars
.groupSpacing(0.1)
.stacked(true);
chart.xAxis.tickFormat(d3.format(',f'));
chart.yAxis.tickFormat(d3.format(',.1f'));
d3.select('#chart1 svg').datum(exampleData()).call(chart);
console.log(exampleData()[0]);
d3.select('#chart2 svg').datum(exampleData()).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});

Adding tool-tip to an image rendered using Highcharts Renderer.image() api

I wanted to add a tool-tip to an image that is rendered using:
image (String source, Number x, Number y, Number width, Number height) api.
I couldn't figure out a way to do this, I am using org.moxieapps.gwt.highcharts API's to
create high charts in my GWT app.
Use chart.renderer.text and chart.renderer.rect to draw and position your own custom tooltip based on the position of the image.
This is a sample code snippet I used to display a tooltip for an image which is generated using chart.renderer.image -
marker = chart.renderer.image(src, x, y, imageSize, imageSize)
.on('click', function() {`enter code here`
})
.attr({
zIndex: 100
})
.on('mouseover', function() {
//Call back for image mouse hover
//Draw a text relative to Image X and Y
text = chart.renderer.text("Your tooltip Text",X, Y)
.add();
var box = text.getBBox();
//Now draw a box surrounding the tool tip text
textBG = chart.renderer.rect(box.x, box.y,box.width, box.height, 5)
.attr({
fill: '#FFFFEF',
stroke: 'gray',
'stroke-width': 1,
zIndex: 4
})
.add();
})
.on('mouseout', function() {
//Call back for mouse out on the image
//Destroy Both markers text and textBG on mouse out
//Make text and textBG as global functions for access accross functions
text.destroy();
textBG.destroy();
})
.add();
In this way you can create custom tooltip for images.
I used this link for syntax of adding tooltip text and its back ground to the chart.

nvd3.js chart ajax data redraw - missing hovereffect + former yAxis scale

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:

Resources