100,000 Persons 65 and over
Source: U.S. Bureau of the Census
The above map attempts to reproduce an example from Judy Olson's 1976 article Noncontiguous Area Cartograms using the Polymaps JavaScript mapping framework. See this blog post for more information.
Just 'view source' to see what all's going on here. Feel free to re-use any code you find there. Below are the main methods that determine area and perform scaling and feature translation.
function applyNoncontiguousCartogramScaling( features, attributeName, maxArea ) { var maxValue = -999999; for (var i = 0; i < features.length; i++) { var f = features[i]; var pop = f.data.properties[ attributeName ]; if ( pop > maxValue ) maxValue = pop; } // now let's loop through again and scale em! for (var i = 0; i < features.length; i++) { var f = features[i]; var featureArea = 0; // f.element is either gonna be a SVGPathElement or a SVGGElement if ( f.element.pathSegList ) { // SVGPathElement featureArea = getPathArea( f.element.pathSegList ); } else { // SVGGElement for ( var ii = 0; ii < f.element.childNodes.length; ii++ ) featureArea += getPathArea( f.element.childNodes[ ii ].pathSegList ); } var desiredArea = ( f.data.properties[ attributeName ] / maxValue ) * maxArea; var scale = Math.sqrt( desiredArea / featureArea ); var bbox = f.element.getBBox(); var centerX = bbox.x + .5 * bbox.width, centerY = bbox.y + .5 * bbox.height; f.element.setAttribute( "transform", "scale(" + scale + " " + scale +")" + " " + "translate(" + -( ( centerX * scale - centerX ) / scale ) + " " + -( ( centerY * scale - centerY ) / scale ) + ")" ); } } function getPathArea( segList ) { var area = 0; var seg1, seg2; // represents a path command var nPts = segList.numberOfItems; // let's see if the last item is a 'Z' (it should be) if ( segList.getItem( nPts - 1 ).pathSegTypeAsLetter.toLowerCase() == 'z' ) nPts --; var j = nPts - 1; segItem_list: for ( var i = 0; i < nPts; j=i++ ) { seg1 = segList.getItem( i ); seg2 = segList.getItem( j ); area += seg1.x * seg2.y; area -= seg1.y * seg2.x; } area /= 2; return Math.abs( area ); }
By Zachary Forest Johnson of indiemaps.com