Skip to content

Commit

Permalink
OGC API - Coverages: add full support for scaling
Browse files Browse the repository at this point in the history
scale-size and scale-axes are now supported in addition to the
previously supported scale-factor. The recently added CoverageScaling
class is used for the implementation.
  • Loading branch information
pont-us committed Dec 11, 2023
1 parent 843a6fb commit 597f8ae
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 51 deletions.
22 changes: 2 additions & 20 deletions test/webapi/ows/coverages/test_controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def test_get_coverage_data_netcdf(self):
'bbox': ['1,51,2,52'],
'datetime': ['2017-01-24T00:00:00Z/2017-01-27T00:00:00Z'],
'properties': ['conc_chl,kd489'],
'scale-factor': [2],
'scale-axes': ['lat(2),lon(2)'],
'crs': [crs],
}
content, content_bbox, content_crs = get_coverage_data(
Expand Down Expand Up @@ -170,7 +170,7 @@ def test_get_coverage_data_png(self):
query = {
'subset': ['lat(52:51),lon(1:2),time(2017-01-25)'],
'properties': ['conc_chl'],
'scale-factor': ['4'],
'scale-size': ['lat(100),lon(100)'],
}
content, content_bbox, content_crs = get_coverage_data(
get_coverages_ctx().datasets_ctx, 'demo', query, 'png'
Expand Down Expand Up @@ -262,24 +262,6 @@ def test_get_crs_from_dataset(self):
ds = xr.Dataset({'crs': ([], None, {'spatial_ref': '3035'})})
self.assertEqual('EPSG:3035', get_crs_from_dataset(ds).to_string())

def test_get_coverage_scale_axes(self):
with self.assertRaises(ApiError.NotImplemented):
get_coverage_data(
get_coverages_ctx().datasets_ctx,
'demo',
{'scale-axes': ['x(2)']},
'application/netcdf',
)

def test_get_coverage_scale_size(self):
with self.assertRaises(ApiError.NotImplemented):
get_coverage_data(
get_coverages_ctx().datasets_ctx,
'demo',
{'scale-size': ['x(2)']},
'application/netcdf',
)

def test_dtype_to_opengis_datatype(self):
expected = [
(
Expand Down
38 changes: 8 additions & 30 deletions xcube/webapi/ows/coverages/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
from xcube.util.timeindex import ensure_time_index_compatible
from xcube.webapi.datasets.context import DatasetsContext
from xcube.webapi.ows.coverages.request import CoverageRequest
from xcube.webapi.ows.coverages.scaling import CoverageScaling
from xcube.webapi.ows.coverages.util import get_h_dim, get_v_dim


def get_coverage_as_json(ctx: DatasetsContext, collection_id: str):
Expand Down Expand Up @@ -140,27 +142,15 @@ def get_coverage_data(
_assert_coverage_size_ok(ds, scale_factor)

source_gm = GridMapping.from_dataset(ds, crs=native_crs)
target_gm = None
target_gm = source_gm

if native_crs != final_crs:
target_gm = source_gm.transform(final_crs).to_regular()

if scale_factor != 1:
if target_gm is None:
target_gm = source_gm
target_gm = target_gm.scale(1. / scale_factor)
if request.scale_axes is not None:
# TODO implement scale-axes
raise ApiError.NotImplemented(
'The scale-axes parameter is not yet supported.'
)
if request.scale_size:
# TODO implement scale-size
raise ApiError.NotImplemented(
'The scale-size parameter is not yet supported.'
)
target_gm = target_gm.transform(final_crs).to_regular()

if target_gm is not None:
scaling = CoverageScaling(request, final_crs, ds)
target_gm = scaling.apply(target_gm)

if target_gm is not source_gm:
ds = resample_in_space(ds, source_gm=source_gm, target_gm=target_gm)

# In this case, the transformed native CRS bbox[es] may have been
Expand Down Expand Up @@ -421,18 +411,6 @@ def _ensure_bbox_y_ascending(bbox: list, xy_order: bool = True):
bbox[y0], bbox[y1] = bbox[y1], bbox[y0]


def get_h_dim(ds: xr.Dataset):
return [
d for d in list(map(str, ds.dims)) if d[:3].lower() in {'x', 'lon'}
][0]


def get_v_dim(ds: xr.Dataset):
return [
d for d in list(map(str, ds.dims)) if d[:3].lower() in {'y', 'lat'}
][0]


def _correct_inverted_y_range_if_necessary(
ds: xr.Dataset, axis: str, range_: tuple[float, float]
) -> tuple[float, float]:
Expand Down
9 changes: 8 additions & 1 deletion xcube/webapi/ows/coverages/scaling.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
import pyproj
import xarray as xr

from xcube.core.gridmapping import GridMapping
from xcube.server.api import ApiError
from xcube.webapi.ows.coverages.controllers import get_h_dim, get_v_dim
from xcube.webapi.ows.coverages.util import get_h_dim, get_v_dim
from xcube.webapi.ows.coverages.request import CoverageRequest


Expand Down Expand Up @@ -120,3 +121,9 @@ def get_axis_from_crs(self, valid_identifiers: set[str]):
if identifiers & valid_identifiers:
return axis.abbrev
return None

def apply(self, gm: GridMapping):
if self.scale == (1, 1):
return gm
else:
return gm.scale((1 / self.scale[0], 1 / self.scale[1]))
35 changes: 35 additions & 0 deletions xcube/webapi/ows/coverages/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# The MIT License (MIT)
# Copyright (c) 2023 by the xcube team and contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.


import xarray as xr


def get_h_dim(ds: xr.Dataset):
return [
d for d in list(map(str, ds.dims)) if d[:3].lower() in {'x', 'lon'}
][0]


def get_v_dim(ds: xr.Dataset):
return [
d for d in list(map(str, ds.dims)) if d[:3].lower() in {'y', 'lat'}
][0]

0 comments on commit 597f8ae

Please sign in to comment.