I have a database with some large, timestamped datasets and a JSON API to request parts of those datasets by setting a "start" and "end" parameter, e.g:
http://domain/data/item/31/?start=2015-06-22T12:35:58.660Z&end=2015-06-23T05:03:33.340Z
I want to display one or more dataset within one chart and allow the user to zoom in/out of the data. Data reduction is already done by the server, so when requesting a larger time frame does not result in more data points but aggregates the data. Also the user should be able to scroll left/right through the data.
But I don't want to load all the data at once but reload it on scroll/zoom events from the server.
Currently this is implemented by clicking some buttons outside the chart and calling chart.load({mimeType: 'json', url: url}); with a new URL (larger/smaller time frame or frame moved back&forth in time).
The problem: calling load() looks quite odd and destroys the visual impression expected by zooming/scrolling as we can see it when the whole data is loaded.
How can I get C3JS to trigger those requests automatically and use its transitions for scrolling/zooming?
Thanks in advance for your hints!
Regards, JS
I am having exactly the same issue. Could you come up with a solution?
Thanks,
David
I did not use the zoomEnd event as it is not always triggered (at least in my setup) but the zoom event:
'onzoom': function(xDomain) {MyModule.onZoom(this, xDomain);}
To prevent (too many) requests to the server while the scrolling e.g. is still in progress this simply triggers a timeout:
onZoom: function(chart, xDomain) {
if (this.zooming) {
clearTimeout(this.zooming);
}
this.zooming = setTimeout(function(){
MyModule.loadRange(chart, xDomain);
}, 100);
},
When the timeout is hit the new datetime range is requested from the server:
/**
* Updates the data URL of the chart with the given time range,
* loads the data from the server and updates the chart.
*
* @param {object} chart c3.chart
* @param {object} range {Date, Date} containing the new xDomain
*/
loadRange: function(chart, range) {
var $$ = chart.internal;
var url = $$.config.data_url;
if (url.indexOf('?') === -1) {
url += '?';
} else {
url += '&';
}
url += 'start=' + range[0].toISOString()
+ '&end=' + range[1].toISOString();
// replaces chart.load() to use our custom loading function
// to prevent redraw() from using the transition effect
$$.convertUrlToData(url, 'json', false, function (data) {
$$.loadNoTransition($$.convertDataToTargets(data), {});
});
}
This function receives the new xDomain object with the start and end time, constructs the according URL and asks the chart to load it. (I use a custom load() function to prevent the transition effect moving the existing data points up and down to their new position, that's why convertUrlToData() is called here instead of load(). $$.config.data_url is not set on the chart by default but after initializing:
chart.load({
mimeType: 'json',
url: data.url
});
chart.internal.config.data_url = data.url;
Because C3 recalculates the zoomDomain after load()ing data from an URL to the returned date range in the JSON I had to overwrite getZoomDomain:
/**
* Overwritten to enforce the configured min/max instead of choosing
* the lowest/highest of config and xDomain.
*
* @returns {Array}
*/
c3.chart.internal.fn.getZoomDomain = function () {
var $$ = this, config = $$.config,
min = config.zoom_x_min ? config.zoom_x_min : $$.orgXDomain[0],
max = config.zoom_x_max ? config.zoom_x_max : $$.orgXDomain[1];
return [min, max];
};
This always uses the options.zoom.x.min|max values (= the oldest and newest existing record on the server) I set on the chart config:
x: {
min: d3.time.format("%Y-%m-%d %H:%M:%S").parse("2014-10-15 22:08:31"),
max: d3.time.format("%Y-%m-%d %H:%M:%S").parse("2015-09-28 14:11:29")
}
On the server side the data for the requested datetime range is fetched from the database and reduced to a maximum of 100 data points if a larger time frame with more data points is requested.
This works so far, but I think it's quite hacky to overwrite the zoomDomain and having no native option to flow in new data or having C3 automatically re-request data from the server after zoom.
Hope this helps, you can find an example chart using this method under http://sensor.vrok.de/
Regards, JS