Pages

Sunday, October 20, 2013

Editing a Shapefile

The PyShp module allows you to read existing shapefiles and write new shapefiles using its Reader and Writer classes respectively.  Sometimes you add, delete, or change a feature.
Editing a shapefile involves adding, deleting or 
changing features or attributes


The shapefile format is actually designed to allow editing a shapefile by modifying the contents of the shp and dbf file directly and then recreating the shx index file (and any dbf indexes).  This method can be beneficial for extremely large files but it can leave "dead space" in the files when you move bytes around.  Some shapefile libraries may not handle that data correctly.

The cleanest and simplest way to edit a shapefile is to create a shapefile Reader, edit the features as Python objects, move the features and attributes to a Writer object, and then save a new shapefile or just write over the original.

We can demonstrate this technique with a simple polygon shapefile.  The following map image is "Tropical Storm Rick" based on a polygon shapefile downloaded from the US National Weather Service:

You can download the shapefile as a zip file here:

The following example will use the technique outlined above to add a new tropical storm polygon east of Rick named "Stanley":

import shapefile
# Polygon shapefile we are updating.
# We must include a file extension in
# this case because the file name
# has multiple dots and pyshp would get
# confused otherwise.
file_name = "ep202009.026_5day_pgn.shp"
# Create a shapefile reader
r = shapefile.Reader(file_name)
# Create a shapefile writer
# using the same shape type
# as our reader
w = shapefile.Writer(r.shapeType)
# Copy over the existing dbf fields
w.fields = list(r.fields)
# Copy over the existing dbf records
w.records.extend(r.records())
# Copy over the existing polygons
w._shapes.extend(r.shapes())
# Add a new polygon
w.poly(parts=[[[-104,24],[-104,25],[-103,25],[-103,24],[-104,24]]])
# Add a new dbf record for our polygon making sure we include
# all of the fields in the original file (r.fields)
w.record("STANLEY","TD","091022/1500","27","21","48","ep")
# Overwrite the old shapefile or change the name and make a copy 
w.save(file_name)
This script will add a simple square polygon to the shapefile with some attributes matching those of the original shapefile.  The following map image shows the changes:
shapefile example
You can download the script source here:

This same technique is also used in my posts on merging shapefiles and subsetting a shapefile by attributes.

2 comments:

  1. I could not follow your example. Not sure where I messed up.

    >>> w.records.extend(r.records())
    Traceback (most recent call last):
    File "", line 1, in
    File "C:\Python27\lib\site-packages\shapefile.py", line 556, in records
    r = self.__record()
    File "C:\Python27\lib\site-packages\shapefile.py", line 490, in __record
    recordContents = self.__recStruct.unpack(f.read(self.__recStruct.size))
    struct.error: unpack requires a string argument of length 17
    >>>

    ReplyDelete
    Replies
    1. Sorry, this is user error. I somehow corrupted my shape file. The exception is thrown in r.records(). Things got better when I restored the shapefile.

      Delete