Skip to content

Commit a9d1509

Browse files
authored
v0.0.14
v0.0.14
2 parents 52192e4 + c7c77e3 commit a9d1509

File tree

8 files changed

+75
-108
lines changed

8 files changed

+75
-108
lines changed

CHANGELOG.md

+21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.0.14] - 2024-04-11
8+
9+
### Fixed
10+
11+
- `GeoSeries` supported in GeometryManager.
12+
- `ed.sel_nearest_dates` accessor avoid duplicated times.
13+
- Issue when managing multiple indices with accessor `xr.ed`.
14+
- Issue when same datetime when rescaling dataset.
15+
16+
### Added
17+
18+
- `mode` for zonal stats in `operations.reducers`.
19+
20+
### Changed
21+
22+
- `ag_cloud_mask_items` queries items per batch.
23+
24+
### Removed
25+
26+
- Typing decorator, expected new typing library.
27+
728
## [0.0.13] - 2024-03-06
829

930
### Fixed

earthdaily/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
# to hide warnings from rioxarray or nano seconds conversion
66
warnings.filterwarnings("ignore")
77

8-
__version__ = "0.0.13"
8+
__version__ = "0.0.14"

earthdaily/accessor/__init__.py

+31-94
Original file line numberDiff line numberDiff line change
@@ -12,86 +12,7 @@
1212
from xarray.core.extensions import AccessorRegistrationWarning
1313
from ..earthdatastore.cube_utils import GeometryManager
1414

15-
warnings.filterwarnings("ignore", category=AccessorRegistrationWarning)
16-
17-
18-
class MisType(Warning):
19-
pass
20-
21-
22-
def _typer(raise_mistype=False, custom_types={}):
23-
"""
24-
25-
Parameters
26-
----------
27-
raise_mistype : TYPE, optional
28-
DESCRIPTION. The default is False.
29-
custom_types : TYPE, optional
30-
DESCRIPTION. The default is {}.
31-
Example : {
32-
np.ndarray:{"func":np.asarray},
33-
xr.Dataset:{"func":xr.DataArray.to_dataset,"kwargs":{"dim":"band"}}
34-
}
35-
Raises
36-
------
37-
MisType
38-
DESCRIPTION.
39-
40-
Returns
41-
-------
42-
TYPE
43-
DESCRIPTION.
44-
45-
"""
46-
47-
def decorator(func):
48-
def force(*args, **kwargs):
49-
_args = list(args)
50-
for key, vals in func.__annotations__.items():
51-
if not isinstance(vals, (list, tuple)):
52-
vals = [vals]
53-
val = vals[0]
54-
idx = func.__code__.co_varnames.index(key)
55-
is_kwargs = key in kwargs.keys()
56-
if not is_kwargs and idx >= len(_args):
57-
break
58-
value = kwargs.get(key, None) if is_kwargs else args[idx]
59-
if type(value) in vals:
60-
continue
61-
if (
62-
type(kwargs.get(key)) not in vals
63-
if is_kwargs
64-
else type(args[idx]) not in vals
65-
):
66-
if raise_mistype:
67-
if is_kwargs:
68-
expected = f"{type(kwargs[key]).__name__} ({kwargs[key]})"
69-
else:
70-
expected = f"{type(args[idx]).__name__} ({args[idx]})"
71-
raise MisType(f"{key} expected {val.__name__}, not {expected}.")
72-
if any(val == k for k in custom_types.keys()):
73-
exp = custom_types[val]
74-
var = args[idx]
75-
res = exp["func"](var, **exp.get("kwargs", {}))
76-
if is_kwargs:
77-
kwargs[key] = res
78-
else:
79-
_args[idx] = res
80-
elif is_kwargs:
81-
kwargs[key] = (
82-
val(kwargs[key]) if val is not list else [kwargs[key]]
83-
)
84-
else:
85-
_args[idx] = val(args[idx]) if val is not list else [args[idx]]
86-
args = tuple(_args)
87-
return func(*args, **kwargs)
88-
89-
return force
90-
91-
return decorator
92-
93-
94-
@_typer()
15+
9516
def xr_loop_func(
9617
dataset: xr.Dataset,
9718
func,
@@ -122,7 +43,6 @@ def _xr_loop_func(dataset, metafunc, loop_dimension, **kwargs):
12243
)
12344

12445

