This commit is contained in:
Tom Augspurger 2024-07-25 09:47:57 -05:00
Родитель d2b46c9ae8
Коммит db28c1439c
3 изменённых файлов: 265 добавлений и 7 удалений

110
hrrr.ipynb Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,15 +1,21 @@
import dataclasses
import logging import logging
from typing import Annotated, Optional from typing import Annotated, Literal, Optional, List, Type
from urllib.parse import quote_plus, urljoin from urllib.parse import quote_plus, urljoin
import fastapi import fastapi
from pydantic import conint
import pystac import pystac
from fastapi import Body, Depends, Query, Request, Response from fastapi import Body, Depends, Path, Query, Request, Response
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from geojson_pydantic.features import Feature from geojson_pydantic.features import Feature
from html_sanitizer.sanitizer import Sanitizer from html_sanitizer.sanitizer import Sanitizer
from starlette.responses import HTMLResponse from starlette.responses import HTMLResponse
from titiler.core.dependencies import CoordCRSParams, DstCRSParams from titiler.core.dependencies import (
CoordCRSParams,
DstCRSParams,
DefaultDependency,
)
from titiler.core.factory import MultiBaseTilerFactory, img_endpoint_params from titiler.core.factory import MultiBaseTilerFactory, img_endpoint_params
from titiler.core.resources.enums import ImageType from titiler.core.resources.enums import ImageType
from titiler.pgstac.dependencies import get_stac_item from titiler.pgstac.dependencies import get_stac_item
@ -29,6 +35,26 @@ except ImportError:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@dataclasses.dataclass
class SubdatasetParams(DefaultDependency):
"""Assets, Expression and Asset's band Indexes parameters."""
subdataset_name: Annotated[
Optional[str],
Query(
title="Subdataset name",
description="The name of a subdataset within the asset.",
),
] = None
subdataset_bands: Annotated[
Optional[List[int]],
Query(
title="Subdataset bands",
description="The name of a subdataset within the asset.",
),
] = None
def ItemPathParams( def ItemPathParams(
request: Request, request: Request,
collection: str = Query(..., description="STAC Collection ID"), collection: str = Query(..., description="STAC Collection ID"),
@ -44,7 +70,91 @@ templates = Jinja2Templates(
) )
pc_tile_factory = MultiBaseTilerFactory( @dataclasses.dataclass
class MyMultiBaseTilerFactory(MultiBaseTilerFactory):
subdataset_dependency: Type[DefaultDependency] = SubdatasetParams
private_router: fastapi.APIRouter = dataclasses.field(
default_factory=fastapi.APIRouter
)
def tile(self): # noqa: C901
"""Register /tiles endpoint."""
@self.router.get(r"/tiles/{z}/{x}/{y}", **img_endpoint_params, deprecated=True)
@self.router.get(
r"/tiles/{z}/{x}/{y}.{format}", **img_endpoint_params, deprecated=True
)
@self.router.get(
r"/tiles/{z}/{x}/{y}@{scale}x", **img_endpoint_params, deprecated=True
)
@self.router.get(
r"/tiles/{z}/{x}/{y}@{scale}x.{format}",
**img_endpoint_params,
deprecated=True,
)
@self.router.get(r"/tiles/{tileMatrixSetId}/{z}/{x}/{y}", **img_endpoint_params)
@self.router.get(
r"/tiles/{tileMatrixSetId}/{z}/{x}/{y}.{format}", **img_endpoint_params
)
@self.router.get(
r"/tiles/{tileMatrixSetId}/{z}/{x}/{y}@{scale}x", **img_endpoint_params
)
@self.router.get(
r"/tiles/{tileMatrixSetId}/{z}/{x}/{y}@{scale}x.{format}",
**img_endpoint_params,
)
def tile(
request: Request,
z: Annotated[
int,
Path(
description="Identifier (Z) selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile.",
),
],
x: Annotated[
int,
Path(
description="Column (X) index of the tile on the selected TileMatrix. It cannot exceed the MatrixHeight-1 for the selected TileMatrix.",
),
],
y: Annotated[
int,
Path(
description="Row (Y) index of the tile on the selected TileMatrix. It cannot exceed the MatrixWidth-1 for the selected TileMatrix.",
),
],
tileMatrixSetId: Annotated[
Literal[tuple(self.supported_tms.list())],
f"Identifier selecting one of the TileMatrixSetId supported (default: '{self.default_tms}')",
] = self.default_tms,
scale: Annotated[
conint(gt=0, le=4), "Tile size scale. 1=256x256, 2=512x512..."
] = 1,
format: Annotated[
ImageType,
"Default will be automatically defined if the output image needs a mask (png) or not (jpeg).",
] = None,
src_path=Depends(self.path_dependency),
layer_params=Depends(self.layer_dependency),
dataset_params=Depends(self.dataset_dependency),
tile_params=Depends(self.tile_dependency),
post_process=Depends(self.process_dependency),
rescale=Depends(self.rescale_dependency),
color_formula=Depends(self.color_formula_dependency),
colormap=Depends(self.colormap_dependency),
render_params=Depends(self.render_dependency),
reader_params=Depends(self.reader_dependency),
env=Depends(self.environment_dependency),
subdataset_params=Depends(self.subdataset_dependency),
):
endpoint = get_endpoint_function(
self.private_router, path="/tiles/{z}/{x}/{y}", method=request.method
)
return endpoint(z, x, y, tileMatrixSetId, scale, format, src_path, layer_params, dataset_params, tile_params, post_process, rescale, color_formula, colormap, render_params, reader_params, env)
pc_tile_factory = MyMultiBaseTilerFactory(
reader=ItemSTACReader, reader=ItemSTACReader,
path_dependency=ItemPathParams, path_dependency=ItemPathParams,
colormap_dependency=PCColorMapParams, colormap_dependency=PCColorMapParams,
@ -52,6 +162,15 @@ pc_tile_factory = MultiBaseTilerFactory(
router_prefix=get_settings().item_endpoint_prefix, router_prefix=get_settings().item_endpoint_prefix,
# We remove the titiler default `/map` viewer # We remove the titiler default `/map` viewer
add_viewer=False, add_viewer=False,
private_router=MultiBaseTilerFactory(
reader=ItemSTACReader,
path_dependency=ItemPathParams,
colormap_dependency=PCColorMapParams,
reader_dependency=ReaderParams,
router_prefix=get_settings().item_endpoint_prefix,
# We remove the titiler default `/map` viewer
add_viewer=False,
).router
) )

Просмотреть файл

@ -1,7 +1,8 @@
import logging import logging
import time import time
import urllib.parse
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Tuple, Type from typing import Any, Dict, List, Optional, Set, Tuple, Type
import attr import attr
import morecantile import morecantile
@ -12,6 +13,7 @@ from geojson_pydantic import Polygon
from rio_tiler.errors import InvalidAssetName, MissingAssets, TileOutsideBounds from rio_tiler.errors import InvalidAssetName, MissingAssets, TileOutsideBounds
from rio_tiler.models import ImageData from rio_tiler.models import ImageData
from rio_tiler.mosaic import mosaic_reader from rio_tiler.mosaic import mosaic_reader
from rio_tiler.io.stac import DEFAULT_VALID_TYPE
from rio_tiler.types import AssetInfo from rio_tiler.types import AssetInfo
from starlette.requests import Request from starlette.requests import Request
from titiler.core.dependencies import DefaultDependency from titiler.core.dependencies import DefaultDependency
@ -46,6 +48,9 @@ class ItemSTACReader(PgSTACReader):
# We make request an optional attribute to avoid re-writing # We make request an optional attribute to avoid re-writing
# the whole list of attribute # the whole list of attribute
request: Optional[Request] = attr.ib(default=None) request: Optional[Request] = attr.ib(default=None)
include_asset_types: Set[str] = attr.ib(
default=DEFAULT_VALID_TYPE | {"application/wmo-GRIB2"}
)
def _get_asset_info(self, asset: str) -> AssetInfo: def _get_asset_info(self, asset: str) -> AssetInfo:
"""return asset's url.""" """return asset's url."""
@ -57,7 +62,19 @@ class ItemSTACReader(PgSTACReader):
if render_config and render_config.requires_token: if render_config and render_config.requires_token:
asset_url = pc.sign(asset_url) asset_url = pc.sign(asset_url)
info["url"] = asset_url vrt_params = {}
if subdataset_name := self.request.query_params.get("subdataset_name"):
vrt_params["sd_name"] = subdataset_name
if subdataset_bands := self.request.query_params.getlist("subdataset_bands"):
vrt_params["bands"] = ",".join([str(band) for band in subdataset_bands])
if vrt_params:
params = urllib.parse.urlencode(vrt_params, safe=",")
asset_url = f"vrt:///vsicurl/{asset_url}?{params}"
info = AssetInfo(url=asset_url)
return info return info
@ -90,7 +107,6 @@ class MosaicSTACReader(pgstac_mosaic.CustomSTACReader):
if render_config and render_config.requires_token: if render_config and render_config.requires_token:
asset_url = pc.sign(asset_url) asset_url = pc.sign(asset_url)
info = AssetInfo(url=asset_url)
if "file:header_size" in self.input["assets"][asset]: if "file:header_size" in self.input["assets"][asset]:
info["env"] = { info["env"] = {
"GDAL_INGESTED_BYTES_AT_OPEN": self.input["assets"][asset][ "GDAL_INGESTED_BYTES_AT_OPEN": self.input["assets"][asset][
@ -98,6 +114,19 @@ class MosaicSTACReader(pgstac_mosaic.CustomSTACReader):
] ]
} }
vrt_params = {}
if subdataset_name := self.request.query_params.get("subdataset_name"):
vrt_params["sd_name"] = subdataset_name
if subdataset_bands := self.request.query_params.getlist("subdataset_bands"):
vrt_params["bands"] = ",".join([str(band) for band in subdataset_bands])
if vrt_params:
params = urllib.parse.urlencode(vrt_params, safe=",")
asset_url = f"vrt:///vsicurl/{asset_url}?{params}"
info = AssetInfo(url=asset_url)
return info return info