mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-03 09:20:10 +00:00
444 lines
13 KiB
JavaScript
444 lines
13 KiB
JavaScript
// http://jsfiddle.net/stephenboak/hYuPb/
|
|
|
|
// Wrapper function
|
|
function do_pie(name, update_url, url_params, units, refresh) {
|
|
var pie = new PieChart(name, update_url, url_params, units, refresh);
|
|
if (refresh)
|
|
pie.setInterval(setInterval(function () { pie.update(); }, refresh));
|
|
|
|
// Return new class instance, with
|
|
return pie;
|
|
}
|
|
|
|
function PieChart(name, update_url, url_params, units, refresh) {
|
|
|
|
// Add object properties like this
|
|
this.name = name;
|
|
this.update_url = update_url;
|
|
this.url_params = url_params;
|
|
this.units = units;
|
|
this.refresh = refresh;
|
|
this.pieInterval;
|
|
|
|
var pieData = [];
|
|
var oldPieData = [];
|
|
var filteredPieData = [];
|
|
var rsp = create_pie_chart(name, units);
|
|
var arc_group = rsp[0];
|
|
var donut = rsp[1];
|
|
var totalValue = rsp[2];
|
|
var totalUnits = rsp[3];
|
|
var color = rsp[4];
|
|
var tweenDuration = rsp[5];
|
|
var arc = rsp[6];
|
|
var label_group = rsp[7];
|
|
var center_group = rsp[8];
|
|
var r = rsp[9];
|
|
var textOffset = rsp[10];
|
|
|
|
|
|
// to run each time data is generated
|
|
|
|
this.update = function () {
|
|
// console.log(this.name);
|
|
// console.log(this.url_params);
|
|
$.ajax({
|
|
type: 'GET',
|
|
url: this.update_url,
|
|
data: this.url_params,
|
|
success: function (content) {
|
|
let parsed_content;
|
|
|
|
if (typeof (content) == "object")
|
|
parsed_content = content;
|
|
else if (typeof (content) == "string")
|
|
parsed_content = jQuery.parseJSON(content);
|
|
|
|
if (parsed_content)
|
|
update_pie_chart(parsed_content);
|
|
}
|
|
});
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// STREAKER CONNECTION ////////////////////////////////////
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// Needed to draw the pie immediately
|
|
this.update();
|
|
this.update();
|
|
|
|
// var updateInterval = window.setInterval(update, refresh);
|
|
|
|
function compare_by_label(a, b) {
|
|
if (a.label < b.label) {
|
|
return -1;
|
|
} else if (a.label > b.label) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function update_pie_chart(data) {
|
|
if (data.rsp) // detect REST API v1
|
|
data = data.rsp;
|
|
|
|
data.sort(compare_by_label);
|
|
streakerDataAdded = data;
|
|
oldPieData = filteredPieData;
|
|
pieData = donut(streakerDataAdded);
|
|
|
|
var totalOctets = 0;
|
|
filteredPieData = pieData.filter(filterData);
|
|
function filterData(element, index, array) {
|
|
element.name = streakerDataAdded[index].label;
|
|
element.value = streakerDataAdded[index].value;
|
|
element.url = streakerDataAdded[index].url;
|
|
totalOctets += element.value;
|
|
return (element.value > 0);
|
|
}
|
|
|
|
if ((filteredPieData.length > 0) && (oldPieData.length > 0)) {
|
|
//REMOVE PLACEHOLDER CIRCLE
|
|
arc_group.selectAll("circle").remove();
|
|
|
|
if (totalValue) {
|
|
totalValue.text(function () {
|
|
var kb = totalOctets / 1024;
|
|
return kb.toFixed(1);
|
|
//return bchart.label.abbreviated(totalOctets*8);
|
|
});
|
|
}
|
|
|
|
//DRAW ARC PATHS
|
|
paths = arc_group.selectAll("path").data(filteredPieData);
|
|
paths.enter().append("svg:path")
|
|
.attr("stroke", "white")
|
|
.attr("stroke-width", 0.5)
|
|
.attr("fill", function (d, i) { return color(i); })
|
|
.transition()
|
|
.duration(tweenDuration)
|
|
.attrTween("d", pieTween);
|
|
paths
|
|
.transition()
|
|
.duration(tweenDuration)
|
|
.attrTween("d", pieTween);
|
|
paths.exit()
|
|
.transition()
|
|
.duration(tweenDuration)
|
|
.attrTween("d", removePieTween)
|
|
.remove();
|
|
|
|
//DRAW TICK MARK LINES FOR LABELS
|
|
lines = label_group.selectAll("line").data(filteredPieData);
|
|
lines.enter().append("svg:line")
|
|
.attr("x1", 0)
|
|
.attr("x2", 0)
|
|
.attr("y1", -r - 3)
|
|
.attr("y2", -r - 8)
|
|
.attr("stroke", "gray")
|
|
.attr("transform", function (d) {
|
|
return "rotate(" + (d.startAngle + d.endAngle) / 2 * (180 / Math.PI) + ")";
|
|
});
|
|
lines.transition()
|
|
.duration(tweenDuration)
|
|
.attr("transform", function (d) {
|
|
return "rotate(" + (d.startAngle + d.endAngle) / 2 * (180 / Math.PI) + ")";
|
|
});
|
|
lines.exit().remove();
|
|
|
|
//DRAW LABELS WITH PERCENTAGE VALUES
|
|
valueLabels = label_group.selectAll("text.value").data(filteredPieData)
|
|
.attr("dy", function (d) {
|
|
if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
|
|
return 5;
|
|
} else {
|
|
return -7;
|
|
}
|
|
})
|
|
.attr("text-anchor", function (d) {
|
|
if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
|
|
return "beginning";
|
|
} else {
|
|
return "end";
|
|
}
|
|
})
|
|
.text(function (d) {
|
|
|
|
const percentage = (d.value / totalOctets) * 100;
|
|
// approssimate the number to the third deciaml digit and show only the first decimal
|
|
let percentageLabel = percentage.toFixed(1) + "%";
|
|
return percentageLabel;
|
|
});
|
|
|
|
valueLabels.enter().append("svg:text")
|
|
.attr("class", "value")
|
|
.attr("transform", function (d) {
|
|
return "translate(" + Math.cos(((d.startAngle + d.endAngle - Math.PI) / 2)) * (r + textOffset) + "," + Math.sin((d.startAngle + d.endAngle - Math.PI) / 2) * (r + textOffset) + ")";
|
|
})
|
|
.attr("dy", function (d) {
|
|
if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
|
|
return 5;
|
|
} else {
|
|
return -7;
|
|
}
|
|
})
|
|
.attr("text-anchor", function (d) {
|
|
if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
|
|
return "beginning";
|
|
} else {
|
|
return "end";
|
|
}
|
|
}).text(function (d) {
|
|
if (totalOctets <= 1) return "";
|
|
const percentage = (d.value / totalOctets) * 100;
|
|
let percentageLabel = percentage.toFixed(1) + "%";
|
|
return percentageLabel;
|
|
});
|
|
|
|
valueLabels.transition().duration(tweenDuration).attrTween("transform", textTween);
|
|
valueLabels.exit().remove();
|
|
|
|
//DRAW LABELS WITH ENTITY NAMES
|
|
nameLabels = label_group.selectAll("text.units").data(filteredPieData)
|
|
.attr("dy", function (d) {
|
|
if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
|
|
return 17;
|
|
} else {
|
|
return 5;
|
|
}
|
|
})
|
|
.attr("text-anchor", function (d) {
|
|
if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
|
|
return "beginning";
|
|
} else {
|
|
return "end";
|
|
}
|
|
}).text(function (d) {
|
|
return d.name;
|
|
})
|
|
.on("click", function (d) { if (d.url) window.location.href = d.url; });
|
|
|
|
nameLabels.enter().append("svg:text")
|
|
.attr("class", "units")
|
|
.attr("transform", function (d) {
|
|
return "translate(" + Math.cos(((d.startAngle + d.endAngle - Math.PI) / 2)) * (r + textOffset) + "," + Math.sin((d.startAngle + d.endAngle - Math.PI) / 2) * (r + textOffset) + ")";
|
|
})
|
|
.attr("dy", function (d) {
|
|
if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
|
|
return 17;
|
|
} else {
|
|
return 5;
|
|
}
|
|
})
|
|
.attr("text-anchor", function (d) {
|
|
if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
|
|
return "beginning";
|
|
} else {
|
|
return "end";
|
|
}
|
|
}).text(function (d) {
|
|
return d.name;
|
|
})
|
|
.on("click", function (d) { if (d.url) window.location.href = d.url; });
|
|
|
|
nameLabels.transition().duration(tweenDuration).attrTween("transform", textTween);
|
|
|
|
nameLabels.exit().remove();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// FUNCTIONS //////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// Interpolate the arcs in data space.
|
|
function pieTween(d, i) {
|
|
var s0;
|
|
var e0;
|
|
if (oldPieData[i]) {
|
|
s0 = oldPieData[i].startAngle;
|
|
e0 = oldPieData[i].endAngle;
|
|
} else if (!(oldPieData[i]) && oldPieData[i - 1]) {
|
|
s0 = oldPieData[i - 1].endAngle;
|
|
e0 = oldPieData[i - 1].endAngle;
|
|
} else if (!(oldPieData[i - 1]) && oldPieData.length > 0) {
|
|
s0 = oldPieData[oldPieData.length - 1].endAngle;
|
|
e0 = oldPieData[oldPieData.length - 1].endAngle;
|
|
} else {
|
|
s0 = 0;
|
|
e0 = 0;
|
|
}
|
|
var i = d3.interpolate({ startAngle: s0, endAngle: e0 }, { startAngle: d.startAngle, endAngle: d.endAngle });
|
|
return function (t) {
|
|
var b = i(t);
|
|
return arc(b);
|
|
};
|
|
}
|
|
|
|
function removePieTween(d, i) {
|
|
s0 = 2 * Math.PI;
|
|
e0 = 2 * Math.PI;
|
|
var i = d3.interpolate({ startAngle: d.startAngle, endAngle: d.endAngle }, { startAngle: s0, endAngle: e0 });
|
|
return function (t) {
|
|
var b = i(t);
|
|
return arc(b);
|
|
};
|
|
}
|
|
|
|
function textTween(d, i) {
|
|
var a;
|
|
if (oldPieData[i]) {
|
|
a = (oldPieData[i].startAngle + oldPieData[i].endAngle - Math.PI) / 2;
|
|
} else if (!(oldPieData[i]) && oldPieData[i - 1]) {
|
|
a = (oldPieData[i - 1].startAngle + oldPieData[i - 1].endAngle - Math.PI) / 2;
|
|
} else if (!(oldPieData[i - 1]) && oldPieData.length > 0) {
|
|
a = (oldPieData[oldPieData.length - 1].startAngle + oldPieData[oldPieData.length - 1].endAngle - Math.PI) / 2;
|
|
} else {
|
|
a = 0;
|
|
}
|
|
var b = (d.startAngle + d.endAngle - Math.PI) / 2;
|
|
|
|
var fn = d3.interpolateNumber(a, b);
|
|
return function (t) {
|
|
var val = fn(t);
|
|
return "translate(" + Math.cos(val) * (r + textOffset) + "," + Math.sin(val) * (r + textOffset) + ")";
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// PUBLIC FUNCIONTS ////////////////////////////////////
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
|
PieChart.prototype.setUrlParams = function (url_params) {
|
|
this.url_params = url_params;
|
|
this.forceUpdate();
|
|
}
|
|
|
|
PieChart.prototype.forceUpdate = function (url_params) {
|
|
this.stopInterval();
|
|
this.update();
|
|
this.startInterval();
|
|
}
|
|
|
|
PieChart.prototype.setInterval = function (p_pieInterval) {
|
|
this.pieInterval = p_pieInterval;
|
|
}
|
|
|
|
PieChart.prototype.stopInterval = function () {
|
|
//disabled graph interval
|
|
clearInterval(this.pieInterval);
|
|
}
|
|
|
|
PieChart.prototype.startInterval = function () {
|
|
this.pieInterval = setInterval(this.update(), this.refresh)
|
|
}
|
|
///////////////////////////////////////////////////////////
|
|
// INIT FUNCIONTS ////////////////////////////////////
|
|
///////////////////////////////////////////////////////////
|
|
|
|
function create_pie_chart(name, units) {
|
|
var w = 500; //380 - Please keep in sync with pie-chart.css
|
|
var h = 325; //280
|
|
var ir = 52; //45
|
|
var textOffset = 14;
|
|
var tweenDuration = 250;
|
|
var r = 116; //100;
|
|
|
|
if ($(name).hasClass("pie-chart-small")) {
|
|
w = 330;
|
|
h = 250;
|
|
r = w / 5 + 15;
|
|
ir = r / 2;
|
|
}
|
|
|
|
var lines, valueLabels, nameLabels;
|
|
|
|
//D3 helper function to populate pie slice parameters from array data
|
|
var donut = d3.layout.pie().value(function (d) {
|
|
if (d.value == 0) { d.value = 1; } // Force to 1, in order to update the graph
|
|
return d.value;
|
|
});
|
|
|
|
//D3 helper function to create colors from an ordinal scale
|
|
var color = d3.scale.category20();
|
|
|
|
//D3 helper function to draw arcs, populates parameter "d" in path object
|
|
var arc = d3.svg.arc()
|
|
.startAngle(function (d) { return d.startAngle; })
|
|
.endAngle(function (d) { return d.endAngle; })
|
|
.innerRadius(ir)
|
|
.outerRadius(r);
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// CREATE VIS & GROUPS ////////////////////////////////////
|
|
///////////////////////////////////////////////////////////
|
|
|
|
var vis = d3.select(name).append("svg:svg")
|
|
.attr("width", w)
|
|
.attr("height", h)
|
|
.attr("viewBox", "0 0 " + w + " " + h)
|
|
.attr("preserveAspectRatio", "xMidYMid");
|
|
|
|
//GROUP FOR ARCS/PATHS
|
|
var arc_group = vis.append("svg:g")
|
|
.attr("class", "arc")
|
|
.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");
|
|
|
|
//GROUP FOR LABELS
|
|
var label_group = vis.append("svg:g")
|
|
.attr("class", "label_group")
|
|
.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");
|
|
|
|
//GROUP FOR CENTER TEXT
|
|
var center_group = vis.append("svg:g")
|
|
.attr("class", "center_group")
|
|
.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");
|
|
|
|
//PLACEHOLDER GRAY CIRCLE
|
|
var paths = arc_group.append("svg:circle")
|
|
.attr("fill", "#EFEFEF")
|
|
.attr("r", r);
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// CENTER TEXT ////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////
|
|
|
|
//WHITE CIRCLE BEHIND LABELS
|
|
var whiteCircle = center_group.append("svg:circle")
|
|
.attr("fill", "white")
|
|
.attr("r", ir);
|
|
|
|
var totalUnits = null;
|
|
var totalLabel = null;
|
|
var totalValue = null;
|
|
|
|
if (units) {
|
|
// "TOTAL" LABEL
|
|
totalLabel = center_group.append("svg:text")
|
|
.attr("class", "label")
|
|
.attr("dy", -15)
|
|
.attr("text-anchor", "middle") // text-align: right
|
|
.text("TOTAL");
|
|
|
|
//TOTAL TRAFFIC VALUE
|
|
totalValue = center_group.append("svg:text")
|
|
.attr("class", "total")
|
|
.attr("dy", 7)
|
|
.attr("text-anchor", "middle") // text-align: right
|
|
.text("Waiting...");
|
|
|
|
//UNITS LABEL
|
|
totalUnits = center_group.append("svg:text")
|
|
.attr("class", "units")
|
|
.attr("dy", 21)
|
|
.attr("text-anchor", "middle") // text-align: right
|
|
.text(units);
|
|
}
|
|
|
|
return ([arc_group, donut, totalValue, totalUnits, color, tweenDuration, arc, label_group, center_group, r, textOffset]);
|
|
}
|