Skip to content

Commit

Permalink
stacking mode implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
konstntokas committed Jul 29, 2024
1 parent b11304f commit 16ec6f4
Show file tree
Hide file tree
Showing 10 changed files with 1,393 additions and 881 deletions.
46 changes: 46 additions & 0 deletions examples/config_server_single_item_large_tile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
DatasetAttribution:
- "© by Brockmann Consult GmbH 2024"

DataStores:
- Identifier: stac
StoreId: stac
StoreParams:
url: https://s3.eu-central-1.wasabisys.com/stac/odse/catalog.json
Datasets:
- Identifier: EU cube
Path: lcv_blue_landsat.glad.ard/lcv_blue_landsat.glad.ard_1999.12.02..2000.03.20/lcv_blue_landsat.glad.ard_1999.12.02..2000.03.20.json
Style: EUCUBE

Styles:
- Identifier: EUCUBE
ColorMappings:
blue_p50:
ColorBar: "viridis"
ValueRange: [0., 30.]
blue_p25:
ColorBar: "viridis"
ValueRange: [0., 30.]
blue_p75:
ColorBar: "viridis"
ValueRange: [0., 30.]
qa_f:
ColorBar: "inferno"
ValueRange: [0., 50.]

ServiceProvider:
ProviderName: "Brockmann Consult GmbH"
ProviderSite: "https://www.brockmann-consult.de"
ServiceContact:
IndividualName: "Norman Fomferra"
PositionName: "Senior Software Engineer"
ContactInfo:
Phone:
Voice: "+49 4152 889 303"
Facsimile: "+49 4152 889 330"
Address:
DeliveryPoint: "HZG / GITZ"
City: "Geesthacht"
AdministrativeArea: "Herzogtum Lauenburg"
PostalCode: "21502"
Country: "Germany"
ElectronicMailAddress: "[email protected]"
62 changes: 62 additions & 0 deletions examples/config_server_stack_items.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
DatasetAttribution:
- "© by Brockmann Consult GmbH 2024"

DatasetChunkCacheSize: 100M

DataStores:
- Identifier: stac
StoreId: stac
StoreParams:
url: https://earth-search.aws.element84.com/v1
stack_mode: True
Datasets:
- Identifier: Sentinel-2 L2a
Path: sentinel-2-l2a
StoreOpenParams:
bbox: [9, 47, 10, 48]
time_range: ["2020-07-01", "2020-07-31"]
bands: ["red", "green", "blue"]
groupby: "time"
chunks: {"time": 1, "x": 2048, "y": 2048}
Style: S2L2A

Styles:
- Identifier: S2L2A
ColorMappings:
red:
ColorBar: "viridis"
ValueRange: [0., 3000.]
green:
ColorBar: "viridis"
ValueRange: [0., 3000.]
blue:
ColorBar: "viridis"
ValueRange: [0., 3000.]
rgb:
Red:
Variable: red
ValueRange: [0., 3000.]
Green:
Variable: green
ValueRange: [0., 3000.]
Blue:
Variable: blue
ValueRange: [0., 3000.]

ServiceProvider:
ProviderName: "Brockmann Consult GmbH"
ProviderSite: "https://www.brockmann-consult.de"
ServiceContact:
IndividualName: "Norman Fomferra"
PositionName: "Senior Software Engineer"
ContactInfo:
Phone:
Voice: "+49 4152 889 303"
Facsimile: "+49 4152 889 330"
Address:
DeliveryPoint: "HZG / GITZ"
City: "Geesthacht"
AdministrativeArea: "Herzogtum Lauenburg"
PostalCode: "21502"
Country: "Germany"
ElectronicMailAddress: "[email protected]"
22 changes: 22 additions & 0 deletions examples/stack_mode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from xcube.core.store import new_data_store
import matplotlib.pyplot as plt

store = new_data_store(
"stac", url="https://earth-search.aws.element84.com/v1", stack_mode=True
)
print(store.list_data_ids())
ds = store.open_data(
data_id="sentinel-2-l2a",
bbox=[9, 47, 10, 48],
time_range=["2020-07-01", "2020-07-05"],
bands=["red", "green", "blue"],
groupby="id",
crs="EPSG:4326",
chunks={"time": 1, "x": 2048, "y": 2048},
)
print(ds.num_levels)
ds = ds.get_dataset(2)
print(ds)
print(ds.time.values)
ds.red.isel(time=1).plot()
plt.show()

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions test/test_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import datetime
import itertools
import unittest
import urllib.request
Expand Down Expand Up @@ -133,7 +132,7 @@ def test_get_data_ids_data_type(self):
data_ids = store.get_data_ids(data_type="mldataset")
data_ids = list(itertools.islice(data_ids, 1))
self.assertEqual(1, len(data_ids))
item = store._access_item(data_ids[0])
item = store._impl.access_item(data_ids[0])
formats = _get_formats_from_item(item)
self.assertEqual(["geotiff"], formats)

Expand Down Expand Up @@ -665,19 +664,19 @@ def test_get_search_params_schema(self):
def test_access_item_failed(self):
store = new_data_store(DATA_STORE_ID, url=self.url_nonsearchable)
with self.assertRaises(requests.exceptions.HTTPError) as cm:
store._access_item(self.data_id_nonsearchable.replace("z", "s"))
store._impl.access_item(self.data_id_nonsearchable.replace("z", "s"))
self.assertIn("404 Client Error: Not Found for url", f"{cm.exception}")

