This is an automated email from the git hooks/post-receive script. sebastic pushed a commit to branch master in repository pyshp.
commit a74b8d174e90143889514314b199fbf75a0e9c1d Author: Bas Couwenberg <[email protected]> Date: Fri Aug 19 09:44:48 2016 +0200 Imported Upstream version 1.2.8+ds --- PKG-INFO | 1275 ++++++++++++++++++++++++++++------------------------------ README.txt | 661 ------------------------------ setup.py | 6 +- shapefile.py | 58 +-- 4 files changed, 649 insertions(+), 1351 deletions(-) diff --git a/PKG-INFO b/PKG-INFO index ed0b3f7..ff18637 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,673 +1,622 @@ Metadata-Version: 1.1 Name: pyshp -Version: 1.2.3 +Version: 1.2.8 Summary: Pure Python read/write support for ESRI Shapefile format Home-page: https://github.com/GeospatialPython/pyshp Author: Joel Lawhead Author-email: [email protected] License: MIT -Download-URL: https://github.com/GeospatialPython/pyshp/archive/1.2.3.tar.gz -Description: PyShp - ======================== - - :Author: Joel Lawhead - [email protected] - - :Version 1.2.2 - - :Revised: January 9, 2015 - - .. contents:: - - Overview - -------- - - 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 Description - July 1998" - located at http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf. - The Esri document describes the shp and shx file formats. However a third file - format called dbf is also required. This format is documented on the web as the - "XBase File Format Description" and is a simple file-based database format created - in the 1960's. For more on this specification see: - http://www.clicketyclick.dk/databases/xbase/format/index.html - - Both the Esri and XBase file-formats are very simple in design and - memory efficient which is part of the reason the shapefile format remains popular - despite the numerous ways to store and exchange GIS data available today. - - Pyshp is compatible with Python 2.4-3.x. - - This document provides examples for using PyShp to read and write shapefiles. - - Currently the sample census blockgroup shapefile referenced in the examples is - only available on the google code project site at http://code.google.com/p/pyshp. - These examples are straight-forward and you can also easily run them against your - own shapefiles manually with minimal modification. Other examples for specific - topics are continually added to the pyshp wiki on google code and the blog - http://GeospatialPython.com. - - Important: For information about map projections, shapefiles, - and Python please visit: http://code.google.com/p/pyshp/wiki/MapProjections - - I sincerely hope this library eliminates the mundane distraction of simply - reading and writing data, and allows you to focus on the challenging and FUN - part of your geospatial project. - - Examples - -------- - - Before doing anything you must import the library. - - >>> import shapefile - - 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 subversion - repository of the pyshp google code site. - - Reading Shapefiles - ++++++++++++++++++ - - To read a shapefile create a new "Reader" object and pass it the name of an - existing shapefile. The shapefile format is acutally a collection of three - files. You specify the base filename of the shapefile or the complete filename - of any of the shapefile component files. - - >>> sf = shapefile.Reader("shapefiles/blockgroups") - - - OR - - >>> sf = shapefile.Reader("shapefiles/blockgroups.shp") - - - OR - - >>> sf = shapefile.Reader("shapefiles/blockgroups.dbf") - - - OR any of the other 5+ formats which are potentially part of a shapefile. - The library does not care about extensions. - - Reading Shapefiles from File-Like Objects - ......................................... - - 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, or in some cases a database. - - >>> myshp = open("shapefiles/blockgroups.shp", "rb") - >>> mydbf = open("shapefiles/blockgroups.dbf", "rb") - >>> 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 shx file - to access shape records a little faster but will do just fine without it. - - Reading Geometry - ................ - - A shapefile's geometry is the collection of points or shapes made from verticies - and implied arcs representing physical locations. All types of shapefiles - just store points. The metadata about the points determine how they are handled by - software. - - You can get the a list of the shapefile's geometry by calling the shapes() - method. - - >>> shapes = sf.shapes() - - The shapes method returns a list of Shape objects describing the - geometry of each shape record. - - >>> len(shapes) - 663 - - You can iterate through the shapefile's geometry using the iterShapes() method. - - >>> len(list(sf.iterShapes())) - 663 - - Each shape record contains the following attributes: - - >>> for name in dir(shapes[3]): - ... if not name.startswith('__'): - ... name - 'bbox' - 'parts' - 'points' - 'shapeType' - - - shapeType: an integer representing the type of shape as defined by the - shapefile specification. - - >>> shapes[3].shapeType - 5 - - - bbox: If the shape type contains multiple points this tuple describes the - lower left (x,y) coordinate and upper right corner coordinate creating a - complete box around the points. If the shapeType is a Null - (shapeType == 0) then an AttributeError is raised. - - >>> # Get the bounding box of the 4th shape. - >>> # Round coordinates to 3 decimal places - >>> bbox = shapes[3].bbox - >>> ['%.3f' % coord for coord in bbox] - ['-122.486', '37.787', '-122.446', '37.811'] - - - parts: Parts simply group collections of points into shapes. If the shape record - has multiple parts this attribute contains the index of the first point of each part. - If there is only one part then a list containing 0 is returned. - - >>> shapes[3].parts - [0] - - - points: The points attribute contains a list of tuples containing an (x,y) - coordinate for each point in the shape. - - >>> len(shapes[3].points) - 173 - >>> # Get the 8th point of the fourth shape - >>> # Truncate coordinates to 3 decimal places - >>> shape = shapes[3].points[7] - >>> ['%.3f' % coord for coord in shape] - ['-122.471', '37.787'] - - To read a single shape by calling its index use the shape() method. The index - is the shape's count from 0. So to read the 8th shape record you would - use its index which is 7. - - >>> s = sf.shape(7) - - >>> # Read the bbox of the 8th shape to verify - >>> # Round coordinates to 3 decimal places - >>> ['%.3f' % coord for coord in s.bbox] - ['-122.450', '37.801', '-122.442', '37.808'] - - 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 geometry and attributes is the foundation of 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. - - The field names of a shapefile are available as soon as you read a shapefile. - You can call the "fields" attribute of the shapefile as a Python list. Each - field is a Python list with the following information: - - - Field name: the name describing the data at this column index. - - - Field type: the type of data at this column index. Types can be: Character, Numbers, Longs, Dates, or Memo. - The "Memo" type has no meaning within a GIS and is part of the xbase spec instead. - - - Field length: the length of the data found at this column index. Older GIS software may truncate this - length to 8 or 11 characters for "Character" fields. - - - Decimal length: the number of decimal places found in "Number" fields. - - To see the fields for the Reader object above (sf) call the "fields" attribute: - - >>> fields = sf.fields - - >>> assert fields == [("DeletionFlag", "C", 1, 0), ["AREA", "N", 18, 5], - ... ["BKG_KEY", "C", 12, 0], ["POP1990", "N", 9, 0], ["POP90_SQMI", "N", 10, 1], - ... ["HOUSEHOLDS", "N", 9, 0], - ... ["MALES", "N", 9, 0], ["FEMALES", "N", 9, 0], ["WHITE", "N", 9, 0], - ... ["BLACK", "N", 8, 0], ["AMERI_ES", "N", 7, 0], ["ASIAN_PI", "N", 8, 0], - ... ["OTHER", "N", 8, 0], ["HISPANIC", "N", 8, 0], ["AGE_UNDER5", "N", 8, 0], - ... ["AGE_5_17", "N", 8, 0], ["AGE_18_29", "N", 8, 0], ["AGE_30_49", "N", 8, 0], - ... ["AGE_50_64", "N", 8, 0], ["AGE_65_UP", "N", 8, 0], - ... ["NEVERMARRY", "N", 8, 0], ["MARRIED", "N", 9, 0], ["SEPARATED", "N", 7, 0], - ... ["WIDOWED", "N", 8, 0], ["DIVORCED", "N", 8, 0], ["HSEHLD_1_M", "N", 8, 0], - ... ["HSEHLD_1_F", "N", 8, 0], ["MARHH_CHD", "N", 8, 0], - ... ["MARHH_NO_C", "N", 8, 0], ["MHH_CHILD", "N", 7, 0], - ... ["FHH_CHILD", "N", 7, 0], ["HSE_UNITS", "N", 9, 0], ["VACANT", "N", 7, 0], - ... ["OWNER_OCC", "N", 8, 0], ["RENTER_OCC", "N", 8, 0], - ... ["MEDIAN_VAL", "N", 7, 0], ["MEDIANRENT", "N", 4, 0], - ... ["UNITS_1DET", "N", 8, 0], ["UNITS_1ATT", "N", 7, 0], ["UNITS2", "N", 7, 0], - ... ["UNITS3_9", "N", 8, 0], ["UNITS10_49", "N", 8, 0], - ... ["UNITS50_UP", "N", 8, 0], ["MOBILEHOME", "N", 7, 0]] - - You can get a list of the shapefile's records by calling the records() method: - - >>> records = sf.records() - - >>> len(records) - 663 - - Similar to the geometry methods, you can iterate through dbf records using the - recordsIter() method. - - >>> len(list(sf.iterRecords())) - 663 - - Each record is a list containing an attribute corresponding to each field in the - field list. - - For example in the 4th record of the blockgroups shapefile the 2nd and 3rd - fields are the blockgroup id and the 1990 population count of - that San Francisco blockgroup: - - >>> records[3][1:3] - ['060750601001', 4715] - - To read a single record call the record() method with the record's index: - - >>> rec = sf.record(3) - - >>> rec[1:3] - ['060750601001', 4715] - - Reading Geometry and Records Simultaneously - ........................................... - - You way want to examine both the geometry and the attributes for a record at the - same time. The shapeRecord() and shapeRecords() method let you do just that. - - Calling the shapeRecords() method will return the geometry and attributes for - all shapes as a list of ShapeRecord objects. Each ShapeRecord instance has a - "shape" and "record" attribute. The shape attribute is a ShapeRecord object as - dicussed in the first section "Reading Geometry". The record attribute is a - list of field values as demonstrated in the "Reading Records" section. - - >>> shapeRecs = sf.shapeRecords() - - 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: - - >>> points = shapeRecs[3].shape.points[0:2] - - >>> len(points) - 2 - - The shapeRec() method reads a single shape/record pair at the specified index. - To get the 4th shape record from the blockgroups shapfile use the third index: - - >>> shapeRec = sf.shapeRecord(3) - - The blockgroup key and population count: - - >>> shapeRec.record[1:3] - ['060750601001', 4715] - - >>> points = shapeRec.shape.points[0:2] - - >>> len(points) - 2 - - There is also an iterShapeRecords() method to iterate through large files: - - >>> shapeRecs = sf.iterShapeRecords() - >>> for shape, rec in shapeRecs: - ... # do something here - - Writing Shapefiles - ++++++++++++++++++ - - PyShp tries to be as flexible as possible when writing shapefiles while - maintaining some degree of automatic validation to make sure you don't - accidentally write an invalid file. - - PyShp can write just one of the component files such as the shp or dbf file - without writing the others. So in addition to being a complete - shapefile library, it can also be used as a basic dbf (xbase) library. Dbf files are - a common database format which are often useful as a standalone simple - database format. And even shp files occasionaly have uses as a standalone - format. Some web-based GIS systems use an user-uploaded shp file to specify - an area of interest. Many precision agriculture chemical field sprayers also - use the shp format as a control file for the sprayer system (usually in - combination with custom database file formats). - - To create a shapefile you add geometry and/or attributes using methods in the - Writer class until you are ready to save the file. - - Create an instance of the Writer class to begin creating a shapefile: - - >>> w = shapefile.Writer() - - - Setting the Shape Type - ...................... - - The shape type defines the type of geometry contained in the shapefile. All of - the shapes must match the shape type setting. - - Shape types are represented by numbers between 0 and 31 as defined by the - shapefile specification. It is important to note that numbering system has - several reserved numbers which have not been used yet therefore the numbers of - the existing shape types are not sequential. - - You can reference shape types by the numbers or by constants defined by PyShp: - shapefile.NULL = 0 - shapefile.POINT = 1 - shapefile.POLYLINE = 3 - shapefile.POLYGON = 5 - shapefile.MULTIPOINT = 8 - shapefile.POINTZ = 11 - shapefile.POLYLINEZ = 13 - shapefile.POLYGONZ = 15 - shapefile.MULTIPOINTZ = 18 - shapefile.POINTM = 21 - shapefile.POLYLINEM = 23 - shapefile.POLYGONM = 25 - shapefile.MULTIPOINTM = 28 - shapefile.MULTIPATCH = 31 - - There are three ways to set the shape type: - - Set it when creating the class instance. - - Set it by assigning a value to an existing class instance. - - Set it automatically to the type of the first shape by saving the shapefile. - - To manually set the shape type for a Writer object when creating the Writer: - - >>> w = shapefile.Writer(shapeType=1) - - or we can use the constants as explained above: - - >>> w = shapefile.Writer(shapefile.POINT) - - As you can see, specifying the shapeType argument explicitly isn't necessary. - - >>> w.shapeType - 1 - - OR you can set it after the Writer is created by changing the property: - - >>> w.shapeType = 3 - - >>> w.shapeType - 3 - - Geometry and Record Balancing - ............................. - - Because every shape must have a corresponding record it is critical that the - number of records equals the number of shapes to create a valid shapefile. 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 feature is NOT turned on by default. To activate it - set the attribute autoBalance to 1 (True): - - >>> w.autoBalance = 1 - - You also have the option of manually calling the balance() method each time you - add a shape or a record to ensure the other side is up to date. When balancing - is used null shapes are created on the geometry side or a record with a value of - "NULL" for each field is created on the attribute side. - - The balancing option gives you flexibility in how you build the shapefile. - - Without auto balancing you can add geometry or records at anytime. You can - create all of the shapes and then create all of the records or vice versa. You - can use the balance method after creating a shape or record each time and make - updates later. If you do not use the balance method and forget to manually - balance the geometry and attributes the shapefile will be viewed as corrupt by - most shapefile software. - - With auto balanacing you can add either shapes or geometry and update blank - entries on either side as needed. Even if you forget to update an entry the - shapefile will still be valid and handled correctly by most shapefile software. - - Adding Geometry - ............... - - Geometry is added using one of three methods: "null", "point", or "poly". The "null" - method is used for null shapes, "point" is used for point shapes, and "poly" is - used for everything else. - - **Adding a Null shape** - - Because Null shape types (shape type 0) have no geometry the "null" method is - called without any arguments. - - >>> w = shapefile.Writer() - - >>> w.null() - - The writer object's shapes list will now have one null shape: - - >>> assert w.shapes()[0].shapeType == shapefile.NULL - - **Adding a Point shape** - - Point shapes are added using the "point" method. A point is specified by an - x, y, and optional z (elevation) and m (measure) value. - - >>> w = shapefile.Writer(shapefile.POINT) - - >>> w.point(122, 37) # No elevation or measure values - - >>> w.shapes()[0].points - [[122, 37, 0, 0]] - - >>> w.point(118, 36, 4, 8) - - >>> w.shapes()[1].points - [[118, 36, 4, 8]] - - **Adding a Poly Shape** - - "Poly" shapes can be either polygons or lines. Shapefile polygons must have at - least 4 points and the last point must be the same as the first. PyShp automatically - enforces closed polygons. - A line must have at least two points. - Because of the similarities between these two shape types they are created using - a single method called "poly". - - >>> w = shapefile.Writer(shapefile.POLYGON) - - >>> w.poly(shapeType=3, parts=[[[122,37,4,9], [117,36,3,4]], [[115,32,8,8], - ... [118,20,6,4], [113,24]]]) - - **Adding a Polygon with Rings** - - Polygons consist of rings which mean they are closed. The first point and last point - of a ring must be the same. PyShp enforces ring closure if the ring is incomplete when - you add the shape. Polygons can have inner rings which create holes. Holes are defined - by the order of the points. Normally points in a ring run clockwise. If the points - run counter-clockwise then they form a hole. If you don't order the points correctly - you'll just have overlapping polygons. - - >>> w = shapefile.Writer(shapefile.POLYGON) - >>> outer_ring = [[10,10],[50,50],[100,10],[50,-50],[10,10]] - >>> inner_ring = [[40,10],[50,30],[70,10],[50,-30],[40,10]] - >>> inner_ring.reverse() - - You can use the "shapefile.signed_area()" method to determine if a ring is clockwise - or counter-clockwise. A value >= 0 means the ring is counter-clockwise and < 0 means - the ring is clockwise. The value returned is also the area of the polygon. - - >>> # Clockwise ring - ... shapefile.signed_area(outer_ring) - -4500.0 - >>> # Counter-clockwise ring - ... shapefile.signed_area(inner_ring) - 900.0 - - **Creating 3D Polygons** - - Elevation values, known as "Z" values allow you to create 3-dimensional shapefiles. The - z value is an extra value specified as part of a point. - - >>> w = shapefile.Writer(shapeType=shapefile.POLYGONZ) - >>> w.poly([[[-89.0, 33, 12], [-90, 31, 11], [-91, 30, 12]]], shapeType=15) - >>> w.field("NAME") - >>> w.record("PolyZTest") - >>> w.save("shapefiles/test/MyPolyZ") - - The z values are stored in a seperate shape attribute. - - >>> r = shapefile.Reader("shapefiles/test/MyPolyZ") - >>> s = r.shape(0) - >>> s.points - [[-89.0, 33.0], [-90.0, 31.0], [-91.0, 30.0], [-89.0, 33.0]] - >>> s.z - [12.0, 11.0, 12.0, 12.0] - - Creating Attributes - ................... - - Creating attributes involves two steps. Step 1 is to create fields to contain - attribute values and step 2 is to populate the fields with values for each - shape record. - - The following attempts to create a complete shapefile: - - >>> w = shapefile.Writer(shapefile.POINT) - >>> w.point(1,1) - >>> w.point(3,1) - >>> w.point(4,3) - >>> w.point(2,2) - >>> w.field('FIRST_FLD') - >>> w.field('SECOND_FLD','C','40') - >>> w.record('First','Point') - >>> w.record('Second','Point') - >>> w.record('Third','Point') - >>> w.record('Fourth','Point') - >>> w.save('shapefiles/test/point') - - >>> w = shapefile.Writer(shapefile.POLYGON) - >>> w.poly(parts=[[[1,5],[5,5],[5,1],[3,3],[1,1]]]) - >>> w.field('FIRST_FLD','C','40') - >>> w.field('SECOND_FLD','C','40') - >>> w.record('First','Polygon') - >>> w.save('shapefiles/test/polygon') - - >>> w = shapefile.Writer(shapefile.POLYLINE) - >>> w.line(parts=[[[1,5],[5,5],[5,1],[3,3],[1,1]]]) - >>> w.poly(parts=[[[1,3],[5,3]]], shapeType=shapefile.POLYLINE) - >>> w.field('FIRST_FLD','C','40') - >>> w.field('SECOND_FLD','C','40') - >>> w.record('First','Line') - >>> w.record('Second','Line') - >>> w.save('shapefiles/test/line') - - You can also add attributes using keyword arguments where the keys are field names. - - >>> w = shapefile.Writer(shapefile.POLYLINE) - >>> w.line(parts=[[[1,5],[5,5],[5,1],[3,3],[1,1]]]) - >>> w.field('FIRST_FLD','C','40') - >>> w.field('SECOND_FLD','C','40') - >>> w.record(FIRST_FLD='First', SECOND_FLD='Line') - >>> w.save('shapefiles/test/line') - - File Names - .......... - - File extensions are optional when reading or writing shapfiles. If you specify them Pyshp - ignores them anyway. When you save files you can specify a base file name that is used for - all three file types. Or you can specify a nmae for one or more file types. In that case, - any file types not assigned will not save and only file types with file names will be saved. - If you do not specify any file names (i.e. save()), then a unique file name is generated with - the prefix "shapefile\_" followed by random characters which is used for all three files. The - unique file name is returned as a string. - - >>> targetName = w.save() - >>> assert("shapefile_" in targetName) - - Saving to File-Like Objects - ........................... - - Just as you can read shapefiles from python file-like objects you can also write them. - - >>> try: - ... from StringIO import StringIO - ... except ImportError: - ... from io import BytesIO as StringIO - >>> shp = StringIO() - >>> shx = StringIO() - >>> dbf = StringIO() - >>> w.saveShp(shp) - >>> w.saveShx(shx) - >>> w.saveDbf(dbf) - >>> # Normally you would call the "StringIO.getvalue()" method on these objects. - >>> shp = shx = dbf = None - - Editing Shapefiles - ++++++++++++++++++ - - The Editor class attempts to make changing existing shapefiles easier by handling the reading and writing details behind the scenes. - - Let's add shapes to existing shapefiles: - - Add a point to a point shapefile - - >>> e = shapefile.Editor(shapefile="shapefiles/test/point.shp") - >>> e.point(0,0,10,2) - >>> e.record("Appended","Point") - >>> # We added z and m values so - >>> # change the shapetype - >>> e.shapeType = shapefile.POINTZ - >>> e.save('shapefiles/test/point') - - Edit the appended point to change the "y" and "z" value - - >>> e = shapefile.Editor(shapefile="shapefiles/test/point.shp") - >>> # Find the point by the attribute - >>> for s in enumerate(e.records): - ... i, record = s - ... if record[0] == "Appended": - ... geom = e._shapes[i] - ... # Change the y value to 5 - ... geom.points[0][1] = 5 - ... # Change the z value to 9 - ... if hasattr(geom, "z"): - ... geom.z = (9,) - ... else: - ... geom.points[0][2] = 9 - >>> e.save('shapefiles/test/point') - - Add a new line to a line shapefile: - - >>> e = shapefile.Editor(shapefile="shapefiles/test/line.shp") - >>> e.line(parts=[[[10,5],[15,5],[15,1],[13,3],[11,1]]]) - >>> e.record('Appended','Line') - >>> e.save('shapefiles/test/line') - >>> e = None - - Add a new polygon to a polygon shapefile: - - >>> e = shapefile.Editor(shapefile="shapefiles/test/polygon.shp") - >>> e.poly(parts=[[[5.1,5],[9.9,5],[9.9,1],[7.5,3],[5.1,1]]]) - >>> e.record("Appended","Polygon") - >>> e.save('shapefiles/test/polygon') - >>> e = None - - Remove the first point in each shapefile - for a point shapefile that is - the first shape and record" - - >>> e = shapefile.Editor(shapefile="shapefiles/test/point.shp") - >>> e.delete(0) - >>> e.save('shapefiles/test/point') - >>> e = None - - Remove the last shape in the polygon shapefile. - - >>> e = shapefile.Editor(shapefile="shapefiles/test/polygon.shp") - >>> e.delete(-1) - >>> e.save('shapefiles/test/polygon') - >>> e = None - - Python __geo_interface__ - ++++++++++++++++++++++++ - - The Python __geo_interface__ convention provides a data interchange interface - among geospatial Python libraries. The interface returns data as GeoJSON. - More information on the __geo_interface__ protocol can be found at: - https://gist.github.com/sgillies/2217756. - More information on GeoJSON is available at http://geojson.org. - - >>> s = sf.shape(0) - >>> s.__geo_interface__["type"] - 'MultiPolygon' - +Download-URL: https://github.com/GeospatialPython/pyshp/archive/1.2.8.tar.gz +Description: ## Contents + + [TOC] + + # Overview + + 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 + Description - July 1998" located at [http://www.esri.com/library/whitepapers/p + dfs/shapefile.pdf](http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf) + . The Esri document describes the shp and shx file formats. However a third + file format called dbf is also required. This format is documented on the web + as the "XBase File Format Description" and is a simple file-based database + format created in the 1960's. For more on this specification see: [http://www.clicketyclick.dk/databases/xbase/format/index.html](http://www.clicketyclick.d + k/databases/xbase/format/index.html) + + Both the Esri and XBase file-formats are very simple in design and memory + efficient which is part of the reason the shapefile format remains popular + despite the numerous ways to store and exchange GIS data available today. + + Pyshp is compatible with Python 2.4-3.x. + + This document provides examples for using pyshp to read and write shapefiles. + + 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 + examples are straight-forward and you can also easily run them against your + own shapefiles manually with minimal modification. Other examples for specific + topics are continually added to the pyshp wiki on GitHub and the blog + [http://GeospatialPython.com](http://GeospatialPython.com). + + Important: For information about map projections, shapefiles, and Python + please visit: [https://github.com/GeospatialPython/pyshp/wiki/Map-Projections](https://github.com/GeospatialPython/pyshp/wiki/Map-Projections) + + I sincerely hope this library eliminates the mundane distraction of simply + reading and writing data, and allows you to focus on the challenging and FUN + part of your geospatial project. + + # Examples + + Before doing anything you must import the library. + + + >>> import shapefile + + 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. + + ## Reading Shapefiles + + To read a shapefile create a new "Reader" object and pass it the name of an + existing shapefile. The shapefile format is acutally a collection of three + files. You specify the base filename of the shapefile or the complete filename + of any of the shapefile component files. + + + >>> sf = shapefile.Reader("shapefiles/blockgroups") + + OR + + + >>> sf = shapefile.Reader("shapefiles/blockgroups.shp") + + OR + + + >>> sf = shapefile.Reader("shapefiles/blockgroups.dbf") + + OR any of the other 5+ formats which are potentially part of a shapefile. The + library does not care about file extensions. + + ### Reading Shapefiles from File-Like Objects + + 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, + or in some cases a database. + + + >>> myshp = open("shapefiles/blockgroups.shp", "rb") + >>> mydbf = open("shapefiles/blockgroups.dbf", "rb") + >>> 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 + shx file to access shape records a little faster but will do just fine without + it. + + ### Reading Geometry + + A shapefile's geometry is the collection of points or shapes made from + verticies and implied arcs representing physical locations. All types of + shapefiles just store points. The metadata about the points determine how they + are handled by software. + + You can get the a list of the shapefile's geometry by calling the shapes() + method. + + + >>> shapes = sf.shapes() + + The shapes method returns a list of Shape objects describing the geometry of + each shape record. + + + >>> len(shapes) + 663 + + You can iterate through the shapefile's geometry using the iterShapes() + method. + + + >>> len(list(sf.iterShapes())) + 663 + + Each shape record contains the following attributes: + + + >>> for name in dir(shapes[3]): + ... if not name.startswith('__'): + ... name + 'bbox' + 'parts' + 'points' + 'shapeType' + + * shapeType: an integer representing the type of shape as defined by the + shapefile specification. + + + >>> shapes[3].shapeType + 5 + + * bbox: If the shape type contains multiple points this tuple describes the + lower left (x,y) coordinate and upper right corner coordinate creating a + complete box around the points. If the shapeType is a + Null (shapeType == 0) then an AttributeError is raised. + + + >>> # Get the bounding box of the 4th shape. + >>> # Round coordinates to 3 decimal places + >>> bbox = shapes[3].bbox + >>> ['%.3f' % coord for coord in bbox] + ['-122.486', '37.787', '-122.446', '37.811'] + + * parts: Parts simply group collections of points into shapes. If the shape + record has multiple parts this attribute contains the index of the first + point of each part. If there is only one part then a list containing 0 is + returned. + + >>> shapes[3].parts + [0] + + * points: The points attribute contains a list of tuples containing an + (x,y) coordinate for each point in the shape. + + >>> len(shapes[3].points) + 173 + >>> # Get the 8th point of the fourth shape + >>> # Truncate coordinates to 3 decimal places + >>> shape = shapes[3].points[7] + >>> ['%.3f' % coord for coord in shape] + ['-122.471', '37.787'] + + To read a single shape by calling its index use the shape() method. The index + is the shape's count from 0. So to read the 8th shape record you would use its + index which is 7. + + + >>> s = sf.shape(7) + + >>> # Read the bbox of the 8th shape to verify + >>> # Round coordinates to 3 decimal places + >>> ['%.3f' % coord for coord in s.bbox] + ['-122.450', '37.801', '-122.442', '37.808'] + + ### 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 + 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. + + The field names of a shapefile are available as soon as you read a shapefile. + You can call the "fields" attribute of the shapefile as a Python list. Each + field is a Python list with the following information: + + * Field name: the name describing the data at this column index. + * Field type: the type of data at this column index. Types can be: Character, + Numbers, Longs, Dates, or Memo. The "Memo" type has no meaning within a + GIS and is part of the xbase spec instead. + * Field length: the length of the data found at this column index. Older GIS + software may truncate this length to 8 or 11 characters for "Character" + fields. + * Decimal length: the number of decimal places found in "Number" fields. + + To see the fields for the Reader object above (sf) call the "fields" + attribute: + + + >>> fields = sf.fields + + >>> assert fields == [("DeletionFlag", "C", 1, 0), ["AREA", "N", 18, 5], + ... ["BKG_KEY", "C", 12, 0], ["POP1990", "N", 9, 0], ["POP90_SQMI", "N", 10, 1], + ... ["HOUSEHOLDS", "N", 9, 0], + ... ["MALES", "N", 9, 0], ["FEMALES", "N", 9, 0], ["WHITE", "N", 9, 0], + ... ["BLACK", "N", 8, 0], ["AMERI_ES", "N", 7, 0], ["ASIAN_PI", "N", 8, 0], + ... ["OTHER", "N", 8, 0], ["HISPANIC", "N", 8, 0], ["AGE_UNDER5", "N", 8, 0], + ... ["AGE_5_17", "N", 8, 0], ["AGE_18_29", "N", 8, 0], ["AGE_30_49", "N", 8, 0], + ... ["AGE_50_64", "N", 8, 0], ["AGE_65_UP", "N", 8, 0], + ... ["NEVERMARRY", "N", 8, 0], ["MARRIED", "N", 9, 0], ["SEPARATED", "N", 7, 0], + ... ["WIDOWED", "N", 8, 0], ["DIVORCED", "N", 8, 0], ["HSEHLD_1_M", "N", 8, 0], + ... ["HSEHLD_1_F", "N", 8, 0], ["MARHH_CHD", "N", 8, 0], + ... ["MARHH_NO_C", "N", 8, 0], ["MHH_CHILD", "N", 7, 0], + ... ["FHH_CHILD", "N", 7, 0], ["HSE_UNITS", "N", 9, 0], ["VACANT", "N", 7, 0], + ... ["OWNER_OCC", "N", 8, 0], ["RENTER_OCC", "N", 8, 0], + ... ["MEDIAN_VAL", "N", 7, 0], ["MEDIANRENT", "N", 4, 0], + ... ["UNITS_1DET", "N", 8, 0], ["UNITS_1ATT", "N", 7, 0], ["UNITS2", "N", 7, 0], + ... ["UNITS3_9", "N", 8, 0], ["UNITS10_49", "N", 8, 0], + ... ["UNITS50_UP", "N", 8, 0], ["MOBILEHOME", "N", 7, 0]] + + You can get a list of the shapefile's records by calling the records() method: + + + >>> records = sf.records() + + >>> len(records) + 663 + + Similar to the geometry methods, you can iterate through dbf records using the + iterRecords() method. + + + >>> len(list(sf.iterRecords())) + 663 + + Each record is a list containing an attribute corresponding to each field in + the field list. + + For example in the 4th record of the blockgroups shapefile the 2nd and 3rd + fields are the blockgroup id and the 1990 population count of that San + Francisco blockgroup: + + + >>> records[3][1:3] + ['060750601001', 4715] + + To read a single record call the record() method with the record's index: + + + >>> rec = sf.record(3) + + >>> rec[1:3] + ['060750601001', 4715] + + ### Reading Geometry and Records Simultaneously + + You way want to examine both the geometry and the attributes for a record at + the same time. The shapeRecord() and shapeRecords() method let you do just + that. + + Calling the shapeRecords() method will return the geometry and attributes for + all shapes as a list of ShapeRecord objects. Each ShapeRecord instance has a + "shape" and "record" attribute. The shape attribute is a ShapeRecord object as + dicussed in the first section "Reading Geometry". The record attribute is a + list of field values as demonstrated in the "Reading Records" section. + + + >>> shapeRecs = sf.shapeRecords() + + 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: + + + >>> points = shapeRecs[3].shape.points[0:2] + + >>> len(points) + 2 + + The shapeRecord() method reads a single shape/record pair at the specified index. + To get the 4th shape record from the blockgroups shapfile use the third index: + + + >>> shapeRec = sf.shapeRecord(3) + + The blockgroup key and population count: + + + >>> shapeRec.record[1:3] + ['060750601001', 4715] + + >>> points = shapeRec.shape.points[0:2] + + >>> len(points) + 2 + + There is also an iterShapeRecords() method to iterate through large files: + + >>> shapeRecs = sf.iterShapeRecords() + >>> for shapeRec in shapeRecs: + ... # do something here + ... pass + + + ## Writing Shapefiles + + PyShp tries to be as flexible as possible when writing shapefiles while + maintaining some degree of automatic validation to make sure you don't + accidentally write an invalid file. + + PyShp can write just one of the component files such as the shp or dbf file + without writing the others. So in addition to being a complete shapefile + library, it can also be used as a basic dbf (xbase) library. Dbf files are a + common database format which are often useful as a standalone simple database + format. And even shp files occasionaly have uses as a standalone format. Some + web-based GIS systems use an user-uploaded shp file to specify an area of + interest. Many precision agriculture chemical field sprayers also use the shp + format as a control file for the sprayer system (usually in combination with + custom database file formats). + + To create a shapefile you add geometry and/or attributes using methods in the + Writer class until you are ready to save the file. + + Create an instance of the Writer class to begin creating a shapefile: + + + >>> w = shapefile.Writer() + + ### Setting the Shape Type + + The shape type defines the type of geometry contained in the shapefile. All of + the shapes must match the shape type setting. + + Shape types are represented by numbers between 0 and 31 as defined by the + shapefile specification. It is important to note that numbering system has + several reserved numbers which have not been used yet therefore the numbers of + the existing shape types are not sequential. + + There are three ways to set the shape type: + * Set it when creating the class instance. + * Set it by assigning a value to an existing class instance. + * Set it automatically to the type of the first shape by saving the shapefile. + + To manually set the shape type for a Writer object when creating the Writer: + + + >>> w = shapefile.Writer(shapeType=1) + + >>> w.shapeType + 1 + + OR you can set it after the Writer is created: + + + >>> w.shapeType = 3 + + >>> w.shapeType + 3 + + ### Geometry and Record Balancing + + Because every shape must have a corresponding record it is critical that the + number of records equals the number of shapes to create a valid shapefile. To + help prevent accidental misalignment the PSL 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 feature is NOT turned on by default. To activate it set + the attribute autoBalance to 1 (True): + + + >>> w.autoBalance = 1 + + You also have the option of manually calling the balance() method each time + you add a shape or a record to ensure the other side is up to date. When + balancing is used null shapes are created on the geometry side or a record + with a value of "NULL" for each field is created on the attribute side. + + The balancing option gives you flexibility in how you build the shapefile. + + Without auto balancing you can add geometry or records at anytime. You can + create all of the shapes and then create all of the records or vice versa. You + can use the balance method after creating a shape or record each time and make + updates later. If you do not use the balance method and forget to manually + balance the geometry and attributes the shapefile will be viewed as corrupt by + most shapefile software. + + With auto balanacing you can add either shapes or geometry and update blank + entries on either side as needed. Even if you forget to update an entry the + shapefile will still be valid and handled correctly by most shapefile + software. + + ### Adding Geometry + + Geometry is added using one of three methods: "null", "point", or "poly". The + "null" method is used for null shapes, "point" is used for point shapes, and + "poly" is used for everything else. + + **Adding a Point shape** + + Point shapes are added using the "point" method. A point is specified by an x, + y, and optional z (elevation) and m (measure) value. + + + >>> w = shapefile.Writer() + + >>> w.point(122, 37) # No elevation or measure values + + >>> w.shapes()[0].points + [[122, 37, 0, 0]] + + >>> w.point(118, 36, 4, 8) + + >>> w.shapes()[1].points + [[118, 36, 4, 8]] + + **Adding a Poly shape** + + "Poly" shapes can be either polygons or lines. Shapefile polygons must have at + least 4 points and the last point must be the same as the first. PyShp + automatically enforces closed polygons. A line must have at least two points. + Because of the similarities between these two shape types they are created + using a single method called "poly". + + + >>> w = shapefile.Writer() + + >>> w.poly(shapeType=3, parts=[[[122,37,4,9], [117,36,3,4]], [[115,32,8,8], + ... [118,20,6,4], [113,24]]]) + + **Adding a Null shape** + + Because Null shape types (shape type 0) have no geometry the "null" method is + called without any arguments. This type of shapefile is rarely used but it is valid. + + + >>> w = shapefile.Writer() + + >>> w.null() + + The writer object's shapes list will now have one null shape: + + + >>> assert w.shapes()[0].shapeType == shapefile.NULL + + ### Creating Attributes + + Creating attributes involves two steps. Step 1 is to create fields to contain + attribute values and step 2 is to populate the fields with values for each + shape record. + + The following attempts to create a complete shapefile. The attribute and + field names are not very creative: + + + >>> w = shapefile.Writer(shapefile.POINT) + >>> w.point(1,1) + >>> w.point(3,1) + >>> w.point(4,3) + >>> w.point(2,2) + >>> w.field('FIRST_FLD') + >>> w.field('SECOND_FLD','C','40') + >>> w.record('First','Point') + >>> w.record('Second','Point') + >>> w.record('Third','Point') + >>> w.record('Fourth','Point') + >>> w.save('shapefiles/test/point') + + >>> w = shapefile.Writer(shapefile.POLYGON) + >>> w.poly(parts=[[[1,5],[5,5],[5,1],[3,3],[1,1]]]) + >>> w.field('FIRST_FLD','C','40') + >>> w.field('SECOND_FLD','C','40') + >>> w.record('First','Polygon') + >>> w.save('shapefiles/test/polygon') + + >>> w = shapefile.Writer(shapefile.POLYLINE) + >>> w.line(parts=[[[1,5],[5,5],[5,1],[3,3],[1,1]]]) + >>> w.poly(parts=[[[1,3],[5,3]]], shapeType=shapefile.POLYLINE) + >>> w.field('FIRST_FLD','C','40') + >>> w.field('SECOND_FLD','C','40') + >>> w.record('First','Line') + >>> w.record('Second','Line') + >>> w.save('shapefiles/test/line') + + You can also add attributes using keyword arguments where the keys are field + names. + + + >>> w = shapefile.Writer(shapefile.POLYLINE) + >>> w.line(parts=[[[1,5],[5,5],[5,1],[3,3],[1,1]]]) + >>> w.field('FIRST_FLD','C','40') + >>> w.field('SECOND_FLD','C','40') + >>> w.record(FIRST_FLD='First', SECOND_FLD='Line') + >>> w.save('shapefiles/test/line') + + ### File Names + + File extensions are optional when reading or writing shapfiles. If you specify + them PyShp ignores them anyway. When you save files you can specify a base + file name that is used for all three file types. Or you can specify a nmae for + one or more file types. In that case, any file types not assigned will not + save and only file types with file names will be saved. If you do not specify + any file names (i.e. save()), then a unique file name is generated with the + prefix "shapefile_" followed by random characters which is used for all three + files. The unique file name is returned as a string. + + + >>> targetName = w.save() + >>> assert("shapefile_" in targetName) + + ### Saving to File-Like Objects + + Just as you can read shapefiles from python file-like objects you can also + write them. + + + >>> try: + ... from StringIO import StringIO + ... except ImportError: + ... from io import BytesIO as StringIO + >>> shp = StringIO() + >>> shx = StringIO() + >>> dbf = StringIO() + >>> w.saveShp(shp) + >>> w.saveShx(shx) + >>> w.saveDbf(dbf) + >>> # Normally you would call the "StringIO.getvalue()" method on these objects. + >>> shp = shx = dbf = None + + ## Editing Shapefiles + + The Editor class attempts to make changing existing shapefiles easier by + handling the reading and writing details behind the scenes. This class is + experimental and should be avoided for production use. You can do the same + thing by reading a shapefile into memory, making changes to the python objects, + and write out a new shapefile with the same or different name. + + Let's add shapes to existing shapefiles: + + Add a point to a point shapefile + + + >>> e = shapefile.Editor(shapefile="shapefiles/test/point.shp") + >>> e.point(0,0,10,2) + >>> e.record("Appended","Point") + >>> e.save('shapefiles/test/point') + + Add a new line to a line shapefile: + + + >>> e = shapefile.Editor(shapefile="shapefiles/test/line.shp") + >>> e.line(parts=[[[10,5],[15,5],[15,1],[13,3],[11,1]]]) + >>> e.record('Appended','Line') + >>> e.save('shapefiles/test/line') + + Add a new polygon to a polygon shapefile: + + + >>> e = shapefile.Editor(shapefile="shapefiles/test/polygon.shp") + >>> e.poly(parts=[[[5.1,5],[9.9,5],[9.9,1],[7.5,3],[5.1,1]]]) + >>> e.record("Appended","Polygon") + >>> e.save('shapefiles/test/polygon') + + Remove the first point in each shapefile - for a point shapefile that is the + first shape and record" + + + >>> e = shapefile.Editor(shapefile="shapefiles/test/point.shp") + >>> e.delete(0) + >>> e.save('shapefiles/test/point') + + Remove the last shape in the polygon shapefile. + + + >>> e = shapefile.Editor(shapefile="shapefiles/test/polygon.shp") + >>> e.delete(-1) + >>> e.save('shapefiles/test/polygon') + + ## Python \_\_geo_interface\_\_ + + The Python \_\_geo_interface\_\_ convention provides a data interchange interface + among geospatial Python libraries. The interface returns data as GeoJSON. More + information on the \_\_geo_interface\_\_ protocol can be found at: [https://gist.g + ithub.com/sgillies/2217756](https://gist.github.com/sgillies/2217756). More + information on GeoJSON is available at + [http://geojson.org](http://geojson.org). + + + >>> s = sf.shape(0) + >>> s.__geo_interface__["type"] + 'MultiPolygon' + + # Testing + + The testing framework is doctest, which are located in this file README.md. + In the same folder as README.md and shapefile.py, from the command line run + ``` + $ python shapefile.py + ``` + + Linux/Mac and similar platforms will need to run `$ dos2unix README.md` in order + correct line endings in README.md. Keywords: gis geospatial geographic shapefile shapefiles Platform: UNKNOWN diff --git a/README.txt b/README.txt deleted file mode 100644 index df8014f..0000000 --- a/README.txt +++ /dev/null @@ -1,661 +0,0 @@ -PyShp -======================== - -:Author: Joel Lawhead - [email protected] - -:Version 1.2.2 - -:Revised: January 9, 2015 - -.. contents:: - -Overview --------- - -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 Description - July 1998" -located at http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf. -The Esri document describes the shp and shx file formats. However a third file -format called dbf is also required. This format is documented on the web as the -"XBase File Format Description" and is a simple file-based database format created -in the 1960's. For more on this specification see: -http://www.clicketyclick.dk/databases/xbase/format/index.html - -Both the Esri and XBase file-formats are very simple in design and -memory efficient which is part of the reason the shapefile format remains popular -despite the numerous ways to store and exchange GIS data available today. - -Pyshp is compatible with Python 2.4-3.x. - -This document provides examples for using PyShp to read and write shapefiles. - -Currently the sample census blockgroup shapefile referenced in the examples is -only available on the google code project site at http://code.google.com/p/pyshp. -These examples are straight-forward and you can also easily run them against your -own shapefiles manually with minimal modification. Other examples for specific -topics are continually added to the pyshp wiki on google code and the blog -http://GeospatialPython.com. - -Important: For information about map projections, shapefiles, -and Python please visit: http://code.google.com/p/pyshp/wiki/MapProjections - -I sincerely hope this library eliminates the mundane distraction of simply -reading and writing data, and allows you to focus on the challenging and FUN -part of your geospatial project. - -Examples --------- - -Before doing anything you must import the library. - ->>> import shapefile - -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 subversion -repository of the pyshp google code site. - -Reading Shapefiles -++++++++++++++++++ - -To read a shapefile create a new "Reader" object and pass it the name of an -existing shapefile. The shapefile format is acutally a collection of three -files. You specify the base filename of the shapefile or the complete filename -of any of the shapefile component files. - ->>> sf = shapefile.Reader("shapefiles/blockgroups") - - -OR - ->>> sf = shapefile.Reader("shapefiles/blockgroups.shp") - - -OR - ->>> sf = shapefile.Reader("shapefiles/blockgroups.dbf") - - -OR any of the other 5+ formats which are potentially part of a shapefile. -The library does not care about extensions. - -Reading Shapefiles from File-Like Objects -......................................... - -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, or in some cases a database. - ->>> myshp = open("shapefiles/blockgroups.shp", "rb") ->>> mydbf = open("shapefiles/blockgroups.dbf", "rb") ->>> 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 shx file -to access shape records a little faster but will do just fine without it. - -Reading Geometry -................ - -A shapefile's geometry is the collection of points or shapes made from verticies -and implied arcs representing physical locations. All types of shapefiles -just store points. The metadata about the points determine how they are handled by -software. - -You can get the a list of the shapefile's geometry by calling the shapes() -method. - ->>> shapes = sf.shapes() - -The shapes method returns a list of Shape objects describing the -geometry of each shape record. - ->>> len(shapes) -663 - -You can iterate through the shapefile's geometry using the iterShapes() method. - ->>> len(list(sf.iterShapes())) -663 - -Each shape record contains the following attributes: - ->>> for name in dir(shapes[3]): -... if not name.startswith('__'): -... name -'bbox' -'parts' -'points' -'shapeType' - - - shapeType: an integer representing the type of shape as defined by the - shapefile specification. - ->>> shapes[3].shapeType -5 - - - bbox: If the shape type contains multiple points this tuple describes the - lower left (x,y) coordinate and upper right corner coordinate creating a - complete box around the points. If the shapeType is a Null - (shapeType == 0) then an AttributeError is raised. - ->>> # Get the bounding box of the 4th shape. ->>> # Round coordinates to 3 decimal places ->>> bbox = shapes[3].bbox ->>> ['%.3f' % coord for coord in bbox] -['-122.486', '37.787', '-122.446', '37.811'] - - - parts: Parts simply group collections of points into shapes. If the shape record - has multiple parts this attribute contains the index of the first point of each part. - If there is only one part then a list containing 0 is returned. - ->>> shapes[3].parts -[0] - - - points: The points attribute contains a list of tuples containing an (x,y) - coordinate for each point in the shape. - ->>> len(shapes[3].points) -173 ->>> # Get the 8th point of the fourth shape ->>> # Truncate coordinates to 3 decimal places ->>> shape = shapes[3].points[7] ->>> ['%.3f' % coord for coord in shape] -['-122.471', '37.787'] - -To read a single shape by calling its index use the shape() method. The index -is the shape's count from 0. So to read the 8th shape record you would -use its index which is 7. - ->>> s = sf.shape(7) - ->>> # Read the bbox of the 8th shape to verify ->>> # Round coordinates to 3 decimal places ->>> ['%.3f' % coord for coord in s.bbox] -['-122.450', '37.801', '-122.442', '37.808'] - -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 geometry and attributes is the foundation of 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. - -The field names of a shapefile are available as soon as you read a shapefile. -You can call the "fields" attribute of the shapefile as a Python list. Each -field is a Python list with the following information: - -- Field name: the name describing the data at this column index. - -- Field type: the type of data at this column index. Types can be: Character, Numbers, Longs, Dates, or Memo. - The "Memo" type has no meaning within a GIS and is part of the xbase spec instead. - -- Field length: the length of the data found at this column index. Older GIS software may truncate this - length to 8 or 11 characters for "Character" fields. - -- Decimal length: the number of decimal places found in "Number" fields. - -To see the fields for the Reader object above (sf) call the "fields" attribute: - ->>> fields = sf.fields - ->>> assert fields == [("DeletionFlag", "C", 1, 0), ["AREA", "N", 18, 5], -... ["BKG_KEY", "C", 12, 0], ["POP1990", "N", 9, 0], ["POP90_SQMI", "N", 10, 1], -... ["HOUSEHOLDS", "N", 9, 0], -... ["MALES", "N", 9, 0], ["FEMALES", "N", 9, 0], ["WHITE", "N", 9, 0], -... ["BLACK", "N", 8, 0], ["AMERI_ES", "N", 7, 0], ["ASIAN_PI", "N", 8, 0], -... ["OTHER", "N", 8, 0], ["HISPANIC", "N", 8, 0], ["AGE_UNDER5", "N", 8, 0], -... ["AGE_5_17", "N", 8, 0], ["AGE_18_29", "N", 8, 0], ["AGE_30_49", "N", 8, 0], -... ["AGE_50_64", "N", 8, 0], ["AGE_65_UP", "N", 8, 0], -... ["NEVERMARRY", "N", 8, 0], ["MARRIED", "N", 9, 0], ["SEPARATED", "N", 7, 0], -... ["WIDOWED", "N", 8, 0], ["DIVORCED", "N", 8, 0], ["HSEHLD_1_M", "N", 8, 0], -... ["HSEHLD_1_F", "N", 8, 0], ["MARHH_CHD", "N", 8, 0], -... ["MARHH_NO_C", "N", 8, 0], ["MHH_CHILD", "N", 7, 0], -... ["FHH_CHILD", "N", 7, 0], ["HSE_UNITS", "N", 9, 0], ["VACANT", "N", 7, 0], -... ["OWNER_OCC", "N", 8, 0], ["RENTER_OCC", "N", 8, 0], -... ["MEDIAN_VAL", "N", 7, 0], ["MEDIANRENT", "N", 4, 0], -... ["UNITS_1DET", "N", 8, 0], ["UNITS_1ATT", "N", 7, 0], ["UNITS2", "N", 7, 0], -... ["UNITS3_9", "N", 8, 0], ["UNITS10_49", "N", 8, 0], -... ["UNITS50_UP", "N", 8, 0], ["MOBILEHOME", "N", 7, 0]] - -You can get a list of the shapefile's records by calling the records() method: - ->>> records = sf.records() - ->>> len(records) -663 - -Similar to the geometry methods, you can iterate through dbf records using the -recordsIter() method. - ->>> len(list(sf.iterRecords())) -663 - -Each record is a list containing an attribute corresponding to each field in the -field list. - -For example in the 4th record of the blockgroups shapefile the 2nd and 3rd -fields are the blockgroup id and the 1990 population count of -that San Francisco blockgroup: - ->>> records[3][1:3] -['060750601001', 4715] - -To read a single record call the record() method with the record's index: - ->>> rec = sf.record(3) - ->>> rec[1:3] -['060750601001', 4715] - -Reading Geometry and Records Simultaneously -........................................... - -You way want to examine both the geometry and the attributes for a record at the -same time. The shapeRecord() and shapeRecords() method let you do just that. - -Calling the shapeRecords() method will return the geometry and attributes for -all shapes as a list of ShapeRecord objects. Each ShapeRecord instance has a -"shape" and "record" attribute. The shape attribute is a ShapeRecord object as -dicussed in the first section "Reading Geometry". The record attribute is a -list of field values as demonstrated in the "Reading Records" section. - ->>> shapeRecs = sf.shapeRecords() - -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: - ->>> points = shapeRecs[3].shape.points[0:2] - ->>> len(points) -2 - -The shapeRec() method reads a single shape/record pair at the specified index. -To get the 4th shape record from the blockgroups shapfile use the third index: - ->>> shapeRec = sf.shapeRecord(3) - -The blockgroup key and population count: - ->>> shapeRec.record[1:3] -['060750601001', 4715] - ->>> points = shapeRec.shape.points[0:2] - ->>> len(points) -2 - -There is also an iterShapeRecords() method to iterate through large files: - ->>> shapeRecs = sf.iterShapeRecords() ->>> for shape, rec in shapeRecs: -... # do something here - -Writing Shapefiles -++++++++++++++++++ - -PyShp tries to be as flexible as possible when writing shapefiles while -maintaining some degree of automatic validation to make sure you don't -accidentally write an invalid file. - -PyShp can write just one of the component files such as the shp or dbf file -without writing the others. So in addition to being a complete -shapefile library, it can also be used as a basic dbf (xbase) library. Dbf files are -a common database format which are often useful as a standalone simple -database format. And even shp files occasionaly have uses as a standalone -format. Some web-based GIS systems use an user-uploaded shp file to specify -an area of interest. Many precision agriculture chemical field sprayers also -use the shp format as a control file for the sprayer system (usually in -combination with custom database file formats). - -To create a shapefile you add geometry and/or attributes using methods in the -Writer class until you are ready to save the file. - -Create an instance of the Writer class to begin creating a shapefile: - ->>> w = shapefile.Writer() - - -Setting the Shape Type -...................... - -The shape type defines the type of geometry contained in the shapefile. All of -the shapes must match the shape type setting. - -Shape types are represented by numbers between 0 and 31 as defined by the -shapefile specification. It is important to note that numbering system has -several reserved numbers which have not been used yet therefore the numbers of -the existing shape types are not sequential. - -You can reference shape types by the numbers or by constants defined by PyShp: -shapefile.NULL = 0 -shapefile.POINT = 1 -shapefile.POLYLINE = 3 -shapefile.POLYGON = 5 -shapefile.MULTIPOINT = 8 -shapefile.POINTZ = 11 -shapefile.POLYLINEZ = 13 -shapefile.POLYGONZ = 15 -shapefile.MULTIPOINTZ = 18 -shapefile.POINTM = 21 -shapefile.POLYLINEM = 23 -shapefile.POLYGONM = 25 -shapefile.MULTIPOINTM = 28 -shapefile.MULTIPATCH = 31 - -There are three ways to set the shape type: -- Set it when creating the class instance. -- Set it by assigning a value to an existing class instance. -- Set it automatically to the type of the first shape by saving the shapefile. - -To manually set the shape type for a Writer object when creating the Writer: - ->>> w = shapefile.Writer(shapeType=1) - -or we can use the constants as explained above: - ->>> w = shapefile.Writer(shapefile.POINT) - -As you can see, specifying the shapeType argument explicitly isn't necessary. - ->>> w.shapeType -1 - -OR you can set it after the Writer is created by changing the property: - ->>> w.shapeType = 3 - ->>> w.shapeType -3 - -Geometry and Record Balancing -............................. - -Because every shape must have a corresponding record it is critical that the -number of records equals the number of shapes to create a valid shapefile. 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 feature is NOT turned on by default. To activate it -set the attribute autoBalance to 1 (True): - ->>> w.autoBalance = 1 - -You also have the option of manually calling the balance() method each time you -add a shape or a record to ensure the other side is up to date. When balancing -is used null shapes are created on the geometry side or a record with a value of -"NULL" for each field is created on the attribute side. - -The balancing option gives you flexibility in how you build the shapefile. - -Without auto balancing you can add geometry or records at anytime. You can -create all of the shapes and then create all of the records or vice versa. You -can use the balance method after creating a shape or record each time and make -updates later. If you do not use the balance method and forget to manually -balance the geometry and attributes the shapefile will be viewed as corrupt by -most shapefile software. - -With auto balanacing you can add either shapes or geometry and update blank -entries on either side as needed. Even if you forget to update an entry the -shapefile will still be valid and handled correctly by most shapefile software. - -Adding Geometry -............... - -Geometry is added using one of three methods: "null", "point", or "poly". The "null" -method is used for null shapes, "point" is used for point shapes, and "poly" is -used for everything else. - -**Adding a Null shape** - -Because Null shape types (shape type 0) have no geometry the "null" method is -called without any arguments. - ->>> w = shapefile.Writer() - ->>> w.null() - -The writer object's shapes list will now have one null shape: - ->>> assert w.shapes()[0].shapeType == shapefile.NULL - -**Adding a Point shape** - -Point shapes are added using the "point" method. A point is specified by an -x, y, and optional z (elevation) and m (measure) value. - ->>> w = shapefile.Writer(shapefile.POINT) - ->>> w.point(122, 37) # No elevation or measure values - ->>> w.shapes()[0].points -[[122, 37, 0, 0]] - ->>> w.point(118, 36, 4, 8) - ->>> w.shapes()[1].points -[[118, 36, 4, 8]] - -**Adding a Poly Shape** - -"Poly" shapes can be either polygons or lines. Shapefile polygons must have at -least 4 points and the last point must be the same as the first. PyShp automatically -enforces closed polygons. -A line must have at least two points. -Because of the similarities between these two shape types they are created using -a single method called "poly". - ->>> w = shapefile.Writer(shapefile.POLYGON) - ->>> w.poly(shapeType=3, parts=[[[122,37,4,9], [117,36,3,4]], [[115,32,8,8], -... [118,20,6,4], [113,24]]]) - -**Adding a Polygon with Rings** - -Polygons consist of rings which mean they are closed. The first point and last point -of a ring must be the same. PyShp enforces ring closure if the ring is incomplete when -you add the shape. Polygons can have inner rings which create holes. Holes are defined -by the order of the points. Normally points in a ring run clockwise. If the points -run counter-clockwise then they form a hole. If you don't order the points correctly -you'll just have overlapping polygons. - ->>> w = shapefile.Writer(shapefile.POLYGON) ->>> outer_ring = [[10,10],[50,50],[100,10],[50,-50],[10,10]] ->>> inner_ring = [[40,10],[50,30],[70,10],[50,-30],[40,10]] ->>> inner_ring.reverse() - -You can use the "shapefile.signed_area()" method to determine if a ring is clockwise -or counter-clockwise. A value >= 0 means the ring is counter-clockwise and < 0 means -the ring is clockwise. The value returned is also the area of the polygon. - ->>> # Clockwise ring -... shapefile.signed_area(outer_ring) --4500.0 ->>> # Counter-clockwise ring -... shapefile.signed_area(inner_ring) -900.0 - -**Creating 3D Polygons** - -Elevation values, known as "Z" values allow you to create 3-dimensional shapefiles. The -z value is an extra value specified as part of a point. - ->>> w = shapefile.Writer(shapeType=shapefile.POLYGONZ) ->>> w.poly([[[-89.0, 33, 12], [-90, 31, 11], [-91, 30, 12]]], shapeType=15) ->>> w.field("NAME") ->>> w.record("PolyZTest") ->>> w.save("shapefiles/test/MyPolyZ") - -The z values are stored in a seperate shape attribute. - ->>> r = shapefile.Reader("shapefiles/test/MyPolyZ") ->>> s = r.shape(0) ->>> s.points -[[-89.0, 33.0], [-90.0, 31.0], [-91.0, 30.0], [-89.0, 33.0]] ->>> s.z -[12.0, 11.0, 12.0, 12.0] - -Creating Attributes -................... - -Creating attributes involves two steps. Step 1 is to create fields to contain -attribute values and step 2 is to populate the fields with values for each -shape record. - -The following attempts to create a complete shapefile: - ->>> w = shapefile.Writer(shapefile.POINT) ->>> w.point(1,1) ->>> w.point(3,1) ->>> w.point(4,3) ->>> w.point(2,2) ->>> w.field('FIRST_FLD') ->>> w.field('SECOND_FLD','C','40') ->>> w.record('First','Point') ->>> w.record('Second','Point') ->>> w.record('Third','Point') ->>> w.record('Fourth','Point') ->>> w.save('shapefiles/test/point') - ->>> w = shapefile.Writer(shapefile.POLYGON) ->>> w.poly(parts=[[[1,5],[5,5],[5,1],[3,3],[1,1]]]) ->>> w.field('FIRST_FLD','C','40') ->>> w.field('SECOND_FLD','C','40') ->>> w.record('First','Polygon') ->>> w.save('shapefiles/test/polygon') - ->>> w = shapefile.Writer(shapefile.POLYLINE) ->>> w.line(parts=[[[1,5],[5,5],[5,1],[3,3],[1,1]]]) ->>> w.poly(parts=[[[1,3],[5,3]]], shapeType=shapefile.POLYLINE) ->>> w.field('FIRST_FLD','C','40') ->>> w.field('SECOND_FLD','C','40') ->>> w.record('First','Line') ->>> w.record('Second','Line') ->>> w.save('shapefiles/test/line') - -You can also add attributes using keyword arguments where the keys are field names. - ->>> w = shapefile.Writer(shapefile.POLYLINE) ->>> w.line(parts=[[[1,5],[5,5],[5,1],[3,3],[1,1]]]) ->>> w.field('FIRST_FLD','C','40') ->>> w.field('SECOND_FLD','C','40') ->>> w.record(FIRST_FLD='First', SECOND_FLD='Line') ->>> w.save('shapefiles/test/line') - -File Names -.......... - -File extensions are optional when reading or writing shapfiles. If you specify them Pyshp -ignores them anyway. When you save files you can specify a base file name that is used for -all three file types. Or you can specify a nmae for one or more file types. In that case, -any file types not assigned will not save and only file types with file names will be saved. -If you do not specify any file names (i.e. save()), then a unique file name is generated with -the prefix "shapefile\_" followed by random characters which is used for all three files. The -unique file name is returned as a string. - ->>> targetName = w.save() ->>> assert("shapefile_" in targetName) - -Saving to File-Like Objects -........................... - -Just as you can read shapefiles from python file-like objects you can also write them. - ->>> try: -... from StringIO import StringIO -... except ImportError: -... from io import BytesIO as StringIO ->>> shp = StringIO() ->>> shx = StringIO() ->>> dbf = StringIO() ->>> w.saveShp(shp) ->>> w.saveShx(shx) ->>> w.saveDbf(dbf) ->>> # Normally you would call the "StringIO.getvalue()" method on these objects. ->>> shp = shx = dbf = None - -Editing Shapefiles -++++++++++++++++++ - -The Editor class attempts to make changing existing shapefiles easier by handling the reading and writing details behind the scenes. - -Let's add shapes to existing shapefiles: - -Add a point to a point shapefile - ->>> e = shapefile.Editor(shapefile="shapefiles/test/point.shp") ->>> e.point(0,0,10,2) ->>> e.record("Appended","Point") ->>> # We added z and m values so ->>> # change the shapetype ->>> e.shapeType = shapefile.POINTZ ->>> e.save('shapefiles/test/point') - -Edit the appended point to change the "y" and "z" value - ->>> e = shapefile.Editor(shapefile="shapefiles/test/point.shp") ->>> # Find the point by the attribute ->>> for s in enumerate(e.records): -... i, record = s -... if record[0] == "Appended": -... geom = e._shapes[i] -... # Change the y value to 5 -... geom.points[0][1] = 5 -... # Change the z value to 9 -... if hasattr(geom, "z"): -... geom.z = (9,) -... else: -... geom.points[0][2] = 9 ->>> e.save('shapefiles/test/point') - -Add a new line to a line shapefile: - ->>> e = shapefile.Editor(shapefile="shapefiles/test/line.shp") ->>> e.line(parts=[[[10,5],[15,5],[15,1],[13,3],[11,1]]]) ->>> e.record('Appended','Line') ->>> e.save('shapefiles/test/line') ->>> e = None - -Add a new polygon to a polygon shapefile: - ->>> e = shapefile.Editor(shapefile="shapefiles/test/polygon.shp") ->>> e.poly(parts=[[[5.1,5],[9.9,5],[9.9,1],[7.5,3],[5.1,1]]]) ->>> e.record("Appended","Polygon") ->>> e.save('shapefiles/test/polygon') ->>> e = None - -Remove the first point in each shapefile - for a point shapefile that is -the first shape and record" - ->>> e = shapefile.Editor(shapefile="shapefiles/test/point.shp") ->>> e.delete(0) ->>> e.save('shapefiles/test/point') ->>> e = None - -Remove the last shape in the polygon shapefile. - ->>> e = shapefile.Editor(shapefile="shapefiles/test/polygon.shp") ->>> e.delete(-1) ->>> e.save('shapefiles/test/polygon') ->>> e = None - -Python __geo_interface__ -++++++++++++++++++++++++ - -The Python __geo_interface__ convention provides a data interchange interface -among geospatial Python libraries. The interface returns data as GeoJSON. -More information on the __geo_interface__ protocol can be found at: -https://gist.github.com/sgillies/2217756. -More information on GeoJSON is available at http://geojson.org. - ->>> s = sf.shape(0) ->>> s.__geo_interface__["type"] -'MultiPolygon' - diff --git a/setup.py b/setup.py index a5a0b8c..890f12e 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,13 @@ from setuptools import setup setup(name='pyshp', - version='1.2.3', + version='1.2.8', description='Pure Python read/write support for ESRI Shapefile format', - long_description=open('README.txt').read(), + long_description=open('README.md').read(), author='Joel Lawhead', author_email='[email protected]', url='https://github.com/GeospatialPython/pyshp', - download_url='https://github.com/GeospatialPython/pyshp/archive/1.2.3.tar.gz', + download_url='https://github.com/GeospatialPython/pyshp/archive/1.2.8.tar.gz', py_modules=['shapefile'], license='MIT', zip_safe=False, diff --git a/shapefile.py b/shapefile.py index c125a90..e665c4f 100644 --- a/shapefile.py +++ b/shapefile.py @@ -3,14 +3,15 @@ shapefile.py Provides read and write support for ESRI Shapefiles. author: jlawhead<at>geospatialpython.com date: 2015/06/22 -version: 1.2.3 +version: 1.2.8 Compatible with Python versions 2.4-3.x -version changelog: Reader.iterShapeRecords() bugfix for Python 3 +version changelog: +- Pushing 1.2.5 changes to PyPi using Travis-CI. """ -__version__ = "1.2.3" +__version__ = "1.2.8" -from struct import pack, unpack, calcsize, error +from struct import pack, unpack, calcsize, error, Struct import os import sys import time @@ -96,8 +97,7 @@ class _Array(array.array): def signed_area(coords): """Return the signed area enclosed by a ring using the linear time - algorithm at http://www.cgafaq.info/wiki/Polygon_Area. A value >= 0 - indicates a counter-clockwise oriented ring. + algorithm. A value >= 0 indicates a counter-clockwise oriented ring. """ xs, ys = map(list, zip(*coords)) xs.append(xs[1]) @@ -337,7 +337,7 @@ class Reader: if shapeType in (3,5,13,15,23,25,31): nParts = unpack("<i", f.read(4))[0] # Shape types with points - if shapeType in (3,5,8,13,15,23,25,31): + if shapeType in (3,5,8,13,15,18,23,25,28,31): nPoints = unpack("<i", f.read(4))[0] # Read parts if nParts: @@ -347,7 +347,8 @@ class Reader: record.partTypes = _Array('i', unpack("<%si" % nParts, f.read(nParts * 4))) # Read points - produces a list of [x,y] values if nPoints: - record.points = [_Array('d', unpack("<2d", f.read(16))) for p in range(nPoints)] + flat = unpack("<%sd" % (2 * nPoints), f.read(16*nPoints)) + record.points = list(izip(*(iter(flat),) * 2)) # Read z extremes and values if shapeType in (13,15,18,31): (zmin, zmax) = unpack("<2d", f.read(16)) @@ -390,10 +391,12 @@ class Reader: numRecords = shxRecordLength // 8 # Jump to the first record. shx.seek(100) - for r in range(numRecords): - # Offsets are 16-bit words just like the file length - self._offsets.append(unpack(">i", shx.read(4))[0] * 2) - shx.seek(shx.tell() + 4) + shxRecords = _Array('i') + # Each offset consists of two nrs, only the first one matters + shxRecords.fromfile(shx, 2 * numRecords) + if sys.byteorder != 'big': + shxRecords.byteswap() + self._offsets = [2 * el for el in shxRecords[::2]] if not i == None: return self._offsets[i] @@ -470,6 +473,8 @@ class Reader: if terminator != b("\r"): raise ShapefileException("Shapefile dbf header lacks expected terminator. (likely corrupt?)") self.fields.insert(0, ('DeletionFlag', 'C', 1, 0)) + fmt,fmtSize = self.__recordFmt() + self.__recStruct = Struct(fmt) def __recordFmt(self): """Calculates the size of a .shp geometry record.""" @@ -482,8 +487,7 @@ class Reader: def __record(self): """Reads and returns a dbf record row as a list of values.""" f = self.__getFileObj(self.dbf) - recFmt = self.__recordFmt() - recordContents = unpack(recFmt[0], f.read(recFmt[1])) + recordContents = self.__recStruct.unpack(f.read(self.__recStruct.size)) if recordContents[0] != b(' '): # deleted record return None @@ -501,9 +505,17 @@ class Reader: if value == b(''): value = None elif deci: - value = float(value) + try: + value = float(value) + except ValueError: + #not parseable as float, set to None + value = None else: - value = int(value) + try: + value = int(value) + except ValueError: + #not parseable as int, set to None + value = None elif typ == b('D'): if value.count(b('0')) == len(value): # QGIS NULL is all '0' chars value = None @@ -528,7 +540,7 @@ class Reader: if not self.numRecords: self.__dbfHeader() i = self.__restrictIndex(i) - recSize = self.__recordFmt()[1] + recSize = self.__recStruct.size f.seek(0) f.seek(self.__dbfHeaderLength() + (i * recSize)) return self.__record() @@ -537,13 +549,11 @@ class Reader: """Returns all records in a dbf file.""" if not self.numRecords: self.__dbfHeader() - records = [] f = self.__getFileObj(self.dbf) f.seek(self.__dbfHeaderLength()) - for i in range(self.numRecords): - r = self.__record() - if r: - records.append(r) + flat = unpack(self.__recStruct.format * self.numRecords, f.read(self.__recStruct.size * self.numRecords)) + rowlen = len(self.fields) - 1 + records = list(izip(*(iter(flat),) * rowlen)) return records def iterRecords(self): @@ -1183,11 +1193,11 @@ class Editor(Writer): def test(): import doctest doctest.NORMALIZE_WHITESPACE = 1 - doctest.testfile("README.txt", verbose=1) + doctest.testfile("README.md", verbose=1) if __name__ == "__main__": """ - Doctests are contained in the file 'README.txt'. This library was originally developed + Doctests are contained in the file 'README.md'. This library was originally developed using Python 2.3. Python 2.4 and above have some excellent improvements in the built-in testing libraries but for now unit testing is done using what's available in 2.3. -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/pyshp.git _______________________________________________ Pkg-grass-devel mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

