Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow wgs84 geographic crs #458

Merged
merged 4 commits into from
Nov 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

# 3.0.0a5 (TBD)

* allow the definition of `geographic_crs` used in the `geographic_bounds` property (https://github.com/cogeotiff/rio-tiler/pull/458)

# 3.0.0a4 (2021-11-10)

* refactor `SpatialMixin.tile_exists` to compare the bounds in the dataset's coordinate system to avoid coordinates overflow (a TMS CRS bounds can be smaller than the dataset CRS bounds) (https://github.com/cogeotiff/rio-tiler/pull/455)
Expand Down
28 changes: 28 additions & 0 deletions docs/v3_migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,34 @@ with COGReader("my_tif.tif") as cog:
print(cog.geographic_bounds) # bounds in WGS84 projection
```

Note a `geographic_crs` attribute is available in `COGReader` and `STACReader` to control which CRS to use for the transformation from the dataset's CRS. `geographic_crs` is outside the `__init__` method for Abstract Base Classes (e.g `BaseReader`)

```python
MARS2000_SPHERE = CRS.from_proj4("+proj=longlat +R=3396190 +no_defs")
MARS_MERCATOR = CRS.from_proj4(
"+proj=merc +R=3396190 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +no_defs"
)
MARS_TMS = TileMatrixSet.custom(
[
-179.9999999999996,
-85.05112877980656,
179.9999999999996,
85.05112877980656,
],
MARS_MERCATOR,
extent_crs=MARS2000_SPHERE,
title="Web Mercator Mars",
geographic_crs=MARS2000_SPHERE,
)
with COGReader(
"martian_dataset.tif",
tms=MARS_TMS,
geographic_crs=rasterio.crs.CRS.from_proj4("+proj=longlat +R=3396190 +no_defs"),
) as cog:
...
```


## No more `max_size`

We've removed the default option `max_size=1024` in `BaseReader.part` and `BaseReader.feature` to return the highest resolution dataset by default.
Expand Down
31 changes: 6 additions & 25 deletions rio_tiler/io/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,22 @@ class SpatialMixin:
"""

tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)

minzoom: int = attr.ib(init=False)
maxzoom: int = attr.ib(init=False)

bounds: BBox = attr.ib(init=False)
crs: CRS = attr.ib(init=False)

geographic_crs: CRS = attr.ib(init=False, default=WGS84_CRS)

@property
def geographic_bounds(self) -> BBox:
"""return bounds in WGS84."""
try:
bounds = transform_bounds(
self.crs,
WGS84_CRS,
self.geographic_crs,
*self.bounds,
densify_pts=21,
)
Expand Down Expand Up @@ -116,12 +119,6 @@ class BaseReader(SpatialMixin, metaclass=abc.ABCMeta):
input: Any = attr.ib()
tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)

minzoom: int = attr.ib(init=False)
maxzoom: int = attr.ib(init=False)

bounds: BBox = attr.ib(init=False)
crs: CRS = attr.ib(init=False)

def __enter__(self):
"""Support using with Context Managers."""
return self
Expand Down Expand Up @@ -223,12 +220,6 @@ class AsyncBaseReader(SpatialMixin, metaclass=abc.ABCMeta):
input: Any = attr.ib()
tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)

minzoom: int = attr.ib(init=False)
maxzoom: int = attr.ib(init=False)

bounds: BBox = attr.ib(init=False)
crs: CRS = attr.ib(init=False)

async def __aenter__(self):
"""Support using with Context Managers."""
return self
Expand Down Expand Up @@ -339,6 +330,7 @@ class MultiBaseReader(BaseReader, metaclass=abc.ABCMeta):
This Reader is suited for dataset that are composed of multiple assets (e.g. STAC).

Attributes:
input (any): input data.
reader (rio_tiler.io.BaseReader): reader.
reader_options (dict, option): options to forward to the reader. Defaults to `{}`.
tms (morecantile.TileMatrixSet, optional): TileMatrixSet grid definition. Defaults to `WebMercatorQuad`.
Expand All @@ -352,12 +344,6 @@ class MultiBaseReader(BaseReader, metaclass=abc.ABCMeta):
reader_options: Dict = attr.ib(factory=dict)
tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)

minzoom: int = attr.ib(init=False)
maxzoom: int = attr.ib(init=False)

bounds: BBox = attr.ib(init=False)
crs: CRS = attr.ib(init=False)

assets: Sequence[str] = attr.ib(init=False)

@abc.abstractmethod
Expand Down Expand Up @@ -787,6 +773,7 @@ class MultiBandReader(BaseReader, metaclass=abc.ABCMeta):
This Reader is suited for dataset that stores spectral bands as separate files (e.g. Sentinel 2).

Attributes:
input (any): input data.
reader (rio_tiler.io.BaseReader): reader.
reader_options (dict, option): options to forward to the reader. Defaults to `{}`.
tms (morecantile.TileMatrixSet, optional): TileMatrixSet grid definition. Defaults to `WebMercatorQuad`.
Expand All @@ -800,12 +787,6 @@ class MultiBandReader(BaseReader, metaclass=abc.ABCMeta):
reader_options: Dict = attr.ib(factory=dict)
tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)

minzoom: int = attr.ib(init=False)
maxzoom: int = attr.ib(init=False)

bounds: BBox = attr.ib(init=False)
crs: CRS = attr.ib(init=False)

bands: Sequence[str] = attr.ib(init=False)

@abc.abstractmethod
Expand Down
9 changes: 8 additions & 1 deletion rio_tiler/io/cogeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ class COGReader(BaseReader):
input (str): Cloud Optimized GeoTIFF path.
dataset (rasterio.io.DatasetReader or rasterio.io.DatasetWriter or rasterio.vrt.WarpedVRT, optional): Rasterio dataset.
bounds (tuple): Dataset bounds (left, bottom, right, top).
crs (rasterio.crs.CRS): Dataset crs.
crs (rasterio.crs.CRS): Dataset CRS.
tms (morecantile.TileMatrixSet, optional): TileMatrixSet grid definition. Defaults to `WebMercatorQuad`.
minzoom (int, optional): Set minzoom for the tiles.
maxzoom (int, optional): Set maxzoom for the tiles.
geographic_crs (rasterio.crs.CRS, optional): CRS to use as geographic coordinate system. Defaults to WGS84.
colormap (dict, optional): Overwrite internal colormap.
nodata (int or float or str, optional): Global options, overwrite internal nodata value.
unscale (bool, optional): Global options, apply internal scale and offset on all read operations.
Expand Down Expand Up @@ -83,6 +84,8 @@ class COGReader(BaseReader):
minzoom: int = attr.ib(default=None)
maxzoom: int = attr.ib(default=None)

geographic_crs: CRS = attr.ib(default=WGS84_CRS)

colormap: Dict = attr.ib(default=None)

# Define global options to be forwarded to functions reading the data (e.g `rio_tiler.reader.read`)
Expand Down Expand Up @@ -672,9 +675,13 @@ class GCPCOGReader(COGReader):
src_dataset: Union[DatasetReader, DatasetWriter, MemoryFile, WarpedVRT] = attr.ib(
default=None
)

tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)
minzoom: int = attr.ib(default=None)
maxzoom: int = attr.ib(default=None)

geographic_crs: CRS = attr.ib(default=WGS84_CRS)

colormap: Dict = attr.ib(default=None)

# Define global options to be forwarded to functions reading the data (e.g `rio_tiler.reader.read`)
Expand Down
4 changes: 4 additions & 0 deletions rio_tiler/io/stac.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import httpx
import pystac
from morecantile import TileMatrixSet
from rasterio.crs import CRS

from ..constants import WEB_MERCATOR_TMS, WGS84_CRS
from ..errors import InvalidAssetName, MissingAssets
Expand Down Expand Up @@ -126,6 +127,7 @@ class STACReader(MultiBaseReader):
item (dict or pystac.Item, STAC): Stac Item.
minzoom (int, optional): Set minzoom for the tiles.
maxzoom (int, optional): Set maxzoom for the tiles.
geographic_crs (rasterio.crs.CRS, optional): CRS to use as geographic coordinate system. Defaults to WGS84.
include (set of string, optional): Only Include specific assets.
exclude (set of string, optional): Exclude specific assets.
include_asset_types (set of string, optional): Only include some assets base on their type.
Expand Down Expand Up @@ -160,6 +162,8 @@ class STACReader(MultiBaseReader):
minzoom: int = attr.ib(default=None)
maxzoom: int = attr.ib(default=None)

geographic_crs: CRS = attr.ib(default=WGS84_CRS)

include_assets: Optional[Set[str]] = attr.ib(default=None)
exclude_assets: Optional[Set[str]] = attr.ib(default=None)

Expand Down
Binary file added tests/fixtures/cog_hirise_mars.tif
Binary file not shown.
50 changes: 50 additions & 0 deletions tests/test_io_cogeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from io import BytesIO
from typing import Any, Dict

import attr
import morecantile
import numpy
import pytest
Expand Down Expand Up @@ -38,6 +39,7 @@
COG_EARTH = os.path.join(PREFIX, "cog_fullearth.tif")
GEOTIFF = os.path.join(PREFIX, "nocog.tif")
COG_EUROPA = os.path.join(PREFIX, "cog_nonearth.tif")
COG_MARS = os.path.join(PREFIX, "cog_hirise_mars.tif")

KEY_ALPHA = "hro_sources/colorado/201404_13SED190110_201404_0x1500m_CL_1_alpha.tif"
COG_ALPHA = os.path.join(PREFIX, "my-bucket", KEY_ALPHA)
Expand Down Expand Up @@ -849,3 +851,51 @@ def test_nonearthbody():
assert img.crs == tms.rasterio_crs

assert len(warnings) == 0


def test_nonearth_custom():
"""Test Custom geographic_crs."""
MARS2000_SPHERE = CRS.from_proj4("+proj=longlat +R=3396190 +no_defs")
MARS_MERCATOR = CRS.from_proj4(
"+proj=merc +R=3396190 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +no_defs"
)

mars_tms = TileMatrixSet.custom(
[
-179.9999999999996,
-85.05112877980656,
179.9999999999996,
85.05112877980656,
],
MARS_MERCATOR,
extent_crs=MARS2000_SPHERE,
title="Web Mercator Mars",
geographic_crs=MARS2000_SPHERE,
)

@attr.s
class MarsReader(COGReader):
"""Use custom geographic CRS."""

geographic_crs: rasterio.crs.CRS = attr.ib(
init=False,
default=rasterio.crs.CRS.from_proj4("+proj=longlat +R=3396190 +no_defs"),
)

with pytest.warns(None) as warnings:
with MarsReader(COG_MARS, tms=mars_tms) as cog:
assert cog.geographic_bounds[0] > -180

assert len(warnings) == 0

with pytest.warns(None) as warnings:
with COGReader(
COG_MARS,
tms=mars_tms,
geographic_crs=rasterio.crs.CRS.from_proj4(
"+proj=longlat +R=3396190 +no_defs"
),
) as cog:
assert cog.geographic_bounds[0] > -180

assert len(warnings) == 0