@pytest.mark.vcr()
def test_get_s3_accessor(self):
store = new_data_store(DATA_STORE_ID, url=self.url_searchable)

opener = store._get_s3_accessor(root="datasets", storage_options={})
opener = store._impl._get_s3_accessor(root="datasets", storage_options={})
self.assertIsInstance(opener, S3DataAccessor)
self.assertEqual("datasets", opener.root)

with self.assertLogs("xcube.stac", level="DEBUG") as cm:
opener2 = store._get_s3_accessor(root="datasets2", storage_options={})
opener2 = store._impl._get_s3_accessor(root="datasets2", storage_options={})
self.assertIsInstance(opener2, S3DataAccessor)
self.assertEqual("datasets2", opener2.root)
self.assertEqual(1, len(cm.output))
Expand All @@ -691,12 +690,14 @@ def test_get_s3_accessor(self):
def test_get_https_accessor(self):
store = new_data_store(DATA_STORE_ID, url=self.url_searchable)

opener = store._get_https_accessor(root="earth-search.aws.element84.com")
opener = store._impl._get_https_accessor(root="earth-search.aws.element84.com")
self.assertIsInstance(opener, HttpsDataAccessor)
self.assertEqual("earth-search.aws.element84.com", opener.root)

with self.assertLogs("xcube.stac", level="DEBUG") as cm:
opener2 = store._get_https_accessor(root="planetarycomputer.microsoft.com")
opener2 = store._impl._get_https_accessor(
root="planetarycomputer.microsoft.com"
)
self.assertIsInstance(opener2, HttpsDataAccessor)
self.assertEqual("planetarycomputer.microsoft.com", opener2.root)
self.assertEqual(1, len(cm.output))
Expand Down
10 changes: 5 additions & 5 deletions test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
_convert_datetime2str,
_convert_str2datetime,
_do_bboxes_intersect,
_is_datetime_in_range,
_is_item_in_time_range,
_update_dict,
)

Expand Down Expand Up @@ -90,13 +90,13 @@ def test_is_datetime_in_range(self):
]

for time_start, time_end, fun in item1_test_paramss:
fun(_is_datetime_in_range(item1, time_range=[time_start, time_end]))
fun(_is_item_in_time_range(item1, time_range=[time_start, time_end]))

for time_start, time_end, fun in item2_test_paramss:
fun(_is_datetime_in_range(item2, time_range=[time_start, time_end]))
fun(_is_item_in_time_range(item2, time_range=[time_start, time_end]))

with self.assertRaises(DataStoreError) as cm:
_is_datetime_in_range(
_is_item_in_time_range(
item3, time_range=[item1_test_paramss[0][0], item1_test_paramss[0][1]]
)
self.assertEqual(
Expand All @@ -123,7 +123,7 @@ def test_do_bboxes_intersect(self):
]

for west, south, east, north, fun in item_test_paramss:
fun(_do_bboxes_intersect(item, bbox=[west, south, east, north]))
fun(_do_bboxes_intersect(item.bbox, bbox=[west, south, east, north]))

def test_update_nested_dict(self):
dic = dict(a=1, b=dict(c=3))
Expand Down
31 changes: 30 additions & 1 deletion xcube_stac/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
# SOFTWARE.

import logging
from typing import Union

from xcube.util.jsonschema import (
JsonArraySchema,
JsonComplexSchema,
JsonBooleanSchema,
JsonDateSchema,
JsonNumberSchema,
JsonStringSchema,
Expand All @@ -31,6 +34,7 @@

DATA_STORE_ID = "stac"
LOG = logging.getLogger("xcube.stac")
FloatInt = Union[float, int]

MAP_MIME_TYP_FORMAT = {
"application/netcdf": "netcdf",
Expand Down Expand Up @@ -64,7 +68,27 @@
"mldataset:levels:s3",
)

STAC_SEARCH_PARAMETERS = dict(
MLDATASET_FORMATS = ["levels", "geotiff"]

STAC_STORE_PARAMETERS = dict(
url=JsonStringSchema(title="URL to STAC catalog"),
stack_mode=JsonComplexSchema(
one_of=[
JsonStringSchema(
title="Backend for stacking STAC items",
description="So far, only 'odc-stac' is supported as a backend.",
const="odc-stac",
),
JsonBooleanSchema(
title="Decide if stacking of STAC items is applied",
description="If True, 'odc-stac' is used as a default backend.",
default=False,
),
],
),
)

STAC_SEARCH_PARAMETERS_STACK_MODE = dict(
time_range=JsonArraySchema(
items=[
JsonDateSchema(nullable=True),
Expand All @@ -86,6 +110,10 @@
),
title="Bounding box [x1,y1,x2,y2] in geographical coordinates",
),
)

STAC_SEARCH_PARAMETERS = dict(
**STAC_SEARCH_PARAMETERS_STACK_MODE,
collections=JsonArraySchema(
items=(JsonStringSchema(min_length=0)),
unique_items=True,
Expand All @@ -94,6 +122,7 @@
),
)


STAC_OPEN_PARAMETERS = dict(
asset_names=JsonArraySchema(
items=(JsonStringSchema(min_length=0)),
Expand Down
Loading

0 comments on commit 16ec6f4

Please sign in to comment.