Skip to content
Commits on Source (4)
Metadata-Version: 1.2
Metadata-Version: 2.1
Name: pyshp
Version: 2.0.1
Version: 2.1.0
Summary: Pure Python read/write support for ESRI Shapefile format
Home-page: https://github.com/GeospatialPython/pyshp
Author: Joel Lawhead
Author-email: jlawhead@geospatialpython.com
License: MIT
Download-URL: https://github.com/GeospatialPython/pyshp/archive/2.0.1.tar.gz
Description-Content-Type: text/markdown
Download-URL: https://github.com/GeospatialPython/pyshp/archive/2.1.0.tar.gz
Description: # PyShp
The Python Shapefile Library (pyshp) reads and writes ESRI Shapefiles in pure Python.
The Python Shapefile Library (PyShp) reads and writes ESRI Shapefiles in pure Python.
![pyshp logo](http://4.bp.blogspot.com/_SBi37QEsCvg/TPQuOhlHQxI/AAAAAAAAAE0/QjFlWfMx0tQ/S350/GSP_Logo.png "PyShp")
......@@ -48,7 +47,7 @@ Description: # PyShp
# Overview
The Python Shapefile Library (pyshp) provides read and write support for the
The Python Shapefile Library (PyShp) provides read and write support for the
Esri Shapefile format. The Shapefile format is a popular Geographic
Information System vector data format created by Esri. For more information
about this format please read the well-written "ESRI Shapefile Technical
......@@ -65,9 +64,9 @@ Description: # PyShp
Pyshp is compatible with Python 2.7-3.x.
This document provides examples for using pyshp to read and write shapefiles. However
This document provides examples for using PyShp to read and write shapefiles. However
many more examples are continually added to the blog [http://GeospatialPython.com](http://GeospatialPython.com),
and by searching for pyshp on [https://gis.stackexchange.com](https://gis.stackexchange.com).
and by searching for PyShp on [https://gis.stackexchange.com](https://gis.stackexchange.com).
Currently the sample census blockgroup shapefile referenced in the examples is available on the GitHub project site at
[https://github.com/GeospatialPython/pyshp](https://github.com/GeospatialPython/pyshp). These
......@@ -84,6 +83,19 @@ Description: # PyShp
# Version Changes
## 2.1.0
### New Features:
- Added back read/write support for unicode field names.
- Improved Record representation
- More support for geojson on Reader, ShapeRecord, ShapeRecords, and shapes()
### Bug fixes:
- Fixed error when reading optional m-values
- Fixed Record attribute autocomplete in Python 3
- Misc readme cleanup
## 2.0.0
The newest version of PyShp, version 2.0 introduced some major new improvements.
......@@ -133,7 +145,7 @@ Description: # PyShp
The examples below will use a shapefile created from the U.S. Census Bureau
Blockgroups data set near San Francisco, CA and available in the git
repository of the pyshp GitHub site.
repository of the PyShp GitHub site.
## Reading Shapefiles
......@@ -173,7 +185,7 @@ Description: # PyShp
You can also load shapefiles from any Python file-like object using keyword
arguments to specify any of the three files. This feature is very powerful and
allows you to load shapefiles from a url, from a zip file, serialized object,
allows you to load shapefiles from a url, a zip file, a serialized object,
or in some cases a database.
......@@ -182,8 +194,8 @@ Description: # PyShp
>>> r = shapefile.Reader(shp=myshp, dbf=mydbf)
Notice in the examples above the shx file is never used. The shx file is a
very simple fixed-record index for the variable length records in the shp
file. This file is optional for reading. If it's available pyshp will use the
very simple fixed-record index for the variable-length records in the shp
file. This file is optional for reading. If it's available PyShp will use the
shx file to access shape records a little faster but will do just fine without
it.
......@@ -198,8 +210,8 @@ Description: # PyShp
5
Shape types are represented by numbers between 0 and 31 as defined by the
shapefile specification and listed below. It is important to note that numbering system has
several reserved numbers which have not been used yet therefore the numbers of
shapefile specification and listed below. It is important to note that the numbering system has
several reserved numbers that have not been used yet, therefore the numbers of
the existing shape types are not sequential:
- NULL = 0
......@@ -231,14 +243,22 @@ Description: # PyShp
>>> sf.shapeTypeName == 'POLYGON'
True
Other pieces of meta-data that we can check includes the number of features,
or the bounding box area the shapefile covers:
Other pieces of meta-data that we can check include the number of features
and the bounding box area the shapefile covers:
>>> len(sf)
663
>>> sf.bbox
[-122.515048, 37.652916, -122.327622, 37.863433]
Finally, if you would prefer to work with the entire shapefile in a different
format, you can convert all of it to a GeoJSON dictionary, although you may lose
some information in the process, such as z- and m-values:
>>> sf.__geo_interface__['type']
'FeatureCollection'
### Reading Geometry
......@@ -272,7 +292,8 @@ Description: # PyShp
>>> ['%.3f' % coord for coord in s.bbox]
['-122.450', '37.801', '-122.442', '37.808']
Each shape record (except Points) contain the following attributes. Records of shapeType Point do not have a bounding box 'bbox'.
Each shape record (except Points) contains the following attributes. Records of
shapeType Point do not have a bounding box 'bbox'.
>>> for name in dir(shapes[3]):
......@@ -330,7 +351,7 @@ Description: # PyShp
>>> ['%.3f' % coord for coord in shape]
['-122.471', '37.787']
In most cases, however, if you need to more than just type or bounds checking, you may want
In most cases, however, if you need to do more than just type or bounds checking, you may want
to convert the geometry to the more human-readable [GeoJSON format](http://geojson.org),
where lines and polygons are grouped for you:
......@@ -340,11 +361,17 @@ Description: # PyShp
>>> geoj["type"]
'MultiPolygon'
The results from the shapes() method similiarly supports converting to GeoJSON:
>>> shapes.__geo_interface__['type']
'GeometryCollection'
### Reading Records
A record in a shapefile contains the attributes for each shape in the
collection of geometry. Records are stored in the dbf file. The link between
collection of geometries. Records are stored in the dbf file. The link between
geometry and attributes is the foundation of all geographic information systems.
This critical link is implied by the order of shapes and corresponding records
in the shp geometry file and the dbf attribute file.
......@@ -459,30 +486,30 @@ Description: # PyShp
>>> shapeRecs[3].record[1:3]
['060750601001', 4715]
Now let's read the first two points for that same record:
The results from the shapeRecords() method is a list-like object that can be easily converted
to GeoJSON through the _\_geo_interface\_\_:
>>> points = shapeRecs[3].shape.points[0:2]
>>> len(points)
2
>>> shapeRecs.__geo_interface__['type']
'FeatureCollection'
The shapeRecord() method reads a single shape/record pair at the specified index.
To get the 4th shape record from the blockgroups shapefile use the third index:
>>> shapeRec = sf.shapeRecord(3)
Each individual shape record also supports the _\_geo_interface\_\_ to convert it to a GeoJSON:
>>> shapeRec.__geo_interface__['type']
'Feature'
The blockgroup key and population count:
>>> shapeRec.record[1:3]
['060750601001', 4715]
>>> points = shapeRec.shape.points[0:2]
>>> len(points)
2
## Writing Shapefiles
......@@ -676,17 +703,17 @@ Description: # PyShp
>>> r = shapefile.Reader('shapefiles/test/dtype')
>>> r.record(0)
[True]
Record #0: [True]
>>> r.record(1)
[True]
Record #1: [True]
>>> r.record(2)
[False]
Record #2: [False]
>>> r.record(3)
[False]
Record #3: [False]
>>> r.record(4)
[None]
Record #4: [None]
>>> r.record(5)
[None]
Record #5: [None]
You can also add attributes using keyword arguments where the keys are field names.
......@@ -750,8 +777,8 @@ Description: # PyShp
**Adding a LineString shape**
For LineString shapefiles, each line shape consists of multiple lines. Line shapes must be given as a list of lines,
even if there is just one line. Also, each line must have at least two points.
For LineString shapefiles, each shape is given as a list of one or more linear features.
Each of the linear features must have at least two points.
>>> w = shapefile.Writer('shapefiles/test/line')
......@@ -769,12 +796,13 @@ Description: # PyShp
**Adding a Polygon shape**
Similarly to LineString, Polygon shapes consist of multiple polygons, and must be given as a list of polygons.
The main difference being that polygons must have at least 4 points and the last point must be the same as the first.
It's also okay if you forget to do so, PyShp automatically checks and closes the polygons if you don't.
The main difference is that polygons must have at least 4 points and the last point must be the same as the first.
It's also okay if you forget to repeat the first point at the end; PyShp automatically checks and closes the polygons
if you don't.
It's important to note that for Polygon shapefiles, your polygon coordinates must be ordered in a clockwise direction.
If any of the polygons have holes, then the hole polygon coordinates must be ordered in a counterclockwise direction.
The direction of your polygons determines how shapefile readers will distinguish between polygons outlines and holes.
The direction of your polygons determines how shapefile readers will distinguish between polygon outlines and holes.
>>> w = shapefile.Writer('shapefiles/test/polygon')
......@@ -832,7 +860,7 @@ Description: # PyShp
>>> w.record("row", "two")
>>> w.point(2, 2)
To help prevent accidental misalignment pyshp has an "auto balance" feature to
To help prevent accidental misalignment PyShp has an "auto balance" feature to
make sure when you add either a shape or a record the two sides of the
equation line up. This way if you forget to update an entry the
shapefile will still be valid and handled correctly by most shapefile
......@@ -867,7 +895,7 @@ Description: # PyShp
>>> w.recNum == w.shpNum
True
If you do not use the autobalance or balance method and forget to manually
If you do not use the autoBalance() or balance() method and forget to manually
balance the geometry and attributes the shapefile will be viewed as corrupt by
most shapefile software.
......@@ -877,17 +905,18 @@ Description: # PyShp
## 3D and Other Geometry Types
Most shapefiles store conventional 2D points, lines, or polygons. But the shapefile format is also capable of storing
various other types of geometries as well, including complex 3D surfaces and objects.
Most shapefiles store conventional 2D points, lines, or polygons. But the shapefile format is also capable
of storing various other types of geometries as well, including complex 3D surfaces and objects.
**Shapefiles with measurement (M) values**
Measured shape types are shapes that include a measurement value at each vertex, for instance speed measurements from a GPS device.
Shapes with measurement (M) values are added with following methods: "pointm", "multipointm", "linem", and "polygonm".
The M-values are specified by adding a third M value to each XY coordinate. Missing or unobserved M-values are specified with a None value,
or by simply omitting the third M-coordinate.
Measured shape types are shapes that include a measurement value at each vertex, for instance
speed measurements from a GPS device. Shapes with measurement (M) values are added with the following
methods: "pointm", "multipointm", "linem", and "polygonm". The M-values are specified by adding a
third M value to each XY coordinate. Missing or unobserved M-values are specified with a None value,
or by simply omitting the third M-coordinate.
>>> w = shapefile.Writer('shapefiles/test/linem')
>>> w.field('name', 'C')
......@@ -904,17 +933,17 @@ Description: # PyShp
>>> r = shapefile.Reader('shapefiles/test/linem')
>>> r.mbox # the lower and upper bound of M values in the shapefile
>>> r.mbox # the lower and upper bound of M-values in the shapefile
[0.0, 3.0]
>>> r.shape(0).m # flat list of M values
>>> r.shape(0).m # flat list of M-values
[0.0, None, 3.0, None, 0.0, None, None]
**Shapefiles with elevation (Z) values**
Elevation shape types are shapes that include an elevation value at each vertex, for instance elevation from a GPS device.
Shapes with an elevation (Z) values are added with following methods: "pointz", "multipointz", "linez", and "polygonz".
Shapes with elevation (Z) values are added with the following methods: "pointz", "multipointz", "linez", and "polygonz".
The Z-values are specified by adding a third Z value to each XY coordinate. Z-values do not support the concept of missing data,
but if you omit the third Z-coordinate it will default to 0. Note that Z-type shapes also support measurement (M) values added
as a fourth M-coordinate. This too is optional.
......@@ -926,7 +955,7 @@ Description: # PyShp
>>> w.linez([
... [[1,5,18],[5,5,20],[5,1,22],[3,3],[1,1]], # line with some omitted Z-values
... [[3,2],[2,6]], # line without any Z-values
... [[3,2,15,0],[2,6,13,3],[1,9,14,2]] # line with both Z and M-values
... [[3,2,15,0],[2,6,13,3],[1,9,14,2]] # line with both Z- and M-values
... ])
>>> w.record('linez1')
......@@ -937,17 +966,17 @@ Description: # PyShp
>>> r = shapefile.Reader('shapefiles/test/linez')
>>> r.zbox # the lower and upper bound of Z values in the shapefile
>>> r.zbox # the lower and upper bound of Z-values in the shapefile
[0.0, 22.0]
>>> r.shape(0).z # flat list of Z values
>>> r.shape(0).z # flat list of Z-values
[18.0, 20.0, 22.0, 0.0, 0.0, 0.0, 0.0, 15.0, 13.0, 14.0]
**3D MultiPatch Shapefiles**
Multipatch shapes are useful for storing composite 3-Dimensional objects.
A MultiPatch shape represents a 3D object made up of one or more surface parts.
Each surface in "parts" is defined by a list of XYZM values (Z and M values optional), and its corresponding type
Each surface in "parts" is defined by a list of XYZM values (Z and M values optional), and its corresponding type is
given in the "partTypes" argument. The part type decides how the coordinate sequence is to be interpreted, and can be one
of the following module constants: TRIANGLE_STRIP, TRIANGLE_FAN, OUTER_RING, INNER_RING, FIRST_RING, or RING.
For instance, a TRIANGLE_STRIP may be used to represent the walls of a building, combined with a TRIANGLE_FAN to represent
......@@ -1011,7 +1040,7 @@ Description: # PyShp
This means that as long as you are able to iterate through a source file without having
to load everything into memory, such as a large CSV table or a large shapefile, you can
process and write any number of items, and even merging many different source files into a single
process and write any number of items, and even merge many different source files into a single
large shapefile. If you need to edit or undo any of your writing you would have to read the
file back in, one record at a time, make your changes, and write it back out.
......@@ -1075,6 +1104,7 @@ Description: # PyShp
David A. Riggs
davidh-ssec
Evan Heidtmann
ezcitron
geospatialpython
Hannes
Ignacio Martinez Vazquez
......@@ -1088,6 +1118,7 @@ Description: # PyShp
Michal Čihař
Mike Toews
Nilo
pakoun
Paulo Ernesto
Raynor Vliegendhart
Razzi Abuissa
......@@ -1108,3 +1139,4 @@ Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >= 2.7
Description-Content-Type: text/markdown
# PyShp
The Python Shapefile Library (pyshp) reads and writes ESRI Shapefiles in pure Python.
The Python Shapefile Library (PyShp) reads and writes ESRI Shapefiles in pure Python.
![pyshp logo](http://4.bp.blogspot.com/_SBi37QEsCvg/TPQuOhlHQxI/AAAAAAAAAE0/QjFlWfMx0tQ/S350/GSP_Logo.png "PyShp")
......@@ -38,7 +38,7 @@ The Python Shapefile Library (pyshp) reads and writes ESRI Shapefiles in pure Py
# Overview
The Python Shapefile Library (pyshp) provides read and write support for the
The Python Shapefile Library (PyShp) provides read and write support for the
Esri Shapefile format. The Shapefile format is a popular Geographic
Information System vector data format created by Esri. For more information
about this format please read the well-written "ESRI Shapefile Technical
......@@ -55,9 +55,9 @@ despite the numerous ways to store and exchange GIS data available today.
Pyshp is compatible with Python 2.7-3.x.
This document provides examples for using pyshp to read and write shapefiles. However
This document provides examples for using PyShp to read and write shapefiles. However
many more examples are continually added to the blog [http://GeospatialPython.com](http://GeospatialPython.com),
and by searching for pyshp on [https://gis.stackexchange.com](https://gis.stackexchange.com).
and by searching for PyShp on [https://gis.stackexchange.com](https://gis.stackexchange.com).
Currently the sample census blockgroup shapefile referenced in the examples is available on the GitHub project site at
[https://github.com/GeospatialPython/pyshp](https://github.com/GeospatialPython/pyshp). These
......@@ -74,6 +74,19 @@ part of your geospatial project.
# Version Changes
## 2.1.0
### New Features:
- Added back read/write support for unicode field names.
- Improved Record representation
- More support for geojson on Reader, ShapeRecord, ShapeRecords, and shapes()
### Bug fixes:
- Fixed error when reading optional m-values
- Fixed Record attribute autocomplete in Python 3
- Misc readme cleanup
## 2.0.0
The newest version of PyShp, version 2.0 introduced some major new improvements.
......@@ -123,7 +136,7 @@ Before doing anything you must import the library.
The examples below will use a shapefile created from the U.S. Census Bureau
Blockgroups data set near San Francisco, CA and available in the git
repository of the pyshp GitHub site.
repository of the PyShp GitHub site.
## Reading Shapefiles
......@@ -163,7 +176,7 @@ objects are properly closed when done reading the data:
You can also load shapefiles from any Python file-like object using keyword
arguments to specify any of the three files. This feature is very powerful and
allows you to load shapefiles from a url, from a zip file, serialized object,
allows you to load shapefiles from a url, a zip file, a serialized object,
or in some cases a database.
......@@ -172,8 +185,8 @@ or in some cases a database.
>>> r = shapefile.Reader(shp=myshp, dbf=mydbf)
Notice in the examples above the shx file is never used. The shx file is a
very simple fixed-record index for the variable length records in the shp
file. This file is optional for reading. If it's available pyshp will use the
very simple fixed-record index for the variable-length records in the shp
file. This file is optional for reading. If it's available PyShp will use the
shx file to access shape records a little faster but will do just fine without
it.
......@@ -188,8 +201,8 @@ shapeType attribute.
5
Shape types are represented by numbers between 0 and 31 as defined by the
shapefile specification and listed below. It is important to note that numbering system has
several reserved numbers which have not been used yet therefore the numbers of
shapefile specification and listed below. It is important to note that the numbering system has
several reserved numbers that have not been used yet, therefore the numbers of
the existing shape types are not sequential:
- NULL = 0
......@@ -221,14 +234,22 @@ For convenience, you can also get the name of the shape type as a string:
>>> sf.shapeTypeName == 'POLYGON'
True
Other pieces of meta-data that we can check includes the number of features,
or the bounding box area the shapefile covers:
Other pieces of meta-data that we can check include the number of features
and the bounding box area the shapefile covers:
>>> len(sf)
663
>>> sf.bbox
[-122.515048, 37.652916, -122.327622, 37.863433]
Finally, if you would prefer to work with the entire shapefile in a different
format, you can convert all of it to a GeoJSON dictionary, although you may lose
some information in the process, such as z- and m-values:
>>> sf.__geo_interface__['type']
'FeatureCollection'
### Reading Geometry
......@@ -262,7 +283,8 @@ index which is 7.
>>> ['%.3f' % coord for coord in s.bbox]
['-122.450', '37.801', '-122.442', '37.808']
Each shape record (except Points) contain the following attributes. Records of shapeType Point do not have a bounding box 'bbox'.
Each shape record (except Points) contains the following attributes. Records of
shapeType Point do not have a bounding box 'bbox'.
>>> for name in dir(shapes[3]):
......@@ -320,7 +342,7 @@ Each shape record (except Points) contain the following attributes. Records of s
>>> ['%.3f' % coord for coord in shape]
['-122.471', '37.787']
In most cases, however, if you need to more than just type or bounds checking, you may want
In most cases, however, if you need to do more than just type or bounds checking, you may want
to convert the geometry to the more human-readable [GeoJSON format](http://geojson.org),
where lines and polygons are grouped for you:
......@@ -330,11 +352,17 @@ where lines and polygons are grouped for you:
>>> geoj["type"]
'MultiPolygon'
The results from the shapes() method similiarly supports converting to GeoJSON:
>>> shapes.__geo_interface__['type']
'GeometryCollection'
### Reading Records
A record in a shapefile contains the attributes for each shape in the
collection of geometry. Records are stored in the dbf file. The link between
collection of geometries. Records are stored in the dbf file. The link between
geometry and attributes is the foundation of all geographic information systems.
This critical link is implied by the order of shapes and corresponding records
in the shp geometry file and the dbf attribute file.
......@@ -449,30 +477,30 @@ Let's read the blockgroup key and the population for the 4th blockgroup:
>>> shapeRecs[3].record[1:3]
['060750601001', 4715]
Now let's read the first two points for that same record:
The results from the shapeRecords() method is a list-like object that can be easily converted
to GeoJSON through the _\_geo_interface\_\_:
>>> points = shapeRecs[3].shape.points[0:2]
>>> len(points)
2
>>> shapeRecs.__geo_interface__['type']
'FeatureCollection'
The shapeRecord() method reads a single shape/record pair at the specified index.
To get the 4th shape record from the blockgroups shapefile use the third index:
>>> shapeRec = sf.shapeRecord(3)
Each individual shape record also supports the _\_geo_interface\_\_ to convert it to a GeoJSON:
>>> shapeRec.__geo_interface__['type']
'Feature'
The blockgroup key and population count:
>>> shapeRec.record[1:3]
['060750601001', 4715]
>>> points = shapeRec.shape.points[0:2]
>>> len(points)
2
## Writing Shapefiles
......@@ -666,17 +694,17 @@ None is interpreted as missing.
>>> r = shapefile.Reader('shapefiles/test/dtype')
>>> r.record(0)
[True]
Record #0: [True]
>>> r.record(1)
[True]
Record #1: [True]
>>> r.record(2)
[False]
Record #2: [False]
>>> r.record(3)
[False]
Record #3: [False]
>>> r.record(4)
[None]
Record #4: [None]
>>> r.record(5)
[None]
Record #5: [None]
You can also add attributes using keyword arguments where the keys are field names.
......@@ -740,8 +768,8 @@ These are specified as a list of xy point coordinates.
**Adding a LineString shape**
For LineString shapefiles, each line shape consists of multiple lines. Line shapes must be given as a list of lines,
even if there is just one line. Also, each line must have at least two points.
For LineString shapefiles, each shape is given as a list of one or more linear features.
Each of the linear features must have at least two points.
>>> w = shapefile.Writer('shapefiles/test/line')
......@@ -759,12 +787,13 @@ even if there is just one line. Also, each line must have at least two points.
**Adding a Polygon shape**
Similarly to LineString, Polygon shapes consist of multiple polygons, and must be given as a list of polygons.
The main difference being that polygons must have at least 4 points and the last point must be the same as the first.
It's also okay if you forget to do so, PyShp automatically checks and closes the polygons if you don't.
The main difference is that polygons must have at least 4 points and the last point must be the same as the first.
It's also okay if you forget to repeat the first point at the end; PyShp automatically checks and closes the polygons
if you don't.
It's important to note that for Polygon shapefiles, your polygon coordinates must be ordered in a clockwise direction.
If any of the polygons have holes, then the hole polygon coordinates must be ordered in a counterclockwise direction.
The direction of your polygons determines how shapefile readers will distinguish between polygons outlines and holes.
The direction of your polygons determines how shapefile readers will distinguish between polygon outlines and holes.
>>> w = shapefile.Writer('shapefiles/test/polygon')
......@@ -822,7 +851,7 @@ data lines up with the geometry data. For example:
>>> w.record("row", "two")
>>> w.point(2, 2)
To help prevent accidental misalignment pyshp has an "auto balance" feature to
To help prevent accidental misalignment PyShp has an "auto balance" feature to
make sure when you add either a shape or a record the two sides of the
equation line up. This way if you forget to update an entry the
shapefile will still be valid and handled correctly by most shapefile
......@@ -857,7 +886,7 @@ You can create all of the shapes and then create all of the records or vice vers
>>> w.recNum == w.shpNum
True
If you do not use the autobalance or balance method and forget to manually
If you do not use the autoBalance() or balance() method and forget to manually
balance the geometry and attributes the shapefile will be viewed as corrupt by
most shapefile software.
......@@ -867,17 +896,18 @@ most shapefile software.
## 3D and Other Geometry Types
Most shapefiles store conventional 2D points, lines, or polygons. But the shapefile format is also capable of storing
various other types of geometries as well, including complex 3D surfaces and objects.
Most shapefiles store conventional 2D points, lines, or polygons. But the shapefile format is also capable
of storing various other types of geometries as well, including complex 3D surfaces and objects.
**Shapefiles with measurement (M) values**
Measured shape types are shapes that include a measurement value at each vertex, for instance speed measurements from a GPS device.
Shapes with measurement (M) values are added with following methods: "pointm", "multipointm", "linem", and "polygonm".
The M-values are specified by adding a third M value to each XY coordinate. Missing or unobserved M-values are specified with a None value,
or by simply omitting the third M-coordinate.
Measured shape types are shapes that include a measurement value at each vertex, for instance
speed measurements from a GPS device. Shapes with measurement (M) values are added with the following
methods: "pointm", "multipointm", "linem", and "polygonm". The M-values are specified by adding a
third M value to each XY coordinate. Missing or unobserved M-values are specified with a None value,
or by simply omitting the third M-coordinate.
>>> w = shapefile.Writer('shapefiles/test/linem')
>>> w.field('name', 'C')
......@@ -894,17 +924,17 @@ Shapefiles containing M-values can be examined in several ways:
>>> r = shapefile.Reader('shapefiles/test/linem')
>>> r.mbox # the lower and upper bound of M values in the shapefile
>>> r.mbox # the lower and upper bound of M-values in the shapefile
[0.0, 3.0]
>>> r.shape(0).m # flat list of M values
>>> r.shape(0).m # flat list of M-values
[0.0, None, 3.0, None, 0.0, None, None]
**Shapefiles with elevation (Z) values**
Elevation shape types are shapes that include an elevation value at each vertex, for instance elevation from a GPS device.
Shapes with an elevation (Z) values are added with following methods: "pointz", "multipointz", "linez", and "polygonz".
Shapes with elevation (Z) values are added with the following methods: "pointz", "multipointz", "linez", and "polygonz".
The Z-values are specified by adding a third Z value to each XY coordinate. Z-values do not support the concept of missing data,
but if you omit the third Z-coordinate it will default to 0. Note that Z-type shapes also support measurement (M) values added
as a fourth M-coordinate. This too is optional.
......@@ -916,7 +946,7 @@ as a fourth M-coordinate. This too is optional.
>>> w.linez([
... [[1,5,18],[5,5,20],[5,1,22],[3,3],[1,1]], # line with some omitted Z-values
... [[3,2],[2,6]], # line without any Z-values
... [[3,2,15,0],[2,6,13,3],[1,9,14,2]] # line with both Z and M-values
... [[3,2,15,0],[2,6,13,3],[1,9,14,2]] # line with both Z- and M-values
... ])
>>> w.record('linez1')
......@@ -927,17 +957,17 @@ To examine a Z-type shapefile you can do:
>>> r = shapefile.Reader('shapefiles/test/linez')
>>> r.zbox # the lower and upper bound of Z values in the shapefile
>>> r.zbox # the lower and upper bound of Z-values in the shapefile
[0.0, 22.0]
>>> r.shape(0).z # flat list of Z values
>>> r.shape(0).z # flat list of Z-values
[18.0, 20.0, 22.0, 0.0, 0.0, 0.0, 0.0, 15.0, 13.0, 14.0]
**3D MultiPatch Shapefiles**
Multipatch shapes are useful for storing composite 3-Dimensional objects.
A MultiPatch shape represents a 3D object made up of one or more surface parts.
Each surface in "parts" is defined by a list of XYZM values (Z and M values optional), and its corresponding type
Each surface in "parts" is defined by a list of XYZM values (Z and M values optional), and its corresponding type is
given in the "partTypes" argument. The part type decides how the coordinate sequence is to be interpreted, and can be one
of the following module constants: TRIANGLE_STRIP, TRIANGLE_FAN, OUTER_RING, INNER_RING, FIRST_RING, or RING.
For instance, a TRIANGLE_STRIP may be used to represent the walls of a building, combined with a TRIANGLE_FAN to represent
......@@ -1001,7 +1031,7 @@ the file.
This means that as long as you are able to iterate through a source file without having
to load everything into memory, such as a large CSV table or a large shapefile, you can
process and write any number of items, and even merging many different source files into a single
process and write any number of items, and even merge many different source files into a single
large shapefile. If you need to edit or undo any of your writing you would have to read the
file back in, one record at a time, make your changes, and write it back out.
......@@ -1065,6 +1095,7 @@ Charles Arnold
David A. Riggs
davidh-ssec
Evan Heidtmann
ezcitron
geospatialpython
Hannes
Ignacio Martinez Vazquez
......@@ -1078,6 +1109,7 @@ Michael Davis
Michal Čihař
Mike Toews
Nilo
pakoun
Paulo Ernesto
Raynor Vliegendhart
Razzi Abuissa
......
VERSION 2.1.0
2019-02-15
New Features:
* Added back read/write support for unicode field names.
* Improved Record representation
* More support for geojson on Reader, ShapeRecord, ShapeRecords, and shapes()
Bug fixes:
* Fixed error when reading optional m-values
* Fixed Record attribute autocomplete in Python 3
* Misc readme cleanup
VERSION 2.0.1
2018-11-05
......
pyshp (2.0.1+ds-2) UNRELEASED; urgency=medium
pyshp (2.1.0+ds-1) unstable; urgency=medium
* Team upload.
* New upstream release.
* Bump Standards-Version to 4.3.0, no changes.
-- Bas Couwenberg <sebastic@debian.org> Tue, 25 Dec 2018 23:04:50 +0100
-- Bas Couwenberg <sebastic@debian.org> Sat, 16 Feb 2019 08:26:49 +0100
pyshp (2.0.1+ds-1) unstable; urgency=medium
......
......@@ -7,14 +7,14 @@ def read_file(file):
return data.decode('utf-8')
setup(name='pyshp',
version='2.0.1',
version='2.1.0',
description='Pure Python read/write support for ESRI Shapefile format',
long_description=read_file('README.md'),
long_description_content_type='text/markdown',
author='Joel Lawhead',
author_email='jlawhead@geospatialpython.com',
url='https://github.com/GeospatialPython/pyshp',
download_url='https://github.com/GeospatialPython/pyshp/archive/2.0.1.tar.gz',
download_url='https://github.com/GeospatialPython/pyshp/archive/2.1.0.tar.gz',
py_modules=['shapefile'],
license='MIT',
zip_safe=False,
......
......@@ -2,11 +2,11 @@
shapefile.py
Provides read and write support for ESRI Shapefiles.
author: jlawhead<at>geospatialpython.com
version: 2.0.1
version: 2.1.0
Compatible with Python versions 2.7-3.x
"""
__version__ = "2.0.1"
__version__ = "2.1.0"
from struct import pack, unpack, calcsize, error, Struct
import os
......@@ -457,8 +457,8 @@ class _Record(list):
"""
return dict((f, self[i]) for f, i in self.__field_positions.items())
def __str__(self):
return 'Record #{} '.format(self.__oid)
def __repr__(self):
return 'Record #{}: {}'.format(self.__oid, list(self))
def __dir__(self):
"""
......@@ -467,15 +467,51 @@ class _Record(list):
:return: List of method names and fields
"""
attrs = [attr for attr in vars(type(self)) if not attr.startswith('_')]
return attrs + self.__field_positions.values() # plus field names (random order)
default = list(dir(type(self))) # default list methods and attributes of this class
fnames = list(self.__field_positions.keys()) # plus field names (random order)
return default + fnames
class ShapeRecord(object):
"""A ShapeRecord object containing a shape along with its attributes."""
"""A ShapeRecord object containing a shape along with its attributes.
Provides the GeoJSON __geo_interface__ to return a Feature dictionary."""
def __init__(self, shape=None, record=None):
self.shape = shape
self.record = record
@property
def __geo_interface__(self):
return {'type': 'Feature',
'properties': self.record.as_dict(),
'geometry': self.shape.__geo_interface__}
class Shapes(list):
"""A class to hold a list of Shape objects. Subclasses list to ensure compatibility with
former work and allows to use all the optimazations of the builtin list.
In addition to the list interface, this also provides the GeoJSON __geo_interface__
to return a GeometryCollection dictionary. """
def __repr__(self):
return 'Shapes: {}'.format(list(self))
@property
def __geo_interface__(self):
return {'type': 'GeometryCollection',
'geometries': [g.__geo_interface__ for g in self]}
class ShapeRecords(list):
"""A class to hold a list of ShapeRecord objects. Subclasses list to ensure compatibility with
former work and allows to use all the optimazations of the builtin list.
In addition to the list interface, this also provides the GeoJSON __geo_interface__
to return a FeatureCollection dictionary. """
def __repr__(self):
return 'ShapeRecords: {}'.format(list(self))
@property
def __geo_interface__(self):
return {'type': 'FeatureCollection',
'features': [f.__geo_interface__ for f in self]}
class ShapefileException(Exception):
"""An exception to handle shapefile specific problems."""
pass
......@@ -585,9 +621,7 @@ class Reader(object):
features = []
for feat in self.iterShapeRecords():
fdict = {'type': 'Feature',
'properties': dict(*zip(fieldnames,
list(feat.record)
)),
'properties': dict(zip(fieldnames,feat.record)),
'geometry': feat.shape.__geo_interface__}
features.append(fdict)
return {'type': 'FeatureCollection',
......@@ -748,14 +782,18 @@ class Reader(object):
record.z = _Array('d', unpack("<%sd" % nPoints, f.read(nPoints * 8)))
# Read m extremes and values
if shapeType in (13,15,18,23,25,28,31):
(mmin, mmax) = unpack("<2d", f.read(16))
if next - f.tell() >= 16:
(mmin, mmax) = unpack("<2d", f.read(16))
# Measure values less than -10e38 are nodata values according to the spec
record.m = []
for m in _Array('d', unpack("<%sd" % nPoints, f.read(nPoints * 8))):
if m > NODATA:
record.m.append(m)
else:
record.m.append(None)
if next - f.tell() >= nPoints * 8:
record.m = []
for m in _Array('d', unpack("<%sd" % nPoints, f.read(nPoints * 8))):
if m > NODATA:
record.m.append(m)
else:
record.m.append(None)
else:
record.m = [None for _ in range(nPoints)]
# Read a single point
if shapeType in (1,11,21):
record.points = [_Array('d', unpack("<2d", f.read(16)))]
......@@ -763,8 +801,11 @@ class Reader(object):
if shapeType == 11:
record.z = list(unpack("<d", f.read(8)))
# Read a single M value
if shapeType == 21 or (shapeType == 11 and f.tell() < next):
(m,) = unpack("<d", f.read(8))
if shapeType in (21,11):
if next - f.tell() >= 8:
(m,) = unpack("<d", f.read(8))
else:
m = NODATA
# Measure values less than -10e38 are nodata values according to the spec
if m > NODATA:
record.m = [m]
......@@ -822,7 +863,7 @@ class Reader(object):
shp.seek(0,2)
self.shpLength = shp.tell()
shp.seek(100)
shapes = []
shapes = Shapes()
while shp.tell() < self.shpLength:
shapes.append(self.__shape())
return shapes
......@@ -856,9 +897,9 @@ class Reader(object):
else:
idx = len(fieldDesc[name]) - 1
fieldDesc[name] = fieldDesc[name][:idx]
fieldDesc[name] = u(fieldDesc[name], "ascii")
fieldDesc[name] = u(fieldDesc[name], self.encoding, self.encodingErrors)
fieldDesc[name] = fieldDesc[name].lstrip()
fieldDesc[1] = u(fieldDesc[1], "ascii")
fieldDesc[1] = u(fieldDesc[1], 'ascii')
self.fields.append(fieldDesc)
terminator = dbf.read(1)
if terminator != b"\r":
......@@ -994,9 +1035,8 @@ class Reader(object):
def shapeRecords(self):
"""Returns a list of combination geometry/attribute records for
all records in a shapefile."""
shapeRecords = []
return [ShapeRecord(shape=rec[0], record=rec[1]) \
for rec in zip(self.shapes(), self.records())]
return ShapeRecords([ShapeRecord(shape=rec[0], record=rec[1]) \
for rec in zip(self.shapes(), self.records())])
def iterShapeRecords(self):
"""Returns a generator of combination geometry/attribute records for
......@@ -1269,7 +1309,7 @@ class Writer(object):
year -= 1900
# Remove deletion flag placeholder from fields
for field in self.fields:
if str(field[0]).startswith("Deletion"):
if field[0].startswith("Deletion"):
self.fields.remove(field)
numRecs = self.recNum
numFields = len(self.fields)
......@@ -1284,10 +1324,10 @@ class Writer(object):
# Field descriptors
for field in self.fields:
name, fieldType, size, decimal = field
name = b(name, 'ascii', self.encodingErrors)
name = b(name, self.encoding, self.encodingErrors)
name = name.replace(b' ', b'_')
name = name.ljust(11).replace(b' ', b'\x00')
fieldType = b(fieldType, 'ascii', self.encodingErrors)
fieldType = b(fieldType, 'ascii')
size = int(size)
fld = pack('<11sc4xBB14x', name, fieldType, size, decimal)
f.write(fld)
......@@ -1833,7 +1873,7 @@ def test(**kwargs):
# run tests
runner = doctest.DocTestRunner(checker=Py23DocChecker(), verbose=verbosity)
with open("README.md","rb") as fobj:
test = doctest.DocTestParser().get_doctest(string=fobj.read().decode("utf8"), globs={}, name="README", filename="README.md", lineno=0)
test = doctest.DocTestParser().get_doctest(string=fobj.read().decode("utf8").replace('\r\n','\n'), globs={}, name="README", filename="README.md", lineno=0)
failure_count, test_count = runner.run(test)
# print results
......