2
2
shapefile.py
3
3
Provides read and write support for ESRI Shapefiles.
4
4
author: jlawhead<at>geospatialpython.com
5
- date: 20130622
6
- version: 1.1.7
5
+ date: 20130727
6
+ version: 1.2.0
7
7
Compatible with Python versions 2.4-3.x
8
8
"""
9
- __version__ = "1.1.7"
9
+
10
+ __version__ = "1.2.0"
10
11
11
12
from struct import pack , unpack , calcsize , error
12
13
import os
13
14
import sys
14
15
import time
15
16
import array
16
17
import tempfile
18
+
17
19
#
18
20
# Constants for shape types
21
+ default_encoding = 'utf-8'
19
22
NULL = 0
20
23
POINT = 1
21
24
POLYLINE = 3
@@ -40,7 +43,7 @@ def b(v):
40
43
if PYTHON3 :
41
44
if isinstance (v , str ):
42
45
# For python 3 encode str to bytes.
43
- return v .encode ('utf-8' )
46
+ return v .encode (default_encoding )
44
47
elif isinstance (v , bytes ):
45
48
# Already bytes.
46
49
return v
@@ -55,7 +58,7 @@ def u(v):
55
58
if PYTHON3 :
56
59
if isinstance (v , bytes ):
57
60
# For python 3 decode bytes to str.
58
- return v .decode ('utf-8' )
61
+ return v .decode (default_encoding )
59
62
elif isinstance (v , str ):
60
63
# Already str.
61
64
return v
@@ -80,7 +83,7 @@ def __repr__(self):
80
83
81
84
def signed_area (coords ):
82
85
"""Return the signed area enclosed by a ring using the linear time
83
- algorithm at http://www.cgafaq.info/wiki/Polygon_Area. A value < = 0
86
+ algorithm at http://www.cgafaq.info/wiki/Polygon_Area. A value > = 0
84
87
indicates a counter-clockwise oriented ring.
85
88
"""
86
89
xs , ys = map (list , zip (* coords ))
@@ -232,7 +235,7 @@ def __init__(self, *args, **kwargs):
232
235
self .dbf = kwargs ["dbf" ]
233
236
if hasattr (self .dbf , "seek" ):
234
237
self .dbf .seek (0 )
235
- if self .shp or self .dbf :
238
+ if self .shp or self .dbf :
236
239
self .load ()
237
240
else :
238
241
raise ShapefileException ("Shapefile Reader requires a shapefile or file-like object." )
@@ -357,7 +360,7 @@ def __shape(self):
357
360
record .m = unpack ("<d" , f .read (8 ))
358
361
# Seek to the end of this record as defined by the record header because
359
362
# the shapefile spec doesn't require the actual content to meet the header
360
- # definition. Probably allowed for lazy feature deletion.
363
+ # definition. Probably allowed for lazy feature deletion.
361
364
f .seek (next )
362
365
return record
363
366
@@ -398,6 +401,12 @@ def shape(self, i=0):
398
401
def shapes (self ):
399
402
"""Returns all shapes in a shapefile."""
400
403
shp = self .__getFileObj (self .shp )
404
+ # Found shapefiles which report incorrect
405
+ # shp file length in the header. Can't trust
406
+ # that so we seek to the end of the file
407
+ # and figure it out.
408
+ shp .seek (0 ,2 )
409
+ self .shpLength = shp .tell ()
401
410
shp .seek (100 )
402
411
shapes = []
403
412
while shp .tell () < self .shpLength :
@@ -408,9 +417,11 @@ def iterShapes(self):
408
417
"""Serves up shapes in a shapefile as an iterator. Useful
409
418
for handling large shapefiles."""
410
419
shp = self .__getFileObj (self .shp )
420
+ shp .seek (0 ,2 )
421
+ self .shpLength = shp .tell ()
411
422
shp .seek (100 )
412
423
while shp .tell () < self .shpLength :
413
- yield self .__shape ()
424
+ yield self .__shape ()
414
425
415
426
def __dbfHeaderLength (self ):
416
427
"""Retrieves the header length of a dbf file header."""
@@ -751,6 +762,8 @@ def __shpRecords(self):
751
762
recNum += 1
752
763
start = f .tell ()
753
764
# Shape Type
765
+ if self .shapeType != 31 :
766
+ s .shapeType = self .shapeType
754
767
f .write (pack ("<i" , s .shapeType ))
755
768
# All shape types capable of having a bounding box
756
769
if s .shapeType in (3 ,5 ,8 ,13 ,15 ,18 ,23 ,25 ,28 ,31 ):
@@ -787,14 +800,19 @@ def __shpRecords(self):
787
800
except error :
788
801
raise ShapefileException ("Failed to write elevation extremes for record %s. Expected floats." % recNum )
789
802
try :
790
- #[f.write(pack("<d", p[2])) for p in s.points]
791
- f .write (pack ("<%sd" % len (s .z ), * s .z ))
803
+ if hasattr (s ,"z" ):
804
+ f .write (pack ("<%sd" % len (s .z ), * s .z ))
805
+ else :
806
+ [f .write (pack ("<d" , p [2 ])) for p in s .points ]
792
807
except error :
793
808
raise ShapefileException ("Failed to write elevation values for record %s. Expected floats." % recNum )
794
809
# Write m extremes and values
795
- if s .shapeType in (23 ,25 ,31 ):
810
+ if s .shapeType in (13 , 15 , 18 , 23 ,25 , 28 ,31 ):
796
811
try :
797
- f .write (pack ("<2d" , * self .__mbox ([s ])))
812
+ if hasattr (s ,"m" ):
813
+ f .write (pack ("<%sd" % len (s .m ), * s .m ))
814
+ else :
815
+ f .write (pack ("<2d" , * self .__mbox ([s ])))
798
816
except error :
799
817
raise ShapefileException ("Failed to write measure extremes for record %s. Expected floats" % recNum )
800
818
try :
@@ -809,16 +827,36 @@ def __shpRecords(self):
809
827
raise ShapefileException ("Failed to write point for record %s. Expected floats." % recNum )
810
828
# Write a single Z value
811
829
if s .shapeType == 11 :
812
- try :
813
- f .write (pack ("<1d" , s .points [0 ][2 ]))
814
- except error :
815
- raise ShapefileException ("Failed to write elevation value for record %s. Expected floats." % recNum )
830
+ if hasattr (s , "z" ):
831
+ try :
832
+ if not s .z :
833
+ s .z = (0 ,)
834
+ f .write (pack ("<d" , s .z [0 ]))
835
+ except error :
836
+ raise ShapefileException ("Failed to write elevation value for record %s. Expected floats." % recNum )
837
+ else :
838
+ try :
839
+ if len (s .points [0 ])< 3 :
840
+ s .points [0 ].append (0 )
841
+ f .write (pack ("<d" , s .points [0 ][2 ]))
842
+ except error :
843
+ raise ShapefileException ("Failed to write elevation value for record %s. Expected floats." % recNum )
816
844
# Write a single M value
817
845
if s .shapeType in (11 ,21 ):
818
- try :
819
- f .write (pack ("<1d" , s .points [0 ][3 ]))
820
- except error :
821
- raise ShapefileException ("Failed to write measure value for record %s. Expected floats." % recNum )
846
+ if hasattr (s , "m" ):
847
+ try :
848
+ if not s .m :
849
+ s .m = (0 ,)
850
+ f .write (pack ("<1d" , s .m [0 ]))
851
+ except error :
852
+ raise ShapefileException ("Failed to write measure value for record %s. Expected floats." % recNum )
853
+ else :
854
+ try :
855
+ if len (s .points [0 ])< 4 :
856
+ s .points [0 ].append (0 )
857
+ f .write (pack ("<1d" , s .points [0 ][3 ]))
858
+ except error :
859
+ raise ShapefileException ("Failed to write measure value for record %s. Expected floats." % recNum )
822
860
# Finalize record length as 16-bit words
823
861
finish = f .tell ()
824
862
length = (finish - start ) // 2
@@ -880,10 +918,13 @@ def poly(self, parts=[], shapeType=POLYGON, partTypes=[]):
880
918
polyShape = _Shape (shapeType )
881
919
polyShape .parts = []
882
920
polyShape .points = []
921
+ # Make sure polygons are closed
922
+ if shapeType in (5 ,15 ,25 ,31 ):
923
+ for part in parts :
924
+ if part [0 ] != part [- 1 ]:
925
+ part .append (part [0 ])
883
926
for part in parts :
884
- # Make sure polygon is closed
885
- if shapeType in (5 ,15 ,25 ,31 ) and part [0 ] != part [- 1 ]:
886
- part .append (part [0 ])
927
+ polyShape .parts .append (len (polyShape .points ))
887
928
for point in part :
888
929
# Ensure point is list
889
930
if not isinstance (point , list ):
@@ -892,7 +933,6 @@ def poly(self, parts=[], shapeType=POLYGON, partTypes=[]):
892
933
while len (point ) < 4 :
893
934
point .append (0 )
894
935
polyShape .points .append (point )
895
- polyShape .parts .append (len (polyShape .points ))
896
936
if polyShape .shapeType == 31 :
897
937
if not partTypes :
898
938
for part in parts :
@@ -970,8 +1010,8 @@ def save(self, target=None, shp=None, shx=None, dbf=None):
970
1010
be written exclusively using saveShp, saveShx, and saveDbf respectively.
971
1011
If target is specified but not shp,shx, or dbf then the target path and
972
1012
file name are used. If no options or specified, a unique base file name
973
- is generated to save the files and the base file name is returned as a
974
- string.
1013
+ is generated to save the files and the base file name is returned as a
1014
+ string.
975
1015
"""
976
1016
# Create a unique file name if one is not defined
977
1017
if shp :
@@ -985,7 +1025,7 @@ def save(self, target=None, shp=None, shx=None, dbf=None):
985
1025
if not target :
986
1026
temp = tempfile .NamedTemporaryFile (prefix = "shapefile_" ,dir = os .getcwd ())
987
1027
target = temp .name
988
- generated = True
1028
+ generated = True
989
1029
self .saveShp (target )
990
1030
self .shp .close ()
991
1031
self .saveShx (target )
@@ -994,7 +1034,6 @@ def save(self, target=None, shp=None, shx=None, dbf=None):
994
1034
self .dbf .close ()
995
1035
if generated :
996
1036
return target
997
-
998
1037
class Editor (Writer ):
999
1038
def __init__ (self , shapefile = None , shapeType = POINT , autoBalance = 1 ):
1000
1039
self .autoBalance = autoBalance
0 commit comments