Skip to content

Instantly share code, notes, and snippets.

@sgillies
Created September 5, 2012 18:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sgillies/3642564 to your computer and use it in GitHub Desktop.
Save sgillies/3642564 to your computer and use it in GitHub Desktop.
Putting it all together
from descartes import PolygonPatch
import fiona
from functools import partial, reduce
from math import sqrt
from itertools import imap
import pylab
from pyproj import Proj, transform
fiona.open = fiona.collection
BLUE = "#6699cc"
RED = "#cc6666"
# The one polygon rendering function we'll reuse throughout this module.
def add_record(fig, rec, fc=BLUE, ec=BLUE, alpha=1.0, zorder=2):
"""Makes a patch of the record's geometry, adds it to the default axes of
the given figure and returns the figure. The color and alpha parameters
can be constants or unary functions of the given record.
Note: not strictly functional because we're mutating the figure."""
facecolor = callable(fc) and fc(rec) or fc
edgecolor = callable(ec) and ec(rec) or ec
alphaval = callable(alpha) and alpha(rec) or alpha
zorderval = callable(zorder) and zorder(rec) or zorder
fig.gca().add_patch(
PolygonPatch(
rec['geometry'],
fc=facecolor,
ec=edgecolor,
alpha=alphaval,
zorder=zorderval))
return fig
add_feature_alpha = partial(add_record, alpha=0.5)
with fiona.open("docs/data/test_uk.shp") as features:
fig = reduce(add_feature_alpha, features, figure(figsize=(8, 8)))
fig.gca().autoscale(tight=False)
fig.savefig("test_uk.png")
# Add in map projection capabilities
def transform_coords(func, rec):
# Transform record geometry coordinates using the provided function.
# Returns a record or None.
try:
assert rec['geometry']['type'] == "Polygon"
record = rec.copy()
new_coords = list(
zip(*func(*zip(*ring))
) for ring in rec['geometry']['coordinates'])
record['geometry']['coordinates'] = new_coords
return record
except Exception, e:
# Writing untransformed features to a different shapefile
# is another option.
logging.exception(
"Error transforming record %s:", rec)
return None
# The transform_coords() function calls another function for the actual
# transformation of coordinate values. We'll make one by "freezing" the
# input and output projections for pyproj.transform().
epsg4326 = Proj(init="epsg:4326", no_defs=True)
robin = Proj(
proj="robin",
lon_0=0,
x_0=0,
y_0=0,
ellps="WGS84",
datum="WGS84",
units="m",
no_defs=True)
transform_robin = partial(transform, epsg4326, robin)
# Here's partial function that makes a Robinson projection of feature records.
# Usage: projected_features = map(robinson, features)
robinson = partial(transform_coords, transform_robin)
with fiona.open(
"/Users/seang/work/UVA-SL/world_borders/world_borders.shp"
) as features:
fig = reduce(
add_feature_alpha, imap(robinson, features), figure(figsize=(24, 12)))
fig.gca().autoscale(tight=False)
fig.savefig("world-robinson.png")
# The rendering function allows image alpha to vary from feature to feature.
# We'll create a weighting function to control the alpha value, normalizing
# population density to 80 per square km.
def weight(rec):
props = rec['properties']
dens = props['POP_CNTRY']/props['AREA']
return min(sqrt(dens/80.0), 1.0)
# Here's our new rendering partial function.
add_feature_choropleth = partial(add_record, fc=RED, ec=RED, alpha=weight)
# And just for fun, we'll make a partial from reduce().
choropleth = partial(reduce, add_feature_choropleth)
with fiona.open(
"/Users/seang/work/UVA-SL/world_borders/world_borders.shp"
) as features:
fig = choropleth(imap(robinson, features), figure(figsize=(24, 12)))
fig.gca().autoscale(tight=False)
fig.savefig("world-dens-robinson.png")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment