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
from typing import Annotated, Optional
from typing import Annotated, Literal, Optional, List, Type
from urllib.parse import quote_plus, urljoin
import fastapi
from pydantic import conint
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 geojson_pydantic.features import Feature
from html_sanitizer.sanitizer import Sanitizer
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.resources.enums import ImageType
from titiler.pgstac.dependencies import get_stac_item
@ -29,6 +35,26 @@ except ImportError:
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(
request: Request,
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,
path_dependency=ItemPathParams,
colormap_dependency=PCColorMapParams,
@ -52,6 +162,15 @@ pc_tile_factory = MultiBaseTilerFactory(
router_prefix=get_settings().item_endpoint_prefix,
# We remove the titiler default `/map` viewer
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 time
import urllib.parse
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 morecantile
@ -12,6 +13,7 @@ from geojson_pydantic import Polygon
from rio_tiler.errors import InvalidAssetName, MissingAssets, TileOutsideBounds
from rio_tiler.models import ImageData
from rio_tiler.mosaic import mosaic_reader
from rio_tiler.io.stac import DEFAULT_VALID_TYPE
from rio_tiler.types import AssetInfo
from starlette.requests import Request
from titiler.core.dependencies import DefaultDependency
@ -46,6 +48,9 @@ class ItemSTACReader(PgSTACReader):
# We make request an optional attribute to avoid re-writing
# the whole list of attribute
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:
"""return asset's url."""
@ -57,7 +62,19 @@ class ItemSTACReader(PgSTACReader):
if render_config and render_config.requires_token:
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
@ -90,7 +107,6 @@ class MosaicSTACReader(pgstac_mosaic.CustomSTACReader):
if render_config and render_config.requires_token:
asset_url = pc.sign(asset_url)
info = AssetInfo(url=asset_url)
if "file:header_size" in self.input["assets"][asset]:
info["env"] = {
"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