Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/developmentseed/titiler int…
Browse files Browse the repository at this point in the history
…o feature/add-xarray-package
  • Loading branch information
vincentsarago committed Nov 4, 2024
2 parents d0804ec + cc607c3 commit 0cc3a58
Show file tree
Hide file tree
Showing 12 changed files with 1,287 additions and 98 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* Use `@attrs.define` instead of dataclass for factories **breaking change**
* Use `@attrs.define` instead of dataclass for factory extensions **breaking change**
* Handle `numpy` types in JSON/GeoJSON response
* In the `map.html` template, use the tilejson's `minzoom` and `maxzoom` to populate `minNativeZoom` and `maxNativeZoom` parameters in leaflet `tileLayer` instead of `minZoom` and `maxZoom`

### titiler.core

Expand Down Expand Up @@ -83,6 +84,10 @@

* avoid `lat/lon` overflow in `map` viewer

* add OGC Tiles `/tiles` and `/tiles/{tileMatrixSet}` endpoints

* add `gif` media type

### titiler.mosaic

* Rename `reader` attribute to `backend` in `MosaicTilerFactory` **breaking change**
Expand All @@ -95,6 +100,8 @@

* re-order endpoints parameters

* add OGC Tiles `/tiles` and `/tiles/{tileMatrixSet}` endpoints

### titiler.extensions

* Encode URL for cog_viewer and stac_viewer (author @guillemc23, https://github.com/developmentseed/titiler/pull/961)
Expand Down
106 changes: 61 additions & 45 deletions docs/src/advanced/endpoints_factories.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion docs/src/endpoints/cog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ The `/cog` routes are based on `titiler.core.factory.TilerFactory` but with `cog
| `GET` | `/cog/info.geojson` | GeoJSON | return dataset's basic info as a GeoJSON feature
| `GET` | `/cog/statistics` | JSON | return dataset's statistics
| `POST` | `/cog/statistics` | GeoJSON | return dataset's statistics for a GeoJSON
| `GET` | `/cog/tiles` | JSON | List of OGC Tilesets available
| `GET` | `/cog/tiles/{tileMatrixSetId}` | JSON | OGC Tileset metadata
| `GET` | `/cog/tiles/{tileMatrixSetId}/{z}/{x}/{y}[@{scale}x][.{format}]` | image/bin | create a web map tile image from a dataset
| `GET` | `/cog/{tileMatrixSetId}/map` | HTML | simple map viewer
| `GET` | `/cog/{tileMatrixSetId}/tilejson.json` | JSON | return a Mapbox TileJSON document
| `GET` | `/cog/{tileMatrixSetId}/WMTSCapabilities.xml` | XML | return OGC WMTS Get Capabilities
| `GET` | `/cog/point/{lon},{lat}` | JSON | return pixel values from a dataset
| `GET` | `/cog/preview[.{format}]` | image/bin | create a preview image from a dataset
| `GET` | `/cog/bbox/{minx},{miny},{maxx},{maxy}[/{width}x{height}].{format}` | image/bin | create an image from part of a dataset
| `POST` | `/cog/feature[/{width}x{height}][].{format}]` | image/bin | create an image from a GeoJSON feature
| `GET` | `/cog/{tileMatrixSetId}/map` | HTML | simple map viewer
| `GET` | `/cog/validate` | JSON | validate a COG and return dataset info (from `titiler.extensions.cogValidateExtension`)
| `GET` | `/cog/viewer` | HTML | demo webpage (from `titiler.extensions.cogViewerExtension`)
| `GET` | `/cog/stac` | GeoJSON | create STAC Items from a dataset (from `titiler.extensions.stacExtension`)
Expand Down
8 changes: 5 additions & 3 deletions docs/src/endpoints/mosaic.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ Read Mosaic Info/Metadata and create Web map Tiles from a multiple COG. The `mos
| `GET` | `/mosaicjson/bounds` | JSON | return mosaic's bounds
| `GET` | `/mosaicjson/info` | JSON | return mosaic's basic info
| `GET` | `/mosaicjson/info.geojson` | GeoJSON | return mosaic's basic info as a GeoJSON feature
| `GET` | `/mosaicjson/tiles/{tileMatrixSetId}/{z}/{x}/{y}[@{scale}x][.{format}]` | image/bin | create a web map tile image from mosaic assets
| `GET` | `/mosaicjson/{tileMatrixSetId}/tilejson.json` | JSON | return a Mapbox TileJSON document
| `GET` | `/mosaicjson/tiles` | JSON | List of OGC Tilesets available
| `GET` | `/mosaicjson/tiles/{tileMatrixSetId}` | JSON | OGC Tileset metadata
| `GET` | `/mosaicjson/tiles/{tileMatrixSetId}/{z}/{x}/{y}[@{scale}x][.{format}]` | image/bin | create a web map tile image from mosaic assets
| `GET` | `/mosaicjson/{tileMatrixSetId}/map` | HTML | simple map viewer
| `GET` | `/mosaicjson/{tileMatrixSetId}/tilejson.json` | JSON | return a Mapbox TileJSON document
| `GET` | `/mosaicjson/{tileMatrixSetId}/WMTSCapabilities.xml` | XML | return OGC WMTS Get Capabilities
| `GET` | `/mosaicjson/point/{lon},{lat}` | JSON | return pixel value from a mosaic assets
| `GET` | `/mosaicjson/{z}/{x}/{y}/assets` | JSON | return list of assets intersecting a XYZ tile
| `GET` | `/mosaicjson/{lon},{lat}/assets` | JSON | return list of assets intersecting a point
| `GET` | `/mosaicjson/{minx},{miny},{maxx},{maxy}/assets` | JSON | return list of assets intersecting a bounding box
| `GET` | `/mosaicjson/{tileMatrixSetId}/map` | HTML | simple map viewer

