public
Last active

Functional style geoprocessing with Fiona, pyproj, Shapely

  • Download Gist
with-pyproj-shapely-functional.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
# Example of using Fiona, pyproj, and Shapely together in a functional
# style.
 
import functools
import logging
import sys
 
import fiona
from pyproj import Proj, transform
from shapely.geometry import mapping, shape
 
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
 
# The two functions below operate on sequences of records and are generators
# for new sequences.
 
def transform_coords(func, records):
# Transform record geometry coordinates using the provided function.
for rec in records:
try:
assert rec['geometry']['type'] == "Polygon"
new_coords = []
for ring in rec['geometry']['coordinates']:
x2, y2 = func(*zip(*ring))
new_coords.append(zip(x2, y2))
rec['geometry']['coordinates'] = new_coords
except Exception, e:
# Writing untransformed features to a different shapefile
# is another option.
logging.exception(
"Error transforming record %s:", rec['id'])
yield rec
 
def clean_geom(records):
# Ensure that record geometries are valid and "clean".
for rec in records:
try:
geom = shape(rec['geometry'])
if not geom.is_valid:
clean = geom.buffer(0.0)
assert clean.is_valid
assert clean.geom_type == 'Polygon'
geom = clean
rec['geometry'] = mapping(geom)
except Exception, e:
# Writing uncleanable features to a different shapefile
# is another option.
logging.exception(
"Error cleaning record %s:", rec['id'])
yield rec
 
# I'm experimenting with changing the name of Fiona's file opening function
# from collection() to open(). Feedback greatly appreciated.
 
fiona.open = fiona.collection
 
# Here is where the work is done.
 
with fiona.open("docs/data/test_uk.shp", "r") as source:
with fiona.open(
"with-pyproj-shapely2.shp",
"w",
driver=source.driver,
schema=source.schema,
crs={'init': "epsg:27700", 'no_defs': True}
) as sink:
# The transform_coords function returns a generator for records.
# Its first argument is a function that transforms a single
# coordinate pair between two coordinate reference systems.
results = transform_coords(
functools.partial(transform, Proj(source.crs), Proj(sink.crs)),
source )
 
# The transformed record generator is used as input for the
# "cleaning" generator function.
results = clean_geom(results)
# Finally we write records from the last generator to the
# output "sink" file.
sink.writerecords(results)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.