Enhance pie chart capabilities

Add support for distribution pie charts

Change-Id: I59e31be87b59c38a59ad861e270d6e20a6493432
Implements: blueprint piechart-enhancement
This commit is contained in:
Ana Krivokapic 2014-02-24 20:00:16 +01:00
parent 54e4dab456
commit 1317e4273e
4 changed files with 219 additions and 86 deletions

View File

@ -42,7 +42,7 @@
Json with variety of settings described below.
used-label-placement='string' OPTIONAL
String determinign where the floating label stating number of percent
String determining where the floating label stating number of percent
will be placed. So far only left is supported.
width="integer" OPTIONAL

View File

@ -1,31 +1,64 @@
/*
Draw pie chart in d3.
Draw pie charts in d3.
To use, a div is required with the class .d3_pie_chart
and a data-used attribute in the div
that stores the percentage to fill the chart
To use, a div is required with the class .d3_pie_chart_usage or
.d3_pie_chart_distribution, and a data-used attribute in the div
that stores the data used to fill the chart.
Example:
<div class="d3_pie_chart"
Example (usage):
<div class="d3_pie_chart_usage"
data-used="{% widthratio current_val max_val 100 %}">
</div>
Example (distribution):
<div class="d3_pie_chart_distribution"
data-used="Controller=1|Compute=2|Object Storage=3|Block Storage=4">
</div>
*/
horizon.d3_pie_chart = {
w: 100,
h: 100,
r: 45,
bkgrnd: "#F2F2F2",
frgrnd: "#006CCF",
full: "#D0342B",
nearlyfull: "orange",
// Pie chart dimensions
var WIDTH = 100;
var HEIGHT = 100;
var RADIUS = 45;
// Colors
var BKGRND = "#F2F2F2";
var FRGRND = "#006CCF";
var FULL = "#D0342B";
var NEARLY_FULL = "#FFA500";
var STROKE = "#CCCCCC";
function create_vis(chart) {
return d3.select(chart).append("svg:svg")
.attr("class", "chart")
.attr("width", WIDTH)
.attr("height", HEIGHT)
.append("g")
.attr("transform",
"translate(" + (RADIUS + 2) + "," + (RADIUS + 2) + ")");
}
function create_arc() {
return d3.svg.arc()
.outerRadius(RADIUS)
.innerRadius(0);
}
function create_pie(param) {
return d3.layout.pie()
.sort(null)
.value(function(d){ return d[param]; });
}
horizon.d3_pie_chart_usage = {
init: function() {
var self = this;
// Pie Charts
var pie_chart_data = $(".d3_pie_chart");
self.chart = d3.selectAll(".d3_pie_chart");
var pie_chart_data = $(".d3_pie_chart_usage");
self.chart = d3.selectAll(".d3_pie_chart_usage");
for (var i = 0; i < pie_chart_data.length; i++) {
var used = Math.min(parseInt($(pie_chart_data[i]).data("used")), 100);
@ -36,79 +69,179 @@ horizon.d3_pie_chart = {
// Draw a pie chart
pieChart: function(i) {
var self = this;
var vis = d3.select(self.chart[0][i]).append("svg:svg")
.attr("class", "chart")
.attr("width", self.w)
.attr("height", self.h)
.append("g")
.attr("transform",
"translate(" + (self.r + 2) + "," + (self.r + 2) + ")");
var arc = d3.svg.arc()
.outerRadius(self.r)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d){ return d.percentage; });
var vis = create_vis(self.chart[0][i]);
var arc = create_arc();
var pie = create_pie("percentage");
// Draw an empty pie chart
var piechart = vis.selectAll(".arc")
vis.selectAll(".arc")
.data(pie([{"percentage":10}]))
.enter()
.append("path")
.attr("class","arc")
.attr("d", arc)
.style("fill", function(d){
if (self.data[0].percentage >= 100) {
return self.full;
} else if (self.data[0].percentage >= 80) {
return self.nearlyfull;
} else {
return self.frgrnd;
}
})
.style("stroke", "#CCCCCC")
.style("stroke-width", 1)
.each(function(d) {
self.current = d;
return d;
});
.append("path")
.attr("class","arc")
.attr("d", arc)
.style("fill", function(){
if (self.data[0].percentage >= 100) {
return FULL;
} else if (self.data[0].percentage >= 80) {
return NEARLY_FULL;
} else {
return FRGRND;
}
})
.style("stroke", STROKE)
.style("stroke-width", 1)
.each(function(d) {
self.current = d;
return d;
});
// Animate filling the pie chart
animate = function(data) {
var piechart = vis.selectAll(".arc")
var animate = function(data) {
vis.selectAll(".arc")
.data(pie(data))
.enter()
.append("path")
.attr("class","arc")
.attr("d", arc)
.style("fill", self.bkgrnd)
.style("stroke", "#CCCCCC")
.style("stroke-width", function(d) {
if (self.data[0].percentage >= 100) {
return 0;
} else {
return 1;
}
})
.each(function(d) {
self.current = d;
return d;
})
.append("path")
.attr("class","arc")
.attr("d", arc)
.style("fill", BKGRND)
.style("stroke", STROKE)
.style("stroke-width", function() {
if (self.data[0].percentage >= 100) {
return 0;
} else {
return 1;
}
})
.each(function(d) {
self.current = d;
return d;
})
.transition()
.duration(500)
.attrTween("d", function(a) {
var tween = d3.interpolate(self.current, a);
self.current = tween(0);
return function(t) { return arc(tween(t)); };
});
.duration(500)
.attrTween("d", function(a) {
var tween = d3.interpolate(self.current, a);
self.current = tween(0);
return function(t) { return arc(tween(t)); };
});
};
animate(self.data);
}
};
horizon.d3_pie_chart_distribution = {
colors: d3.scale.category20(),
init: function() {
var self = this;
var pie_chart_data = $(".d3_pie_chart_distribution");
self.chart = d3.selectAll(".d3_pie_chart_distribution");
for (var i = 0; i < pie_chart_data.length; i++) {
var parts = $(pie_chart_data[i]).data("used").split("|");
self.data = [];
self.keys = [];
for (var j = 0; j < parts.length; j++) {
var key_value = parts[j].split("=");
var d = {};
d["key"] = key_value[0];
d["value"] = key_value[1];
self.data.push(d);
self.keys.push(key_value[0]);
}
self.pieChart(i);
}
},
// Draw a pie chart
pieChart: function(i) {
var self = this;
var vis = create_vis(self.chart[0][i]);
var arc = create_arc();
var pie = create_pie("value");
var total = 0;
for (var j = 0; j < self.data.length; j++) {
total = total + parseInt(self.data[j]["value"]);
}
// Draw an empty pie chart
vis.selectAll(".arc")
.data(pie([]))
.enter()
.append("path")
.attr("class","arc")
.attr("d", arc)
.style("stroke", STROKE)
.style("stroke-width", 1);
// Animate filling the pie chart
var animate = function(data) {
vis.selectAll(".arc")
.data(pie(data))
.enter()
.append("path")
.attr("class","arc")
.attr("d", arc)
.style("fill", function(d) { return self.colors(d.data.key); })
.style("stroke", STROKE)
.style("stroke-width", 1)
.transition()
.duration(500)
.attrTween("d", function(start) {
start.endAngle = start.startAngle;
var end = jQuery.extend({}, start);
end.endAngle = end.startAngle + 2 * Math.PI / total * end.value;
var tween = d3.interpolate(start, end);
return function(t) { return arc(tween(t)); };
});
};
animate(self.data);
// Add a legend
var legend = d3.select(self.chart[0][i])
.append("svg")
.attr("class", "legend")
.attr("width", WIDTH * 2)
.attr("height", self.data.length * 18 + 20)
.selectAll("g")
.data(self.keys)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", self.colors);
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) {
var value = 0;
for (var j = 0; j < self.data.length; j++) {
if (self.data[j]["key"] == d) {
value = self.data[j]["value"];
break;
}
}
return d + " " + Math.round(value/total * 100) + "%";
});
}
};
horizon.addInitFunction(function () {
horizon.d3_pie_chart.init();
horizon.d3_pie_chart_usage.init();
});
horizon.addInitFunction(function () {
horizon.d3_pie_chart_distribution.init();
});

View File

@ -3,49 +3,49 @@
<div class="quota-dynamic">
<h3 class="quota-heading">{% trans "Limit Summary" %}</h3>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalInstancesUsed usage.limits.maxTotalInstances 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio usage.limits.totalInstancesUsed usage.limits.maxTotalInstances 100 %}"></div>
<strong>{% trans "Instances" %} <br />
{% blocktrans with used=usage.limits.totalInstancesUsed|intcomma available=usage.limits.maxTotalInstances|quotainf|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalCoresUsed usage.limits.maxTotalCores 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio usage.limits.totalCoresUsed usage.limits.maxTotalCores 100 %}"></div>
<strong>{% trans "VCPUs" %} <br />
{% blocktrans with used=usage.limits.totalCoresUsed|intcomma available=usage.limits.maxTotalCores|quotainf|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalRAMUsed usage.limits.maxTotalRAMSize 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio usage.limits.totalRAMUsed usage.limits.maxTotalRAMSize 100 %}"></div>
<strong>{% trans "RAM" %} <br />
{% blocktrans with used=usage.limits.totalRAMUsed|mb_float_format available=usage.limits.maxTotalRAMSize|quotainf|mb_float_format %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalFloatingIpsUsed usage.limits.maxTotalFloatingIps 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio usage.limits.totalFloatingIpsUsed usage.limits.maxTotalFloatingIps 100 %}"></div>
<strong>{% trans "Floating IPs" %} <br />
{% blocktrans with used=usage.limits.totalFloatingIpsUsed|intcomma available=usage.limits.maxTotalFloatingIps|quotainf|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalSecurityGroupsUsed usage.limits.maxSecurityGroups 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio usage.limits.totalSecurityGroupsUsed usage.limits.maxSecurityGroups 100 %}"></div>
<strong>{% trans "Security Groups" %} <br />
{% blocktrans with used=usage.limits.totalSecurityGroupsUsed|intcomma available=usage.limits.maxSecurityGroups|quotainf|intcomma%}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
{% if usage.limits.totalVolumesUsed >= 0 %}
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalVolumesUsed usage.limits.maxTotalVolumes 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio usage.limits.totalVolumesUsed usage.limits.maxTotalVolumes 100 %}"></div>
<strong>{% trans "Volumes" %} <br />
{% blocktrans with used=usage.limits.totalVolumesUsed|intcomma available=usage.limits.maxTotalVolumes|quotainf|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalGigabytesUsed usage.limits.maxTotalVolumeGigabytes 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio usage.limits.totalGigabytesUsed usage.limits.maxTotalVolumeGigabytes 100 %}"></div>
<strong>{% trans "Volume Storage" %} <br />
{% blocktrans with used=usage.limits.totalGigabytesUsed|diskgbformat available=usage.limits.maxTotalVolumeGigabytes|quotainf|diskgbformat %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>

View File

@ -11,21 +11,21 @@
<div class="quota-dynamic">
<h3>{% trans "Hypervisor Summary" %}</h3>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio stats.vcpus_used stats.vcpus 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio stats.vcpus_used stats.vcpus 100 %}"></div>
<strong>{% trans "VCPU Usage" %} <br />
{% blocktrans with used=stats.vcpus_used|intcomma available=stats.vcpus|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio stats.memory_mb_used stats.memory_mb 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio stats.memory_mb_used stats.memory_mb 100 %}"></div>
<strong>{% trans "Memory Usage" %} <br />
{% blocktrans with used=stats.memory_mb_used|mbformat available=stats.memory_mb|mbformat %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio stats.local_gb_used stats.local_gb 100 %}"></div>
<div class="d3_pie_chart_usage" data-used="{% widthratio stats.local_gb_used stats.local_gb 100 %}"></div>
<strong>{% trans "Disk Usage" %} <br />
{% blocktrans with used=stats.local_gb_used|diskgbformat available=stats.local_gb|diskgbformat %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>