## Description

Expand Down
10 changes: 6 additions & 4 deletions docs/src/endpoints/stac.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ The `/stac` routes are based on `titiler.core.factory.MultiBaseTilerFactory` but
| `GET` | `/stac/asset_statistics` | JSON | return per asset statistics
| `GET` | `/stac/statistics` | JSON | return asset's statistics
| `POST` | `/stac/statistics` | GeoJSON | return asset's statistics for a GeoJSON
| `GET` | `/stac/tiles/{tileMatrixSetId}/{z}/{x}/{y}[@{scale}x][.{format}]` | image/bin | create a web map tile image from assets
| `GET` | `/stac/{tileMatrixSetId}/tilejson.json` | JSON | return a Mapbox TileJSON document
| `GET` | `/stac/tiles` | JSON | List of OGC Tilesets available
| `GET` | `/stac/tiles/{tileMatrixSetId}` | JSON | OGC Tileset metadata
| `GET` | `/stac/tiles/{tileMatrixSetId}/{z}/{x}/{y}[@{scale}x][.{format}]` | image/bin | create a web map tile image from assets
| `GET` | `/stac/{tileMatrixSetId}/map` | HTML | simple map viewer
| `GET` | `/stac/{tileMatrixSetId}/tilejson.json` | JSON | return a Mapbox TileJSON document
| `GET` | `/stac/{tileMatrixSetId}/WMTSCapabilities.xml` | XML | return OGC WMTS Get Capabilities
| `GET` | `/stac/point/{lon},{lat}` | JSON | return pixel value from assets
| `GET` | `/stac/preview[.{format}]` | image/bin | create a preview image from assets
| `GET` | `/stac/bbox/{minx},{miny},{maxx},{maxy}[/{width}x{height}].{format}` | image/bin | create an image from part of assets
| `POST` | `/stac/feature[/{width}x{height}][].{format}]` | image/bin | create an image from a geojson covering the assets
| `GET` | `/stac/{tileMatrixSetId}/map` | HTML | simple map viewer
| `POST` | `/stac/feature[/{width}x{height}][].{format}]` | image/bin | create an image from a geojson covering the assets
| `GET` | `/stac/viewer` | HTML | demo webpage (from `titiler.extensions.stacViewerExtension`)

## Description
Expand Down
98 changes: 77 additions & 21 deletions src/titiler/core/tests/test_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
import xml.etree.ElementTree as ET
from enum import Enum
from io import BytesIO
from typing import Dict, Optional, Type
from typing import Dict, Optional, Sequence, Type
from unittest.mock import patch
from urllib.parse import urlencode

import attr
import httpx
import morecantile
import numpy
import pytest
from attrs import define, field
from attrs import define
from fastapi import Depends, FastAPI, HTTPException, Path, Query, security, status
from morecantile.defaults import TileMatrixSets
from rasterio.crs import CRS
Expand Down Expand Up @@ -48,7 +49,7 @@
def test_TilerFactory():
"""Test TilerFactory class."""
cog = TilerFactory()
assert len(cog.router.routes) == 20
assert len(cog.router.routes) == 22
assert len(cog.supported_tms.list()) == NB_DEFAULT_TMS

cog = TilerFactory(router_prefix="something", supported_tms=WEB_TMS)
Expand All @@ -75,7 +76,7 @@ def test_TilerFactory():
assert response.status_code == 422

cog = TilerFactory(add_preview=False, add_part=False, add_viewer=False)
assert len(cog.router.routes) == 12
assert len(cog.router.routes) == 14

app = FastAPI()
cog = TilerFactory()
Expand Down Expand Up @@ -725,14 +726,33 @@ def test_TilerFactory():
assert meta["dtype"] == "uint8"
assert meta["count"] == 3

