Skip to content

Commit 1a1bed1

Browse files
author
Jeff Whitaker
committed
Merge pull request #131 from jswhit/master
fix for issue 130
2 parents 813ebfe + 475c586 commit 1a1bed1

File tree

4 files changed

+72
-31
lines changed

4 files changed

+72
-31
lines changed

Changelog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
version 1.0.8 (not yet released)
22
--------------------------------
3+
* update shapefile.py to version 1.2.0 from pyshp.googlecode.com. Add back
4+
in modification clobbered in upgrade to version 1.1.7 (issue 30).
35
* added 'facecolor' keyward argument to drawcounties() method; gives user
46
ability to fill counties with specified matplotlib color argument.
57

lib/mpl_toolkits/basemap/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
else:
4848
basemap_datadir = os.sep.join([os.path.dirname(__file__), 'data'])
4949

50-
__version__ = '1.0.7'
50+
__version__ = '1.0.8'
5151

5252
# module variable that sets the default value for the 'latlon' kwarg.
5353
# can be set to True by user so plotting functions can take lons,lats

lib/mpl_toolkits/basemap/shapefile.py

Lines changed: 68 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@
22
shapefile.py
33
Provides read and write support for ESRI Shapefiles.
44
author: jlawhead<at>geospatialpython.com
5-
date: 20130622
6-
version: 1.1.7
5+
date: 20130727
6+
version: 1.2.0
77
Compatible with Python versions 2.4-3.x
88
"""
9-
__version__ = "1.1.7"
9+
10+
__version__ = "1.2.0"
1011

1112
from struct import pack, unpack, calcsize, error
1213
import os
1314
import sys
1415
import time
1516
import array
1617
import tempfile
18+
1719
#
1820
# Constants for shape types
21+
default_encoding = 'utf-8'
1922
NULL = 0
2023
POINT = 1
2124
POLYLINE = 3
@@ -40,7 +43,7 @@ def b(v):
4043
if PYTHON3:
4144
if isinstance(v, str):
4245
# For python 3 encode str to bytes.
43-
return v.encode('utf-8')
46+
return v.encode(default_encoding)
4447
elif isinstance(v, bytes):
4548
# Already bytes.
4649
return v
@@ -55,7 +58,7 @@ def u(v):
5558
if PYTHON3:
5659
if isinstance(v, bytes):
5760
# For python 3 decode bytes to str.
58-
return v.decode('utf-8')
61+
return v.decode(default_encoding)
5962
elif isinstance(v, str):
6063
# Already str.
6164
return v
@@ -80,7 +83,7 @@ def __repr__(self):
8083

8184
def signed_area(coords):
8285
"""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
8487
indicates a counter-clockwise oriented ring.
8588
"""
8689
xs, ys = map(list, zip(*coords))
@@ -232,7 +235,7 @@ def __init__(self, *args, **kwargs):
232235
self.dbf = kwargs["dbf"]
233236
if hasattr(self.dbf, "seek"):
234237
self.dbf.seek(0)
235-
if self.shp or self.dbf:
238+
if self.shp or self.dbf:
236239
self.load()
237240
else:
238241
raise ShapefileException("Shapefile Reader requires a shapefile or file-like object.")
@@ -357,7 +360,7 @@ def __shape(self):
357360
record.m = unpack("<d", f.read(8))
358361
# Seek to the end of this record as defined by the record header because
359362
# 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.
361364
f.seek(next)
362365
return record
363366

@@ -398,6 +401,12 @@ def shape(self, i=0):
398401
def shapes(self):
399402
"""Returns all shapes in a shapefile."""
400403
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()
401410
shp.seek(100)
402411
shapes = []
403412
while shp.tell() < self.shpLength:
@@ -408,9 +417,11 @@ def iterShapes(self):
408417
"""Serves up shapes in a shapefile as an iterator. Useful
409418
for handling large shapefiles."""
410419
shp = self.__getFileObj(self.shp)
420+
shp.seek(0,2)
421+
self.shpLength = shp.tell()
411422
shp.seek(100)
412423
while shp.tell() < self.shpLength:
413-
yield self.__shape()
424+
yield self.__shape()
414425

415426
def __dbfHeaderLength(self):
416427
"""Retrieves the header length of a dbf file header."""
@@ -751,6 +762,8 @@ def __shpRecords(self):
751762
recNum += 1
752763
start = f.tell()
753764
# Shape Type
765+
if self.shapeType != 31:
766+
s.shapeType = self.shapeType
754767
f.write(pack("<i", s.shapeType))
755768
# All shape types capable of having a bounding box
756769
if s.shapeType in (3,5,8,13,15,18,23,25,28,31):
@@ -787,14 +800,19 @@ def __shpRecords(self):
787800
except error:
788801
raise ShapefileException("Failed to write elevation extremes for record %s. Expected floats." % recNum)
789802
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]
792807
except error:
793808
raise ShapefileException("Failed to write elevation values for record %s. Expected floats." % recNum)
794809
# 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):
796811
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])))
798816
except error:
799817
raise ShapefileException("Failed to write measure extremes for record %s. Expected floats" % recNum)
800818
try:
@@ -809,16 +827,36 @@ def __shpRecords(self):
809827
raise ShapefileException("Failed to write point for record %s. Expected floats." % recNum)
810828
# Write a single Z value
811829
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)
816844
# Write a single M value
817845
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)
822860
# Finalize record length as 16-bit words
823861
finish = f.tell()
824862
length = (finish - start) // 2
@@ -880,10 +918,13 @@ def poly(self, parts=[], shapeType=POLYGON, partTypes=[]):
880918
polyShape = _Shape(shapeType)
881919
polyShape.parts = []
882920
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])
883926
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))
887928
for point in part:
888929
# Ensure point is list
889930
if not isinstance(point, list):
@@ -892,7 +933,6 @@ def poly(self, parts=[], shapeType=POLYGON, partTypes=[]):
892933
while len(point) < 4:
893934
point.append(0)
894935
polyShape.points.append(point)
895-
polyShape.parts.append(len(polyShape.points))
896936
if polyShape.shapeType == 31:
897937
if not partTypes:
898938
for part in parts:
@@ -970,8 +1010,8 @@ def save(self, target=None, shp=None, shx=None, dbf=None):
9701010
be written exclusively using saveShp, saveShx, and saveDbf respectively.
9711011
If target is specified but not shp,shx, or dbf then the target path and
9721012
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.
9751015
"""
9761016
# Create a unique file name if one is not defined
9771017
if shp:
@@ -985,7 +1025,7 @@ def save(self, target=None, shp=None, shx=None, dbf=None):
9851025
if not target:
9861026
temp = tempfile.NamedTemporaryFile(prefix="shapefile_",dir=os.getcwd())
9871027
target = temp.name
988-
generated = True
1028+
generated = True
9891029
self.saveShp(target)
9901030
self.shp.close()
9911031
self.saveShx(target)
@@ -994,7 +1034,6 @@ def save(self, target=None, shp=None, shx=None, dbf=None):
9941034
self.dbf.close()
9951035
if generated:
9961036
return target
997-
9981037
class Editor(Writer):
9991038
def __init__(self, shapefile=None, shapeType=POINT, autoBalance=1):
10001039
self.autoBalance = autoBalance

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def checkversion(GEOS_dir):
111111

112112
setup(
113113
name = "basemap",
114-
version = "1.0.7",
114+
version = "1.0.8",
115115
description = "Plot data on map projections with matplotlib",
116116
long_description = """
117117
An add-on toolkit for matplotlib that lets you plot data

0 commit comments

Comments
 (0)