125-
@_typer()
12646
def _lee_filter(img, window_size: int):
12747
img_ = img.copy()
12848
if isinstance(img, np.ndarray):
@@ -166,7 +86,6 @@ def clip(self, geom):
16686
def _max_time_wrap(self, wish=5, col="time"):
16787
return np.min((wish, self._obj[col].size))
16888

169-
@_typer()
17089
def plot_band(self, cmap="Greys", col="time", col_wrap=5, **kwargs):
17190
return self._obj.plot.imshow(
17291
cmap=cmap,
@@ -175,6 +94,29 @@ def plot_band(self, cmap="Greys", col="time", col_wrap=5, **kwargs):
17594
**kwargs,
17695
)
17796

97+
def whittaker(
98+
self,
99+
lmbd: float,
100+
weights: np.ndarray = None,
101+
a: float = 0.5,
102+
min_value: float = -np.inf,
103+
max_value: float = np.inf,
104+
max_iter: int = 10,
105+
time="time",
106+
):
107+
from . import whittaker
108+
109+
return whittaker.xr_wt(
110+
self._obj.to_dataset(name="index"),
111+
lmbd,
112+
time=time,
113+
weights=weights,
114+
a=a,
115+
min_value=min_value,
116+
max_value=max_value,
117+
max_iter=max_iter,
118+
)["index"]
119+
178120

179121
@xr.register_dataset_accessor("ed")
180122
class EarthDailyAccessorDataset:
@@ -187,7 +129,6 @@ def clip(self, geom):
187129
def _max_time_wrap(self, wish=5, col="time"):
188130
return np.min((wish, self._obj[col].size))
189131

190-
@_typer()
191132
def plot_rgb(
192133
self,
193134
red: str = "red",
@@ -205,7 +146,6 @@ def plot_rgb(
205146
)
206147
)
207148

208-
@_typer()
209149
def plot_band(self, band, cmap="Greys", col="time", col_wrap=5, **kwargs):
210150
return self._obj[band].plot.imshow(
211151
cmap=cmap,
@@ -214,7 +154,6 @@ def plot_band(self, band, cmap="Greys", col="time", col_wrap=5, **kwargs):
214154
**kwargs,
215155
)
216156

217-
@_typer()
218157
def lee_filter(self, window_size: int):
219158
return xr.apply_ufunc(
220159
_lee_filter,
@@ -225,7 +164,6 @@ def lee_filter(self, window_size: int):
225164
kwargs=dict(window_size=window_size),
226165
)
227166

228-
@_typer()
229167
def centroid(self, to_wkt: str = False, to_4326: bool = True):
230168
"""Return the geographic center point in 4326/WKT of this dataset."""
231169
# we can use a cache on our accessor objects, because accessors
@@ -288,7 +226,6 @@ def available_indices(self, details=False):
288226
available_indices.append(spyndex.indices[k] if details else k)
289227
return available_indices
290228

