Skip to content

Commit

Permalink
fix/docker-compose and add POST endpoint (#63)
Browse files Browse the repository at this point in the history
* fix: fix gdal flushing and add description to vrt

* fix: fix typo of urls parameter to url

* feat: add POST request

* refactor: remove unused import
  • Loading branch information
Thuhaa authored Mar 11, 2024
1 parent 4ce1f36 commit a4647e2
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 21 deletions.
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ RUN pipenv run pip install -U pip
COPY requirements.txt requirements.txt
RUN pipenv run pip install -r requirements.txt
COPY src/cogserver cogserver
#ENV HOST=0.0.0.0
#ENV PORT=80
ENV HOST=0.0.0.0
ENV PORT=8000
#ENV WEB_CONCURRENCY=1
#ENV CPL_TMPDIR=/tmp
#ENV GDAL_CACHEMAX=75%
Expand All @@ -25,7 +25,7 @@ COPY src/cogserver cogserver
#ENV VSI_CACHE=FALSE
#ENV RIO_TILER_MAX_THREADS=2
ENV LOG_LEVEL=info

ENV RELOAD=--reload

#CMD pipenv run uvicorn cogserver:app --host ${HOST} --port ${PORT} --log-config cogserver/logconf.yaml
CMD pipenv run uvicorn cogserver:app --host ${HOST} --port ${PORT} --log-level ${LOG_LEVEL}
CMD pipenv run uvicorn cogserver:app --host ${HOST} --port ${PORT} --log-level ${LOG_LEVEL} ${RELOAD}
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ services:

container_name: cogserver
# Enable the following volumes if you want to mount source code to Docker
# volumes:
# - "./app/wmts.py:/opt/wmts/wmts.py"
volumes:
- "./src/cogserver:/opt/server/cogserver"
# env_file:
# - gdal_rio.env
ports:
Expand Down
2 changes: 1 addition & 1 deletion gdal_rio.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Application
HOST=0.0.0.0
PORT=80
PORT=8000
## Uvicorn
## http://www.uvicorn.org/settings/#production
WEB_CONCURRENCY=2
Expand Down
66 changes: 52 additions & 14 deletions src/cogserver/extensions/vrt.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@

from fastapi import Query, Response
from osgeo import gdal
from pydantic import BaseModel
from titiler.core.factory import FactoryExtension
from cogserver.vrt import VRTFactory
from xml.etree import ElementTree as ET


async def create_vrt_from_urls(
def create_vrt_from_urls(
urls: List[str],
resolution: Literal["highest", "lowest", "average", "user"] = "average",
xRes: float = 0.1,
yRes: float = 0.1,
vrtNoData: List[str] = 0,
srcNoData: List[str] = 0,
xRes: float = None,
yRes: float = None,
vrtNoData: List[int] = None,
srcNoData: List[int] = None,
resamplingAlg: Literal["nearest", "bilinear", "cubic", "cubicspline", "lanczos", "average", "mode"] = "nearest",

):
Expand All @@ -27,18 +28,22 @@ async def create_vrt_from_urls(
resolution (Literal["highest", "lowest", "average", "user"], optional): Resolution to use for the resulting VRT. Defaults to "average".
xRes (float, optional): X resolution. Defaults to 0.1. Ignored if resolution is not "user".
yRes (float, optional): Y resolution. Defaults to 0.1. Ignored if resolution is not "user".
vrtNoData (List[str], optional): Set nodata values at the VRT band level (different values can be supplied for each band). If the option is not specified, intrinsic nodata settings on the first dataset will be used (if they exist). The value set by this option is written in the NoDataValue element of each VRTRasterBand element. Use a value of None to ignore intrinsic nodata settings on the source datasets. Defaults to 0.
srcNoData (List[str], optional): Set nodata values for input bands (different values can be supplied for each band). If the option is not specified, the intrinsic nodata settings on the source datasets will be used (if they exist). The value set by this option is written in the NODATA element of each ComplexSource element. Use a value of None to ignore intrinsic nodata settings on the source datasets. Defaults to 0.
vrtNoData (List[int], optional): Set nodata values at the VRT band level (different values can be supplied for each band). If the option is not specified, intrinsic nodata settings on the first dataset will be used (if they exist). The value set by this option is written in the NoDataValue element of each VRTRasterBand element. Use a value of None to ignore intrinsic nodata settings on the source datasets. Defaults to 0.
srcNoData (List[int], optional): Set nodata values for input bands (different values can be supplied for each band). If the option is not specified, the intrinsic nodata settings on the source datasets will be used (if they exist). The value set by this option is written in the NODATA element of each ComplexSource element. Use a value of None to ignore intrinsic nodata settings on the source datasets. Defaults to 0.
resamplingAlg (Literal["nearest", "bilinear", "cubic", "cubicspline", "lanczos", "average", "mode"], optional): Resampling algorithm. Defaults to "nearest".
Returns:
str: VRT XML
"""
urls = [f"/vsicurl/{url}" for url in urls]

if vrtNoData:
vrtNoData = [str(x) for x in vrtNoData]
vrtNoData = " ".join(vrtNoData)
if srcNoData:
srcNoData = [str(x) for x in srcNoData]
srcNoData = " ".join(srcNoData)

options = gdal.BuildVRTOptions(
separate=True,
xRes=xRes,
Expand Down Expand Up @@ -91,6 +96,17 @@ async def create_vrt_from_urls(
return ET.tostring(file_text, encoding="unicode")


class VrtCreationParameters(BaseModel):
urls: List[str]
xRes: Optional[float] = None
yRes: Optional[float] = None
srcNoData: Optional[List[int]] = None
vrtNoData: Optional[List[int]] = None
resamplingAlg: Literal[
"nearest", "bilinear", "cubic", "cubicspline", "lanczos", "average", "mode"]
resolution: Literal["highest", "lowest", "average"]


class VRTExtension(FactoryExtension):
"""
VRT Extension for the VRTFactory
Expand All @@ -113,12 +129,12 @@ def register(self, factory: VRTFactory):
responses={200: {"description": "Return a VRT from multiple COGs."}},
summary="Create a VRT from multiple COGs",
)
async def create_vrt(
urls: List[str] = Query(..., description="Dataset URLs"),
def create_vrt(
url: List[str] = Query(..., description="Dataset URLs"),

srcNoData: List[str] = Query(None,
srcNoData: List[int] = Query(None,
description="Set nodata values for input bands (different values can be supplied for each band). If the option is not specified, the intrinsic nodata settings on the source datasets will be used (if they exist). The value set by this option is written in the NODATA element of each ComplexSource element. Use a value of None to ignore intrinsic nodata settings on the source datasets."),
vrtNoData: List[str] = Query(None,
vrtNoData: List[int] = Query(None,
description="Set nodata values at the VRT band level (different values can be supplied for each band). If the option is not specified, intrinsic nodata settings on the first dataset will be used (if they exist). The value set by this option is written in the NoDataValue element of each VRTRasterBand element. Use a value of None to ignore intrinsic nodata settings on the source datasets."),
resamplingAlg: Literal[
"nearest", "bilinear", "cubic", "cubicspline", "lanczos", "average", "mode"] = Query("nearest",
Expand All @@ -130,18 +146,40 @@ async def create_vrt(
yRes: Optional[float] = Query(None,
description="Y resolution. Applicable only when `resolution` is `user`")
):
if len(urls) < 1:
if len(url) < 1:
return Response("Please provide at least two URLs", status_code=400)

if resolution == "user" and (not xRes or not yRes):
return Response("Please provide xRes and yRes for user resolution", status_code=400)

return Response(await create_vrt_from_urls(
urls=urls,
return Response(create_vrt_from_urls(
urls=url,
xRes=xRes,
yRes=yRes,
srcNoData=srcNoData,
vrtNoData=vrtNoData,
resamplingAlg=resamplingAlg,
resolution=resolution
), media_type="application/xml")

@factory.router.post(
"",
response_class=Response,
responses={200: {"description": "Return a VRT from multiple COGs."}},
summary="Create a VRT from multiple COGs",
)
def create_vrt(
payload: VrtCreationParameters,
):
urls = payload.urls
if len(urls) < 1:
return Response("Please provide at least one URL", status_code=400)
return Response(create_vrt_from_urls(
urls=urls,
xRes=payload.xRes,
yRes=payload.yRes,
srcNoData=payload.srcNoData,
vrtNoData=payload.vrtNoData,
resamplingAlg=payload.resamplingAlg,
resolution=payload.resolution
), media_type="application/xml")

0 comments on commit a4647e2

Please sign in to comment.