How to scroll/zoom large dataset via dynamic JSON reloads?

882 views
Skip to first unread message

j_sch...@gmx.de

unread,
Jun 23, 2015, 9:55:21 AM6/23/15
to c3...@googlegroups.com
Hello!

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

da...@mining.ch

unread,
Sep 25, 2015, 8:39:50 AM9/25/15
to c3js, j_sch...@gmx.de
Hi JS,

I am having exactly the same issue. Could you come up with a solution?

Thanks,
David

rapha...@gmail.com

unread,
Sep 28, 2015, 7:20:16 AM9/28/15
to c3js, j_sch...@gmx.de, da...@mining.ch

j_sch...@gmx.de

unread,
Sep 28, 2015, 8:46:38 AM9/28/15
to c3js, da...@mining.ch
Hello 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

Reply all
Reply to author
Forward
0 new messages