Pyshp: Pure Python ESRI Shapefile Reader/Writer
Pyshp (The Python Shapefile Library) is a pure Python library designed for reading and writing ESRI Shapefile format files. It allows developers to work with geospatial vector data without external dependencies. The library is currently in version 3.0.3 and maintains an active development and release cadence.
Warnings
- breaking Pyshp version 3.0.0 and later dropped support for Python 2 and Python versions up to 3.8. It now requires Python 3.9 or newer.
- breaking Major API changes were introduced in 3.0.0. Field info tuples are now `namedtuple`s, field type codes are `FieldType` enum members, `bbox`, `mbox`, and `zbox` attributes are `Namedtuples`. The `Writer` object no longer mutates `Shapes`, and new custom subclasses for each shape type were added.
- gotcha In PyShp 2.x, the method for copying records from one `Reader` to a `Writer` changed. Directly using `w.records.extend(r.records())` is no longer supported for `Writer` objects.
- gotcha Pyshp is strict about the 'deleted flag' in DBF records. Only records with a flag of `0x20` (valid) or `0x2A` (deleted) are properly interpreted. Other values (e.g., `0x00` from some GIS software) will be interpreted as deleted, potentially leading to empty records when reading.
- gotcha Pyshp 3.0.2 removed the `py.typed` marker file from the main package. This was done to prevent accidental type checking enforcement on unrelated projects in the same environment. For explicit type checking, an optional `pyshp-stubs` package is now provided.
Install
-
pip install pyshp
Imports
- Reader
import shapefile reader = shapefile.Reader('my_shapefile.shp') - Writer
import shapefile writer = shapefile.Writer('new_shapefile.shp')
Quickstart
import shapefile
import os
# Create a dummy shapefile for reading example
output_dir = 'temp_shapefile_dir'
os.makedirs(output_dir, exist_ok=True)
w = shapefile.Writer(os.path.join(output_dir, 'test_points.shp'))
w.field('name', 'C')
w.point(1, 1)
w.record('Point A')
w.point(2, 2)
w.record('Point B')
w.close()
# Reading a shapefile
# You specify the base filename of the shapefile or any of its component files.
# Using a context manager is recommended for automatic file closing.
with shapefile.Reader(os.path.join(output_dir, 'test_points.shp')) as sf:
print(f"Shape type: {sf.shapeTypeName}")
print(f"Number of shapes: {len(sf.shapes())}")
for shapeRec in sf.iterShapeRecords():
print(f"Geometry: {shapeRec.shape.points}, Record: {shapeRec.record}")
# Writing a new shapefile
with shapefile.Writer(os.path.join(output_dir, 'new_polygon.shp')) as w:
w.shapeType = shapefile.POLYGON
w.field("Name", "C")
w.field("Area", "N")
# Add a polygon
w.poly([[[0,0],[0,10],[10,10],[10,0],[0,0]]])
w.record("Square", 100)
# Add another polygon with a hole
w.poly([
[[20,20],[20,30],[30,30],[30,20],[20,20]], # Outer ring
[[22,22],[22,28],[28,28],[28,22],[22,22]] # Inner ring (hole)
])
w.record("Donut", 56)
print(f"Shapefile '{os.path.join(output_dir, 'new_polygon.shp')}' created successfully.")
# Clean up (optional)
# for ext in ['.shp', '.shx', '.dbf', '.prj']: # .prj might not be created by default
# if os.path.exists(os.path.join(output_dir, 'new_polygon' + ext)):
# os.remove(os.path.join(output_dir, 'new_polygon' + ext))
# if os.path.exists(os.path.join(output_dir, 'test_points' + ext)):
# os.remove(os.path.join(output_dir, 'test_points' + ext))
# os.rmdir(output_dir)