291-
@_typer()
292229
def add_indices(self, indices: list, **kwargs):
293230
"""
294231
Uses spyndex to compute and add index.
@@ -313,12 +250,11 @@ def add_indices(self, indices: list, **kwargs):
313250
idx = spyndex.computeIndex(index=indices, params=params, **kwargs)
314251

315252
if len(indices) == 1:
316-
idx = idx.expand_dims(indices=indices)
317-
idx = idx.to_dataset(dim="indices")
253+
idx = idx.expand_dims(index=indices)
254+
idx = idx.to_dataset(dim="index")
318255

319256
return xr.merge((self._obj, idx))
320257

321-
@_typer()
322258
def sel_nearest_dates(
323259
self,
324260
target: (xr.Dataset, xr.DataArray),
@@ -334,14 +270,14 @@ def sel_nearest_dates(
334270
for i, j in enumerate(pos)
335271
if j.days <= max_delta
336272
]
273+
pos = np.unique(pos)
337274
if return_target:
338275
method_convert = {"bfill": "ffill", "ffill": "bfill", "nearest": "nearest"}
339276
return self._obj.sel(time=pos), target.sel(
340277
time=pos, method=method_convert[method]
341278
)
342279
return self._obj.sel(time=pos)
343280

344-
@_typer()
345281
def whittaker(
346282
self,
347283
lmbd: float,
@@ -350,15 +286,16 @@ def whittaker(
350286
min_value: float = -np.inf,
351287
max_value: float = np.inf,
352288
max_iter: int = 10,
289+
time="time",
353290
):
354291
from . import whittaker
355292

356293
return whittaker.xr_wt(
357294
self._obj,
358295
lmbd,
359-
time="time",
360-
weights=None,
361-
a=0.5,
296+
time=time,
297+
weights=weights,
298+
a=a,
362299
min_value=min_value,
363300
max_value=max_value,
364301
max_iter=max_iter,

earthdaily/accessor/whittaker/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,4 @@ def xr_wt(
110110
output_core_dims=ocd,
111111
dask="allowed",
112112
)
113-
return xr.where(np.isnan(datacube_), datacube_, datacube)
113+
return xr.where(np.isnan(datacube_).all(dim=time), datacube_, datacube)

earthdaily/earthdatastore/cube_utils/_zonal.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import numpy as np
1010
import xarray as xr
1111
import tqdm
12-
from . import custom_operations
12+
13+
from . import custom_reducers
1314
from .preprocessing import rasterize
1415
from scipy.sparse import csr_matrix
1516

earthdaily/earthdatastore/cube_utils/asset_mapper/__update_asset_mapper_config_json.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
earthdaily.earthdatastore.cube_utils.asset_mapper.__asset_mapper_config_path
1414
)
1515
asset_mapper_config = (
16-
earthdaily.earthdatastore.cube_utils.asset_mapper.__asset_mapper_config
16+
earthdaily.earthdatastore.cube_utils.asset_mapper._asset_mapper_config
1717
)
1818

1919
for collection in eds.explore():
@@ -23,6 +23,7 @@
2323
print(f"collection {collection} has no items")
2424
continue
2525
for asset_name in assets_name:
26+
print(asset_name)
2627
if asset_mapper_config.get(collection) is None:
2728
asset_mapper_config[collection] = [{}]
2829
if asset_name not in asset_mapper_config[collection][0].values():

earthdaily/earthdatastore/cube_utils/custom_operations.py earthdaily/earthdatastore/cube_utils/custom_reducers.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import numpy as np
44

55

6-
class CustomOperations:
6+
class CustomReducers:
77
@staticmethod
88
def _np_mode(arr, **kwargs):
99
if isinstance(arr, list):
@@ -22,17 +22,17 @@ def _np_mode(arr, **kwargs):
2222
def mode(data_array_grouped, optional_arg=None):
2323
# Apply _xrmode to DataArrayGroupBy object
2424
result = data_array_grouped.reduce(
25-
CustomOperations._np_mode,
25+
CustomReducers._np_mode,
2626
list(dim for dim in data_array_grouped.dims if dim != "time"),
2727
)
2828
return result
2929

3030
@staticmethod
31-
def register_custom_operations():
31+
def register_custom_reducers():
3232
# register custom methods fo DataArrayGroupBy
33-
xr.core.groupby.DataArrayGroupBy.mode = CustomOperations.mode
34-
xr.core.groupby.DatasetGroupBy.mode = CustomOperations.mode
35-
np.mode = CustomOperations._np_mode
33+
xr.core.groupby.DataArrayGroupBy.mode = CustomReducers.mode
34+
xr.core.groupby.DatasetGroupBy.mode = CustomReducers.mode
35+
np.mode = CustomReducers._np_mode
3636

3737

38-
CustomOperations.register_custom_operations()
38+
CustomReducers.register_custom_reducers()

earthdaily/earthdatastore/cube_utils/geometry_manager.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ def __init__(self, geometry):
99
self.geometry = geometry
1010
self._obj = self.to_geopandas()
1111

12+
def __call__(self):
13+
return self._obj
14+
1215
def to_intersects(self, crs="EPSG:4326"):
1316
return json.loads(self._obj.to_crs(crs).dissolve().to_json(drop_id=True))[
1417
"features"
@@ -55,8 +58,12 @@ def _unknow_geometry_to_geodataframe(self, geometry):
5558
[geometry["coordinates"][0]]
5659
)
5760
return gpd.GeoDataFrame(geometry=[geom], crs="EPSG:4326")
58-
elif isinstance(geometry, gpd.GeoSeries):
59-
self.input_type = "GeoSeries"
60-
return gpd.GeoDataFrame(geometry=geometry, crs="EPSG:4326")
61+
elif isinstance(geometry, gpd.GeoSeries):
62+
self.input_type = "GeoSeries"
63+
64+
return gpd.GeoDataFrame(
65+
geometry=geometry,
66+
crs="EPSG:4326" if geometry.crs is None else geometry.crs,
67+
)
6168
else:
6269
raise NotImplementedError("Couldn't guess your geometry type")

0 commit comments

Comments
 (0)