1+ from functools import lru_cache
12import math
23import logging
34from enum import Enum
4- from typing import Optional , List , Tuple , Any , Union
5+ from typing import Optional , List , Tuple , Any , Union , Dict , Callable
56from concurrent .futures import ThreadPoolExecutor
67from io import BytesIO
78
89import requests
910import numpy as np
10-
1111from google .api_core import retry
1212from PIL import Image
1313from pyproj import Transformer
1919from labelbox .data .annotation_types .geometry .point import Point
2020from labelbox .data .annotation_types .geometry .line import Line
2121from labelbox .data .annotation_types .geometry .rectangle import Rectangle
22-
2322from ..geometry import Point
2423from .base_data import BaseData
2524from .raster import RasterData
@@ -100,7 +99,7 @@ class TileLayer(BaseModel):
10099 url : str
101100 name : Optional [str ] = "default"
102101
103- def asdict (self ):
102+ def asdict (self ) -> Dict :
104103 return {"tileLayerUrl" : self .url , "name" : self .name }
105104
106105 @validator ('url' )
@@ -139,11 +138,11 @@ class TiledImageData(BaseData):
139138 version : Optional [int ] = 2
140139 multithread : bool = True
141140
142- def __post_init__ (self ):
141+ def __post_init__ (self ) -> None :
143142 if self .max_native_zoom is None :
144143 self .max_native_zoom = self .zoom_levels [0 ]
145144
146- def asdict (self ):
145+ def asdict (self ) -> Dict :
147146 return {
148147 "tileLayerUrl" : self .tile_layer .url ,
149148 "bounds" : [[
@@ -160,10 +159,10 @@ def asdict(self):
160159 "version" : self .version
161160 }
162161
163- def as_raster_data (self ,
164- zoom : int = 0 ,
165- max_tiles : int = 32 ,
166- multithread = True ) -> RasterData :
162+ def raster_data (self ,
163+ zoom : int = 0 ,
164+ max_tiles : int = 32 ,
165+ multithread = True ) -> RasterData :
167166 """Converts the tiled image asset into a RasterData object containing an
168167 np.ndarray.
169168
@@ -172,23 +171,18 @@ def as_raster_data(self,
172171 if self .tile_bounds .epsg == EPSG .SIMPLEPIXEL :
173172 xstart , ystart , xend , yend = self ._get_simple_image_params (zoom )
174173 elif self .tile_bounds .epsg == EPSG .EPSG4326 :
175- xstart , ystart , xend , yend = self ._get_3857_image_params (zoom )
174+ xstart , ystart , xend , yend = self ._get_3857_image_params (
175+ zoom , self .tile_bounds )
176176 elif self .tile_bounds .epsg == EPSG .EPSG3857 :
177177 #transform to 4326
178178 transformer = EPSGTransformer .create_geo_to_geo_transformer (
179179 EPSG .EPSG3857 , EPSG .EPSG4326 )
180- self .tile_bounds .bounds = [
181- transformer (self .tile_bounds .bounds [0 ]),
182- transformer (self .tile_bounds .bounds [1 ])
183- ]
184- xstart , ystart , xend , yend = self ._get_3857_image_params (zoom )
185- #transform back to 3857
186- transformer = EPSGTransformer .create_geo_to_geo_transformer (
187- EPSG .EPSG4326 , EPSG .EPSG3857 )
188- self .tile_bounds .bounds = [
180+ transforming_bounds = [
189181 transformer (self .tile_bounds .bounds [0 ]),
190182 transformer (self .tile_bounds .bounds [1 ])
191183 ]
184+ xstart , ystart , xend , yend = self ._get_3857_image_params (
185+ zoom , transforming_bounds )
192186 else :
193187 raise ValueError (f"Unsupported epsg found: { self .tile_bounds .epsg } " )
194188
@@ -207,8 +201,8 @@ def as_raster_data(self,
207201 def value (self ) -> np .ndarray :
208202 """Returns the value of a generated RasterData object.
209203 """
210- return self .as_raster_data (self .zoom_levels [0 ],
211- multithread = self .multithread ).value
204+ return self .raster_data (self .zoom_levels [0 ],
205+ multithread = self .multithread ).value
212206
213207 def _get_simple_image_params (self ,
214208 zoom ) -> Tuple [float , float , float , float ]:
@@ -229,14 +223,14 @@ def _get_simple_image_params(self,
229223 for x in [xstart , ystart , xend , yend ]
230224 ],)
231225
232- def _get_3857_image_params (self , zoom ) -> Tuple [float , float , float , float ]:
226+ def _get_3857_image_params (
227+ self , zoom : int ,
228+ bounds : TiledBounds ) -> Tuple [float , float , float , float ]:
233229 """Computes the x and y tile bounds for fetching an image that
234230 captures the entire labeling region (TiledData.bounds) given a specific zoom
235231 """
236- lat_start , lat_end = self .tile_bounds .bounds [
237- 1 ].y , self .tile_bounds .bounds [0 ].y
238- lng_start , lng_end = self .tile_bounds .bounds [
239- 1 ].x , self .tile_bounds .bounds [0 ].x
232+ lat_start , lat_end = bounds .bounds [1 ].y , bounds .bounds [0 ].y
233+ lng_start , lng_end = bounds .bounds [1 ].x , bounds .bounds [0 ].x
240234
241235 # Convert to zoom 0 tile coordinates
242236 xstart , ystart = self ._latlng_to_tile (lat_start , lng_start )
@@ -350,7 +344,8 @@ def _validate_num_tiles(self, xstart: float, ystart: float, xend: float,
350344 total_n_tiles = (yend - ystart + 1 ) * (xend - xstart + 1 )
351345 if total_n_tiles > max_tiles :
352346 raise ValueError (f"Requested zoom results in { total_n_tiles } tiles."
353- f"Max allowed tiles are { max_tiles } " )
347+ f"Max allowed tiles are { max_tiles } "
348+ f"Increase max tiles or reduce zoom level." )
354349
355350 @validator ('zoom_levels' )
356351 def validate_zoom_levels (cls , zoom_levels ):
@@ -378,7 +373,7 @@ def _is_simple(epsg: EPSG) -> bool:
378373 return epsg == EPSG .SIMPLEPIXEL
379374
380375 @staticmethod
381- def _get_ranges (bounds : np .ndarray ):
376+ def _get_ranges (bounds : np .ndarray ) -> Tuple [ int , int ] :
382377 """helper function to get the range between bounds.
383378
384379 returns a tuple (x_range, y_range)"""
@@ -387,7 +382,7 @@ def _get_ranges(bounds: np.ndarray):
387382 return (x_range , y_range )
388383
389384 @staticmethod
390- def _min_max_x_y (bounds : np .ndarray ):
385+ def _min_max_x_y (bounds : np .ndarray ) -> Tuple [ int , int , int , int ] :
391386 """returns the min x, max x, min y, max y of a numpy array
392387 """
393388 return np .min (bounds [:, 0 ]), np .max (bounds [:, 0 ]), np .min (
@@ -398,7 +393,7 @@ def geo_and_pixel(cls,
398393 src_epsg ,
399394 pixel_bounds : TiledBounds ,
400395 geo_bounds : TiledBounds ,
401- zoom = 0 ):
396+ zoom = 0 ) -> Callable :
402397 """method to change from one projection to simple projection"""
403398
404399 pixel_bounds = pixel_bounds .bounds
@@ -420,7 +415,7 @@ def geo_and_pixel(cls,
420415
421416 if src_epsg == EPSG .SIMPLEPIXEL :
422417
423- def transform (x : int , y : int ):
418+ def transform (x : int , y : int ) -> Callable :
424419 scaled_xy = (x * (global_x_range ) / (local_x_range ),
425420 y * (global_y_range ) / (local_y_range ))
426421
@@ -440,7 +435,7 @@ def transform(x: int, y: int):
440435 #handles 4326 from lat,lng
441436 elif src_epsg == EPSG .EPSG4326 :
442437
443- def transform (x : int , y : int ):
438+ def transform (x : int , y : int ) -> Callable :
444439 point_in_px = PygeoPoint .from_latitude_longitude (
445440 latitude = y , longitude = x ).pixels (zoom )
446441
@@ -455,7 +450,7 @@ def transform(x: int, y: int):
455450 #handles 3857 from meters
456451 elif src_epsg == EPSG .EPSG3857 :
457452
458- def transform (x : int , y : int ):
453+ def transform (x : int , y : int ) -> Callable :
459454 point_in_px = PygeoPoint .from_meters (meter_y = y ,
460455 meter_x = x ).pixels (zoom )
461456
@@ -469,7 +464,7 @@ def transform(x: int, y: int):
469464
470465 @classmethod
471466 def create_geo_to_geo_transformer (cls , src_epsg : EPSG ,
472- tgt_epsg : EPSG ) -> None :
467+ tgt_epsg : EPSG ) -> Callable :
473468 """method to change from one projection to another projection.
474469
475470 supports EPSG transformations not Simple.
@@ -487,7 +482,7 @@ def create_geo_to_pixel_transformer(cls,
487482 src_epsg ,
488483 pixel_bounds : TiledBounds ,
489484 geo_bounds : TiledBounds ,
490- zoom = 0 ):
485+ zoom = 0 ) -> Callable :
491486 """method to change from a geo projection to Simple"""
492487
493488 transform_function = cls .geo_and_pixel (src_epsg = src_epsg ,
@@ -501,7 +496,7 @@ def create_pixel_to_geo_transformer(cls,
501496 src_epsg ,
502497 pixel_bounds : TiledBounds ,
503498 geo_bounds : TiledBounds ,
504- zoom = 0 ):
499+ zoom = 0 ) -> Callable :
505500 """method to change from a geo projection to Simple"""
506501 transform_function = cls .geo_and_pixel (src_epsg = src_epsg ,
507502 pixel_bounds = pixel_bounds ,
@@ -513,7 +508,9 @@ def _get_point_obj(self, point) -> Point:
513508 point = self .transformer (point .x , point .y )
514509 return Point (x = point [0 ], y = point [1 ])
515510
516- def __call__ (self , shape : Union [Point , Line , Rectangle , Polygon ]):
511+ def __call__ (
512+ self , shape : Union [Point , Line , Rectangle , Polygon ]
513+ ) -> Union [Point , Line , Rectangle , Polygon ]:
517514 if isinstance (shape , Point ):
518515 return self ._get_point_obj (shape )
519516 if isinstance (shape , Line ) or isinstance (shape , Polygon ):
0 commit comments