Number of Persons 65 Years of Age and Over: 2009

100,000 Persons 65 and over

Source: U.S. Bureau of the Census

Reproduction of Judy Olson's 1976 Noncontiguous cartogram rendered in Polymaps

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.

Source code

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 );
}
		

Credits

By Zachary Forest Johnson of indiemaps.com