Drupal.behaviors.tripalFeature_adminSummaryChart = {
attach: function (context, settings) {
// Set-up the dimensions for our chart canvas.
// Note: these are adjusted below so think of these are your minimum size.
var margin = {top: 20, right: 50, bottom: 20, left: 100},
fullWidth = document.getElementById('tripal-feature-admin-summary').offsetWidth,
width = fullWidth - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var color = d3.scale.ordinal()
.range(["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a","#ffff99","#b15928"]);
var formatNum = d3.format("0,000");
// The data was parsed and saved into tripalFeature.admin.summary
// in the preprocess function for this template.
if (Drupal.settings.tripalFeature.admin.summary) {
// Determine the max number of characters in both the type name
// and the total number of features per bar for use in width/magin adjustments.
var maxTypeLength = 0;
var maxTotalLength = 0;
var numBars = Drupal.settings.tripalFeature.admin.summary.length;
for(var i=0; i < numBars; i++){
var element = Drupal.settings.tripalFeature.admin.summary[i];
if(element.name.length > maxTypeLength){
maxTypeLength = element.name.length;
}
if(element.total_features.length > maxTotalLength){
maxTotalLength = element.total_features.length;
}
}
// Ensure a minimum in case something goes wrong...
if (maxTotalLength < 4) { maxTotalLength = 4; }
if (maxTypeLength < 10) { maxTypeLength = 10; }
// Adjust our bottom margin based on the length of type names in the data.
// Assume 4px/character based on the slope of the label.
xAxisHeight = maxTypeLength * 3;
margin.bottom = xAxisHeight + 25;
// Adjust the width of the chart based on the number or bars (types)
// and the length of the bar totals which need to fit on the top of the bar.
// Assume 9px/character since it's not rotated.
if ((width + margin.left + margin.right) < (numBars * (maxTotalLength * 9))) {
width = numBars * (maxTotalLength * 9);
}
// Determine the best place for the legend. Default to top since that
// will for sure not cause conflict... even though it looks better
// on the right ;).
// Logic: If the difference between the max & min bar heights is greater
// than 1/2 the chart height (max bar height) then there "should"
// be room for the chart nested on the right.
minBarHeight = d3.min(Drupal.settings.tripalFeature.admin.summary, function(d,i) { return d.total_features; });
barHeightDifference = Drupal.settings.tripalFeature.admin.maxBarHeight - minBarHeight;
if (barHeightDifference >= Drupal.settings.tripalFeature.admin.maxBarHeight/2) {
Drupal.settings.tripalFeature.admin.legendPosition = 'right';
}
// Also if we need to put the legend along the top we need to
// increase the top margin.
if (Drupal.settings.tripalFeature.admin.legendPosition == 'top') {
// Draw a top legend in the margin.
var columnWidth = d3.max(Drupal.settings.tripalFeature.admin.organisms, function(d,i) {return d.length;}) * 10;
var maxNumColumns = Math.round(width / columnWidth);
var numRows = Math.ceil(Drupal.settings.tripalFeature.admin.organisms.length / maxNumColumns);
var idealNumColumns = Math.round(Drupal.settings.tripalFeature.admin.organisms.length / numRows);
var legendMargin = {
left: (width - (idealNumColumns * columnWidth))/2,
right: (width - (idealNumColumns * columnWidth))/2,
bottom: 25
};
margin.top = margin.top + (numRows * 20) + legendMargin.bottom;
}
// Set-up the scales of the chart.
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
// Now set-up the axis functions.
var xAxis = d3.svg.axis()
.scale(x0)
.orient('bottom');
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.ticks(10, '');
// Create our chart canvas.
var svg = d3.select('#tripal-feature-admin-summary-chart').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// map the data to the x & y axis' of our chart.
data = Drupal.settings.tripalFeature.admin.summary;
x0.domain(data.map(function(d) { return d.name; }));
x1.domain(Drupal.settings.tripalFeature.admin.organisms).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, Drupal.settings.tripalFeature.admin.maxBarHeight]);
// Create the x-axis.
var xaxis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
xaxis.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) { return "rotate(-25)"; });
// Label the x-axis.
xaxis.append('g')
.attr('class', 'axis-label')
.attr('transform', 'translate(' + width/2 + ',' + xAxisHeight + ')')
.append('text')
.attr('font-size', '16px')
.attr('dy', '.71em')
.style('text-anchor', 'middle')
.text('Types of Features');
// Create the y-axis.
var yaxis = svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
// Label the y-axis.
yaxis.append('g')
.attr('class', 'axis-label')
.attr('transform', 'translate(-70,' + height/2 + ')')
.append('text')
.attr('transform', 'rotate(-90)')
.attr('font-size', '16px')
.attr('dy', '.71em')
.style('text-anchor', 'middle')
.text('Total Number of Features');
// Add a g element to contain each set of bars (1 per type).
var type = svg.selectAll(".type")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.name) + ",0)"; });
// Now add the bars :)
// Keep in mind some processing was done in the preprocess function to
// generate the bars array based on the organisms array
// and pre-calculated the y0 & y1 used here.
type.selectAll("rect")
.data(function(d) { return d.bars; })
.enter().append("rect")
.attr("width", x0.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); })
.append("svg:title")
.text(function(d) { return formatNum(d.y1 - d.y0); });
// Add the total to the top of the bar.
svg.selectAll("g.bar-totals")
.data(data)
.enter().append('g')
.classed('bar-totals', true)
.append("text")
.attr("class", "bar-label")
.attr("text-anchor", "middle")
.attr('font-size', '10px')
.attr("x", function(d) { return x0(d.name) + x0.rangeBand()/2; })
.attr("y", function(d) { return y(d.total_features) -5; })
.text(function(d) { return formatNum(d.total_features); });
// Legend
//---------
// NOTE: we would prefer to have the legend overlapping the chart on the
// right side but this could be a problem if you have a similar count
// for all features (legend overlaps bars) or a large number of types
// (legend gets pushed out of the viewable screen area). In these cases
// switch to a bottom legend.
if (Drupal.settings.tripalFeature.admin.legendPosition == 'top') {
// Draw a bottom legend in the margin.
var legend = svg.append('g')
.classed('legend', true)
.attr('transform', 'translate(' + (legendMargin.left - 20) + ',-' + margin.top + ')');
var legendItem = legend.selectAll('g')
.data(Drupal.settings.tripalFeature.admin.organisms.slice().reverse())
.enter().append("g")
.attr("class", "legend-item")
.attr("transform", function(d,i) {
xOff = (i % idealNumColumns) * columnWidth;
yOff = Math.floor(i / idealNumColumns) * 25;
return "translate(" + xOff + "," + yOff + ")"
});
legendItem.append("rect")
//.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legendItem.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.attr('font-style','italic')
.text(function(d) { return d; });
}
else {
// Draw a right inset legend.
var legend = svg.append('g')
.classed('legend', true)
.attr('transform', 'translate(0,-' + margin.top + ')');
var legendItem = legend.selectAll('g')
.data(Drupal.settings.tripalFeature.admin.organisms.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legendItem.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legendItem.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.attr('font-style','italic')
.text(function(d) { return d; });
}
}
}
};