Move and modify legend on Geochart?

1,205 views
Skip to first unread message

Bryan Maloney

unread,
Aug 6, 2014, 5:34:48 PM8/6/14
to google-visua...@googlegroups.com
How do I move and modify the legend on a regional Geochart? My region is the USA, and the legend would work much better in the Gulf of Mexico than off on the lower left corner. Likewise, I would like to attach a legend to the legend, so to speak, some text to define it, as in "Aggregate Gross Income". I would also like to round off and add labels to the two ends, so, instead of -50000, it would be "-50 million"

I do not just want to turn it into a static picture and alter the legend. I want to preserve the interactivity of the full GeoChart. How is this done?

Andrew Gallant

unread,
Aug 6, 2014, 8:14:37 PM8/6/14
to google-visua...@googlegroups.com
The API does not provide the means to set the location of the legend, nor does it provide much in the way of number formatting.  To do what you want, you are going to have to get down and dirty with the rendered chart code.  Here's an example:

google.visualization.events.addListener(chart, 'ready', function () {
    // define an offset to move the legend by
    var offset = {
        x: 300,
        y: 0
    };
    // get the legend elements
    var rect = document.querySelector('#chart_div svg > g:nth-child(2) > g:nth-child(3) rect');
    var textNodes = document.querySelectorAll('#chart_div svg > g:nth-child(2) > g:nth-child(3) g > text');
    
    // get the width of the left text node, so we know how much we need to adjust the positioning of the bar and right text node when we make text changes
    var wBase = textNodes[1].offsetWidth;
    // convert the text node format to #.# million
    for (var i = 0; i < textNodes.length; i++) {
        var val = parseInt(textNodes[i].innerHTML.replace(/,/g, '')) / 1000000;
        textNodes[i].innerHTML = val.toFixed(1) + ' million';
    }
    var wOffset = textNodes[1].offsetWidth - wBase;
    
    // move the legend
    rect.setAttribute('transform', 'translate(' + (offset.x + wOffset) + ', ' + offset.y + ')');
    for (var i = 0; i < textNodes.length; i++) {
        textNodes[i].setAttribute('transform', 'translate(' + (offset.x + (i < 2 ? 0: wOffset)) + ', ' + offset.y + ')');
    }
});


This works only in modern browsers; if you need to support IE8 and older, you have to write a separate code path to work with VML instead of SVG.

Bryan Maloney

unread,
Aug 7, 2014, 10:53:57 AM8/7/14
to google-visua...@googlegroups.com

1: That's really cool.
2: When I mouseover, the legend jumps back to the lower left corner and loses all formatting improvements.

 

Andrew Gallant

unread,
Aug 7, 2014, 7:49:56 PM8/7/14
to google-visua...@googlegroups.com
Hmmm...I would normally suggest using an "onmouseover" handler for that, but the GeoCharts don't fire mouse events.  I think if you use a MutationObserver (or a DOMNodeInsertion event handler, if MutationObserver is not available) to watch for changes to the legend's container, and fix the legend position from that:

function fixLegend () {
    // define an offset to move the legend by
    var offset = {
        x: 300,
        y: 0
    };
    // get the legend elements
    var rect = document.querySelector('#chart_div svg > g:nth-child(2) > g:nth-child(3) rect');
    var textNodes = document.querySelectorAll('#chart_div svg > g:nth-child(2) > g:nth-child(3) g > text');
    
    // get the width of the left text node, so we know how much we need to adjust the positioning of the bar and right text node when we make text changes
    var wBase = textNodes[1].offsetWidth;
    // convert the text node format to #.# million
    for (var i = 0; i < textNodes.length; i++) {
        var val = parseInt(textNodes[i].innerHTML.replace(/,/g, '')) / 1000000;
        textNodes[i].innerHTML = val.toFixed(1) + ' million';
    }
    var wOffset = textNodes[1].offsetWidth - wBase;
    
    // move the legend
    rect.setAttribute('transform', 'translate(' + (offset.x + wOffset) + ', ' + offset.y + ')');
    for (var i = 0; i < textNodes.length; i++) {
        textNodes[i].setAttribute('transform', 'translate(' + (offset.x + (i < 2 ? 0: wOffset)) + ', ' + offset.y + ')');
    }
}

google.visualization.events.addListener(chart, 'ready', function () {
    // fix the legend on ready
    fixLegend();
    
    // set up observer to watch for changes to the legend and readjust
    var container = document.querySelector('#chart_div svg > g:nth-child(2) > g:nth-child(3)');
    // use a MutationObserver if it is available
    if (typeof MutationObserver === 'function') {
        var observer = new MutationObserver(function (m) {
            fixLegend();
        });
        observer.observe(container, {
            childList: true
        });
    }
    // otherwise, use an event listener
    else if (document.addEventListener) {
        container.addEventListener('DOMNodeInserted', fixLegend);
    }
    else {
        container.attachEvent('onDOMNodeInserted', fixLegend);
    }
});

Reply all
Reply to author
Forward
0 new messages