Google Bar Chart Whitespace Fix

Written 02:14 pm 10/1/2012 Modifed 12:48 pm 11/14/2012

For some web projects, I use google charts to visualize user statistics. They offer many various types of charts, like bar graphs, line charts, scatter points, pie charts, and even QR codes. Developers can see more here

Specifically with the bar charts, we have encountered issues where the chart gives itself a lot of leading and trailing white-space before we actually see any chart content. It usually centers the page around the bar graph.

This is a write-up of how I mitigated some of the unpredictable effects of google's chart system.

In order to put a chart on the page, you have to have an element to place it into, and the element should have a pre-defined height. In this case, we want it to be around 25 pixels per bar, with a total header height of 90 pixels, and a total footer height of 60 pixels. We also want a minimum inner chart height of 250 pixels. The numbers are arbitrary, but these will give us a nice consistent user experience. So the formula for our target height is:

Formula

Unfortunately for me, the results give us something unpredictable. Searching through documentation, I couldn't figure out how google calculates its chart heights. I've tried changing various parameters within the chart options, but nothing seemed to fix it. That's when I decided to don my science glasses and do some experimentation.

I hastily wrote some quick javascript to generate some data to give me a good idea of how the height of the container element affected the height of the chart's inner size.

The script is as follows:

var data = {};

var stopping = false;

function stop() { stopping = true; }

function update() { $('#generate').click(); setTimeout(function() {update_inf(); console.log(    JSON.stringify(data)); if (! stopping) { update(); } }, 2000); };

function get_inf() { return [ $('svg>g:eq(1)>g:eq(0)>g:eq(1)>rect').length/2, parseInt($('svg>    g:eq(1)>rect:eq(0)').attr('height'), 10), $('svg').height()]; };

function update_inf() { var inf = get_inf(); data[inf[2]] = inf; };

var height = 5000;
window.get_height = function() {
    var out = height + 'px';
    height -= 100;
    return out;
}

Ever time I called the function 'update()', the script would add data to the 'data' object. I actually automated the calling of the script so I could run off and grab some cheese, but this got me some very useful data.

{"100":[86,62,100],"200":[86,124,200],"300":[86,185,300],"400":[86,247,400],"500":[86,309,500],"600":[86,371,600],"700":[86,433,700],"800":[86,494,800],"900":[86,556,900],"1000":[86,618,1000],"1100":[86,680,1100],"1200":[86,742,1200],"1300":[86,803,1300],"1400":[86,865,1400],"1500":[86,927,1500],"1600":[86,1002,1600],"1700":[86,1089,1700],"1800":[86,1176,1800],"1900":[86,1263,1900],"2000":[86,1351,2000],"2100":[86,1438,2100],"2300":[86,1612,2300],"2400":[86,1700,2400],"2500":[86,1787,2500],"2600":[86,1874,2600],"2700":[86,1962,2700],"2800":[86,2049,2800],"2900":[86,2136,2900],"3000":[86,2223,3000],"3100":[86,2311,3100],"3200":[86,2398,3200],"3300":[86,2485,3300],"3400":[86,2572,3400],"3500":[86,2660,3500],"3600":[86,2747,3600],"3700":[86,2834,3700],"3800":[86,2921,3800],"3900":[86,3009,3900],"4000":[86,3096,4000],"4100":[86,3183,4100],"4200":[86,3271,4200],"4300":[86,3358,4300],"4400":[86,3445,4400],"4500":[86,3532,4500],"4600":[86,3620,4600],"4700":[86,3707,4700],"4800":[86,3794,4800],"4900":[86,3881,4900],"5000":[86,3969,5000]}

This is a JSON object keyed by the size of the container element, containing the number of bars, the inner chart height, and the container element respectively.

I wrote a script in PHP to analyze the data using a simple best fit algorithm, and to create a tab delimited file containing x and y values for a scatter plot. You can see my best fit code here.

Points

This shows me that the function is piecewise, and has two linear formulae, When the target inner chart height is less than 1000px, it behaves differently than when it is greater than 1000px. Using the best fits, we get the following approximations:

Results

I grabbed approximations which looked correct, and got the following graph:

Results

Perfect! Knowing the way that the inner chart height relates to the containing element, I was able to apply this knowledge to our javascript.

function dom_formatting_chart_container(target_inner_height, target_header_height,     target_footer_height) {

    if (target_inner_height < 1000) {
        outer_height = 1.61290 * target_inner_height + 1.61290;
    } else {
        outer_height = 1.14943 * target_inner_height + 443.67816;
    }

    margins = (outer_height - target_inner_height);

    margin_top = -1 * Math.max(0, ((margins / 2) - target_header_height));
    margin_bottom = -1 * Math.max(0, (margins / 2) - target_footer_height);

    return {'height':outer_height, 'margin-top':margin_top, 'margin-bottom':margin_top};
}

You can calculate your target inner height however you want. Just apply the results to the css of the chart element. in jQuery:

$('#chart').css(dom_formatting_chart_container(target_inner_height, target_header_height, target_footer_height));

Just make sure that the DOM element ('#chart' in this example) has a parent that has the style 'overflow:hidden' to remove the margin bugs. That's it!