POC for HRRR data
This commit is contained in:
Родитель
d2b46c9ae8
Коммит
db28c1439c
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче