Source code for zooui.tilesystem.tileproviders.dynamictileprovider

## ZooUI - Zooming User Interface
## Copyright (C) 2009 David Roberts <d@vidr.cc>
##
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License
## as published by the Free Software Foundation; either version 3
## of the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <https://www.gnu.org/licenses/>.

"""Class for loading tiles into memory from somewhere other than the local
filesystem (abstract base class)."""

import os
from typing import TYPE_CHECKING, Any

from PIL import Image as PILImage

from .. import tilestore as TileStore
from .tileprovider import TileProvider

if TYPE_CHECKING:
    pass

TileID = tuple[str, int, int, int]  # type: ignore[misc]


[docs] class DynamicTileProvider(TileProvider): """ Constructor : DynamicTileProvider(tilecache) Parameters : tilecache : TileCache DynamicTileProvider(tilecache) --> DynamicTileProvider DynamicTileProvider objects are used for either generating tiles or loading them from a remote host, and then loading them into a TileCache. This is an abstract base class for tile providers that create tiles dynamically (either by generation or remote retrieval) rather than loading pre-existing tiles from disk. Derived classes must implement the _load_dynamic() method. Implementation Notes: - Inherits from TileProvider base class - Provides default values for filext, tilesize, and aspect_ratio - _load() checks if tile exists locally before calling _load_dynamic() - Uses PIL for loading tiles after they are created - Derived classes should override _load_dynamic() to implement specific tile generation or retrieval logic Dynamic Tile Provider Flow (Fern Example):: ┌─────────────────────────┐ | ┌──────────┐ ┌────────────────────┐ │ FernDynamicTileProvider │ | │ Load │ │ Generate tile │ │ receives request │ | │ cached │ │ algorithmically │ └────────────┬────────────┘ | │ tile │ │ • Calculate fern │ │ | └────┬─────┘ │ parameters │ ▼ | │ │ • Draw fractal │ ┌─────────────────────────┐ | │ │ • Create image │ │ Parse tile ID │ | │ └─────┬──────────────┘ │ • Zoom level │ | │ │ │ • Tile coordinates │ | └─────┬───────┘ └────────────┬────────────┘ | │ │ | ▼ ▼ | ┌─────────────────────────┐ ┌─────────────────────────┐ | │ Wrap in Tile object │ │ Check TileStore │ | └────────────┬────────────┘ │ (may have been │ | │ │ generated before) │ | ▼ └────────────┬────────────┘ | ┌─────────────────────────┐ │ | │ Store in TileCache │ ┌────┴────┐ | └────────────┬────────────┘ │ │ | │ EXISTS NEW TILE | ▼ │ │ | ┌─────────────────────────┐ ▼ ▼ | │ Save to TileStore │ | └────────────┬────────────┘ | │ | ▼ | ┌─────────────────────────┐ | │ Return to TileManager │ | └─────────────────────────┘ """ def __init__(self, tilecache: Any) -> None: """ Constructor : DynamicTileProvider(tilecache) Parameters : tilecache : Any DynamicTileProvider(tilecache) --> None Create a new DynamicTileProvider with the given TileCache. The tilecache parameter is the TileCache instance that this provider will use to store dynamically generated or retrieved tiles. """ TileProvider.__init__(self, tilecache) ## set default values (derived classes may override these values) filext = "png" tilesize = 256 aspect_ratio = 1.0 ## width / height
[docs] def _load_dynamic(self, tile_id: TileID, outfile: str) -> None: """ Method : DynamicTileProvider._load_dynamic(tile_id, outfile) Parameters : tile_id : Tuple[str, int, int, int] outfile : str DynamicTileProvider._load_dynamic(tile_id, outfile) --> None Perform whatever actions necessary to load the tile identified by the given tile_id into the location given by outfile. """ pass
[docs] def _load(self, tile_id: TileID) -> PILImage.Image | None: """ Method : DynamicTileProvider._load(tile_id) Parameters : tile_id : Tuple[str, int, int, int] DynamicTileProvider._load(tile_id) --> PIL Image or None Load a tile, generating it dynamically if it doesn't exist locally. The tile_id tuple contains (media_id, tilelevel, row, col). This method first checks if the tile exists in the local tilestore. If not, it calls _load_dynamic() to generate or retrieve the tile. Finally, it loads the tile as a PIL Image and returns it. Implementation Notes: - Gets tile path from TileStore with create=True flag - Only calls _load_dynamic() if tile file doesn't exist - Uses PIL to load the tile after creation (safe in non-GUI threads) - Returns None if tile loading fails (logs exception) - Assumes tile is unavailable if any exception occurs """ filename = TileStore.get_tile_path(tile_id, True, filext=self.filext) if not os.path.exists(filename): ## tile has not been retrieved yet self._load_dynamic(tile_id, filename) try: return PILImage.open(filename) except Exception: self._logger.exception("error loading tile, assuming it is unavailable") return None