def __shapefileHeader(self, fileObj, headerType='shp'): """Writes the specified header type to the specified file-like object. Several of the shapefile formats are so similar that a single generic method to read or write them is warranted.""" f = self.__getFileObj(fileObj) f.seek(0) # File code, Unused bytes f.write(pack(">6i", 9994, 0, 0, 0, 0, 0)) # File length (Bytes / 2 = 16-bit words) if headerType == 'shp': f.write(pack(">i", self.__shpFileLength())) elif headerType == 'shx': f.write(pack('>i', ((100 + (len(self._shapes) * 8)) // 2))) # Version, Shape type f.write(pack("<2i", 1000, self.shapeType)) # The shapefile's bounding box (lower left, upper right) if self.shapeType != 0: try: f.write(pack("<4d", *self.bbox())) except error: raise ShapefileException( "Failed to write shapefile bounding box. Floats required.") else: f.write(pack("<4d", 0, 0, 0, 0)) # Elevation z = self.zbox() # Measure m = self.mbox() try: f.write(pack("<4d", z[0], z[1], m[0], m[1])) except error: raise ShapefileException( "Failed to write shapefile elevation and measure values. Floats required." )
def __init__(self, *args, **kwargs): self.shp = None self.shx = None self.dbf = None self.shapeName = "Not specified" self._offsets = [] self.shpLength = None self.numRecords = None self.fields = [] self.__dbfHdrLength = 0 # See if a shapefile name was passed as an argument if len(args) > 0: if is_string(args[0]): self.load(args[0]) return if "shp" in kwargs.keys(): if hasattr(kwargs["shp"], "read"): self.shp = kwargs["shp"] if hasattr(self.shp, "seek"): self.shp.seek(0) if "shx" in kwargs.keys(): if hasattr(kwargs["shx"], "read"): self.shx = kwargs["shx"] if hasattr(self.shx, "seek"): self.shx.seek(0) if "dbf" in kwargs.keys(): if hasattr(kwargs["dbf"], "read"): self.dbf = kwargs["dbf"] if hasattr(self.dbf, "seek"): self.dbf.seek(0) if self.shp or self.dbf: self.load() else: raise ShapefileException( "Shapefile Reader requires a shapefile or file-like object.")
def __getFileObj(self, f): """Safety handler to verify file-like objects""" if not f: raise ShapefileException("No file-like object available.") elif hasattr(f, "write"): return f else: pth = os.path.split(f)[0] if pth and not os.path.exists(pth): os.makedirs(pth) return open(f, "wb")
def __getFileObj(self, f): """Checks to see if the requested shapefile file object is available. If not a ShapefileException is raised.""" if not f: raise ShapefileException( "Shapefile Reader requires a shapefile or file-like object.") if self.shp and self.shpLength is None: self.load() if self.dbf and len(self.fields) == 0: self.load() return f
def load(self, shapefile=None): """Opens a shapefile from a filename or file-like object. Normally this method would be called by the constructor with the file object or file name as an argument.""" if shapefile: shapeName, ext = os.path.splitext(shapefile) self.shapeName = shapeName try: self.shp = open("%s.shp" % shapeName, "rb") except IOError: raise ShapefileException("Unable to open %s.shp" % shapeName) try: self.shx = open("%s.shx" % shapeName, "rb") except IOError: raise ShapefileException("Unable to open %s.shx" % shapeName) try: self.dbf = open("%s.dbf" % shapeName, "rb") except IOError: raise ShapefileException("Unable to open %s.dbf" % shapeName) if self.shp: self.__shpHeader() if self.dbf: self.__dbfHeader()
def __shpHeader(self): """Reads the header information from a .shp or .shx file.""" if not self.shp: raise ShapefileException( "Shapefile Reader requires a shapefile or file-like object. (no shp file found" ) shp = self.shp # File length (16-bit word * 2 = bytes) shp.seek(24) self.shpLength = unpack(">i", shp.read(4))[0] * 2 # Shape type shp.seek(32) self.shapeType = unpack("<i", shp.read(4))[0] # The shapefile's bounding box (lower left, upper right) self.bbox = Array('d', unpack("<4d", shp.read(32))) # Elevation self.elevation = Array('d', unpack("<2d", shp.read(16))) # Measure self.measure = Array('d', unpack("<2d", shp.read(16)))
def __shpRecords(self): """Write the shp records""" f = self.__getFileObj(self.shp) f.seek(100) recNum = 1 for s in self._shapes: self._offsets.append(f.tell()) # Record number, Content length place holder f.write(pack(">2i", recNum, 0)) recNum += 1 start = f.tell() # Shape Type if self.shapeType != 31: s.shapeType = self.shapeType f.write(pack("<i", s.shapeType)) # All shape types capable of having a bounding box if s.shapeType in (3, 5, 8, 13, 15, 18, 23, 25, 28, 31): try: f.write(pack("<4d", *self.__bbox([s]))) except error: raise ShapefileException( "Falied to write bounding box for record %s. Expected floats." % recNum) # Shape types with parts if s.shapeType in (3, 5, 13, 15, 23, 25, 31): # Number of parts f.write(pack("<i", len(s.parts))) # Shape types with multiple points per record if s.shapeType in (3, 5, 8, 13, 15, 23, 25, 31): # Number of points f.write(pack("<i", len(s.points))) # Write part indexes if s.shapeType in (3, 5, 13, 15, 23, 25, 31): for p in s.parts: f.write(pack("<i", p)) # Part types for Multipatch (31) if s.shapeType == 31: for pt in s.partTypes: f.write(pack("<i", pt)) # Write points for multiple-point records if s.shapeType in (3, 5, 8, 13, 15, 23, 25, 31): try: [f.write(pack("<2d", *p[:2])) for p in s.points] except error: raise ShapefileException( "Failed to write points for record %s. Expected floats." % recNum) # Write z extremes and values if s.shapeType in (13, 15, 18, 31): try: f.write(pack("<2d", *self.__zbox([s]))) except error: raise ShapefileException( "Failed to write elevation extremes for record %s. Expected floats." % recNum) try: if hasattr(s, "z"): f.write(pack("<%sd" % len(s.z), *s.z)) else: [f.write(pack("<d", p[2])) for p in s.points] except error: raise ShapefileException( "Failed to write elevation values for record %s. Expected floats." % recNum) # Write m extremes and values if s.shapeType in (13, 15, 18, 23, 25, 28, 31): try: if hasattr(s, "m"): f.write(pack("<%sd" % len(s.m), *s.m)) else: f.write(pack("<2d", *self.__mbox([s]))) except error: raise ShapefileException( "Failed to write measure extremes for record %s. Expected floats" % recNum) try: [f.write(pack("<d", p[3])) for p in s.points] except error: raise ShapefileException( "Failed to write measure values for record %s. Expected floats" % recNum) # Write a single point if s.shapeType in (1, 11, 21): try: f.write(pack("<2d", s.points[0][0], s.points[0][1])) except error: raise ShapefileException( "Failed to write point for record %s. Expected floats." % recNum) # Write a single Z value if s.shapeType == 11: if hasattr(s, "z"): try: if not s.z: s.z = (0, ) f.write(pack("<d", s.z[0])) except error: raise ShapefileException( "Failed to write elevation value for record %s. Expected floats." % recNum) else: try: if len(s.points[0]) < 3: s.points[0].append(0) f.write(pack("<d", s.points[0][2])) except error: raise ShapefileException( "Failed to write elevation value for record %s. Expected floats." % recNum) # Write a single M value if s.shapeType in (11, 21): if hasattr(s, "m"): try: if not s.m: s.m = (0, ) f.write(pack("<1d", s.m[0])) except error: raise ShapefileException( "Failed to write measure value for record %s. Expected floats." % recNum) else: try: if len(s.points[0]) < 4: s.points[0].append(0) f.write(pack("<1d", s.points[0][3])) except error: raise ShapefileException( "Failed to write measure value for record %s. Expected floats." % recNum) # Finalize record length as 16-bit words finish = f.tell() length = (finish - start) // 2 self._lengths.append(length) # start - 4 bytes is the content length field f.seek(start - 4) f.write(pack(">i", length)) f.seek(finish)
def assertFile(self, attr): # attr should be 'shp', 'dbf', or 'shx' if not getattr(self, attr, None): raise ShapefileException( "Shapefile Reader requires a shapefile or file-like object." "(no %s file found)" % attr)