|
|
@ -339,7 +339,11 @@ LineGraph.prototype.setGroups = function(groups) { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Update the datapoints |
|
|
|
* @param [ids] |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
LineGraph.prototype._onUpdate = function(ids) { |
|
|
|
this._updateUngrouped(); |
|
|
|
this._updateAllGroupData(); |
|
|
@ -417,7 +421,8 @@ LineGraph.prototype._updateGroup = function (group, groupId) { |
|
|
|
LineGraph.prototype._updateAllGroupData = function () { |
|
|
|
if (this.itemsData != null) { |
|
|
|
var groupsContent = {}; |
|
|
|
for (var groupId in this.groups) { |
|
|
|
var groupId; |
|
|
|
for (groupId in this.groups) { |
|
|
|
if (this.groups.hasOwnProperty(groupId)) { |
|
|
|
groupsContent[groupId] = []; |
|
|
|
} |
|
|
@ -429,7 +434,7 @@ LineGraph.prototype._updateAllGroupData = function () { |
|
|
|
groupsContent[item.group].push(item); |
|
|
|
} |
|
|
|
} |
|
|
|
for (var groupId in this.groups) { |
|
|
|
for (groupId in this.groups) { |
|
|
|
if (this.groups.hasOwnProperty(groupId)) { |
|
|
|
this.groups[groupId].setItems(groupsContent[groupId]); |
|
|
|
} |
|
|
@ -467,20 +472,6 @@ LineGraph.prototype._updateUngrouped = function() { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// much much slower
|
|
|
|
// var datapoints = this.itemsData.get({
|
|
|
|
// filter: function (item) {return item.group === undefined;},
|
|
|
|
// showInternalIds:true
|
|
|
|
// });
|
|
|
|
// if (datapoints.length > 0) {
|
|
|
|
// var updateQuery = [];
|
|
|
|
// for (var i = 0; i < datapoints.length; i++) {
|
|
|
|
// updateQuery.push({id:datapoints[i].id, group: UNGROUPED});
|
|
|
|
// }
|
|
|
|
// this.itemsData.update(updateQuery, true);
|
|
|
|
// }
|
|
|
|
// var t1 = new Date();
|
|
|
|
// var pointInUNGROUPED = this.itemsData.get({filter: function (item) {return item.group == UNGROUPED;}});
|
|
|
|
if (ungroupedCounter == 0) { |
|
|
|
delete this.groups[UNGROUPED]; |
|
|
|
this.legendLeft.removeGroup(UNGROUPED); |
|
|
@ -488,8 +479,6 @@ LineGraph.prototype._updateUngrouped = function() { |
|
|
|
this.yAxisLeft.removeGroup(UNGROUPED); |
|
|
|
this.yAxisRight.removeGroup(UNGROUPED); |
|
|
|
} |
|
|
|
// console.log("getting amount ungrouped",new Date() - t1);
|
|
|
|
// console.log("putting in ungrouped",new Date() - t0);
|
|
|
|
} |
|
|
|
else { |
|
|
|
delete this.groups[UNGROUPED]; |
|
|
@ -618,17 +607,17 @@ LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, |
|
|
|
// what data we need to draw. Sorted data is much faster.
|
|
|
|
// more optimization is possible by doing the sampling before and using the binary search
|
|
|
|
// to find the end date to determine the increment.
|
|
|
|
var group; |
|
|
|
var group, i, j, item; |
|
|
|
if (groupIds.length > 0) { |
|
|
|
for (var i = 0; i < groupIds.length; i++) { |
|
|
|
for (i = 0; i < groupIds.length; i++) { |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
groupsData[groupIds[i]] = []; |
|
|
|
var dataContainer = groupsData[groupIds[i]]; |
|
|
|
// optimization for sorted data
|
|
|
|
if (group.options.sort == true) { |
|
|
|
var guess = Math.max(0, util.binarySearchGeneric(group.itemsData, minDate, 'x', 'before')); |
|
|
|
for (var j = guess; j < group.itemsData.length; j++) { |
|
|
|
var item = group.itemsData[j]; |
|
|
|
for (j = guess; j < group.itemsData.length; j++) { |
|
|
|
item = group.itemsData[j]; |
|
|
|
if (item !== undefined) { |
|
|
|
if (item.x > maxDate) { |
|
|
|
dataContainer.push(item); |
|
|
@ -641,8 +630,8 @@ LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
for (var j = 0; j < group.itemsData.length; j++) { |
|
|
|
var item = group.itemsData[j]; |
|
|
|
for (j = 0; j < group.itemsData.length; j++) { |
|
|
|
item = group.itemsData[j]; |
|
|
|
if (item !== undefined) { |
|
|
|
if (item.x > minDate && item.x < maxDate) { |
|
|
|
dataContainer.push(item); |
|
|
@ -663,64 +652,70 @@ LineGraph.prototype._applySampling = function (groupIds, groupsData) { |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
if (group.options.sampling == true) { |
|
|
|
var dataContainer = groupsData[groupIds[i]]; |
|
|
|
var increment = 1; |
|
|
|
var amountOfPoints = dataContainer.length; |
|
|
|
if (dataContainer.length > 0) { |
|
|
|
var increment = 1; |
|
|
|
var amountOfPoints = dataContainer.length; |
|
|
|
|
|
|
|
// the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop
|
|
|
|
// of width changing of the yAxis.
|
|
|
|
var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); |
|
|
|
var pointsPerPixel = amountOfPoints / xDistance; |
|
|
|
increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); |
|
|
|
// the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop
|
|
|
|
// of width changing of the yAxis.
|
|
|
|
var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); |
|
|
|
var pointsPerPixel = amountOfPoints / xDistance; |
|
|
|
increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); |
|
|
|
|
|
|
|
var sampledData = []; |
|
|
|
for (var j = 0; j < amountOfPoints; j += increment) { |
|
|
|
sampledData.push(dataContainer[j]); |
|
|
|
var sampledData = []; |
|
|
|
for (var j = 0; j < amountOfPoints; j += increment) { |
|
|
|
sampledData.push(dataContainer[j]); |
|
|
|
|
|
|
|
} |
|
|
|
groupsData[groupIds[i]] = sampledData; |
|
|
|
} |
|
|
|
groupsData[groupIds[i]] = sampledData; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { |
|
|
|
var groupData, group; |
|
|
|
var groupData, group, i,j; |
|
|
|
var barCombinedDataLeft = []; |
|
|
|
var barCombinedDataRight = []; |
|
|
|
var barCombinedData; |
|
|
|
if (groupIds.length > 0) { |
|
|
|
for (var i = 0; i < groupIds.length; i++) { |
|
|
|
for (i = 0; i < groupIds.length; i++) { |
|
|
|
groupData = groupsData[groupIds[i]]; |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
if (group.options.style == 'line' || group.options.barChart.handleOverlap != "stack") { |
|
|
|
var yMin = groupData[0].y; |
|
|
|
var yMax = groupData[0].y; |
|
|
|
for (var j = 0; j < groupData.length; j++) { |
|
|
|
yMin = yMin > groupData[j].y ? groupData[j].y : yMin; |
|
|
|
yMax = yMax < groupData[j].y ? groupData[j].y : yMax; |
|
|
|
} |
|
|
|
groupRanges[groupIds[i]] = {min: yMin, max: yMax, yAxisOrientation: group.options.yAxisOrientation}; |
|
|
|
} |
|
|
|
else if (group.options.style == 'bar') { |
|
|
|
if (group.options.yAxisOrientation == 'left') { |
|
|
|
barCombinedData = barCombinedDataLeft; |
|
|
|
} |
|
|
|
else { |
|
|
|
barCombinedData = barCombinedDataRight; |
|
|
|
if (groupData.length > 0) { |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
if (group.options.style == 'line' || group.options.barChart.handleOverlap != "stack") { |
|
|
|
var yMin = groupData[0].y; |
|
|
|
var yMax = groupData[0].y; |
|
|
|
for (j = 0; j < groupData.length; j++) { |
|
|
|
yMin = yMin > groupData[j].y ? groupData[j].y : yMin; |
|
|
|
yMax = yMax < groupData[j].y ? groupData[j].y : yMax; |
|
|
|
} |
|
|
|
groupRanges[groupIds[i]] = {min: yMin, max: yMax, yAxisOrientation: group.options.yAxisOrientation}; |
|
|
|
} |
|
|
|
else if (group.options.style == 'bar') { |
|
|
|
if (group.options.yAxisOrientation == 'left') { |
|
|
|
barCombinedData = barCombinedDataLeft; |
|
|
|
} |
|
|
|
else { |
|
|
|
barCombinedData = barCombinedDataRight; |
|
|
|
} |
|
|
|
|
|
|
|
groupRanges[groupIds[i]] = {min: 0, max: 0, yAxisOrientation: group.options.yAxisOrientation, ignore: true}; |
|
|
|
groupRanges[groupIds[i]] = {min: 0, max: 0, yAxisOrientation: group.options.yAxisOrientation, ignore: true}; |
|
|
|
|
|
|
|
// combine data
|
|
|
|
for (var j = 0; j < groupData.length; j++) { |
|
|
|
barCombinedData.push({ |
|
|
|
x: groupData[j].x, |
|
|
|
y: groupData[j].y, |
|
|
|
groupId: groupIds[i] |
|
|
|
}); |
|
|
|
// combine data
|
|
|
|
for (j = 0; j < groupData.length; j++) { |
|
|
|
barCombinedData.push({ |
|
|
|
x: groupData[j].x, |
|
|
|
y: groupData[j].y, |
|
|
|
groupId: groupIds[i] |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var intersections; |
|
|
|
if (barCombinedDataLeft.length > 0) { |
|
|
|
// sort by time and by group
|
|
|
|
barCombinedDataLeft.sort(function (a, b) { |
|
|
@ -729,8 +724,8 @@ LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { |
|
|
|
} else { |
|
|
|
return a.x - b.x; |
|
|
|
} |
|
|
|
}) |
|
|
|
var intersections = {}; |
|
|
|
}); |
|
|
|
intersections = {}; |
|
|
|
this._getDataIntersections(intersections, barCombinedDataLeft); |
|
|
|
groupRanges["__barchartLeft"] = this._getStackedBarYRange(intersections, barCombinedDataLeft); |
|
|
|
groupRanges["__barchartLeft"].yAxisOrientation = "left"; |
|
|
@ -744,8 +739,8 @@ LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { |
|
|
|
} else { |
|
|
|
return a.x - b.x; |
|
|
|
} |
|
|
|
}) |
|
|
|
var intersections = {}; |
|
|
|
}); |
|
|
|
intersections = {}; |
|
|
|
this._getDataIntersections(intersections, barCombinedDataRight); |
|
|
|
groupRanges["__barchartRight"] = this._getStackedBarYRange(intersections, barCombinedDataRight); |
|
|
|
groupRanges["__barchartRight"].yAxisOrientation = "right"; |
|
|
@ -781,7 +776,8 @@ LineGraph.prototype._getStackedBarYRange = function (intersections, combinedData |
|
|
|
|
|
|
|
/** |
|
|
|
* this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. |
|
|
|
* @param {array} groupIds |
|
|
|
* @param {Array} groupIds |
|
|
|
* @param {Object} groupRanges |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { |
|
|
@ -789,23 +785,24 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { |
|
|
|
var yAxisLeftUsed = false; |
|
|
|
var yAxisRightUsed = false; |
|
|
|
var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; |
|
|
|
|
|
|
|
// if groups are present
|
|
|
|
if (groupIds.length > 0) { |
|
|
|
for (var i = 0; i < groupIds.length; i++) { |
|
|
|
if (groupRanges[groupIds[i]].ignore !== true) { |
|
|
|
minVal = groupRanges[groupIds[i]].min; |
|
|
|
maxVal = groupRanges[groupIds[i]].max; |
|
|
|
|
|
|
|
if (groupRanges[groupIds[i]].yAxisOrientation == 'left') { |
|
|
|
yAxisLeftUsed = true; |
|
|
|
minLeft = minLeft > minVal ? minVal : minLeft; |
|
|
|
maxLeft = maxLeft < maxVal ? maxVal : maxLeft; |
|
|
|
} |
|
|
|
else { |
|
|
|
yAxisRightUsed = true; |
|
|
|
minRight = minRight > minVal ? minVal : minRight; |
|
|
|
maxRight = maxRight < maxVal ? maxVal : maxRight; |
|
|
|
if (groupRanges.hasOwnProperty(groupIds[i])) { |
|
|
|
if (groupRanges[groupIds[i]].ignore !== true) { |
|
|
|
minVal = groupRanges[groupIds[i]].min; |
|
|
|
maxVal = groupRanges[groupIds[i]].max; |
|
|
|
|
|
|
|
if (groupRanges[groupIds[i]].yAxisOrientation == 'left') { |
|
|
|
yAxisLeftUsed = true; |
|
|
|
minLeft = minLeft > minVal ? minVal : minLeft; |
|
|
|
maxLeft = maxLeft < maxVal ? maxVal : maxLeft; |
|
|
|
} |
|
|
|
else { |
|
|
|
yAxisRightUsed = true; |
|
|
|
minRight = minRight > minVal ? minVal : minRight; |
|
|
|
maxRight = maxRight < maxVal ? maxVal : maxRight; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -883,14 +880,15 @@ LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { |
|
|
|
|
|
|
|
/** |
|
|
|
* draw a bar graph |
|
|
|
* @param datapoints |
|
|
|
* @param group |
|
|
|
* |
|
|
|
* @param groupIds |
|
|
|
* @param processedGroupData |
|
|
|
*/ |
|
|
|
LineGraph.prototype._drawBarGraphs = function (groupIds, processedGroupData) { |
|
|
|
var combinedData = []; |
|
|
|
var intersections = {}; |
|
|
|
var coreDistance; |
|
|
|
var key; |
|
|
|
var key, drawData; |
|
|
|
var group; |
|
|
|
var i,j; |
|
|
|
var barPoints = 0; |
|
|
@ -936,14 +934,14 @@ LineGraph.prototype._drawBarGraphs = function (groupIds, processedGroupData) { |
|
|
|
if (intersections[key] === undefined) { |
|
|
|
if (i+1 < combinedData.length) {coreDistance = Math.abs(combinedData[i+1].x - key);} |
|
|
|
if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[i-1].x - key));} |
|
|
|
var drawData = this._getSafeDrawData(coreDistance, group, minWidth); |
|
|
|
drawData = this._getSafeDrawData(coreDistance, group, minWidth); |
|
|
|
} |
|
|
|
else { |
|
|
|
var nextKey = i + (intersections[key].amount - intersections[key].resolved); |
|
|
|
var prevKey = i - (intersections[key].resolved + 1); |
|
|
|
if (nextKey < combinedData.length) {coreDistance = Math.abs(combinedData[nextKey].x - key);} |
|
|
|
if (prevKey > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[prevKey].x - key));} |
|
|
|
var drawData = this._getSafeDrawData(coreDistance, group, minWidth); |
|
|
|
drawData = this._getSafeDrawData(coreDistance, group, minWidth); |
|
|
|
intersections[key].resolved += 1; |
|
|
|
|
|
|
|
if (group.options.barChart.handleOverlap == 'stack') { |
|
|
@ -953,8 +951,8 @@ LineGraph.prototype._drawBarGraphs = function (groupIds, processedGroupData) { |
|
|
|
else if (group.options.barChart.handleOverlap == 'sideBySide') { |
|
|
|
drawData.width = drawData.width / intersections[key].amount; |
|
|
|
drawData.offset += (intersections[key].resolved) * drawData.width - (0.5*drawData.width * (intersections[key].amount+1)); |
|
|
|
if (group.options.barChart.align == 'left') {offset -= 0.5*drawData.width;} |
|
|
|
else if (group.options.barChart.align == 'right') {offset += 0.5*drawData.width;} |
|
|
|
if (group.options.barChart.align == 'left') {drawData.offset -= 0.5*drawData.width;} |
|
|
|
else if (group.options.barChart.align == 'right') {drawData.offset += 0.5*drawData.width;} |
|
|
|
} |
|
|
|
} |
|
|
|
DOMutil.drawBar(combinedData[i].x + drawData.offset, combinedData[i].y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].y, group.className + ' bar', this.svgElements, this.svg); |
|
|
@ -965,7 +963,12 @@ LineGraph.prototype._drawBarGraphs = function (groupIds, processedGroupData) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Fill the intersections object with counters of how many datapoints share the same x coordinates |
|
|
|
* @param intersections |
|
|
|
* @param combinedData |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
LineGraph.prototype._getDataIntersections = function (intersections, combinedData) { |
|
|
|
// get intersections
|
|
|
|
var coreDistance; |
|
|
@ -985,10 +988,15 @@ LineGraph.prototype._getDataIntersections = function (intersections, combinedDat |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
//LineGraph.prototype._accumulate = function (intersections, combinedData) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Get the width and offset for bargraphs based on the coredistance between datapoints |
|
|
|
* |
|
|
|
* @param coreDistance |
|
|
|
* @param group |
|
|
|
* @param minWidth |
|
|
|
* @returns {{width: Number, offset: Number}} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
LineGraph.prototype._getSafeDrawData = function (coreDistance, group, minWidth) { |
|
|
|
var width, offset; |
|
|
|
if (coreDistance < group.options.barChart.width && coreDistance > 0) { |
|
|
@ -1003,7 +1011,7 @@ LineGraph.prototype._getSafeDrawData = function (coreDistance, group, minWidth) |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
// no collisions, plot with default settings
|
|
|
|
// default settings
|
|
|
|
width = group.options.barChart.width; |
|
|
|
offset = 0; |
|
|
|
if (group.options.barChart.align == 'left') { |
|
|
@ -1021,7 +1029,7 @@ LineGraph.prototype._getSafeDrawData = function (coreDistance, group, minWidth) |
|
|
|
/** |
|
|
|
* draw a line graph |
|
|
|
* |
|
|
|
* @param datapoints |
|
|
|
* @param dataset |
|
|
|
* @param group |
|
|
|
*/ |
|
|
|
LineGraph.prototype._drawLineGraph = function (dataset, group) { |
|
|
@ -1067,10 +1075,11 @@ LineGraph.prototype._drawLineGraph = function (dataset, group) { |
|
|
|
/** |
|
|
|
* draw the data points |
|
|
|
* |
|
|
|
* @param dataset |
|
|
|
* @param JSONcontainer |
|
|
|
* @param svg |
|
|
|
* @param group |
|
|
|
* @param {Array} dataset |
|
|
|
* @param {Object} JSONcontainer |
|
|
|
* @param {Object} svg | SVG DOM element |
|
|
|
* @param {GraphGroup} group |
|
|
|
* @param {Number} [offset] |
|
|
|
*/ |
|
|
|
LineGraph.prototype._drawPoints = function (dataset, group, JSONcontainer, svg, offset) { |
|
|
|
if (offset === undefined) {offset = 0;} |
|
|
|