mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
refactor barchart
This commit is contained in:
parent
06c3e78312
commit
c76a92b7d8
8 changed files with 420 additions and 314 deletions
|
|
@ -111,25 +111,24 @@
|
|||
{
|
||||
id: 'multiBarChart',
|
||||
name: 'Bar Chart',
|
||||
icon: 'fa fa-bar-chart'
|
||||
icon: 'fa fa-bar-chart',
|
||||
transformation: 'pivot'
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Holds class actual runtime instance and related infos of built-in visualizations
|
||||
* Holds class and actual runtime instance and related infos of built-in visualizations
|
||||
*/
|
||||
var builtInVisualizations = {
|
||||
'table': {
|
||||
class: zeppelin.TableVisualization,
|
||||
transformation: undefined,
|
||||
instance: undefined // created from setGraphMode()
|
||||
},
|
||||
'multiBarChart': {
|
||||
class: zeppelin.BarchartVisualization,
|
||||
transformation: zeppelin.Pivot,
|
||||
instance: undefined
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* TableData instance
|
||||
|
|
@ -157,7 +156,7 @@
|
|||
if ($scope.getResultType() === 'TABLE') {
|
||||
var TableData = zeppelin.TableData;
|
||||
tableData = new TableData();
|
||||
tableData.loadParagraphResult($scope.paragraph.result);
|
||||
tableData.loadParagraphResult($scope.paragraph.result);
|
||||
$scope.setGraphMode($scope.getGraphMode(), false, false);
|
||||
} else if ($scope.getResultType() === 'HTML') {
|
||||
$scope.renderHtml();
|
||||
|
|
@ -983,8 +982,6 @@
|
|||
var graphContainerEl = angular.element('#p' + $scope.paragraph.id + '_graph');
|
||||
graphContainerEl.height(height);
|
||||
|
||||
var data = $scope.paragraph.result;
|
||||
|
||||
if (!type) {
|
||||
type = 'table';
|
||||
}
|
||||
|
|
@ -1003,7 +1000,7 @@
|
|||
|
||||
// instantiate visualization
|
||||
var Visualization = builtInViz.class;
|
||||
builtInViz.instance = new Visualization(targetEl);
|
||||
builtInViz.instance = new Visualization(targetEl, $scope.paragraph.config.graph);
|
||||
builtInViz.instance.render(tableData);
|
||||
} catch (err) {
|
||||
console.log('Graph drawing error %o', err);
|
||||
|
|
@ -1013,7 +1010,7 @@
|
|||
}
|
||||
};
|
||||
$timeout(retryRenderer);
|
||||
} else {
|
||||
} else if (refresh) {
|
||||
builtInViz.instance.targetEl.height(height);
|
||||
builtInViz.instance.render(tableData);
|
||||
}
|
||||
|
|
@ -1021,8 +1018,8 @@
|
|||
|
||||
if (!type || type === 'table') {
|
||||
//setTable($scope.paragraph.result, refresh);
|
||||
} else {
|
||||
setD3Chart(type, $scope.paragraph.result, refresh);
|
||||
} else if (type === 'nomore') {
|
||||
setD3Chart(type, tableData, refresh);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1268,9 +1265,9 @@
|
|||
for (var i = 0; i < list.length; i++) {
|
||||
// remove non existing column
|
||||
var found = false;
|
||||
for (var j = 0; j < tableData.columnNames.length; j++) {
|
||||
for (var j = 0; j < tableData.columns.length; j++) {
|
||||
var a = list[i];
|
||||
var b = tableData.columnNames[j];
|
||||
var b = tableData.columns[j];
|
||||
if (a.index === b.index && a.name === b.name) {
|
||||
found = true;
|
||||
break;
|
||||
|
|
@ -1286,9 +1283,9 @@
|
|||
for (var f in fields) {
|
||||
if (fields[f]) {
|
||||
var found = false;
|
||||
for (var i = 0; i < tableData.columnNames.length; i++) {
|
||||
for (var i = 0; i < tableData.columns.length; i++) {
|
||||
var a = fields[f];
|
||||
var b = tableData.columnNames[i];
|
||||
var b = tableData.columns[i];
|
||||
if (a.index === b.index && a.name === b.name) {
|
||||
found = true;
|
||||
break;
|
||||
|
|
@ -1314,20 +1311,20 @@
|
|||
|
||||
/* select default key and value if there're none selected */
|
||||
var selectDefaultColsForGraphOption = function() {
|
||||
if ($scope.paragraph.config.graph.keys.length === 0 && $scope.paragraph.result.columnNames.length > 0) {
|
||||
$scope.paragraph.config.graph.keys.push($scope.paragraph.result.columnNames[0]);
|
||||
if ($scope.paragraph.config.graph.keys.length === 0 && tableData.columns.length > 0) {
|
||||
$scope.paragraph.config.graph.keys.push(tableData.columns[0]);
|
||||
}
|
||||
|
||||
if ($scope.paragraph.config.graph.values.length === 0 && $scope.paragraph.result.columnNames.length > 1) {
|
||||
$scope.paragraph.config.graph.values.push($scope.paragraph.result.columnNames[1]);
|
||||
if ($scope.paragraph.config.graph.values.length === 0 && tableData.columns.length > 1) {
|
||||
$scope.paragraph.config.graph.values.push(tableData.columns[1]);
|
||||
}
|
||||
|
||||
if (!$scope.paragraph.config.graph.scatter.xAxis && !$scope.paragraph.config.graph.scatter.yAxis) {
|
||||
if ($scope.paragraph.result.columnNames.length > 1) {
|
||||
$scope.paragraph.config.graph.scatter.xAxis = $scope.paragraph.result.columnNames[0];
|
||||
$scope.paragraph.config.graph.scatter.yAxis = $scope.paragraph.result.columnNames[1];
|
||||
} else if ($scope.paragraph.result.columnNames.length === 1) {
|
||||
$scope.paragraph.config.graph.scatter.xAxis = $scope.paragraph.result.columnNames[0];
|
||||
if (tableData.columns.length > 1) {
|
||||
$scope.paragraph.config.graph.scatter.xAxis = tableData.columns[0];
|
||||
$scope.paragraph.config.graph.scatter.yAxis = tableData.columns[1];
|
||||
} else if (tableData.columns.length === 1) {
|
||||
$scope.paragraph.config.graph.scatter.xAxis = tableData.columns[0];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1910,8 +1907,8 @@
|
|||
|
||||
$scope.exportToDSV = function(delimiter) {
|
||||
var dsv = '';
|
||||
for (var titleIndex in $scope.paragraph.result.columnNames) {
|
||||
dsv += $scope.paragraph.result.columnNames[titleIndex].name + delimiter;
|
||||
for (var titleIndex in tableData.columns) {
|
||||
dsv += tableData.columns[titleIndex].name + delimiter;
|
||||
}
|
||||
dsv = dsv.substring(0, dsv.length - 1) + '\n';
|
||||
for (var r in $scope.paragraph.result.msgTable) {
|
||||
|
|
@ -2304,9 +2301,9 @@
|
|||
|
||||
if (newType === 'TABLE') {
|
||||
if (oldType !== 'TABLE' || resultRefreshed) {
|
||||
var TableData = new zeppelin.TableData;
|
||||
var TableData = zeppelin.TableData;
|
||||
tableData = new TableData();
|
||||
tableData.loadParagraphResult($scope.paragraph.result);
|
||||
tableData.loadParagraphResult($scope.paragraph.result);
|
||||
clearUnknownColsFromGraphOption();
|
||||
selectDefaultColsForGraphOption();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var zeppelin = zeppelin || {};
|
||||
|
||||
/**
|
||||
* pivot table data and return d3 chart data
|
||||
*/
|
||||
zeppelin.PivotTransformation = function(config) {
|
||||
zeppelin.Transformation.call(this, config);
|
||||
};
|
||||
|
||||
zeppelin.PivotTransformation.prototype = Object.create(zeppelin.Transformation.prototype);
|
||||
|
||||
/**
|
||||
* Method will be invoked when tableData or config changes
|
||||
*/
|
||||
zeppelin.PivotTransformation.prototype.transform = function(tableData) {
|
||||
return this.pivot(
|
||||
tableData,
|
||||
this.config.keys,
|
||||
this.config.groups,
|
||||
this.config.values);
|
||||
};
|
||||
|
||||
zeppelin.PivotTransformation.prototype.pivot = function(data, keys, groups, values) {
|
||||
var aggrFunc = {
|
||||
sum: function(a, b) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return varA + varB;
|
||||
},
|
||||
count: function(a, b) {
|
||||
var varA = (a !== undefined) ? parseInt(a) : 0;
|
||||
var varB = (b !== undefined) ? 1 : 0;
|
||||
return varA + varB;
|
||||
},
|
||||
min: function(a, b) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return Math.min(varA,varB);
|
||||
},
|
||||
max: function(a, b) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return Math.max(varA,varB);
|
||||
},
|
||||
avg: function(a, b, c) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return varA + varB;
|
||||
}
|
||||
};
|
||||
|
||||
var aggrFuncDiv = {
|
||||
sum: false,
|
||||
count: false,
|
||||
min: false,
|
||||
max: false,
|
||||
avg: true
|
||||
};
|
||||
|
||||
var schema = {};
|
||||
var rows = {};
|
||||
|
||||
for (var i = 0; i < data.rows.length; i++) {
|
||||
var row = data.rows[i];
|
||||
var s = schema;
|
||||
var p = rows;
|
||||
|
||||
for (var k = 0; k < keys.length; k++) {
|
||||
var key = keys[k];
|
||||
|
||||
// add key to schema
|
||||
if (!s[key.name]) {
|
||||
s[key.name] = {
|
||||
order: k,
|
||||
index: key.index,
|
||||
type: 'key',
|
||||
children: {}
|
||||
};
|
||||
}
|
||||
s = s[key.name].children;
|
||||
|
||||
// add key to row
|
||||
var keyKey = row[key.index];
|
||||
if (!p[keyKey]) {
|
||||
p[keyKey] = {};
|
||||
}
|
||||
p = p[keyKey];
|
||||
}
|
||||
|
||||
for (var g = 0; g < groups.length; g++) {
|
||||
var group = groups[g];
|
||||
var groupKey = row[group.index];
|
||||
|
||||
// add group to schema
|
||||
if (!s[groupKey]) {
|
||||
s[groupKey] = {
|
||||
order: g,
|
||||
index: group.index,
|
||||
type: 'group',
|
||||
children: {}
|
||||
};
|
||||
}
|
||||
s = s[groupKey].children;
|
||||
|
||||
// add key to row
|
||||
if (!p[groupKey]) {
|
||||
p[groupKey] = {};
|
||||
}
|
||||
p = p[groupKey];
|
||||
}
|
||||
|
||||
for (var v = 0; v < values.length; v++) {
|
||||
var value = values[v];
|
||||
var valueKey = value.name + '(' + value.aggr + ')';
|
||||
|
||||
// add value to schema
|
||||
if (!s[valueKey]) {
|
||||
s[valueKey] = {
|
||||
type: 'value',
|
||||
order: v,
|
||||
index: value.index
|
||||
};
|
||||
}
|
||||
|
||||
// add value to row
|
||||
if (!p[valueKey]) {
|
||||
p[valueKey] = {
|
||||
value: (value.aggr !== 'count') ? row[value.index] : 1,
|
||||
count: 1
|
||||
};
|
||||
} else {
|
||||
p[valueKey] = {
|
||||
value: aggrFunc[value.aggr](p[valueKey].value, row[value.index], p[valueKey].count + 1),
|
||||
count: (aggrFuncDiv[value.aggr]) ? p[valueKey].count + 1 : p[valueKey].count
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('schema=%o, rows=%o', schema, rows);
|
||||
return {
|
||||
keys: keys,
|
||||
groups: groups,
|
||||
values: values,
|
||||
schema: schema,
|
||||
rows: rows
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -88,275 +88,3 @@ zeppelin.TableData.prototype.parseTableCell = function(cell) {
|
|||
}
|
||||
return cell;
|
||||
};
|
||||
|
||||
zeppelin.TableData.prototype.pivot = function(keys, groups, values, allowTextXAxis, fillMissingValues, chartType) {
|
||||
var aggrFunc = {
|
||||
sum: function(a, b) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return varA + varB;
|
||||
},
|
||||
count: function(a, b) {
|
||||
var varA = (a !== undefined) ? parseInt(a) : 0;
|
||||
var varB = (b !== undefined) ? 1 : 0;
|
||||
return varA + varB;
|
||||
},
|
||||
min: function(a, b) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return Math.min(varA,varB);
|
||||
},
|
||||
max: function(a, b) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return Math.max(varA,varB);
|
||||
},
|
||||
avg: function(a, b, c) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return varA + varB;
|
||||
}
|
||||
};
|
||||
|
||||
var aggrFuncDiv = {
|
||||
sum: false,
|
||||
count: false,
|
||||
min: false,
|
||||
max: false,
|
||||
avg: true
|
||||
};
|
||||
|
||||
var schema = {};
|
||||
var rows = {};
|
||||
|
||||
for (var i = 0; i < this.rows.length; i++) {
|
||||
var row = this.rows[i];
|
||||
var s = schema;
|
||||
var p = rows;
|
||||
|
||||
for (var k = 0; k < keys.length; k++) {
|
||||
var key = keys[k];
|
||||
|
||||
// add key to schema
|
||||
if (!s[key.name]) {
|
||||
s[key.name] = {
|
||||
order: k,
|
||||
index: key.index,
|
||||
type: 'key',
|
||||
children: {}
|
||||
};
|
||||
}
|
||||
s = s[key.name].children;
|
||||
|
||||
// add key to row
|
||||
var keyKey = row[key.index];
|
||||
if (!p[keyKey]) {
|
||||
p[keyKey] = {};
|
||||
}
|
||||
p = p[keyKey];
|
||||
}
|
||||
|
||||
for (var g = 0; g < groups.length; g++) {
|
||||
var group = groups[g];
|
||||
var groupKey = row[group.index];
|
||||
|
||||
// add group to schema
|
||||
if (!s[groupKey]) {
|
||||
s[groupKey] = {
|
||||
order: g,
|
||||
index: group.index,
|
||||
type: 'group',
|
||||
children: {}
|
||||
};
|
||||
}
|
||||
s = s[groupKey].children;
|
||||
|
||||
// add key to row
|
||||
if (!p[groupKey]) {
|
||||
p[groupKey] = {};
|
||||
}
|
||||
p = p[groupKey];
|
||||
}
|
||||
|
||||
for (var v = 0; v < values.length; v++) {
|
||||
var value = values[v];
|
||||
var valueKey = value.name + '(' + value.aggr + ')';
|
||||
|
||||
// add value to schema
|
||||
if (!s[valueKey]) {
|
||||
s[valueKey] = {
|
||||
type: 'value',
|
||||
order: v,
|
||||
index: value.index
|
||||
};
|
||||
}
|
||||
|
||||
// add value to row
|
||||
if (!p[valueKey]) {
|
||||
p[valueKey] = {
|
||||
value: (value.aggr !== 'count') ? row[value.index] : 1,
|
||||
count: 1
|
||||
};
|
||||
} else {
|
||||
p[valueKey] = {
|
||||
value: aggrFunc[value.aggr](p[valueKey].value, row[value.index], p[valueKey].count + 1),
|
||||
count: (aggrFuncDiv[value.aggr]) ? p[valueKey].count + 1 : p[valueKey].count
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("schema=%o, rows=%o", schema, rows);
|
||||
return {
|
||||
schema: schema,
|
||||
rows: rows
|
||||
}
|
||||
};
|
||||
|
||||
zeppelin.TableData.prototype._buildTableDataFromPivot = function(schema, rows, keys, groups, values, values, allowTextXAxis, fillMissingValues, chartType) {
|
||||
// construct table data
|
||||
var d3g = [];
|
||||
|
||||
var concat = function(o, n) {
|
||||
if (!o) {
|
||||
return n;
|
||||
} else {
|
||||
return o + '.' + n;
|
||||
}
|
||||
};
|
||||
|
||||
var getSchemaUnderKey = function(key, s) {
|
||||
for (var c in key.children) {
|
||||
s[c] = {};
|
||||
getSchemaUnderKey(key.children[c], s[c]);
|
||||
}
|
||||
};
|
||||
|
||||
var traverse = function(sKey, s, rKey, r, func, rowName, rowValue, colName) {
|
||||
//console.log("TRAVERSE sKey=%o, s=%o, rKey=%o, r=%o, rowName=%o, rowValue=%o, colName=%o", sKey, s, rKey, r, rowName, rowValue, colName);
|
||||
|
||||
if (s.type === 'key') {
|
||||
rowName = concat(rowName, sKey);
|
||||
rowValue = concat(rowValue, rKey);
|
||||
} else if (s.type === 'group') {
|
||||
colName = concat(colName, rKey);
|
||||
} else if (s.type === 'value' && sKey === rKey || valueOnly) {
|
||||
colName = concat(colName, rKey);
|
||||
func(rowName, rowValue, colName, r);
|
||||
}
|
||||
|
||||
for (var c in s.children) {
|
||||
if (fillMissingValues && s.children[c].type === 'group' && r[c] === undefined) {
|
||||
var cs = {};
|
||||
getSchemaUnderKey(s.children[c], cs);
|
||||
traverse(c, s.children[c], c, cs, func, rowName, rowValue, colName);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var j in r) {
|
||||
if (s.children[c].type === 'key' || c === j) {
|
||||
traverse(c, s.children[c], j, r[j], func, rowName, rowValue, colName);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var valueOnly = (keys.length === 0 && groups.length === 0 && values.length > 0);
|
||||
var noKey = (keys.length === 0);
|
||||
var isMultiBarChart = (chartType === 'multiBarChart');
|
||||
|
||||
var sKey = Object.keys(schema)[0];
|
||||
|
||||
var rowNameIndex = {};
|
||||
var rowIdx = 0;
|
||||
var colNameIndex = {};
|
||||
var colIdx = 0;
|
||||
var rowIndexValue = {};
|
||||
|
||||
for (var k in rows) {
|
||||
traverse(sKey, schema[sKey], k, rows[k], function(rowName, rowValue, colName, value) {
|
||||
//console.log("RowName=%o, row=%o, col=%o, value=%o", rowName, rowValue, colName, value);
|
||||
if (rowNameIndex[rowValue] === undefined) {
|
||||
rowIndexValue[rowIdx] = rowValue;
|
||||
rowNameIndex[rowValue] = rowIdx++;
|
||||
}
|
||||
|
||||
if (colNameIndex[colName] === undefined) {
|
||||
colNameIndex[colName] = colIdx++;
|
||||
}
|
||||
var i = colNameIndex[colName];
|
||||
if (noKey && isMultiBarChart) {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (!d3g[i]) {
|
||||
d3g[i] = {
|
||||
values: [],
|
||||
key: (noKey && isMultiBarChart) ? 'values' : colName
|
||||
};
|
||||
}
|
||||
|
||||
var xVar = isNaN(rowValue) ? ((allowTextXAxis) ? rowValue : rowNameIndex[rowValue]) : parseFloat(rowValue);
|
||||
var yVar = 0;
|
||||
if (xVar === undefined) { xVar = colName; }
|
||||
if (value !== undefined) {
|
||||
yVar = isNaN(value.value) ? 0 : parseFloat(value.value) / parseFloat(value.count);
|
||||
}
|
||||
d3g[i].values.push({
|
||||
x: xVar,
|
||||
y: yVar
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// clear aggregation name, if possible
|
||||
var namesWithoutAggr = {};
|
||||
var colName;
|
||||
var withoutAggr;
|
||||
// TODO - This part could use som refactoring - Weird if/else with similar actions and variable names
|
||||
for (colName in colNameIndex) {
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('));
|
||||
if (!namesWithoutAggr[withoutAggr]) {
|
||||
namesWithoutAggr[withoutAggr] = 1;
|
||||
} else {
|
||||
namesWithoutAggr[withoutAggr]++;
|
||||
}
|
||||
}
|
||||
|
||||
if (valueOnly) {
|
||||
for (var valueIndex = 0; valueIndex < d3g[0].values.length; valueIndex++) {
|
||||
colName = d3g[0].values[valueIndex].x;
|
||||
if (!colName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('));
|
||||
if (namesWithoutAggr[withoutAggr] <= 1) {
|
||||
d3g[0].values[valueIndex].x = withoutAggr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
|
||||
colName = d3g[d3gIndex].key;
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('));
|
||||
if (namesWithoutAggr[withoutAggr] <= 1) {
|
||||
d3g[d3gIndex].key = withoutAggr;
|
||||
}
|
||||
}
|
||||
|
||||
// use group name instead of group.value as a column name, if there're only one group and one value selected.
|
||||
if (groups.length === 1 && values.length === 1) {
|
||||
for (d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
|
||||
colName = d3g[d3gIndex].key;
|
||||
colName = colName.split('.').slice(0, -1).join('.');
|
||||
d3g[d3gIndex].key = colName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
xLabels: rowIndexValue,
|
||||
d3g: d3g
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,12 +19,17 @@ var zeppelin = zeppelin || {};
|
|||
/**
|
||||
* Base class for visualization
|
||||
*/
|
||||
zeppelin.Transformation = function() {
|
||||
zeppelin.Transformation = function(config) {
|
||||
this.config = config;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method will be invoked when tableData or config changes
|
||||
*/
|
||||
zeppelin.Transformation.prototype.transform = function(tableData, config) {
|
||||
zeppelin.Transformation.prototype.transform = function(tableData) {
|
||||
// override this
|
||||
};
|
||||
|
||||
zeppelin.Transformation.prototype.setConfig = function(config) {
|
||||
this.config = config;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@
|
|||
/**
|
||||
* Visualize data in table format
|
||||
*/
|
||||
zeppelin.BarchartVisualization = function(targetEl) {
|
||||
zeppelin.Nvd3ChartVisualization.call(this, targetEl);
|
||||
zeppelin.BarchartVisualization = function(targetEl, config) {
|
||||
zeppelin.Nvd3ChartVisualization.call(this, targetEl, config);
|
||||
|
||||
var PivotTransformation = zeppelin.PivotTransformation;
|
||||
this.pivot = new PivotTransformation(config);
|
||||
};
|
||||
|
||||
zeppelin.BarchartVisualization.prototype = Object.create(zeppelin.Nvd3ChartVisualization.prototype);
|
||||
|
|
@ -27,8 +30,29 @@ zeppelin.BarchartVisualization.prototype.type = function() {
|
|||
return 'multiBarChart';
|
||||
};
|
||||
|
||||
zeppelin.BarchartVisualization.prototype.getTransformation = function() {
|
||||
return this.pivot;
|
||||
};
|
||||
|
||||
zeppelin.BarchartVisualization.prototype.render = function(tableData) {
|
||||
var pivot = this.pivot.transform(tableData);
|
||||
var d3Data = this.d3DataFromPivot(
|
||||
pivot.schema,
|
||||
pivot.rows,
|
||||
pivot.keys,
|
||||
pivot.groups,
|
||||
pivot.values,
|
||||
true,
|
||||
false,
|
||||
true);
|
||||
|
||||
zeppelin.Nvd3ChartVisualization.prototype.render.call(this, d3Data);
|
||||
};
|
||||
|
||||
zeppelin.BarchartVisualization.prototype.configureChart = function(chart) {
|
||||
// override this to configure chart
|
||||
var self = this;
|
||||
chart.yAxis.axisLabelDistance(50);
|
||||
chart.yAxis.tickFormat(function(d) {return this.yAxisTickFormat(d);});
|
||||
chart.yAxis.tickFormat(function(d) {return self.yAxisTickFormat(d);});
|
||||
console.log('configure chart');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,23 +17,51 @@
|
|||
/**
|
||||
* Visualize data in table format
|
||||
*/
|
||||
zeppelin.Nvd3ChartVisualization = function(targetEl) {
|
||||
zeppelin.Visualization.call(this, targetEl);
|
||||
zeppelin.Nvd3ChartVisualization = function(targetEl, config) {
|
||||
zeppelin.Visualization.call(this, targetEl, config);
|
||||
this.targetEl.append("<svg></svg>");
|
||||
};
|
||||
|
||||
zeppelin.Nvd3ChartVisualization.prototype = Object.create(zeppelin.Visualization.prototype);
|
||||
|
||||
zeppelin.Nvd3ChartVisualization.prototype.render = function(tableData) {
|
||||
zeppelin.Nvd3ChartVisualization.prototype.render = function(data) {
|
||||
var type = this.type();
|
||||
console.log('chart type = %o', type);
|
||||
var d3g = data.d3g;
|
||||
|
||||
if (!this.chart) {
|
||||
this.chart = nv.models[type]();
|
||||
}
|
||||
|
||||
tableData.pivot(['key'],[],['value']);
|
||||
|
||||
this.configureChart(this.chart);
|
||||
|
||||
var animationDuration = 300;
|
||||
var numberOfDataThreshold = 150;
|
||||
var height = this.targetEl.height();
|
||||
|
||||
// turn off animation when dataset is too large. (for performance issue)
|
||||
// still, since dataset is large, the chart content sequentially appears like animated.
|
||||
try {
|
||||
if (d3g[0].values.length > numberOfDataThreshold) {
|
||||
animationDuration = 0;
|
||||
}
|
||||
} catch (ignoreErr) {
|
||||
}
|
||||
|
||||
d3.select('#' + this.targetEl[0].id + ' svg')
|
||||
.attr('height', height)
|
||||
.datum(d3g)
|
||||
.transition()
|
||||
.duration(animationDuration)
|
||||
.call(this.chart);
|
||||
d3.select('#' + this.targetEl[0].id + ' svg').style.height = height + 'px';
|
||||
this.chart.update();
|
||||
nv.utils.windowResize(this.chart.update);
|
||||
};
|
||||
|
||||
zeppelin.Nvd3ChartVisualization.prototype.onResize = function() {
|
||||
if (this.chart) {
|
||||
this.chart.update();
|
||||
}
|
||||
};
|
||||
|
||||
zeppelin.Nvd3ChartVisualization.prototype.type = function() {
|
||||
|
|
@ -70,3 +98,152 @@ zeppelin.Nvd3ChartVisualization.prototype.yAxisTickFormat = function(d) {
|
|||
}
|
||||
return this.groupedThousandsWith3DigitsFormatter(d);
|
||||
};
|
||||
|
||||
zeppelin.Nvd3ChartVisualization.prototype.d3DataFromPivot = function(
|
||||
schema, rows, keys, groups, values, allowTextXAxis, fillMissingValues, multiBarChart) {
|
||||
// construct table data
|
||||
var d3g = [];
|
||||
|
||||
var concat = function(o, n) {
|
||||
if (!o) {
|
||||
return n;
|
||||
} else {
|
||||
return o + '.' + n;
|
||||
}
|
||||
};
|
||||
|
||||
var getSchemaUnderKey = function(key, s) {
|
||||
for (var c in key.children) {
|
||||
s[c] = {};
|
||||
getSchemaUnderKey(key.children[c], s[c]);
|
||||
}
|
||||
};
|
||||
|
||||
var traverse = function(sKey, s, rKey, r, func, rowName, rowValue, colName) {
|
||||
//console.log("TRAVERSE sKey=%o, s=%o, rKey=%o, r=%o, rowName=%o, rowValue=%o, colName=%o", sKey, s, rKey, r, rowName, rowValue, colName);
|
||||
|
||||
if (s.type === 'key') {
|
||||
rowName = concat(rowName, sKey);
|
||||
rowValue = concat(rowValue, rKey);
|
||||
} else if (s.type === 'group') {
|
||||
colName = concat(colName, rKey);
|
||||
} else if (s.type === 'value' && sKey === rKey || valueOnly) {
|
||||
colName = concat(colName, rKey);
|
||||
func(rowName, rowValue, colName, r);
|
||||
}
|
||||
|
||||
for (var c in s.children) {
|
||||
if (fillMissingValues && s.children[c].type === 'group' && r[c] === undefined) {
|
||||
var cs = {};
|
||||
getSchemaUnderKey(s.children[c], cs);
|
||||
traverse(c, s.children[c], c, cs, func, rowName, rowValue, colName);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var j in r) {
|
||||
if (s.children[c].type === 'key' || c === j) {
|
||||
traverse(c, s.children[c], j, r[j], func, rowName, rowValue, colName);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var valueOnly = (keys.length === 0 && groups.length === 0 && values.length > 0);
|
||||
var noKey = (keys.length === 0);
|
||||
var isMultiBarChart = multiBarChart;
|
||||
|
||||
var sKey = Object.keys(schema)[0];
|
||||
|
||||
var rowNameIndex = {};
|
||||
var rowIdx = 0;
|
||||
var colNameIndex = {};
|
||||
var colIdx = 0;
|
||||
var rowIndexValue = {};
|
||||
|
||||
for (var k in rows) {
|
||||
traverse(sKey, schema[sKey], k, rows[k], function(rowName, rowValue, colName, value) {
|
||||
//console.log("RowName=%o, row=%o, col=%o, value=%o", rowName, rowValue, colName, value);
|
||||
if (rowNameIndex[rowValue] === undefined) {
|
||||
rowIndexValue[rowIdx] = rowValue;
|
||||
rowNameIndex[rowValue] = rowIdx++;
|
||||
}
|
||||
|
||||
if (colNameIndex[colName] === undefined) {
|
||||
colNameIndex[colName] = colIdx++;
|
||||
}
|
||||
var i = colNameIndex[colName];
|
||||
if (noKey && isMultiBarChart) {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (!d3g[i]) {
|
||||
d3g[i] = {
|
||||
values: [],
|
||||
key: (noKey && isMultiBarChart) ? 'values' : colName
|
||||
};
|
||||
}
|
||||
|
||||
var xVar = isNaN(rowValue) ? ((allowTextXAxis) ? rowValue : rowNameIndex[rowValue]) : parseFloat(rowValue);
|
||||
var yVar = 0;
|
||||
if (xVar === undefined) { xVar = colName; }
|
||||
if (value !== undefined) {
|
||||
yVar = isNaN(value.value) ? 0 : parseFloat(value.value) / parseFloat(value.count);
|
||||
}
|
||||
d3g[i].values.push({
|
||||
x: xVar,
|
||||
y: yVar
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// clear aggregation name, if possible
|
||||
var namesWithoutAggr = {};
|
||||
var colName;
|
||||
var withoutAggr;
|
||||
// TODO - This part could use som refactoring - Weird if/else with similar actions and variable names
|
||||
for (colName in colNameIndex) {
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('));
|
||||
if (!namesWithoutAggr[withoutAggr]) {
|
||||
namesWithoutAggr[withoutAggr] = 1;
|
||||
} else {
|
||||
namesWithoutAggr[withoutAggr]++;
|
||||
}
|
||||
}
|
||||
|
||||
if (valueOnly) {
|
||||
for (var valueIndex = 0; valueIndex < d3g[0].values.length; valueIndex++) {
|
||||
colName = d3g[0].values[valueIndex].x;
|
||||
if (!colName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('));
|
||||
if (namesWithoutAggr[withoutAggr] <= 1) {
|
||||
d3g[0].values[valueIndex].x = withoutAggr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
|
||||
colName = d3g[d3gIndex].key;
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('));
|
||||
if (namesWithoutAggr[withoutAggr] <= 1) {
|
||||
d3g[d3gIndex].key = withoutAggr;
|
||||
}
|
||||
}
|
||||
|
||||
// use group name instead of group.value as a column name, if there're only one group and one value selected.
|
||||
if (groups.length === 1 && values.length === 1) {
|
||||
for (d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
|
||||
colName = d3g[d3gIndex].key;
|
||||
colName = colName.split('.').slice(0, -1).join('.');
|
||||
d3g[d3gIndex].key = colName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
xLabels: rowIndexValue,
|
||||
d3g: d3g
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,12 +19,20 @@ var zeppelin = zeppelin || {};
|
|||
/**
|
||||
* Base class for visualization
|
||||
*/
|
||||
zeppelin.Visualization = function(targetEl) {
|
||||
zeppelin.Visualization = function(targetEl, config) {
|
||||
this.targetEl = targetEl;
|
||||
this.config = config;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method will be invoked
|
||||
* get transformation
|
||||
*/
|
||||
zeppelin.Visualization.prototype.getTransformation = function() {
|
||||
// override this
|
||||
};
|
||||
|
||||
/**
|
||||
* Method will be invoked when data or configuration changed
|
||||
*/
|
||||
zeppelin.Visualization.prototype.render = function(tableData) {
|
||||
// override this
|
||||
|
|
|
|||
|
|
@ -154,6 +154,8 @@ limitations under the License.
|
|||
<script src="app/app.controller.js"></script>
|
||||
<script src="app/home/home.controller.js"></script>
|
||||
<script src="app/tabledata/tabledata.js"></script>
|
||||
<script src="app/tabledata/transformation.js"></script>
|
||||
<script src="app/tabledata/pivot.js"></script>
|
||||
<script src="app/visualization/visualization.js"></script>
|
||||
<script src="app/visualization/builtins/visualization-table.js"></script>
|
||||
<script src="app/visualization/builtins/visualization-nvd3chart.js"></script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue