Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wms default time #676

Merged
merged 8 commits into from
Jul 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions datacube_ows/ows_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ def cache_headers(self, n_datasets):

TIMERES_VALS = [TIMERES_RAW, TIMERES_DAY, TIMERES_MON, TIMERES_YR]

DEF_TIME_LATEST = "latest"
DEF_TIME_EARLIEST = "earliest"


class OWSNamedLayer(OWSExtensibleConfigEntry, OWSLayer):
INDEX_KEYS = ["layer"]
Expand Down Expand Up @@ -378,6 +381,14 @@ def __init__(self, cfg, global_cfg, parent_layer=None, **kwargs):
if self.time_resolution not in TIMERES_VALS:
raise ConfigException(
"Invalid time resolution value %s in named layer %s" % (self.time_resolution, self.name))
self.default_time_rule = cfg.get("default_time", DEF_TIME_LATEST)
if self.default_time_rule not in (DEF_TIME_LATEST, DEF_TIME_EARLIEST):
try:
self.default_time_rule = datetime.date.fromisoformat(self.default_time_rule)
except ValueError:
raise ConfigException(
f"Invalid default_time value in named layer {self.name} ({self.default_time_rule})"
)
self.time_axis = cfg.get("time_axis")
if self.time_axis:
self.regular_time_axis = True
Expand Down Expand Up @@ -412,6 +423,7 @@ def __init__(self, cfg, global_cfg, parent_layer=None, **kwargs):

self.dynamic = cfg.get("dynamic", False)

self.declare_unready("default_time")
self.declare_unready("_ranges")
self.declare_unready("bboxes")
# TODO: sub-ranges
Expand Down Expand Up @@ -763,6 +775,21 @@ def force_range_update(self, ext_dc=None):
if self._ranges is None:
raise Exception("Null product range")
self.bboxes = self.extract_bboxes()
if self.default_time_rule == DEF_TIME_EARLIEST:
self.default_time = self._ranges["start_time"]
elif isinstance(self.default_time_rule,
datetime.date) and self.default_time_rule in self._ranges["time_set"]:
self.default_time = self.default_time_rule
elif isinstance(self.default_time_rule, datetime.date):
_LOG.warning("default_time for named_layer %s is explicit date (%s) that is "
" not available for the layer. Using most recent available date instead.",
self.name,
self.default_time_rule.isoformat()
)
self.default_time = self._ranges["end_time"]
else:
self.default_time = self._ranges["end_time"]

# pylint: disable=broad-except
except Exception as a:
if not self.global_cfg.called_from_update_ranges:
Expand Down
4 changes: 2 additions & 2 deletions datacube_ows/styles/hybrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self, product, style_cfg, defer_multi_date=False, stand_alone=False
def transform_single_date_data(self, data):
#pylint: disable=too-many-locals
if self.index_function is not None:
data['index_function'] = (data.dims, self.index_function(data))
data['index_function'] = (data.dims, self.index_function(data).data)

imgdata = Dataset(coords=data)

Expand All @@ -47,7 +47,7 @@ def transform_single_date_data(self, data):
+ self.component_ratio * component_band_data)
else:
img_band_data = rampdata * 255.0
imgdata[band] = (d.dims, img_band_data.astype("uint8"))
imgdata[band] = (d.dims, img_band_data.astype("uint8").data)

return imgdata

