From a5539d1ce12532b4e73e63b11fcd1017c119535a Mon Sep 17 00:00:00 2001 From: Florian de Boissieu <fdeboiss@gmail.com> Date: Wed, 15 May 2024 18:49:19 +0200 Subject: [PATCH 1/8] fix issue of drop_non_raster with no proj:bbox: now looking for proj: or raster: properties --- simplestac/utils.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/simplestac/utils.py b/simplestac/utils.py index 410094e..20b9612 100644 --- a/simplestac/utils.py +++ b/simplestac/utils.py @@ -909,9 +909,10 @@ def apply_item(x, fun, name, output_dir, overwrite=False, x.add_asset(key=n, asset=asset) return x -def drop_assets_without_proj(item, inplace=False): +def drop_assets_without_proj(item, inplace=False, pattern="^proj:|^raster:"): """ - Drops assets from the given item that do not have the "proj:bbox" field in their extra_fields. + Drops assets from the given item that do not have + extra_fields with "proj:" or "raster:" prefix. Parameters: item (Item): The item from which to drop assets. @@ -922,7 +923,12 @@ def drop_assets_without_proj(item, inplace=False): """ if not inplace: item = item.clone() - item.assets = {k:v for k,v in item.assets.items() if "proj:bbox" in v.extra_fields} + + item.assets = {k:v for k,v in item.assets.items() if any([bool(re.search(pattern, p)) for p in v.extra_fields])} + + if len(item.assets) == 0: + logger.warning(f"Item {item.id} has no raster assets.") + return item ####################################### -- GitLab From 6b02f7c8d4eee51a8123333bd39d199f27d39762 Mon Sep 17 00:00:00 2001 From: Florian de Boissieu <fdeboiss@gmail.com> Date: Wed, 15 May 2024 18:51:44 +0200 Subject: [PATCH 2/8] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a17711..75902ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# v1.1.2 +- fix issue of drop_non_raster with no proj:bbox: now looking for any "proj:" or "raster:" properties + # v1.1.1 ## Add -- GitLab From 15959da85746d48e8e03f2969ae5fb673dbee16b Mon Sep 17 00:00:00 2001 From: Florian de Boissieu <fdeboiss@gmail.com> Date: Wed, 15 May 2024 19:19:37 +0200 Subject: [PATCH 3/8] add support for recursive item finding in build_item_collection --- simplestac/local.py | 46 +++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/simplestac/local.py b/simplestac/local.py index 40576eb..baa8f4c 100644 --- a/simplestac/local.py +++ b/simplestac/local.py @@ -640,7 +640,35 @@ class MyStacItem(object): return item +def get_item_dirs(input_dir, fmt): + """ + Recursively retrieves item directories based on the input directory and format. + Parameters + ---------- + input_dir : str + The input directory to search for item directories. + fmt : dict + The format containing the pattern for item directories. + See `collection_format`. + Returns + ------- + list + A list of item directories found based on the format. + """ + item_dirs = [] + if isinstance(input_dir, list): + for d in input_dir: + item_dirs.extend(get_item_dirs(d, fmt)) + return item_dirs + + input_dir = Path(input_dir).expand() + if re.match(fmt["item"]["pattern"], input_dir.name): + item_dirs.append(input_dir) + else: + item_dirs = get_item_dirs(input_dir.dirs(), fmt) + return item_dirs + def build_item_collection(input_dir, fmt, item_parser=stac_item_parser, @@ -680,19 +708,13 @@ def build_item_collection(input_dir, >>> col = build_item_collection(input_dir, fmt) """ from simplestac.utils import ItemCollection # avoids circular import - - if isinstance(input_dir, list): - items = [] - for d in input_dir: - col = build_item_collection(d, fmt, **kwargs) - items.extend(col.items) - return ItemCollection(items, clone_items=False, **kwargs) - input_dir = Path(input_dir).expand() - if re.match(fmt["item"]["pattern"], input_dir.name): - item_dirs = [input_dir] - else: - item_dirs = [d for d in input_dir.dirs() if re.match(fmt["item"]["pattern"], d.name)] + item_dirs = get_item_dirs(input_dir, fmt) + + if len(item_dirs) == 0: + logger.warning(f"No item found in {input_dir}") + return + items = [] logger.info("Building item collection...") item_creator = MyStacItem(fmt, item_parser=item_parser, asset_parser=asset_parser) -- GitLab From 111884c730b9e9ded16810e7ffef7b9bcae02d69 Mon Sep 17 00:00:00 2001 From: Florian de Boissieu <fdeboiss@gmail.com> Date: Wed, 15 May 2024 20:04:11 +0200 Subject: [PATCH 4/8] add parameter pattern to drop_non_raster and drop_asset_without_proj --- simplestac/utils.py | 62 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/simplestac/utils.py b/simplestac/utils.py index 20b9612..1258ec9 100644 --- a/simplestac/utils.py +++ b/simplestac/utils.py @@ -62,11 +62,15 @@ S2_SEN2COR_BANDS = [f"B{i+1:02}" for i in range(12)]+["B8A"] class ExtendPystacClasses: """Add capacities to_xarray and filter to pystac Catalog, Collection, ItemCollection""" - def drop_non_raster(self, inplace=False): - """Drop non raster assets from each item in the collection. + def drop_non_raster(self, pattern="^proj:|^raster:", inplace=False): + """Drop non raster assets from each item in the collection, + based on pattern searched in asset extra_fields. Parameters ---------- + pattern : str + The pattern to search for in asset extra_fields keys. + Defaults to "^proj:|^raster:". inplace : bool Whether to modify the collection in place. Defaults to False. @@ -81,7 +85,7 @@ class ExtendPystacClasses: x = self.clone() for item in x.items: - drop_assets_without_proj(item, inplace=True) + drop_assets_without_proj(item, pattern=pattern, inplace=True) if not inplace: return x @@ -89,9 +93,31 @@ class ExtendPystacClasses: def to_xarray(self, xy_coords="center", bbox=None, geometry=None, gdal_env=DEFAULT_GDAL_ENV, **kwargs): """Returns a DASK xarray() - This is a proxy to stackstac.stac + This is a proxy to stackstac.stack + + Parameters + ---------- + xy_coords : str + Argument passed to stackstac.stack. Defaults to "center". + bbox : tuple, list + A bounding box to clip the xarray to, in the format (xmin, ymin, xmax, ymax). + geometry : shapely.geometry | geopandas.GeoSeries | geopandas.GeoDataFrame + A geometry to clip the xarray to. + gdal_env : stackstac.rio_reader.LayeredEnv + See stackstac.rio_reader.LayeredEnv. The default is DEFAULT_GDAL_ENV, + which is the same as stackstac.rio_reader.DEFAULT_GDAL_ENV with the addition + of GDAL_HTTP_MAX_RETRY=5 and GDAL_HTTP_RETRY_DELAY=1. + kwargs: dict + Additional keyword arguments passed to stackstac.stack. - Arguments are: + Returns + ------- + dask.DataArray + + + Notes + ----- + The parameters available in stackstac.stack: assets=frozenset({'image/jp2', 'image/tiff', 'image/vnd.stac.geotiff', 'image/x.geotiff'}), epsg=None, resolution=None, bounds=None, bounds_latlon=None, snap_bounds=True, resampling=Resampling.nearest, chunksize=1024, @@ -101,12 +127,10 @@ class ExtendPystacClasses: errors_as_nodata=(RasterioIOError('HTTP response code: 404'), ), reader=<class 'stackstac.rio_reader.AutoParallelRioReader'> - For details, see [stackstac.stac](https://stackstac.readthedocs.io/en/latest/api/main/stackstac.stack.html) + For details, see [stackstac.stack](https://stackstac.readthedocs.io/en/latest/api/main/stackstac.stack.html). - Notes: - ------ Here, xy_coords="center" is the default to be consistent with rioxarray, - cf https://github.com/gjoseph92/stackstac/issues/207. Otherwise, stackstac.stac has + cf https://github.com/gjoseph92/stackstac/issues/207. Otherwise, stackstac.stack has xy_coords="topleft" as the default. Also, by default it is sorted by ascending datetime, see sortby_date. @@ -909,17 +933,25 @@ def apply_item(x, fun, name, output_dir, overwrite=False, x.add_asset(key=n, asset=asset) return x -def drop_assets_without_proj(item, inplace=False, pattern="^proj:|^raster:"): +def drop_assets_without_proj(item, pattern="^proj:|^raster:", inplace=False): """ Drops assets from the given item that do not have extra_fields with "proj:" or "raster:" prefix. - Parameters: - item (Item): The item from which to drop assets. - inplace (bool, optional): If True, the assets will be dropped in place. Otherwise, a clone of the item will be created and modified. + Parameters + ---------- + item: pystac.Item + The item from which to drop assets. + pattern: str, optional. + The pattern to search for in asset extra_fields keys. + inplace: bool, optional + If True, the assets will be dropped in place. + Otherwise, a clone of the item will be created and modified. - Returns: - Item: The modified item with the dropped assets. + Returns + ------ + pystac.Item + The modified item with the dropped assets. """ if not inplace: item = item.clone() -- GitLab From e51da4d576a8e9aac1c7bcd7c119351c9f10bab9 Mon Sep 17 00:00:00 2001 From: Florian de Boissieu <fdeboiss@gmail.com> Date: Wed, 15 May 2024 21:51:41 +0200 Subject: [PATCH 5/8] add tests and update changelog --- CHANGELOG.md | 9 +++++- tests/conftest.py | 24 +++++++++++++--- tests/test_remote.py | 68 ++++++++++++++++---------------------------- 3 files changed, 52 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75902ba..d1250eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # v1.1.2 -- fix issue of drop_non_raster with no proj:bbox: now looking for any "proj:" or "raster:" properties + +## Add +- parameter `pattern` to `ItemCollection.drop_non_raster` and `drop_asset_without_proj` +- support for recursive item search in `build_item_collection` + +## Fix +- fix issue of drop_non_raster with no proj:bbox: now looking for any "proj:" or "raster:" properties. + A parameter `pattern` was added to # v1.1.1 diff --git a/tests/conftest.py b/tests/conftest.py index 39b8819..30c7dd1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,10 @@ -import pystac -import pytest -from path import Path import geopandas as gpd import logging +from path import Path +import planetary_computer as pc +import pystac_client +import pytest + logging.basicConfig(level=logging.INFO) here = Path(__file__).parent @@ -28,4 +30,18 @@ def roi(roi_file): @pytest.fixture(scope="session") def s2scene_pc_dir(): scene_dir = here / "data" / "s2_scenes_pc" - yield scene_dir \ No newline at end of file + yield scene_dir + +@pytest.fixture(scope="session") +def pc_col(roi): + URL = "https://planetarycomputer.microsoft.com/api/stac/v1" + time_range = "2022-01-20/2022-01-31" + catalog = pystac_client.Client.open(URL, modifier=pc.sign_inplace) + search = catalog.search( + collections=["sentinel-2-l2a"], + bbox=roi.to_crs(4326).total_bounds, + datetime=time_range, + sortby="datetime", + ) + col = search.item_collection() + yield col \ No newline at end of file diff --git a/tests/test_remote.py b/tests/test_remote.py index 91153a3..8a6c045 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -6,31 +6,13 @@ import numpy as np URL = "https://planetarycomputer.microsoft.com/api/stac/v1" -def test_to_xarray(roi, s2scene_pc_dir): - time_range = "2022-01-01/2022-01-31" - - catalog = pystac_client.Client.open(URL, modifier=pc.sign_inplace) - search = catalog.search( - collections=["sentinel-2-l2a"], - bbox=roi.to_crs(4326).total_bounds, - datetime=time_range, - sortby="datetime", - ) - col = ItemCollection(search.item_collection()) +def test_to_xarray(pc_col, roi): + col = ItemCollection(pc_col) x = col.drop_non_raster().to_xarray() assert len(x.time) == len(col) -def test_offset_harmonization(roi, s2scene_pc_dir): - time_range = "2022-01-20/2022-01-31" - - catalog = pystac_client.Client.open(URL, modifier=pc.sign_inplace) - search = catalog.search( - collections=["sentinel-2-l2a"], - bbox=roi.to_crs(4326).total_bounds, - datetime=time_range, - sortby="datetime", - ) - col = search.item_collection() +def test_offset_harmonization(pc_col): + col = ItemCollection(pc_col) harmonize_sen2cor_offset(col, inplace=True) of0 = col[0].assets["B02"].extra_fields["raster:bands"][0]["offset"] ofN = col[-1].assets["B02"].extra_fields["raster:bands"][0]["offset"] @@ -38,34 +20,31 @@ def test_offset_harmonization(roi, s2scene_pc_dir): assert of0 == 0 assert ofN == -1000 -def test_drop_duplicates(roi, s2scene_pc_dir): - time_range = "2022-01-20/2022-01-31" - catalog = pystac_client.Client.open(URL, modifier=pc.sign_inplace) - search = catalog.search( - collections=["sentinel-2-l2a"], - bbox=roi.to_crs(4326).total_bounds, - datetime=time_range, - sortby="datetime", - ) - col = search.item_collection() +def test_drop_duplicates(pc_col): + col = ItemCollection(pc_col) col1 = ItemCollection(col.clone()+col.clone()) + assert len(col1) == 2*len(col) col1.drop_duplicates(inplace=True) assert len(col1) == len(col) -def test_write_assets(roi, s2scene_pc_dir): +def test_drop_non_raster(pc_col): + col = ItemCollection(pc_col) + col1 = col.drop_non_raster() + assert "preview" in col[0].assets + assert "preview" not in col1[0].assets - s2scene_pc_dir.rmtree_p().mkdir_p() - time_range = "2016-01-01/2016-01-31" +def test_filter(pc_col): + col = ItemCollection(pc_col) + col1 = col.filter(assets="B02") + assert "B03" in col[0].assets + assert "B03" not in col1[0].assets - catalog = pystac_client.Client.open(URL) - search = catalog.search( - collections=["sentinel-2-l2a"], - bbox=roi.to_crs(4326).total_bounds, - datetime=time_range, - query={"eo:cloud_cover": {"lt": 80}}, - ) +def test_write_assets(pc_col, roi, s2scene_pc_dir): + + s2scene_pc_dir.rmtree_p().mkdir_p() - col = ItemCollection(search.item_collection()).drop_non_raster() + col = ItemCollection(pc_col) + col.drop_non_raster(inplace=True) bbox = roi.to_crs(col.to_xarray().rio.crs).total_bounds encoding=dict( dtype="int16", @@ -86,6 +65,7 @@ def test_write_assets(roi, s2scene_pc_dir): new_col2 = write_assets(col, tempdir, geometry=roi.buffer(5), encoding=encoding, modifier=pc.sign_inplace) assert len(new_col2) == len(new_col) assert new_col2[0].bbox == new_col[0].bbox - + + -- GitLab From df915b3afb0268362fdbfb409b64234fd4c086f9 Mon Sep 17 00:00:00 2001 From: Florian de Boissieu <fdeboiss@gmail.com> Date: Thu, 16 May 2024 01:50:36 +0200 Subject: [PATCH 6/8] add cllection_ready parameter to apply_items --- simplestac/utils.py | 41 ++++++++++++++++++++++++++++++++++------- tests/test_local.py | 43 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/simplestac/utils.py b/simplestac/utils.py index 1258ec9..e6427db 100644 --- a/simplestac/utils.py +++ b/simplestac/utils.py @@ -364,6 +364,7 @@ class ExtendPystacClasses: def apply_items(self, fun, name, output_dir, + collection_ready=False, overwrite=False, inplace=False, datetime=None, @@ -386,6 +387,9 @@ class ExtendPystacClasses: This also serves as the file name suffix: "{item.id}_{name}.tif" output_dir : str The directory where the output will be saved. Created if it does not exist. + collection_ready : bool, optional + If True, the assets directory will be `output_dir / item.id`, ready for a pystac.Collection. + Defaults to False. overwrite : bool, optional Whether to overwrite existing files. Defaults to False. inplace : bool, optional @@ -421,16 +425,18 @@ class ExtendPystacClasses: for item in tqdm(x.items, disable=not progress): apply_item(item, fun, name=name, output_dir=output_dir, - overwrite=overwrite, copy=False, - bbox=bbox, geometry=geometry, - writer_args=writer_args, - **kwargs) + collection_ready=collection_ready, + overwrite=overwrite, copy=False, + bbox=bbox, geometry=geometry, + writer_args=writer_args, + **kwargs) if not inplace: return x def apply_rolling(self, fun, name, output_dir, + collection_ready=False, overwrite=False, window=2, inplace=False, @@ -455,6 +461,8 @@ class ExtendPystacClasses: This also serves as the file name suffix: "{item.id}_{name}.tif" output_dir : str The directory where the output will be saved. Created if it does not exist. + collection_ready : bool, optional + If True, the assets directory will be `output_dir / item.id`, ready for a pystac.Collection. overwrite : bool, optional Whether to overwrite existing files. Defaults to False. inplace : bool, optional @@ -512,15 +520,24 @@ class ExtendPystacClasses: raise ValueError("Argument `writer_args` must have length 1 or the same length as `name`.") Nout = len(name) - output_dir = [Path(d).expand().mkdir_p() for d in output_dir] # make sure they exist + for i in tqdm(range(len(x.items)), disable=not progress): subitems = x.items[max((i-window+1),0):i+1] + item_id = x.items[i].id + if center: + if window%2 == 0: + raise ValueError("window must be odd if center=True") subitems = x.items[max(i-window//2,0):i+(window-1)//2+1] subcol = self.__class__(subitems, clone_items=False) - raster_file = [d / f"{subitems[-1].id}_{n}.tif" for n, d in zip(name, output_dir)] + raster_dir = output_dir + if collection_ready: + raster_dir = [(d / item_id) for d in output_dir] + raster_file = [d / f"{item_id}_{n}.tif" for n, d in zip(name, raster_dir)] + + if not overwrite and all([r.exists() for r in raster_file]): logger.debug(f"File already exists, skipping computation: {raster_file}") res = tuple([None]*Nout) @@ -528,6 +545,8 @@ class ExtendPystacClasses: # compute fun with xr.set_options(keep_attrs=True): res = fun(subcol.to_xarray(bbox=bbox, geometry=geometry), **kwargs) + if res is None: + continue if not isinstance(res, tuple): res = (res,) if len(res) != Nout: @@ -538,6 +557,7 @@ class ExtendPystacClasses: # write result logger.debug("Writing: ", f) r.name = n + f.parent.mkdir_p() write_raster(r, f, overwrite=overwrite, **wa) for n, f in zip(name, raster_file): @@ -801,7 +821,7 @@ def update_item_properties(x: pystac.Item, remove_item_props=DEFAULT_REMOVE_PROP for k in pop_props: x.properties.pop(k) -def apply_item(x, fun, name, output_dir, overwrite=False, +def apply_item(x, fun, name, output_dir, collection_ready=False, overwrite=False, copy=True, bbox=None, geometry=None, writer_args=None, **kwargs): """ Applies a function to an item in a collection, @@ -818,6 +838,9 @@ def apply_item(x, fun, name, output_dir, overwrite=False, The name or names of the output raster file(s). output_dir : str or list of str The directory or directories to save the output raster file(s) to. + collection_ready : bool, optional + If True, the assets directory will be `output_dir / item.id`, ready for a pystac.Collection. + Defaults to `False`. overwrite : bool, optional Whether to overwrite existing raster files. Defaults to `False`. copy : bool, optional @@ -895,6 +918,10 @@ def apply_item(x, fun, name, output_dir, overwrite=False, Nout = len(name) output_dir = [Path(d).expand().mkdir_p() for d in output_dir] + # add item id level: output_dir / item.id + if collection_ready: + output_dir = [(d / x.id).mkdir_p() for d in output_dir] + raster_file = [d / f"{x.id}_{n}.tif" for n, d in zip(name, output_dir)] if not overwrite and all([r.exists() for r in raster_file]): logger.debug(f"File already exists, skipping computation: {raster_file}") diff --git a/tests/test_local.py b/tests/test_local.py index 49e7263..df75cc7 100644 --- a/tests/test_local.py +++ b/tests/test_local.py @@ -45,20 +45,36 @@ def test_datetime(s2scene_dir): def test_apply_items(s2scene_dir, roi): col = build_item_collection(s2scene_dir, collection_format()) - NDVI_dir = s2scene_dir.parent / "NDVI" - NDVI_dir.rmtree_p() + output_dir = s2scene_dir.parent / "NDVI" + output_dir.rmtree_p() col.apply_items( apply_formula, name="NDVI", geometry=roi.geometry, formula="((B08-B04)/(B08+B04))", - output_dir=NDVI_dir, + output_dir=output_dir, inplace=True) assert "NDVI" in col.items[-1].assets - assert len(NDVI_dir.files()) == len(col) + assert len(output_dir.files()) == len(col) # check if COG assert col.items[-1].assets["NDVI"].media_type == pystac.MediaType.COG + # with collection_ready + output_dir = s2scene_dir.parent / "S2-SSI" + output_dir.rmtree_p() + col.apply_items( + apply_formula, + name="NDVI", + geometry=roi.geometry, + formula="((B08-B04)/(B08+B04))", + output_dir=output_dir, + collection_ready=True, + inplace=True) + assert "NDVI" in col.items[-1].assets + assert len(output_dir.dirs()) == len(col) + + + def test_apply_rolling(s2scene_dir): col = build_item_collection(s2scene_dir, collection_format()) output_dir = s2scene_dir.parent / "B07_diff" @@ -76,7 +92,24 @@ def test_apply_rolling(s2scene_dir): assert "B07_diff" in col.items[-1].assets assert len(output_dir.files()) == (len(col)-1) - + # with collection_ready and multiple outputs + def band_diff(x, bands=["B07", "B08"]): + if len(x.time) > 1: + res = x.sel(band=bands).diff("time") + return tuple([res.sel(band=b) for b in bands]) + output_dir = s2scene_dir.parent / "S2-diff" + output_dir.rmtree_p() + col.apply_rolling( + band_diff, + name=["B07_diff", "B08_diff"], + output_dir=output_dir, + collection_ready=True, + inplace=True, + window=2) + assert "B07_diff" in col.items[-1].assets + assert len(output_dir.dirs()) == (len(col)-1) + assert len(list(output_dir.walkfiles())) == (len(col)-1)*2 + def test_apply_items_raster_args(s2scene_dir, roi): col = build_item_collection(s2scene_dir, collection_format()) -- GitLab From 2578a5b265aab78f2f5b2283eda0607c912d9dc8 Mon Sep 17 00:00:00 2001 From: Florian de Boissieu <fdeboiss@gmail.com> Date: Thu, 16 May 2024 01:55:47 +0200 Subject: [PATCH 7/8] add example to apply_formula --- simplestac/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplestac/utils.py b/simplestac/utils.py index e6427db..d53f9ca 100644 --- a/simplestac/utils.py +++ b/simplestac/utils.py @@ -1049,7 +1049,7 @@ def apply_formula(x, formula): x : xarray.DataArray It should have a 'band' dimension with the names that will be used by formula. formula : str - Formula, e.g. "B02>700", "CLM > 0", "SLC in [4,5]", "(B08-B06)/(B08+B06)" + Formula, e.g. "B02 > 600", "CLM > 0", "B02 > 600 | ~SLC in [4,5]", "(B08-B06)/(B08+B06)" Returns ------- -- GitLab From e3cac735536844a71ff860e938e97c2e2847c122 Mon Sep 17 00:00:00 2001 From: Florian de Boissieu <fdeboiss@gmail.com> Date: Thu, 16 May 2024 01:58:41 +0200 Subject: [PATCH 8/8] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1250eb..9c1f1bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Add - parameter `pattern` to `ItemCollection.drop_non_raster` and `drop_asset_without_proj` - support for recursive item search in `build_item_collection` +- parameter `collection_ready` to `apply_item`, `apply_items`, `apply_rolling` ## Fix - fix issue of drop_non_raster with no proj:bbox: now looking for any "proj:" or "raster:" properties. -- GitLab