3

I would like to extract a named subset of the text records from a shapefile using Python/pyshp. Is there a clean/efficient way to do this?

Ideally I'd like to use dictionary-like access such as:

import shapefile
sf = shapefile.Reader("MyFile.shp")
data = sf.record['desired_record_name']

But of course this doesn't work. So far my solution is the brute force approach of looping over all entries:

import shapefile
sf = shapefile.Reader("MyFile.shp")
for r in sf.iterRecords():
    if r[0]=='desired_record_name':
        data = r
        break

But this leaves a lot to be desired, especially for large/many files.

Note: Although I like the simplicity of pyshp, I am not tied to it, so other free & open-source solutions using python are also of interest.

1
  • If you are not hung up on dictionary access, just use OGR and write a SELECT statement for the records you want.
    – blah238
    Oct 18, 2013 at 19:06

3 Answers 3

7

You can easily create a dictionary of attributes:

import shapefile
sf = shapefile.Reader('MyFile.shp')
# name of fields
fields = sf.fields[1:] 
field_names = [field[0] for field in fields] 
# construction of a dctionary field_name:value  
for r in sf.shapeRecords():  
     atr = dict(zip(field_names, r.record))  

  As a result:

{'field1': 'value1', 'field2': 'value2', ...,  'fieldn': 'valuen'}

and

if atr['field1'] == value:
       print atr
       action

With large/many files, you can use a generator:

def records(filename):  
    # generator 
    reader = shapefile.Reader(filename)  
    fields = reader.fields[1:]  
    field_names = [field[0] for field in fields]  
    for sr in reader.shapeRecords():   
        yield dict(zip(field_names, sr.record)) 
rec = records('MyFile.shp')
rec.next()
{'field1': 'value1', 'field2': 'value2', ...,  'fieldn': 'valuen'}
# or
for rec in records('MyFile.shp')
     if rec['field1'] == value:
         data = rec

Sean Gillies has proposed the The geo_interface protocol, an interface that describes a spatial object using a GeoJSON like structure (dictionaries). Many modules implement this protocol (see Python Geo_interface applications), including pyshp, since version 1.1.7:

With one of my examples:

for r in sf.shapeRecords():  
   atr = dict(zip(field_names, r.record))  
   geom = r.shape.__geo_interface__ 
   print dict(geometry=geom,properties=atr)  
   {'geometry': {'type': 'Point', 'coordinates': (161821.09375, 79076.0703125)}, 'properties': {'DIP_DIR': 120, 'STRATI_TYP': 1, 'DIP': 30}}

You can also use Fiona or osgeo.ogr in the same way.

2

Here is how you might use OGR's Python bindings to do this:

from osgeo import ogr
ds = ogr.Open(r"path_to\shapefile.shp")
lyr = ds.GetLayer()

# Apply a SQL WHERE Clause
lyr.SetAttributeFilter("FIELD = 'Value'")

# Loop over features
for feature in lyr:
    # do stuff with features selected by attribute filter

SetAttributeFilter lets you apply a SQL WHERE clause, which filters the rows returned or affected by layer methods.

1

Good news... The PyShp library has been updated to include this exact feature.

The newest version (2.0) is not available through PyPI or pip yet, but I would expect that to change soon.

Source: https://github.com/GeospatialPython/pyshp#major-changes

Reading shapefiles is now more convenient:

  • More convenient accessing of Record values as attributes. [@philippkraft]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.