diff --git a/src/shapefile.py b/src/shapefile.py index b5e6ed4..d0ec177 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -653,10 +653,8 @@ def __geo_interface__(self) -> GeoJSONHomogeneousGeometryObject: # the geojson spec does not define a proper null-geometry type # however, it does allow geometry types with 'empty' coordinates to be interpreted as null-geometries return {"type": "Point", "coordinates": ()} - # return {"type": "Point", "coordinates": tuple()} #type: ignore return {"type": "Point", "coordinates": self.points[0]} - # return {"type": "Point", "coordinates": tuple(self.points[0])} # type: ignore if self.shapeType in [MULTIPOINT, MULTIPOINTM, MULTIPOINTZ]: if len(self.points) == 0: @@ -669,7 +667,6 @@ def __geo_interface__(self) -> GeoJSONHomogeneousGeometryObject: return { "type": "MultiPoint", "coordinates": self.points, - # "coordinates": [tuple(p) for p in self.points], #type: ignore } if self.shapeType in [POLYLINE, POLYLINEM, POLYLINEZ]: @@ -684,7 +681,6 @@ def __geo_interface__(self) -> GeoJSONHomogeneousGeometryObject: return { "type": "LineString", "coordinates": self.points, - # "coordinates": [tuple(p) for p in self.points], #type: ignore } # multilinestring @@ -695,11 +691,9 @@ def __geo_interface__(self) -> GeoJSONHomogeneousGeometryObject: ps = part continue - # coordinates.append([tuple(p) for p in self.points[ps:part]]) coordinates.append(list(self.points[ps:part])) ps = part - # coordinates.append([tuple(p) for p in self.points[part:]]) # assert len(self.parts) >1 # so disable pylint rule coordinates.append(list(self.points[part:])) # pylint: disable=undefined-loop-variable return {"type": "MultiLineString", "coordinates": coordinates} @@ -713,16 +707,14 @@ def __geo_interface__(self) -> GeoJSONHomogeneousGeometryObject: # get all polygon rings rings = [] - for i in range(len(self.parts)): + for i, start in enumerate(self.parts): # get indexes of start and end points of the ring - start = self.parts[i] try: end = self.parts[i + 1] except IndexError: end = len(self.points) # extract the points that make up the ring - # ring = [tuple(p) for p in self.points[start:end]] ring = list(self.points[start:end]) rings.append(ring) @@ -859,7 +851,7 @@ def from_byte_stream(cls, b_io, next_shape, oid=None, bbox=None): # pylint: dis @staticmethod def write_to_byte_stream(b_io, s, i, bbox, mbox, zbox): # pylint: disable=unused-argument - pass + return 0 class _CanHaveBBox(Shape): @@ -892,7 +884,7 @@ def _set_bbox_from_byte_stream(self, b_io): @staticmethod def _write_bbox_to_byte_stream(b_io, i, bbox): try: - b_io.write(pack("<4d", *bbox)) + return b_io.write(pack("<4d", *bbox)) except error: raise ShapefileException( f"Failed to write bounding box for record {i}. Expected floats." @@ -904,7 +896,7 @@ def _get_npoints_from_byte_stream(b_io): @staticmethod def _write_npoints_to_byte_stream(b_io, s): - b_io.write(pack(" mpos and p[mpos] is not None - else NODATA, - ) - ) + if len(p) > mpos and p[mpos] is not None: + ms.append(p[mpos]) + else: + ms.append(NODATA) + + num_bytes_written += b_io.write(pack(f"<{len(ms)}d", *ms)) + except error: raise ShapefileException( f"Failed to write measure values for record {i}. Expected floats" ) + return num_bytes_written + class _HasZ(_CanHaveBBox): # Not a Point @@ -1196,7 +1192,7 @@ def _write_zs_to_byte_stream(b_io, s, i, zbox): # Write z extremes and values # Note: missing z values are autoset to 0, but not sure if this is ideal. try: - b_io.write(pack("<2d", *zbox)) + num_bytes_written = b_io.write(pack("<2d", *zbox)) except error: raise ShapefileException( f"Failed to write elevation extremes for record {i}. Expected floats." @@ -1204,16 +1200,19 @@ def _write_zs_to_byte_stream(b_io, s, i, zbox): try: if hasattr(s, "z"): # if z values are stored in attribute - b_io.write(pack(f"<{len(s.z)}d", *s.z)) + zs = s.z else: # if z values are stored as 3rd dimension - for p in s.points: - b_io.write(pack(" 2 else 0)) + zs = [p[2] if len(p) > 2 else 0 for p in s.points] + + num_bytes_written += b_io.write(pack(f"<{len(zs)}d", *zs)) except error: raise ShapefileException( f"Failed to write elevation values for record {i}. Expected floats." ) + return num_bytes_written + class MultiPatch(_HasM, _HasZ, _CanHaveParts): shapeType = MULTIPATCH @@ -1223,8 +1222,7 @@ def _set_part_types_from_byte_stream(self, b_io, nParts): @staticmethod def _write_part_types_to_byte_stream(b_io, s): - for partType in s.partTypes: - b_io.write(pack("2i", self.shpNum, 0)) - start = f.tell() + # Shape Type if self.shapeType is None and s.shapeType != NULL: self.shapeType = s.shapeType @@ -2986,11 +2985,25 @@ def __shpRecord(self, s): self.__zbox(s) if s.shapeType in {POINTZ} | _HasZ._shapeTypes else None ) - f.write(pack("2i", self.shpNum, -1)) + + # Track number of content bytes written. Excluding self.shpNum and length t.b.c. + n = 0 + + n += b_io.write(pack("i", length)) + # Finalize record length as 16-bit words + length = n // 2 - f.seek(finish) + # 4 bytes in is the content length field + b_io.seek(4) + b_io.write(pack(">i", length)) + # Flush to file. + b_io.seek(0) + f.write(b_io.read()) return offset, length def __shxRecord(self, offset, length):