# OGC Tileset
response = client.get(f"/tiles?url={DATA_DIR}/cog.tif")
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
resp = response.json()
assert len(resp["tilesets"]) == NB_DEFAULT_TMS

first_tms = resp["tilesets"][0]
first_id = DEFAULT_TMS.list()[0]
assert first_id in first_tms["title"]
assert len(first_tms["links"]) == 2 # no link to the tms definition

response = client.get(f"/tiles/WebMercatorQuad?url={DATA_DIR}/cog.tif")
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
resp = response.json()
# covers only 5 zoom levels
assert len(resp["tileMatrixSetLimits"]) == 5


@patch("rio_tiler.io.rasterio.rasterio")
def test_MultiBaseTilerFactory(rio):
"""test MultiBaseTilerFactory."""
rio.open = mock_rasterio_open

stac = MultiBaseTilerFactory(reader=STACReader)
assert len(stac.router.routes) == 22
assert len(stac.router.routes) == 24

app = FastAPI()
app.include_router(stac.router)
Expand Down Expand Up @@ -1054,29 +1074,43 @@ def test_MultiBaseTilerFactory(rio):
assert len(props) == 1
assert "(B09 - B01) / (B09 + B01)" in props

# OGC Tileset
response = client.get(f"/tiles?url={DATA_DIR}/item.json")
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
resp = response.json()
assert len(resp["tilesets"]) == NB_DEFAULT_TMS

first_tms = resp["tilesets"][0]
first_id = DEFAULT_TMS.list()[0]
assert first_id in first_tms["title"]
assert len(first_tms["links"]) == 2 # no link to the tms definition

response = client.get(f"/tiles/WebMercatorQuad?url={DATA_DIR}/item.json")
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
resp = response.json()
# default minzoom/maxzoom are 0->24
assert len(resp["tileMatrixSetLimits"]) == 25


@define
@attr.s
class BandFileReader(MultiBandReader):
"""Test MultiBand"""

input: str = field()
tms: morecantile.TileMatrixSet = field(
input: str = attr.ib()
tms: morecantile.TileMatrixSet = attr.ib(
default=morecantile.tms.get("WebMercatorQuad")
)
reader_options: Dict = field(factory=dict)

reader: Type[BaseReader] = field(default=Reader)
reader: Type[BaseReader] = attr.ib(default=Reader)
reader_options: Dict = attr.ib(factory=dict)

minzoom: int = field()
maxzoom: int = field()
bands: Sequence[str] = attr.ib(init=False)
default_bands: Optional[Sequence[str]] = attr.ib(init=False, default=None)

@minzoom.default
def _minzoom(self):
return self.tms.minzoom

@maxzoom.default
def _maxzoom(self):
return self.tms.maxzoom
minzoom: int = attr.ib(init=False)
maxzoom: int = attr.ib(init=False)

def __attrs_post_init__(self):
"""Parse Sceneid and get grid bounds."""
Expand All @@ -1086,6 +1120,9 @@ def __attrs_post_init__(self):
self.crs = cog.crs
self.minzoom = cog.minzoom
self.maxzoom = cog.maxzoom
self.width = cog.width
self.height = cog.height
self.transform = cog.transform

def _get_band_url(self, band: str) -> str:
"""Validate band's name and return band's url."""
Expand All @@ -1103,7 +1140,7 @@ def test_MultiBandTilerFactory():
bands = MultiBandTilerFactory(
reader=BandFileReader, path_dependency=CustomPathParams
)
assert len(bands.router.routes) == 21
assert len(bands.router.routes) == 23

app = FastAPI()
app.include_router(bands.router)
Expand Down Expand Up @@ -1397,6 +1434,25 @@ def test_MultiBandTilerFactory():
assert props["B01"]
assert props["B09"]

# OGC Tileset
response = client.get(f"/tiles?directory={DATA_DIR}")
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
resp = response.json()
assert len(resp["tilesets"]) == NB_DEFAULT_TMS

first_tms = resp["tilesets"][0]
first_id = DEFAULT_TMS.list()[0]
assert first_id in first_tms["title"]
assert len(first_tms["links"]) == 2 # no link to the tms definition

response = client.get(f"/tiles/WebMercatorQuad?directory={DATA_DIR}")
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
resp = response.json()
# 1 Zoom level (8)
assert len(resp["tileMatrixSetLimits"]) == 1


def test_TMSFactory():
"""test TMSFactory."""
Expand Down Expand Up @@ -1475,7 +1531,7 @@ def must_be_bob(credentials: security.HTTPBasicCredentials = Depends(http_basic)
],
router_prefix="something",
)
assert len(cog.router.routes) == 20
assert len(cog.router.routes) == 22

app = FastAPI()
app.include_router(cog.router, prefix="/something")
Expand Down
Loading

0 comments on commit 0cc3a58

Please sign in to comment.