def hyper_min_median_max(udf_data: UdfData): """Compute the min, median and max of the time dimension of a hyper cube Hypercubes with time dimensions are required. The min, median and max reduction of th time axis will be applied to all hypercube dimensions. Args: udf_data (UdfData): The UDF data object that contains raster and vector tiles as well as hypercubes and structured data. Returns: This function will not return anything, the UdfData object "udf_data" must be used to store the resulting data. """ # Iterate over each tile cube_list = [] for cube in udf_data.get_datacube_list(): min = cube.array.min(dim="t") median = cube.array.median(dim="t") max = cube.array.max(dim="t") min.name = cube.id + "_min" median.name = cube.id + "_median" max.name = cube.id + "_max" cube_list.append(XarrayDataCube(array=min)) cube_list.append(XarrayDataCube(array=median)) cube_list.append(XarrayDataCube(array=max)) udf_data.set_datacube_list(cube_list)
def apply_datacube(cube: XarrayDataCube, context: dict) -> XarrayDataCube: inarr = cube.get_array() B4 = inarr.loc[:, 'bandzero'] B8 = inarr.loc[:, 'bandone'] ndvi = (B8 - B4) / (B8 + B4) ndvi = ndvi.expand_dims(dim='bands', axis=-3).assign_coords(bands=['ndvi']) return XarrayDataCube(ndvi)
def apply_hypercube(cube: XarrayDataCube, context: dict) -> XarrayDataCube: from scipy.signal import savgol_filter array: xarray.DataArray = cube.get_array() filled = array.interpolate_na(dim='t') smoothed_array = savgol_filter(filled.values, 5, 2, axis=0) return XarrayDataCube(xarray.DataArray(smoothed_array, dims=array.dims, coords=array.coords))
def apply_datacube(cube: XarrayDataCube, context: dict) -> XarrayDataCube: array: xarray.DataArray = cube.get_array() array += 1000 # Shape (#dates, #bands, #rows, #cols) array.loc[:, 'red'] += 10 array.loc[:, 'nir'] += 100 return XarrayDataCube(array)
def test_xarraydatacube_to_dict(): array = xarray.DataArray( numpy.zeros(shape=(2, 3)), coords={ 'x': [1, 2], 'y': [1, 2, 3] }, dims=('x', 'y'), name="testdata", attrs={"description": "This is an xarray with two dimensions"}, ) xdc = XarrayDataCube(array=array) assert xdc.to_dict() == { "id": "testdata", "description": 'This is an xarray with two dimensions', "data": [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], "dimensions": [ { 'name': 'x', 'coordinates': [1, 2] }, { 'name': 'y', 'coordinates': [1, 2, 3] }, ], }
def test_datacube_list(): xa = xarray.DataArray(numpy.zeros((2, 3)), coords={ "x": [1, 2], "y": [3, 4, 5] }, dims=("x", "y"), name="testdata") cube = XarrayDataCube(xa) udf_data = UdfData(datacube_list=[cube], user_context={"kernel": 3}) assert udf_data.to_dict() == { "datacubes": [{ "id": "testdata", "data": [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], "dimensions": [{ "name": "x", "coordinates": [1, 2] }, { "name": "y", "coordinates": [3, 4, 5] }], }], "feature_collection_list": None, "structured_data_list": None, "proj": None, "user_context": { "kernel": 3 } } assert repr(udf_data) \ == '<UdfData datacube_list:[<XarrayDataCube shape:(2, 3)>] feature_collection_list:None structured_data_list:None>'
def apply_datacube(cube: XarrayDataCube, context: dict) -> XarrayDataCube: """ Apply Savitzky-Golay smoothing to a timeseries datacube. This UDF preserves dimensionality, and assumes an input datacube with a temporal dimension 't' as input. """ array: xarray.DataArray = cube.get_array() filled = array.interpolate_na(dim='t') smoothed_array = savgol_filter(filled.values, 5, 2, axis=0) return XarrayDataCube(array=xarray.DataArray( smoothed_array, dims=array.dims, coords=array.coords))
def test_xarraydatacube_to_dict_minimal(): array = xarray.DataArray(numpy.zeros(shape=(2, 3))) xdc = XarrayDataCube(array=array) assert xdc.to_dict() == { "data": [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], "dimensions": [{ "name": "dim_0" }, { "name": "dim_1" }], }
def apply_datacube(cube: XarrayDataCube, context: dict) -> XarrayDataCube: """Compute the NDVI based on sentinel2 tiles""" array: xarray.DataArray = cube.get_array() red = array.sel(bands="TOC-B04_10M") nir = array.sel(bands="TOC-B08_10M") ndvi = (nir - red) / (nir + red) import os statinfo = os.stat("/data/users/Public") print(statinfo) return XarrayDataCube(ndvi)
def apply_datacube(cube: XarrayDataCube, context: dict) -> XarrayDataCube: # access the underlying xarray inarr=cube.get_array() # ndvi B4=inarr.loc[:,'B04'] B8=inarr.loc[:,'B08'] ndvi=(B8-B4)/(B8+B4) # extend bands dim ndvi=ndvi.expand_dims(dim='bands', axis=-3).assign_coords(bands=['ndvi']) # wrap back to datacube and return return XarrayDataCube(ndvi)
def hyper_map_fabs(udf_data: UdfData): """Compute the absolute values of each hyper cube in the provided data Args: udf_data (UdfData): The UDF data object that contains raster and vector tiles as well as hypercubes and structured data. Returns: This function will not return anything, the UdfData object "udf_data" must be used to store the resulting data. """ # Iterate over each tile cube_list = [] for cube in udf_data.get_datacube_list(): result = numpy.fabs(cube.array) result.name = cube.id + "_fabs" cube_list.append(XarrayDataCube(array=result)) udf_data.set_datacube_list(cube_list)
def apply_datacube(cube: XarrayDataCube, context: dict) -> XarrayDataCube: """ Applies a rolling window median composite to a timeseries datacube. This UDF preserves dimensionality, and assumes a datacube with a temporal dimension 't' as input. """ array: xarray.DataArray = cube.get_array() #this method computes dekad's, can be used to resample data to desired frequency time_dimension_index = array.get_index('t') d = time_dimension_index.day - np.clip((time_dimension_index.day - 1) // 10, 0, 2) * 10 - 1 date = time_dimension_index.values - np.array(d, dtype="timedelta64[D]") #replace each value with 30-day window median #first median rolling window to fill gaps on all dates composited = array.rolling(t=30,min_periods=1, center=True).median().dropna("t") #resample rolling window medians to dekads ten_daily_composite = composited.groupby_bins("t",date).median() return XarrayDataCube(ten_daily_composite)
def _build_txy_data(ts: list, xs: list, ys: list, name: str, offset=0, t_factor=100, x_factor=10, y_factor=1) -> XarrayDataCube: """Build `XarrayDataCube` with given t, x and y labels""" t, x, y = numpy.ogrid[0:len(ts), 0:len(xs), 0:len(ys)] a = offset + t_factor * t + x_factor * x + y_factor * y return XarrayDataCube( xarray.DataArray( data=a, coords={ "t": ts, "x": xs, "y": ys }, dims=['t', 'x', 'y'], name=name, ))
def _build_xdc( ts: Union[list, int] = None, bands: Union[list, int] = None, xs: Union[list, int] = None, ys: Union[list, int] = None, dtype=numpy.int32, ): """ Build multi-dimensional XarrayDataCube containing given dimensions/coordinates """ dims = [] coords = {} value = numpy.zeros(shape=()) for dim, cs in [("t", ts), ("bands", bands), ("x", xs), ("y", ys)]: if cs: dims.append(dim) if isinstance(cs, list): coords[dim] = cs cs = len(cs) value = 10 * value[..., None] + numpy.arange(cs) return XarrayDataCube( xarray.DataArray(value.astype(dtype), dims=dims, coords=coords))
def hyper_ndvi(udf_data: UdfData): """Compute the NDVI based on RED and NIR hypercubes Hypercubes with ids "red" and "nir" are required. The NDVI computation will be applied to all hypercube dimensions. Args: udf_data (UdfData): The UDF data object that contains raster and vector tiles as well as hypercubes and structured data. Returns: This function will not return anything, the UdfData object "udf_data" must be used to store the resulting data. """ red = None nir = None # Iterate over each tile for cube in udf_data.get_datacube_list(): if "red" in cube.id.lower(): red = cube if "nir" in cube.id.lower(): nir = cube if red is None: raise Exception("Red hypercube is missing in input") if nir is None: raise Exception("Nir hypercube is missing in input") ndvi = (nir.array - red.array) / (nir.array + red.array) ndvi.name = "NDVI" hc = XarrayDataCube(array=ndvi) udf_data.set_datacube_list([ hc, ])
def apply_datacube(cube: XarrayDataCube, context: dict) -> XarrayDataCube: array: xarray.DataArray = cube.get_array() array += 60 return XarrayDataCube(array)