diff --git a/xpublish_wms/wms/__init__.py b/xpublish_wms/wms/__init__.py index b6dcf00..fd2ef35 100644 --- a/xpublish_wms/wms/__init__.py +++ b/xpublish_wms/wms/__init__.py @@ -2,6 +2,7 @@ OGC WMS router for datasets with CF convention metadata """ +import asyncio import logging import cachey @@ -32,19 +33,19 @@ async def wms_handler( logger.info(f"WMS: {method}") if method == "getcapabilities": - return get_capabilities(dataset, request, query_params) + return await asyncio.to_thread(get_capabilities, dataset, request, query_params) elif method == "getmap": getmap_service = GetMap(cache=cache) return await getmap_service.get_map(dataset, query_params) elif method == "getfeatureinfo" or method == "gettimeseries": - return get_feature_info(dataset, query_params) + return await asyncio.to_thread(get_feature_info, dataset, query_params) elif method == "getverticalprofile": query_params["elevation"] = "all" - return get_feature_info(dataset, query_params) + return await asyncio.to_thread(get_feature_info, dataset, query_params) elif method == "getmetadata": - return get_metadata(dataset, cache, query_params) + return await get_metadata(dataset, cache, query_params) elif method == "getlegendgraphic": - return get_legend_info(dataset, query_params) + return await asyncio.to_thread(get_legend_info, dataset, query_params) else: raise HTTPException( status_code=404, diff --git a/xpublish_wms/wms/get_map.py b/xpublish_wms/wms/get_map.py index 53f5ac9..d137189 100644 --- a/xpublish_wms/wms/get_map.py +++ b/xpublish_wms/wms/get_map.py @@ -33,8 +33,6 @@ class GetMap: DEFAULT_STYLE: str = "raster/default" DEFAULT_PALETTE: str = "turbo" - BBOX_BUFFER = 30_000 # meters - cache: cachey.Cache # Data selection @@ -280,36 +278,38 @@ async def render( if minmax_only: logger.warning("Falling back to default minmax") return {"min": float(da.min()), "max": float(da.max())} - - try: - da = filter_data_within_bbox(da, self.bbox, self.BBOX_BUFFER) - except Exception as e: - logger.error(f"Error filtering data within bbox: {e}") - logger.warning("Falling back to full layer") - print(f"Projection time: {time.time() - projection_start}") + # x and y are only set for triangle grids, we dont subset the data for triangle grids + # at this time. + if x is None: + try: + # Grab a buffer around the bbox to ensure we have enough data to render + # TODO: Base this on actual data resolution? + if self.crs == "EPSG:4326": + buffer = 0.5 # degrees + elif self.crs == "EPSG:3857": + buffer = 30000 # meters + else: + # Default to 0.5, this should never happen + buffer = 0.5 + + # Filter the data to only include the data within the bbox + buffer so + # we don't have to render a ton of empty space or pull down more chunks + # than we need + da = filter_data_within_bbox(da, self.bbox, buffer) + except Exception as e: + logger.error(f"Error filtering data within bbox: {e}") + logger.warning("Falling back to full layer") + + logger.debug(f"Projection time: {time.time() - projection_start}") start_dask = time.time() da = await asyncio.to_thread(da.compute) - # da = da.persist() - # if x is not None and y is not None: - # x = x.persist() - # y = y.persist() - # else: - # da["x"] = da.x.persist() - # da["y"] = da.y.persist() - - print(da.x[1].values -da.x[0].values) - print(da.y[1].values - da.y[0].values) - - print(f"dask compute: {time.time() - start_dask}") + logger.debug(f"dask compute: {time.time() - start_dask}") if minmax_only: - # da = da.persist() - # data_sel = filter_data_within_bbox(da, self.bbox, self.BBOX_BUFFER) - try: return { "min": float(np.nanmin(da)), @@ -366,7 +366,7 @@ async def render( how="linear", span=(vmin, vmax), ) - print(f"Shade time: {time.time() - start_shade}") + logger.debug(f"Shade time: {time.time() - start_shade}") im = shaded.to_pil() im.save(buffer, format="PNG") diff --git a/xpublish_wms/wms/get_metadata.py b/xpublish_wms/wms/get_metadata.py index 72064fb..e5baf8d 100644 --- a/xpublish_wms/wms/get_metadata.py +++ b/xpublish_wms/wms/get_metadata.py @@ -11,7 +11,7 @@ from .get_map import GetMap -def get_metadata(ds: xr.Dataset, cache: cachey.Cache, params: dict) -> Response: +async def get_metadata(ds: xr.Dataset, cache: cachey.Cache, params: dict) -> Response: """ Return the WMS metadata for the dataset @@ -39,7 +39,7 @@ def get_metadata(ds: xr.Dataset, cache: cachey.Cache, params: dict) -> Response: da = ds[layer_name] payload = get_timesteps(da, params) elif metadata_type == "minmax": - payload = get_minmax(ds, cache, params) + payload = await get_minmax(ds, cache, params) else: raise HTTPException( status_code=400, @@ -79,14 +79,14 @@ def get_timesteps(da: xr.DataArray, params: dict) -> dict: } -def get_minmax(ds: xr.Dataset, cache: cachey.Cache, params: dict) -> dict: +async def get_minmax(ds: xr.Dataset, cache: cachey.Cache, params: dict) -> dict: """ Returns the min and max range of values for a given layer in a given area If BBOX is not specified, the entire selected temporal and elevation range is used. """ getmap = GetMap(cache=cache) - return getmap.get_minmax(ds, params) + return await getmap.get_minmax(ds, params) def get_layer_details(ds: xr.Dataset, layer_name: str) -> dict: