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 OpenLayers

The above map attempts to reproduce an example from Judy Olson's 1976 article Noncontiguous Area Cartograms using the OpenLayers 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's the main method that applies the transformation. It could be a lot shorter but I allow for classed as well as more typical unclassed cartograms.

OpenLayersThematic.applyNoncontiguousCartogramScalesToVectorLayer = 
function( layer, propertyName, classificationMethod, numClasses, breaks, maxArea, classAreas )
{
	if ( classificationMethod == null ) classificationMethod = OpenLayersThematic.ClassificationMethods.UNCLASSED;
	if ( maxArea == null ) maxArea = 12000;
	if ( classificationMethod != OpenLayersThematic.ClassificationMethods.UNCLASSED )
	{
		if ( numClasses == null ) numClasses = 5;
		if ( breaks == null ) 
		{
			var values = [];
			for ( var i in layer.features ) values.push( Number( layer.features[ i ].attributes[ propertyName ] ) );
			breaks = OpenLayersThematic.calculateClassBreaks( values, numClasses, classificationMethod );
		}
		if ( classAreas == null || classAreas.length != numClasses )
		{
			classAreas = [];
			for ( var i = 1; i <= numClasses; i++ ) classAreas.push( i/numClasses * maxArea );
		}
	}	
	// determine the maximum attribute value for all these features
	var maxValue = -999999;
	for ( var i in layer.features ) 
		if ( Number(layer.features[ i ].attributes[ propertyName ]) > maxValue ) maxValue = Number(layer.features[ i ].attributes[ propertyName ]);
	// now we'll loop thru the features and scale them
	var originalArea, desiredArea, value, desiredScale;
	for ( var i in layer.features )
	{
		value = Number( layer.features[ i ].attributes[ propertyName ] );
		originalArea = layer.features[ i ].geometry.getArea() / Math.pow( layer.map.getResolution(), 2 );
		if ( classificationMethod != OpenLayersThematic.ClassificationMethods.UNCLASSED )
		{
			for ( var ii=1; ii<breaks.length; ii++ ) 
				if ( value < breaks[ ii ] || ( ( ii == breaks.length - 1 ) && value == breaks[ ii] ) ) break;
			desiredArea = classAreas[ ii - 1 ];
		}
		else desiredArea = ( value / maxValue ) * maxArea;
		var centroid = layer.features[ i ].geometry.getCentroid();
		desiredScale = Math.sqrt( desiredArea / originalArea );	
		layer.features[ i ].geometry.resize( desiredScale, centroid );
	}
};
		

Credits

By Zachary Forest Johnson of indiemaps.com