Add a 'tree' chart type and a 'treegroup' function.
These allow you to automatically create treemap chart hierarchies using the
levels of your key. So treegroup=a,b,c gives you a tree with the toplevel
showing all 'a', then a second level showing all 'b' for each 'a', and so
on.
diff --git a/render.js b/render.js
index 04d5c3c..4886a9b 100644
--- a/render.js
+++ b/render.js
@@ -32,7 +32,14 @@
for (var rowi in data) {
var row = [];
for (var coli in data[rowi]) {
- row.push({v:data[rowi][coli]});
+ var col = { v: data[rowi][coli] };
+ if (col.v && col.v.split) {
+ var lastseg = col.v.split('\0').pop();
+ if (lastseg != col.v) {
+ col.f = lastseg;
+ }
+ }
+ row.push(col);
}
ddata.push({c: row});
}
@@ -260,6 +267,63 @@
}
+function stringifiedCols(row, types) {
+ var out = []
+ for (var coli in types) {
+ if (types[coli] === T_DATE) {
+ out.push(row[coli].strftime('%Y-%m-%d'));
+ } else if (types[coli] === T_DATETIME) {
+ out.push(row[coli].strftime('%Y-%m-%d %H:%M:%S'));
+ } else {
+ out.push((row[coli] + '') || '(none)');
+ }
+ }
+ return out;
+}
+
+
+KEY_ALL = ['ALL'];
+function treeify(ingrid, nkeys) {
+ var outgrid = {
+ headers: ['_id', '_parent'].concat(ingrid.headers.slice(nkeys)),
+ types: [T_STRING, T_STRING].concat(ingrid.types.slice(nkeys)),
+ data: []
+ };
+
+ var seen = {};
+ var missing = {};
+
+ var add = function(key, values) {
+ var pkey = key.slice(0, key.length - 1);
+ if (!pkey.length && key != KEY_ALL) pkey = KEY_ALL;
+ outgrid.data.push([key.join('\0'), pkey.join('\0')].concat(values));
+ if (pkey.length && !(pkey in seen)) {
+ missing[pkey] = pkey;
+ }
+ if (key in missing) {
+ delete missing[key];
+ }
+ seen[key] = 1;
+ }
+
+ for (var rowi in ingrid.data) {
+ var row = ingrid.data[rowi];
+ var key = row.slice(0, nkeys);
+ add(stringifiedCols(row.slice(0, nkeys),
+ ingrid.types.slice(0, nkeys)),
+ row.slice(nkeys));
+ }
+ for (var i = 0; i < 100; i++) {
+ for (var missi in missing) {
+ var miss = missing[missi];
+ add(miss, []);
+ break;
+ }
+ }
+ return outgrid;
+}
+
+
function splitNoEmpty(s, splitter) {
if (!s) return [];
return s.split(splitter);
@@ -298,6 +362,28 @@
}
+function doTreeGroupBy(grid, argval) {
+ console.debug('treeGroupBy:', argval);
+ var parts = argval.split(';', 2);
+ var keys = splitNoEmpty(parts[0], ',');
+ var values;
+ if (parts.length >= 2) {
+ // if there's a ';' separator, the names after it are the desired
+ // value columns (and that list may be empty).
+ values = splitNoEmpty(parts[1], ',');
+ } else {
+ // if there is no ';' at all, the default is to just pull in all the
+ // remaining non-key columns as values.
+ values = keysOtherThan(grid, keys);
+ }
+ console.debug('treegrouping by', keys, values);
+ grid = groupBy(grid, keys, values);
+ grid = treeify(grid, keys.length);
+ console.debug('grid:', grid);
+ return grid;
+}
+
+
function doPivotBy(grid, argval) {
console.debug('pivotBy:', argval);
@@ -565,6 +651,8 @@
var argkey = args.all[argi][0], argval = args.all[argi][1];
if (argkey == 'group') {
grid = doGroupBy(grid, argval);
+ } else if (argkey == 'treegroup') {
+ grid = doTreeGroupBy(grid, argval);
} else if (argkey == 'pivot') {
grid = doPivotBy(grid, argval);
} else if (argkey == 'filter') {
@@ -598,8 +686,15 @@
t = new google.visualization.ColumnChart(el);
} else if (chartops == 'bar') {
t = new google.visualization.BarChart(el);
+ } else if (chartops == 'line') {
+ t = new google.visualization.LineChart(el);
} else if (chartops == 'pie') {
t = new google.visualization.PieChart(el);
+ } else if (chartops == 'tree') {
+ options.maxDepth = 3;
+ options.maxPostDepth = 1;
+ options.showScale = 1;
+ t = new google.visualization.TreeMap(el);
} else if (chartops == 'candle' || chartops == 'candlestick') {
t = new google.visualization.CandlestickChart(el);
} else if (chartops == 'timeline') {
@@ -611,8 +706,7 @@
options.errorBars = true;
}
} else {
- // default to a line chart if unrecognized type
- t = new google.visualization.LineChart(el);
+ throw new Error('unknown chart type "' + chartops + '"');
}
} else {
var el = document.getElementById('viztable');