Skip to content

Commit

Permalink
Block loading unsafe request when not enough overviews and show warning
Browse files Browse the repository at this point in the history
  • Loading branch information
tebben committed Jan 22, 2024
1 parent cc2fe61 commit c435d06
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 5 deletions.
41 changes: 38 additions & 3 deletions ctod/core/cog/cog_reader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import time

from morecantile import TileMatrixSet
import math
import logging
from morecantile import TileMatrixSet, Tile
from rio_tiler.io import Reader
from rio_tiler.models import ImageData

Expand Down Expand Up @@ -35,9 +36,14 @@ def download_tile(self, x: int, y: int, z: int, resampling_method="bilinear") ->
resampling_method (str, optional): RasterIO resampling algorithm. Defaults to "bilinear".
Returns:
ImageData: _description_
ImageData: Image data from the Cloud Optimized GeoTIFF.
"""

is_safe = self._check_is_safe(z, x, y)
if not is_safe:
logging.warning(f"Skipping {self.cog} {z,x,y}, tile is not safe to load, generate more overviews")
return None

try:

image_data = self.rio_reader.tile(tile_z=z, tile_x=x, tile_y=y, resampling_method=resampling_method)
Expand All @@ -52,6 +58,30 @@ def download_tile(self, x: int, y: int, z: int, resampling_method="bilinear") ->
except Exception:
return None

def _check_is_safe(self, z: int, x: int, y: int) -> bool:
"""Check if it is safe to load the tile.
When there are not enough overviews there will be a lot of request to load
a tile at a low zoom level. This will cause long load times and high memory usage.
ToDo: This is an estimation, it is not 100% accurate.
Args:
z (int):
x (int):
y (int):
Returns:
bool: Is it safe to load the tile
"""

tile_bounds = self.tms.xy_bounds(Tile(x=x, y=y, z=z))
tile_wgs = tile_bounds.right - tile_bounds.left
#tile_wgs_with_clip = min(tile_bounds.right, self.dataset_bounds.right) - min(tile_bounds.left, dataset_bounds.left)
tile_pixels_needed = tile_wgs * self.pixels_per_wgs
needed_tiles = math.ceil(tile_pixels_needed / self.pixels_per_tile_downsampled)

return needed_tiles <= 1

def return_reader(self):
"""Done with the reader, return it to the pool."""

Expand All @@ -62,6 +92,11 @@ def _set_rio_reader(self):
"""Get the reader for the COG."""

self.rio_reader = Reader(self.cog, tms=self.tms)
self.dataset_bounds = self.rio_reader.info().bounds
self.dataset_width = self.rio_reader.dataset.width
self.dataset_wgs_width = self.dataset_bounds.right - self.dataset_bounds.left
self.pixels_per_wgs = self.dataset_width / self.dataset_wgs_width
self.pixels_per_tile_downsampled = 256 * max(self.rio_reader.dataset.overviews(1))

def _set_nodata_value(self):
"""Set the nodata value for the reader."""
Expand Down
6 changes: 5 additions & 1 deletion ctod/core/layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ def generate_layer_json(tms, geotiff_path, max_zoom=20):
"""

with COGReader(geotiff_path) as src:

bounds = src.geographic_bounds

# Zoom 0 always needs to be 0,0,1,0
# Cesium always expects all tiles at zoom 0 (startX: 0, endX: 1)
# With the function available_tiles it is likely it only will return
# one tile: startX: 1, endX: 1 for example. So here we skip generating
# the first zoom level
available_tiles = [
[{"startX": 0, "startY": 0, "endX": 1, "endY": 0}]
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def _get_vertice_condition(self, vertices, direction) -> np.ndarray:
if direction == Direction.NORTH:
vertices[:, 1] -= tile_size
return vertices[:, 1] == 0
if direction == Direction.NORTHEAST:
elif direction == Direction.NORTHEAST:
vertices[:, 0] -= tile_size
vertices[:, 1] -= tile_size
return (vertices[:, 0] == 0) & (vertices[:, 1] == 0)
Expand Down

0 comments on commit c435d06

Please sign in to comment.