Expand Down
2 changes: 1 addition & 1 deletion datacube_ows/styles/ramp.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ def __init__(self, product, style_cfg, stand_alone=False, defer_multi_date=False

def apply_index(self, data):
index_data = self.index_function(data)
data['index_function'] = (index_data.dims, index_data)
data['index_function'] = (index_data.dims, index_data.data)
return data["index_function"]

def transform_single_date_data(self, data):
Expand Down
4 changes: 2 additions & 2 deletions datacube_ows/templates/wms_capabilities.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@
/>
{% endfor %}
{% if lyr.regular_time_axis %}
<Dimension name="time" units="ISO8601" default="{{ lyr_ranges.times[-1].isoformat() }}">{{ lyr.time_axis_representation() }}</Dimension>
<Dimension name="time" units="ISO8601" default="{{ lyr.default_time.isoformat() }}">{{ lyr.time_axis_representation() }}</Dimension>
{% elif lyr_ranges.times|length > 1 %}
<Dimension name="time" units="ISO8601" default="{{ lyr_ranges.times[-1].isoformat() }}">
<Dimension name="time" units="ISO8601" default="{{ lyr.default_time.isoformat() }}">
{% for t in lyr_ranges.times %}{{ t }}{% if not loop.last %},{% endif %}{% endfor %}
</Dimension>
{% endif %}
Expand Down
5 changes: 3 additions & 2 deletions datacube_ows/wcs1_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ def __init__(self, args):
# self.times = [parse(self.product.wcs_sole_time).date()]
if "time" not in args:
# CEOS treats no supplied time argument as all time.
# I'm really not sure what the right thing to do is, but QGIS wants us to do SOMETHING - treat it as "now"
self.times = [self.product.ranges["times"][-1]]
# I'm really not sure what the right thing to do is, but QGIS wants us to do SOMETHING - use configured
# default.
self.times = [self.product.default_time]
else:
# TODO: the min/max/res format option?
# It's a bit underspeced. I'm not sure what the "res" would look like.
Expand Down
23 changes: 23 additions & 0 deletions docs/cfg_layers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,29 @@ Note that it will usually be necessary to rerun `datacube-ows-update
<https://datacube-ows.readthedocs.io/en/latest/database.html#updating-range-tables-for-individual-layers>`_
for the layer after changing the time resolution.

-------------------------------------
WMS Default Time Value (default_time)
-------------------------------------

Specifies which time value to use by default if not specified in request. Applies to WMS, WMTS and WCS1.

Optional (default = "latest")

Allowed values:

1. "latest" (the default). Use most recent available date.
2. "earliest". Use earliest available date.
3. ISO Format date (e.g. "2021-05-26"). If the specified date is not available, a warning is raised and the latest
available date is used instead.

E.g.

::

"default_time": "latest",
# "default_time": "earliest",
# "default_time": "2020-07-25",

-----------------------------
Regular Time Axis (time_axis)
-----------------------------
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ def minimal_multiprod_cfg():

@pytest.fixture
def mock_range():
times = [datetime.datetime(2010, 1, 1), datetime.datetime(2010, 1, 2)]
times = [datetime.date(2010, 1, 1), datetime.date(2010, 1, 2), datetime.date(2010, 1, 3)]
return {
"lat": {
"min": -0.1,
Expand Down
72 changes: 72 additions & 0 deletions tests/test_cfg_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ def test_minimal_named_layer(minimal_layer_cfg, minimal_global_cfg, minimal_dc,
lyr.make_ready(minimal_dc)
assert lyr.ready
assert not lyr.hide
assert lyr.default_time == mock_range["times"][-1]
assert "a_layer" in str(lyr)
assert len(lyr.low_res_products) == 0

Expand Down Expand Up @@ -652,3 +653,74 @@ def test_time_axis_errors(minimal_layer_cfg, minimal_global_cfg):
with pytest.raises(ConfigException) as e:
lyr = parse_ows_layer(minimal_layer_cfg, global_cfg=minimal_global_cfg)
assert "time_axis end_date must be greater than or equal to the start_date if both are provided" in str(e.value)


def test_earliest_default_time(minimal_layer_cfg, minimal_global_cfg, minimal_dc, mock_range):
minimal_layer_cfg["default_time"] = "earliest"
lyr = parse_ows_layer(minimal_layer_cfg,
global_cfg=minimal_global_cfg)
assert lyr.name == "a_layer"
assert not lyr.ready
with patch("datacube_ows.product_ranges.get_ranges") as get_rng:
get_rng.return_value = mock_range
lyr.make_ready(minimal_dc)
assert lyr.ready
assert not lyr.hide
assert lyr.default_time == mock_range["times"][0]
assert "a_layer" in str(lyr)
assert len(lyr.low_res_products) == 0

def test_latest_default_time(minimal_layer_cfg, minimal_global_cfg, minimal_dc, mock_range):
minimal_layer_cfg["default_time"] = "latest"
lyr = parse_ows_layer(minimal_layer_cfg,
global_cfg=minimal_global_cfg)
assert lyr.name == "a_layer"
assert not lyr.ready
with patch("datacube_ows.product_ranges.get_ranges") as get_rng:
get_rng.return_value = mock_range
lyr.make_ready(minimal_dc)
assert lyr.ready
assert not lyr.hide
assert lyr.default_time == mock_range["times"][-1]
assert "a_layer" in str(lyr)
assert len(lyr.low_res_products) == 0

def test_valid_default_time(minimal_layer_cfg, minimal_global_cfg, minimal_dc, mock_range):
minimal_layer_cfg["default_time"] = "2010-01-02"
lyr = parse_ows_layer(minimal_layer_cfg,
global_cfg=minimal_global_cfg)
assert lyr.name == "a_layer"
assert not lyr.ready
with patch("datacube_ows.product_ranges.get_ranges") as get_rng:
get_rng.return_value = mock_range
lyr.make_ready(minimal_dc)
assert lyr.ready
assert not lyr.hide
assert lyr.default_time == datetime.date(2010, 1, 2)
assert "a_layer" in str(lyr)
assert len(lyr.low_res_products) == 0

def test_missing_default_time(minimal_layer_cfg, minimal_global_cfg, minimal_dc, mock_range):
minimal_layer_cfg["default_time"] = "2020-01-22"
lyr = parse_ows_layer(minimal_layer_cfg,
global_cfg=minimal_global_cfg)
assert lyr.name == "a_layer"
assert not lyr.ready
with patch("datacube_ows.product_ranges.get_ranges") as get_rng:
get_rng.return_value = mock_range
lyr.make_ready(minimal_dc)
assert lyr.ready
assert not lyr.hide
assert lyr.default_time == mock_range["times"][-1]
assert "a_layer" in str(lyr)
assert len(lyr.low_res_products) == 0

def test_invalid_default_time(minimal_layer_cfg, minimal_global_cfg, minimal_dc, mock_range):
minimal_layer_cfg["default_time"] = "Not-a-date"
with pytest.raises(ConfigException) as e:
lyr = parse_ows_layer(minimal_layer_cfg,
global_cfg=minimal_global_cfg)
assert "a_layer" in str(e.value)
assert "Not-a-date" in str(e.value)
assert "Invalid default_time value" in str(e.value)