This commit is contained in:
Lee moon soo 2016-10-18 08:45:05 +09:00
parent 4e84467d4d
commit 9367b78fc5

View file

@ -1051,12 +1051,6 @@
$timeout(function() {emitWindowResizeEvent();}, 1);
}
}
if (!type || type === 'table') {
//setTable($scope.paragraph.result, refresh);
} else if (type === 'nomore') {
setD3Chart(type, tableData, refresh);
}
}
};
@ -1100,152 +1094,6 @@
websocketMsgSrv.commitParagraph($scope.paragraph.id, title, text, config, params);
};
var groupedThousandsWith3DigitsFormatter = function(x) {
return d3.format(',')(d3.round(x, 3));
};
var customAbbrevFormatter = function(x) {
var s = d3.format('.3s')(x);
switch (s[s.length - 1]) {
case 'G': return s.slice(0, -1) + 'B';
}
return s;
};
var xAxisTickFormat = function(d, xLabels) {
if (xLabels[d] && (isNaN(parseFloat(xLabels[d])) || !isFinite(xLabels[d]))) { // to handle string type xlabel
return xLabels[d];
} else {
return d;
}
};
var yAxisTickFormat = function(d) {
if (Math.abs(d) >= Math.pow(10,6)) {
return customAbbrevFormatter(d);
}
return groupedThousandsWith3DigitsFormatter(d);
};
var setD3Chart = function(type, data, refresh) {
if (!$scope.chart[type]) {
var chart = nv.models[type]();
chart._options.noData = 'Invalid Data, check graph settings';
$scope.chart[type] = chart;
}
var d3g = [];
var xLabels;
var yLabels;
if (type === 'scatterChartxxx') {
var scatterData = setScatterChart(data, refresh);
xLabels = scatterData.xLabels;
yLabels = scatterData.yLabels;
d3g = scatterData.d3g;
$scope.chart[type].xAxis.tickFormat(function(d) {return xAxisTickFormat(d, xLabels);});
$scope.chart[type].yAxis.tickFormat(function(d) {return yAxisTickFormat(d, yLabels);});
// configure how the tooltip looks.
$scope.chart[type].tooltipContent(function(key, x, y, graph, data) {
var tooltipContent = '<h3>' + key + '</h3>';
if ($scope.paragraph.config.graph.scatter.size &&
$scope.isValidSizeOption($scope.paragraph.config.graph.scatter, $scope.paragraph.result.rows)) {
tooltipContent += '<p>' + data.point.size + '</p>';
}
return tooltipContent;
});
$scope.chart[type].showDistX(true)
.showDistY(true);
//handle the problem of tooltip not showing when muliple points have same value.
} else {
var p = pivot(data);
if (type === 'pieChart') {
var d = pivotDataToD3ChartFormat(p, true).d3g;
$scope.chart[type].x(function(d) { return d.label;})
.y(function(d) { return d.value;});
if (d.length > 0) {
for (var i = 0; i < d[0].values.length ; i++) {
var e = d[0].values[i];
d3g.push({
label: e.x,
value: e.y
});
}
}
} else if (type === 'multiBarChart') {
d3g = pivotDataToD3ChartFormat(p, true, false, type).d3g;
$scope.chart[type].yAxis.axisLabelDistance(50);
$scope.chart[type].yAxis.tickFormat(function(d) {return yAxisTickFormat(d);});
} else if (type === 'lineChart' || type === 'stackedAreaChart' || type === 'lineWithFocusChart') {
var pivotdata = pivotDataToD3ChartFormat(p, false, true);
xLabels = pivotdata.xLabels;
d3g = pivotdata.d3g;
$scope.chart[type].xAxis.tickFormat(function(d) {return xAxisTickFormat(d, xLabels);});
if (type === 'stackedAreaChart') {
$scope.chart[type].yAxisTickFormat(function(d) {return yAxisTickFormat(d);});
} else {
$scope.chart[type].yAxis.tickFormat(function(d) {return yAxisTickFormat(d, xLabels);});
}
$scope.chart[type].yAxis.axisLabelDistance(50);
if ($scope.chart[type].useInteractiveGuideline) { // lineWithFocusChart hasn't got useInteractiveGuideline
$scope.chart[type].useInteractiveGuideline(true); // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
}
if ($scope.paragraph.config.graph.forceY) {
$scope.chart[type].forceY([0]); // force y-axis minimum to 0 for line chart.
} else {
$scope.chart[type].forceY([]);
}
}
}
var renderChart = function() {
if (!refresh) {
// TODO force destroy previous chart
}
var height = $scope.paragraph.config.graph.height;
var animationDuration = 300;
var numberOfDataThreshold = 150;
// 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('#p' + $scope.paragraph.id + '_' + type + ' svg')
.attr('height', $scope.paragraph.config.graph.height)
.datum(d3g)
.transition()
.duration(animationDuration)
.call($scope.chart[type]);
d3.select('#p' + $scope.paragraph.id + '_' + type + ' svg').style.height = height + 'px';
nv.utils.windowResize($scope.chart[type].update);
};
var retryRenderer = function() {
if (angular.element('#p' + $scope.paragraph.id + '_' + type + ' svg').length !== 0) {
try {
renderChart();
} catch (err) {
console.log('Chart drawing error %o', err);
}
} else {
$timeout(retryRenderer,10);
}
};
$timeout(retryRenderer);
};
$scope.isGraphMode = function(graphName) {
var activeAppId = _.get($scope.paragraph.config, 'helium.activeApp');
if ($scope.getResultType() === 'TABLE' && $scope.getGraphMode() === graphName && !activeAppId) {
@ -1388,488 +1236,6 @@
}
};
var pivot = function(data) {
var keys = $scope.paragraph.config.graph.keys;
var groups = $scope.paragraph.config.graph.groups;
var values = $scope.paragraph.config.graph.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 {
schema: schema,
rows: rows
};
};
var pivotDataToD3ChartFormat = function(data, allowTextXAxis, fillMissingValues, chartType) {
// construct d3 data
var d3g = [];
var schema = data.schema;
var rows = data.rows;
var values = $scope.paragraph.config.graph.values;
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 keys = $scope.paragraph.config.graph.keys;
var groups = $scope.paragraph.config.graph.groups;
values = $scope.paragraph.config.graph.values;
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
};
};
var setDiscreteScatterData = function(data) {
var xAxis = $scope.paragraph.config.graph.scatter.xAxis;
var yAxis = $scope.paragraph.config.graph.scatter.yAxis;
var group = $scope.paragraph.config.graph.scatter.group;
var xValue;
var yValue;
var grp;
var rows = {};
for (var i = 0; i < data.rows.length; i++) {
var row = data.rows[i];
if (xAxis) {
xValue = row[xAxis.index];
}
if (yAxis) {
yValue = row[yAxis.index];
}
if (group) {
grp = row[group.index];
}
var key = xValue + ',' + yValue + ',' + grp;
if (!rows[key]) {
rows[key] = {
x: xValue,
y: yValue,
group: grp,
size: 1
};
} else {
rows[key].size++;
}
}
// change object into array
var newRows = [];
for (var r in rows) {
var newRow = [];
if (xAxis) { newRow[xAxis.index] = rows[r].x; }
if (yAxis) { newRow[yAxis.index] = rows[r].y; }
if (group) { newRow[group.index] = rows[r].group; }
newRow[data.rows[0].length] = rows[r].size;
newRows.push(newRow);
}
return newRows;
};
var setScatterChart = function(data, refresh) {
var xAxis = $scope.paragraph.config.graph.scatter.xAxis;
var yAxis = $scope.paragraph.config.graph.scatter.yAxis;
var group = $scope.paragraph.config.graph.scatter.group;
var size = $scope.paragraph.config.graph.scatter.size;
var xValues = [];
var yValues = [];
var rows = {};
var d3g = [];
var rowNameIndex = {};
var colNameIndex = {};
var grpNameIndex = {};
var rowIndexValue = {};
var colIndexValue = {};
var grpIndexValue = {};
var rowIdx = 0;
var colIdx = 0;
var grpIdx = 0;
var grpName = '';
var xValue;
var yValue;
var row;
if (!xAxis && !yAxis) {
return {
d3g: []
};
}
for (var i = 0; i < data.rows.length; i++) {
row = data.rows[i];
if (xAxis) {
xValue = row[xAxis.index];
xValues[i] = xValue;
}
if (yAxis) {
yValue = row[yAxis.index];
yValues[i] = yValue;
}
}
var isAllDiscrete = ((xAxis && yAxis && isDiscrete(xValues) && isDiscrete(yValues)) ||
(!xAxis && isDiscrete(yValues)) ||
(!yAxis && isDiscrete(xValues)));
if (isAllDiscrete) {
rows = setDiscreteScatterData(data);
} else {
rows = data.rows;
}
if (!group && isAllDiscrete) {
grpName = 'count';
} else if (!group && !size) {
if (xAxis && yAxis) {
grpName = '(' + xAxis.name + ', ' + yAxis.name + ')';
} else if (xAxis && !yAxis) {
grpName = xAxis.name;
} else if (!xAxis && yAxis) {
grpName = yAxis.name;
}
} else if (!group && size) {
grpName = size.name;
}
for (i = 0; i < rows.length; i++) {
row = rows[i];
if (xAxis) {
xValue = row[xAxis.index];
}
if (yAxis) {
yValue = row[yAxis.index];
}
if (group) {
grpName = row[group.index];
}
var sz = (isAllDiscrete) ? row[row.length - 1] : ((size) ? row[size.index] : 1);
if (grpNameIndex[grpName] === undefined) {
grpIndexValue[grpIdx] = grpName;
grpNameIndex[grpName] = grpIdx++;
}
if (xAxis && rowNameIndex[xValue] === undefined) {
rowIndexValue[rowIdx] = xValue;
rowNameIndex[xValue] = rowIdx++;
}
if (yAxis && colNameIndex[yValue] === undefined) {
colIndexValue[colIdx] = yValue;
colNameIndex[yValue] = colIdx++;
}
if (!d3g[grpNameIndex[grpName]]) {
d3g[grpNameIndex[grpName]] = {
key: grpName,
values: []
};
}
d3g[grpNameIndex[grpName]].values.push({
x: xAxis ? (isNaN(xValue) ? rowNameIndex[xValue] : parseFloat(xValue)) : 0,
y: yAxis ? (isNaN(yValue) ? colNameIndex[yValue] : parseFloat(yValue)) : 0,
size: isNaN(parseFloat(sz)) ? 1 : parseFloat(sz)
});
}
return {
xLabels: rowIndexValue,
yLabels: colIndexValue,
d3g: d3g
};
};
var isDiscrete = function(field) {
var getUnique = function(f) {
var uniqObj = {};
var uniqArr = [];
var j = 0;
for (var i = 0; i < f.length; i++) {
var item = f[i];
if (uniqObj[item] !== 1) {
uniqObj[item] = 1;
uniqArr[j++] = item;
}
}
return uniqArr;
};
for (var i = 0; i < field.length; i++) {
if (isNaN(parseFloat(field[i])) &&
(typeof field[i] === 'string' || field[i] instanceof String)) {
return true;
}
}
var threshold = 0.05;
var unique = getUnique(field);
if (unique.length / field.length < threshold) {
return true;
} else {
return false;
}
};
$scope.isValidSizeOption = function(options) {
var builtInViz = builtInVisualizations.scatterChart;
if (builtInViz && builtInViz.instance) {