@@ -108,18 +108,40 @@ def determine_center_zoom(self, width: int, height: int) -> typing.Tuple[s2spher
108108 if self ._center is not None :
109109 if self ._zoom is not None :
110110 return self ._center , self ._clamp_zoom (self ._zoom )
111+ b = self .object_bounds ()
112+ return self ._center , self ._determine_zoom (width , height , b , self ._center )
113+
111114 b = self .object_bounds ()
112115 if b is None :
113- return self ._center , self ._clamp_zoom (self ._zoom )
114- if self ._zoom is not None :
115- return b .get_center (), self ._clamp_zoom (self ._zoom )
116- if self ._center is not None :
117- b = b .union (s2sphere .LatLngRect (self ._center , self ._center ))
116+ return None , None
117+
118+ c = self ._determine_center (b )
119+ z = self ._zoom
120+ if z is None :
121+ z = self ._determine_zoom (width , height , b , c )
122+ if z is None :
123+ return None , None
124+ return self ._adjust_center (width , height , c , z ), z
125+
126+ def _determine_zoom (
127+ self , width : int , height : int , b : typing .Optional [s2sphere .LatLngRect ], c : s2sphere .LatLngRect
128+ ) -> typing .Optional [int ]:
129+ if b is None :
130+ b = s2sphere .LatLngRect (c , c )
131+ else :
132+ b = b .union (s2sphere .LatLngRect (c , c ))
118133 if b .is_point ():
119- return b .get_center (), None
134+ return self ._clamp_zoom (15 )
135+
120136 pixel_margin = self .extra_pixel_bounds ()
121- w = (width - 2.0 * max (pixel_margin [0 ], pixel_margin [2 ])) / self ._tile_provider .tile_size ()
122- h = (height - 2.0 * max (pixel_margin [1 ], pixel_margin [3 ])) / self ._tile_provider .tile_size ()
137+
138+ w = (width - pixel_margin [0 ] - pixel_margin [2 ]) / self ._tile_provider .tile_size ()
139+ h = (height - pixel_margin [1 ] - pixel_margin [3 ]) / self ._tile_provider .tile_size ()
140+ # margins are bigger than target image size => ignore them
141+ if w <= 0 or h <= 0 :
142+ w = width / self ._tile_provider .tile_size ()
143+ h = height / self ._tile_provider .tile_size ()
144+
123145 min_y = (1.0 - math .log (math .tan (b .lat_lo ().radians ) + (1.0 / math .cos (b .lat_lo ().radians )))) / (2 * math .pi )
124146 max_y = (1.0 - math .log (math .tan (b .lat_hi ().radians ) + (1.0 / math .cos (b .lat_hi ().radians )))) / (2 * math .pi )
125147 dx = (b .lng_hi ().degrees - b .lng_lo ().degrees ) / 360.0
@@ -132,8 +154,8 @@ def determine_center_zoom(self, width: int, height: int) -> typing.Tuple[s2spher
132154 for zoom in range (1 , self ._tile_provider .max_zoom ()):
133155 tiles = 2 ** zoom
134156 if (dx * tiles > w ) or (dy * tiles > h ):
135- return self ._determine_center ( b ), zoom - 1
136- return self ._determine_center ( b ), self . _tile_provider . max_zoom ( )
157+ return self ._clamp_zoom ( zoom - 1 )
158+ return self ._clamp_zoom ( 15 )
137159
138160 @staticmethod
139161 def _determine_center (b : s2sphere .LatLngRect ) -> s2sphere .LatLng :
@@ -143,6 +165,39 @@ def _determine_center(b: s2sphere.LatLngRect) -> s2sphere.LatLng:
143165 lng = b .get_center ().lng ().degrees
144166 return s2sphere .LatLng .from_degrees (lat , lng )
145167
168+ def _adjust_center (self , width : int , height : int , center : s2sphere .LatLng , zoom : int ) -> s2sphere .LatLng :
169+ if len (self ._objects ) == 0 :
170+ return center
171+
172+ trans = Transformer (width , height , zoom , center , self ._tile_provider .tile_size ())
173+
174+ min_x = None
175+ max_x = None
176+ min_y = None
177+ max_y = None
178+ for obj in self ._objects :
179+ l , t , r , b = obj .pixel_rect (trans )
180+ if min_x is None :
181+ min_x = l
182+ max_x = r
183+ min_y = t
184+ max_y = b
185+ else :
186+ min_x = min (min_x , l )
187+ max_x = max (max_x , r )
188+ min_y = min (min_y , t )
189+ max_y = max (max_y , b )
190+ assert min_x is not None
191+ assert max_x is not None
192+ assert min_y is not None
193+ assert max_y is not None
194+
195+ # margins are bigger than the image => ignore
196+ if (max_x - min_x ) > width or (max_y - min_y ) > height :
197+ return center
198+
199+ return trans .pixel2ll ((max_x + min_x ) * 0.5 , (max_y + min_y ) * 0.5 )
200+
146201 def _fetch_tile (self , z : int , x : int , y : int ) -> typing .Optional [bytes ]:
147202 return self ._tile_downloader .get (self ._tile_provider , self ._cache_dir , z , x , y )
148203
0 commit comments