2020from collections .abc import Collection
2121from datetime import date
2222from struct import Struct , calcsize , error , pack , unpack
23- from typing import Any , Iterable , Iterator , Optional , Reversible , Union
23+ from typing import Any , Iterable , Iterator , Optional , Reversible , TypedDict , Union
2424from urllib .error import HTTPError
2525from urllib .parse import urlparse , urlunparse
2626from urllib .request import Request , urlopen
8787 5 : "RING" ,
8888}
8989
90+ # Custom type variables
91+
92+ Point2D = tuple [float , float ]
93+ PointZ = tuple [float , float , float ]
94+ PointZM = tuple [float , float , float , float ]
95+
96+ Coord = Union [Point2D , PointZ , PointZM ]
97+ Coords = list [Coord ]
98+
99+ BBox = tuple [float , float , float , float ]
100+
101+
102+ class GeoJSONT (TypedDict ):
103+ type : str
104+ coordinates : Union [
105+ tuple [()], Point2D , PointZ , PointZM , Coords , list [Coords ], list [list [Coords ]]
106+ ]
107+
108+
90109# Helpers
91110
92111MISSING = [None , "" ]
@@ -149,14 +168,6 @@ def __repr__(self):
149168 return str (self .tolist ())
150169
151170
152- Point2D = tuple [float , float ]
153- PointZ = tuple [float , float , float ]
154- PointZM = tuple [float , float , float , float ]
155-
156- Coord = Union [Point2D , PointZ , PointZM ]
157- Coords = Collection [Coord ]
158-
159-
160171def signed_area (
161172 coords : Coords ,
162173 fast : bool = False ,
@@ -189,9 +200,6 @@ def rewind(coords: Reversible[Coord]) -> list[Coord]:
189200 return list (reversed (coords ))
190201
191202
192- BBox = tuple [float , float , float , float ]
193-
194-
195203def ring_bbox (coords : Coords ) -> BBox :
196204 """Calculates and returns the bounding box of a ring."""
197205 xs , ys = zip (* coords )
@@ -445,7 +453,7 @@ class Shape:
445453 def __init__ (
446454 self ,
447455 shapeType : int = NULL ,
448- points : Optional [Coords ] = None ,
456+ points : Optional [list [ Coord ] ] = None ,
449457 parts : Optional [list [int ]] = None ,
450458 partTypes : Optional [list [int ]] = None ,
451459 oid : Optional [int ] = None ,
@@ -477,16 +485,18 @@ def __init__(
477485 self .__oid = - 1
478486
479487 @property
480- def __geo_interface__ (self ):
488+ def __geo_interface__ (self ) -> GeoJSONT :
481489 if self .shapeType in [POINT , POINTM , POINTZ ]:
482490 # point
483491 if len (self .points ) == 0 :
484492 # the shape has no coordinate information, i.e. is 'empty'
485493 # the geojson spec does not define a proper null-geometry type
486494 # however, it does allow geometry types with 'empty' coordinates to be interpreted as null-geometries
487- return {"type" : "Point" , "coordinates" : tuple ()}
495+ return {"type" : "Point" , "coordinates" : ()}
496+ # return {"type": "Point", "coordinates": tuple()} #type: ignore
488497 else :
489- return {"type" : "Point" , "coordinates" : tuple (self .points [0 ])}
498+ return {"type" : "Point" , "coordinates" : self .points [0 ]}
499+ # return {"type": "Point", "coordinates": tuple(self.points[0])} # type: ignore
490500 elif self .shapeType in [MULTIPOINT , MULTIPOINTM , MULTIPOINTZ ]:
491501 if len (self .points ) == 0 :
492502 # the shape has no coordinate information, i.e. is 'empty'
@@ -497,7 +507,8 @@ def __geo_interface__(self):
497507 # multipoint
498508 return {
499509 "type" : "MultiPoint" ,
500- "coordinates" : [tuple (p ) for p in self .points ],
510+ "coordinates" : self .points ,
511+ # "coordinates": [tuple(p) for p in self.points], #type: ignore
501512 }
502513 elif self .shapeType in [POLYLINE , POLYLINEM , POLYLINEZ ]:
503514 if len (self .parts ) == 0 :
@@ -509,7 +520,8 @@ def __geo_interface__(self):
509520 # linestring
510521 return {
511522 "type" : "LineString" ,
512- "coordinates" : [tuple (p ) for p in self .points ],
523+ "coordinates" : self .points ,
524+ # "coordinates": [tuple(p) for p in self.points], #type: ignore
513525 }
514526 else :
515527 # multilinestring
@@ -520,10 +532,12 @@ def __geo_interface__(self):
520532 ps = part
521533 continue
522534 else :
523- coordinates .append ([tuple (p ) for p in self .points [ps :part ]])
535+ # coordinates.append([tuple(p) for p in self.points[ps:part]])
536+ coordinates .append ([p for p in self .points [ps :part ]])
524537 ps = part
525538 else :
526- coordinates .append ([tuple (p ) for p in self .points [part :]])
539+ # coordinates.append([tuple(p) for p in self.points[part:]])
540+ coordinates .append ([p for p in self .points [part :]])
527541 return {"type" : "MultiLineString" , "coordinates" : coordinates }
528542 elif self .shapeType in [POLYGON , POLYGONM , POLYGONZ ]:
529543 if len (self .parts ) == 0 :
@@ -543,7 +557,8 @@ def __geo_interface__(self):
543557 end = len (self .points )
544558
545559 # extract the points that make up the ring
546- ring = [tuple (p ) for p in self .points [start :end ]]
560+ # ring = [tuple(p) for p in self.points[start:end]]
561+ ring = [p for p in self .points [start :end ]]
547562 rings .append (ring )
548563
549564 # organize rings into list of polygons, where each polygon is defined as list of rings.
@@ -918,7 +933,7 @@ class Reader:
918933 but they can be.
919934 """
920935
921- def __init__ (self , * args , ** kwargs ):
936+ def __init__ (self , * args , encoding = "utf-8" , encodingErrors = "strict" , ** kwargs ):
922937 self .shp = None
923938 self .shx = None
924939 self .dbf = None
@@ -931,8 +946,8 @@ def __init__(self, *args, **kwargs):
931946 self .fields = []
932947 self .__dbfHdrLength = 0
933948 self .__fieldLookup = {}
934- self .encoding = kwargs . pop ( " encoding" , "utf-8" )
935- self .encodingErrors = kwargs . pop ( " encodingErrors" , "strict" )
949+ self .encoding = encoding
950+ self .encodingErrors = encodingErrors
936951 # See if a shapefile name was passed as the first argument
937952 if len (args ) > 0 :
938953 path = pathlike_obj (args [0 ])
@@ -1876,7 +1891,15 @@ def iterShapeRecords(self, fields=None, bbox=None):
18761891class Writer :
18771892 """Provides write support for ESRI Shapefiles."""
18781893
1879- def __init__ (self , target = None , shapeType = None , autoBalance = False , ** kwargs ):
1894+ def __init__ (
1895+ self ,
1896+ target = None ,
1897+ shapeType = None ,
1898+ autoBalance = False ,
1899+ encoding = "utf-8" ,
1900+ encodingErrors = "strict" ,
1901+ ** kwargs ,
1902+ ):
18801903 self .target = target
18811904 self .autoBalance = autoBalance
18821905 self .fields = []
@@ -1920,8 +1943,8 @@ def __init__(self, target=None, shapeType=None, autoBalance=False, **kwargs):
19201943 # Use deletion flags in dbf? Default is false (0). Note: Currently has no effect, records should NOT contain deletion flags.
19211944 self .deletionFlag = 0
19221945 # Encoding
1923- self .encoding = kwargs . pop ( " encoding" , "utf-8" )
1924- self .encodingErrors = kwargs . pop ( " encodingErrors" , "strict" )
1946+ self .encoding = encoding
1947+ self .encodingErrors = encodingErrors
19251948
19261949 def __len__ (self ):
19271950 """Returns the current number of features written to the shapefile.
0 commit comments