Choropleth maps also show concentration, however, they use different shades of color to show concentration. This method is useful if related data spans multiple polygons. For example, in a worldwide population density map by country, many countries have disconnected polygons (for example, Hawaii is an island state of the U.S.). In this example, we'll use the Python Imaging Library (PIL) discussed in Chapter 3, The Geospatial Technology Landscape. PIL is not purely Python, but it is designed specifically for Python. We'll recreate our previous dot density example as a choropleth map. We'll calculate a density ratio for each census tract based on the number of people (population) per square kilometer and use that value to adjust the color. The dark areas are more densely populated and the lighter ones are less densely populated, as shown here:
import math import shapefile try: import Image import ImageDraw except: from PIL import Image, ImageDraw def world2screen(bbox, w, h, x, y): """convert geospatial coordinates to pixels""" minx,miny,maxx,maxy = bbox xdist = maxx - minx ydist = maxy - miny xratio = w/xdist yratio = h/ydist px = int(w - ((maxx - x) * xratio)) py = int((maxy - y) * yratio) return (px,py) # Open our shapefile inShp = shapefile.Reader("GIS_CensusTract_poly") iwidth = 600 iheight = 400 # PIL Image img = Image.new("RGB", (iwidth,iheight), (255,255,255)) # PIL Draw module for polygon fills draw = ImageDraw.Draw(img) # Get the population AND area index pop_index = None area_index = None # Shade the census tracts for i,f in enumerate(inShp.fields): if f[0] == "POPULAT11": # Account for deletion flag pop_index = i-1 elif f[0] == "AREASQKM": area_index = i-1 # Draw the polygons for sr in inShp.shapeRecords(): density = sr.record[pop_index]/sr.record[area_index] # The "weight" is a scaled value to adjust the color # intensity based on population weight = min(math.sqrt(density/80.0), 1.0) * 50 R = int(205 - weight) G = int(215 - weight) B = int(245 - weight) pixels = [] for x,y in sr.shape.points: (px,py) = world2screen(inShp.bbox, iwidth, iheight, x, y) pixels.append((px,py)) draw.polygon(pixels, outline=(255,255,255), fill=(R,G,B)) img.save("choropleth.png")
This script produces the following figure showing the relative density of tracks. You can adjust the color using the R, G, and B variables: