diff --git a/CHANGES.md b/CHANGES.md index 1095006..90a1e99 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,38 @@ ## Unreleased +## 8.0.0 (2024-10-21) + +* remove deprecated methods + +* update morecantile requirement to `>=5.0,<7.0` + +* update rio-tiler requirement to `>=7.0,<8.0` + +* update `Info` model + + ```python + # before + class Info(BaseModel): + bounds: BBox = Field(default=(-180, -90, 180, 90)) + center: Optional[Tuple[float, float, int]] = None + minzoom: int = Field(0, ge=0, le=30) + maxzoom: int = Field(30, ge=0, le=30) + name: Optional[str] = None + quadkeys: List[str] = [] + tilematrixset: Optional[str] = None + + # now + class Info(BaseModel): + bounds: BBox = Field(default=(-180, -90, 180, 90)) + crs: str + center: Optional[Tuple[float, float, int]] = None + name: Optional[str] = None + quadkeys: List[str] = [] + mosaic_tilematrixset: Optional[str] = None + mosaic_minzoom: int = Field(0, ge=0, le=30) + mosaic_maxzoom: int = Field(30, ge=0, le=30) + ``` + ## 7.2.0 (2024-10-04) * update BaseBackend to use a default coord_crs from the tms (author @AndrewAnnex, https://github.com/developmentseed/cogeo-mosaic/pull/234) diff --git a/cogeo_mosaic/__init__.py b/cogeo_mosaic/__init__.py index ba8873f..db0b82c 100644 --- a/cogeo_mosaic/__init__.py +++ b/cogeo_mosaic/__init__.py @@ -1,3 +1,3 @@ """Cogeo_mosaic.""" -__version__ = "7.2.0" +__version__ = "8.0.0" diff --git a/cogeo_mosaic/backends/base.py b/cogeo_mosaic/backends/base.py index d250589..8c401fd 100644 --- a/cogeo_mosaic/backends/base.py +++ b/cogeo_mosaic/backends/base.py @@ -11,12 +11,14 @@ from morecantile import Tile, TileMatrixSet from rasterio.crs import CRS from rasterio.warp import transform, transform_bounds -from rio_tiler.constants import WEB_MERCATOR_TMS, WGS84_CRS +from rio_tiler.constants import WEB_MERCATOR_TMS from rio_tiler.errors import PointOutsideBounds from rio_tiler.io import BaseReader, MultiBandReader, MultiBaseReader, Reader from rio_tiler.models import ImageData, PointData from rio_tiler.mosaic import mosaic_reader from rio_tiler.tasks import multi_values +from rio_tiler.types import BBox +from rio_tiler.utils import CRS_to_uri from cogeo_mosaic.backends.utils import get_hash from cogeo_mosaic.cache import cache_config @@ -63,11 +65,8 @@ class BaseBackend(BaseReader): ] = attr.ib(default=Reader) reader_options: Dict = attr.ib(factory=dict) - bounds: Tuple[float, float, float, float] = attr.ib( - init=False, default=(-180, -90, 180, 90) - ) - crs: CRS = attr.ib(init=False, default=WGS84_CRS) - geographic_crs: CRS = attr.ib(init=False, default=WGS84_CRS) + bounds: BBox = attr.ib(init=False) + crs: CRS = attr.ib(init=False) _backend_name: str _file_byte_size: Optional[int] = 0 @@ -81,10 +80,8 @@ def __attrs_post_init__(self): mosaic_tms = self.mosaic_def.tilematrixset or WEB_MERCATOR_TMS # By mosaic definition, its `bounds` is defined using the mosaic's TMS - # Geographic CRS so we define both `crs` and `geographic_crs` using the mosaic - # TMS geographic_crs. + # Geographic CRS. self.crs = mosaic_tms.rasterio_geographic_crs - self.geographic_crs = mosaic_tms.rasterio_geographic_crs # if we open the Mosaic with a TMS which is not the mosaic TMS # the min/max zoom will default to the TMS (read) zooms @@ -177,8 +174,10 @@ def assets_for_point( ) -> List[str]: """Retrieve assets for point.""" mosaic_tms = self.mosaic_def.tilematrixset or WEB_MERCATOR_TMS + # default coord_crs should be the TMS's geographic CRS coord_crs = coord_crs or self.tms.rasterio_geographic_crs + # If coord_crs is not the same as the mosaic's geographic CRS # we reproject the coordinates if coord_crs != mosaic_tms.rasterio_geographic_crs: @@ -202,8 +201,10 @@ def assets_for_bbox( ) -> List[str]: """Retrieve assets for bbox.""" mosaic_tms = self.mosaic_def.tilematrixset or WEB_MERCATOR_TMS + # default coord_crs should be the TMS's geographic CRS coord_crs = coord_crs or self.tms.rasterio_geographic_crs + # If coord_crs is not the same as the mosaic's geographic CRS # we reproject the bounding box if coord_crs != mosaic_tms.rasterio_geographic_crs: @@ -324,6 +325,7 @@ def point( """Get Point value from multiple observation.""" # default coord_crs should be the TMS's geographic CRS coord_crs = coord_crs or self.tms.rasterio_geographic_crs + mosaic_assets = self.assets_for_point(lon, lat, coord_crs=coord_crs) if not mosaic_assets: raise NoAssetFoundError(f"No assets found for point ({lon},{lat})") @@ -347,13 +349,14 @@ def _reader( def info(self, quadkeys: bool = False) -> Info: # type: ignore """Mosaic info.""" return Info( - bounds=self.geographic_bounds, + bounds=self.bounds, + crs=CRS_to_uri(self.crs) or self.crs.to_wkt(), center=self.center, - maxzoom=self.maxzoom, - minzoom=self.minzoom, name=self.mosaic_def.name if self.mosaic_def.name else "mosaic", quadkeys=[] if not quadkeys else self._quadkeys, - tilematrixset=repr(self.mosaic_def.tilematrixset or WEB_MERCATOR_TMS), + mosaic_tilematrixset=repr(self.mosaic_def.tilematrixset or WEB_MERCATOR_TMS), + mosaic_minzoom=self.mosaic_def.minzoom, + mosaic_maxzoom=self.mosaic_def.maxzoom, ) @property diff --git a/cogeo_mosaic/backends/stac.py b/cogeo_mosaic/backends/stac.py index cb897d3..3202bdc 100644 --- a/cogeo_mosaic/backends/stac.py +++ b/cogeo_mosaic/backends/stac.py @@ -2,7 +2,7 @@ import json import os -from typing import Dict, List, Optional, Sequence, Tuple, Type +from typing import Dict, List, Optional, Sequence, Type import attr import httpx @@ -12,6 +12,7 @@ from rasterio.crs import CRS from rio_tiler.constants import WEB_MERCATOR_TMS, WGS84_CRS from rio_tiler.io import STACReader +from rio_tiler.types import BBox from cogeo_mosaic.backends.base import BaseBackend from cogeo_mosaic.cache import cache_config @@ -66,11 +67,8 @@ class STACBackend(BaseBackend): reader: Type[STACReader] = attr.ib(default=STACReader) reader_options: Dict = attr.ib(factory=dict) - bounds: Tuple[float, float, float, float] = attr.ib( - init=False, default=(-180, -90, 180, 90) - ) + bounds: BBox = attr.ib(init=False, default=(-180, -90, 180, 90)) crs: CRS = attr.ib(init=False, default=WGS84_CRS) - geographic_crs: CRS = attr.ib(init=False, default=WGS84_CRS) # STAC API related options # max_items | next_link_key | limit diff --git a/cogeo_mosaic/cache.py b/cogeo_mosaic/cache.py index 8bf43b8..7320e6f 100644 --- a/cogeo_mosaic/cache.py +++ b/cogeo_mosaic/cache.py @@ -10,7 +10,7 @@ class CacheSettings(BaseSettings): # TTL of the cache in seconds ttl: int = 300 - # Maximum size of the LRU cache in MB + # Maximum size of the LRU cache in Number of Items maxsize: int = 512 # Whether or not caching is enabled diff --git a/cogeo_mosaic/models.py b/cogeo_mosaic/models.py index d708792..f0c0375 100644 --- a/cogeo_mosaic/models.py +++ b/cogeo_mosaic/models.py @@ -1,26 +1,19 @@ """cogeo-mosaic models.""" -import warnings from typing import List, Optional, Tuple from pydantic import BaseModel, Field +from rio_tiler.types import BBox class Info(BaseModel): """Mosaic info responses.""" - bounds: Tuple[float, float, float, float] = Field((-180, -90, 180, 90)) + bounds: BBox = Field(default=(-180, -90, 180, 90)) + crs: str center: Optional[Tuple[float, float, int]] = None - minzoom: int = Field(0, ge=0, le=30) - maxzoom: int = Field(30, ge=0, le=30) name: Optional[str] = None quadkeys: List[str] = [] - tilematrixset: Optional[str] = None - - def __getitem__(self, item): - """Access item like in Dict.""" - warnings.warn( - "'key' access will has been deprecated and will be removed in cogeo-mosaic 8.0.", - DeprecationWarning, - ) - return self.__dict__[item] + mosaic_tilematrixset: Optional[str] = None + mosaic_minzoom: int = Field(0, ge=0, le=30) + mosaic_maxzoom: int = Field(30, ge=0, le=30) diff --git a/cogeo_mosaic/mosaic.py b/cogeo_mosaic/mosaic.py index 61cba42..3eece56 100644 --- a/cogeo_mosaic/mosaic.py +++ b/cogeo_mosaic/mosaic.py @@ -10,6 +10,7 @@ import click import morecantile from pydantic import BaseModel, Field, field_validator, model_validator +from rio_tiler.types import BBox from shapely import linearrings, polygons, total_bounds from shapely.strtree import STRtree from supermorecado import burnTiles @@ -71,7 +72,7 @@ class MosaicJSON(BaseModel, validate_assignment=True): minzoom: int = Field(0, ge=0, le=30) maxzoom: int = Field(30, ge=0, le=30) quadkey_zoom: Optional[int] = None - bounds: Tuple[float, float, float, float] = Field(default=(-180, -90, 180, 90)) + bounds: BBox = Field(default=(-180, -90, 180, 90)) center: Optional[Tuple[float, float, int]] = None tiles: Dict[str, List[str]] tilematrixset: Optional[morecantile.TileMatrixSet] = None diff --git a/cogeo_mosaic/utils.py b/cogeo_mosaic/utils.py index e28fc57..b36eb27 100644 --- a/cogeo_mosaic/utils.py +++ b/cogeo_mosaic/utils.py @@ -46,12 +46,8 @@ def get_dataset_info( tms: morecantile.TileMatrixSet = WEB_MERCATOR_TMS, ) -> Dict: """Get rasterio dataset meta.""" - with Reader( - src_path, - tms=tms, - geographic_crs=tms.rasterio_geographic_crs, - ) as src: - bounds = src.geographic_bounds + with Reader(src_path, tms=tms) as src: + bounds = src.get_geographic_bounds(tms.rasterio_geographic_crs) return { "geometry": { "type": "Polygon", diff --git a/pyproject.toml b/pyproject.toml index db3beb1..0106303 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,13 +23,13 @@ classifiers = [ dynamic = ["version", "readme"] dependencies = [ "attrs", - "morecantile>=5.0,<6.0", + "morecantile", "shapely>=2.0,<3.0", "pydantic~=2.0", "pydantic-settings~=2.0", "httpx", "rasterio", - "rio-tiler>=6.0,<7.0", + "rio-tiler>=7.0,<8.0", "supermorecado", "cachetools", "numpy", @@ -155,7 +155,7 @@ max-complexity = 14 "tests/*.py" = ["D1"] [tool.bumpversion] -current_version = "7.2.0" +current_version = "8.0.0" search = "{current_version}" replace = "{new_version}" regex = false diff --git a/tests/test_backends.py b/tests/test_backends.py index ffc6b6c..61f5f79 100644 --- a/tests/test_backends.py +++ b/tests/test_backends.py @@ -65,17 +65,16 @@ def test_file_backend(): info = mosaic.info() assert not info.quadkeys - with pytest.warns(DeprecationWarning): - assert not info["quadkeys"] assert list(info.model_dump()) == [ "bounds", + "crs", "center", - "minzoom", - "maxzoom", "name", "quadkeys", - "tilematrixset", + "mosaic_tilematrixset", + "mosaic_minzoom", + "mosaic_maxzoom", ] info = mosaic.info(quadkeys=True) @@ -652,12 +651,13 @@ def test_dynamoDB_backend(client): assert not info.quadkeys assert list(info.model_dump()) == [ "bounds", + "crs", "center", - "minzoom", - "maxzoom", "name", "quadkeys", - "tilematrixset", + "mosaic_tilematrixset", + "mosaic_minzoom", + "mosaic_maxzoom", ] info = mosaic.info(quadkeys=True) @@ -998,12 +998,13 @@ def test_InMemoryReader(): info = mosaic.info() assert list(info.model_dump()) == [ "bounds", + "crs", "center", - "minzoom", - "maxzoom", "name", "quadkeys", - "tilematrixset", + "mosaic_tilematrixset", + "mosaic_minzoom", + "mosaic_maxzoom", ] mosaic_oneasset = MosaicJSON.from_urls([asset1], quiet=True) @@ -1032,12 +1033,13 @@ def test_sqlite_backend(): assert not info.quadkeys assert list(info.model_dump()) == [ "bounds", + "crs", "center", - "minzoom", - "maxzoom", "name", "quadkeys", - "tilematrixset", + "mosaic_tilematrixset", + "mosaic_minzoom", + "mosaic_maxzoom", ] info = mosaic.info(quadkeys=True) diff --git a/tests/test_create.py b/tests/test_create.py index 73c3ba2..9b6bab3 100644 --- a/tests/test_create.py +++ b/tests/test_create.py @@ -178,7 +178,6 @@ def test_mars_mosaic_create(): MARS_MERCATOR, extent_crs=MARS2000_SPHERE, title="Web Mercator Mars", - geographic_crs=MARS2000_SPHERE, ) # load the mars_ctx_stac_assset.json file with open(mars_ctx_asset, "r") as f: @@ -202,7 +201,6 @@ def test_mars_mosaic_create(): assert isinstance(mosaic, FileBackend) assert mosaic.tms == mars_tms assert mosaic.crs == mars_tms.rasterio_geographic_crs - assert mosaic.geographic_crs == mars_tms.rasterio_geographic_crs assert len(mosaic.assets_for_point(77.28, 18, mars_tms.geographic_crs)) > 0 assert ( len(mosaic.assets_for_bbox(77.2, 17.5, 77.4, 18.5, mars_tms.geographic_crs))