def merge_arrays( dataarrays: Iterable[DataArray], bounds: Optional[Tuple] = None, res: Optional[Tuple] = None, nodata: Optional[float] = None, precision: Optional[float] = None, method: Union[str, Callable, None] = None, crs: Optional[CRS] = None, parse_coordinates: bool = True, ) -> DataArray: """ Merge data arrays geospatially. Uses rasterio.merge.merge: https://rasterio.readthedocs.io/en/stable/api/rasterio.merge.html#rasterio.merge.merge .. versionadded:: 0.2 crs Parameters ---------- dataarrays: list List of xarray.DataArray's with all geo attributes. The first one is assumed to have the same CRS, dtype, and dimensions as the others in the array. bounds: tuple, optional Bounds of the output image (left, bottom, right, top). If not set, bounds are determined from bounds of input DataArrays. res: tuple, optional Output resolution in units of coordinate reference system. If not set, the resolution of the first DataArray is used. If a single value is passed, output pixels will be square. nodata: float, optional nodata value to use in output file. If not set, uses the nodata value in the first input DataArray. precision: float, optional Number of decimal points of precision when computing inverse transform. method: str or callable, optional See rasterio docs. crs: rasterio.crs.CRS, optional Output CRS. If not set, the CRS of the first DataArray is used. parse_coordinates: bool, optional If False, it will disable loading spatial coordinates. Returns ------- :obj:`xarray.DataArray`: The geospatially merged data. """ input_kwargs = dict(bounds=bounds, res=res, nodata=nodata, precision=precision, method=method) if crs is None: crs = dataarrays[0].rio.crs if res is None: res = tuple(abs(res_val) for res_val in dataarrays[0].rio.resolution()) # prepare the duck arrays rioduckarrays = [] for dataarray in dataarrays: da_res = tuple(abs(res_val) for res_val in dataarray.rio.resolution()) if da_res != res or dataarray.rio.crs != crs: rioduckarrays.append( RasterioDatasetDuck( dataarray.rio.reproject(dst_crs=crs, resolution=res))) else: rioduckarrays.append(RasterioDatasetDuck(dataarray)) # use rasterio to merge merged_data, merged_transform = _rio_merge( rioduckarrays, **{key: val for key, val in input_kwargs.items() if val is not None}, ) # generate merged data array representative_array = rioduckarrays[0]._xds if parse_coordinates: coords = _make_coords( representative_array, merged_transform, merged_data.shape[-1], merged_data.shape[-2], ) else: coords = _get_nonspatial_coords(representative_array) xda = DataArray( name=representative_array.name, data=merged_data, coords=coords, dims=tuple(representative_array.dims), attrs=representative_array.attrs, ) xda.rio.write_nodata( nodata if nodata is not None else representative_array.rio.nodata, inplace=True) xda.rio.write_crs(representative_array.rio.crs, inplace=True) xda.rio.write_transform(merged_transform, inplace=True) return xda
def merge_arrays( dataarrays: Iterable[DataArray], bounds: Union[Tuple, None] = None, res: Union[Tuple, None] = None, nodata: Union[float, None] = None, precision: Union[float, None] = None, method: Union[str, Callable, None] = None, parse_coordinates: bool = True, ) -> DataArray: """ Merge data arrays geospatially. Uses rasterio.merge.merge: https://rasterio.readthedocs.io/en/stable/api/rasterio.merge.html#rasterio.merge.merge Parameters ---------- dataarrays: list List of xarray.DataArray's with all geo attributes. The first one is assumed to have the same CRS, dtype, and dimensions as the others in the array. bounds: tuple, optional Bounds of the output image (left, bottom, right, top). If not set, bounds are determined from bounds of input DataArrays. res: tuple, optional Output resolution in units of coordinate reference system. If not set, the resolution of the first DataArray is used. If a single value is passed, output pixels will be square. nodata: float, optional nodata value to use in output file. If not set, uses the nodata value in the first input DataArray. precision: float, optional Number of decimal points of precision when computing inverse transform. method: str or callable, optional See rasterio docs. parse_coordinates: bool, optional If False, it will disable loading spatial coordinates. Returns ------- DataArray: The geospatially merged data. """ input_kwargs = dict(bounds=bounds, res=res, nodata=nodata, precision=precision, method=method) merged_data, merged_transform = _rio_merge( [RasterioDatasetDuck(dataarray) for dataarray in dataarrays], **{key: val for key, val in input_kwargs.items() if val is not None}, ) merged_shape = merged_data.shape representative_array = dataarrays[0] merged_crs = representative_array.rio.crs if parse_coordinates: coords = _make_coords( representative_array, merged_transform, merged_shape[-1], merged_shape[-2], merged_crs, ) else: coords = _get_nonspatial_coords(representative_array) out_attrs = representative_array.attrs out_attrs["transform"] = tuple(merged_transform)[:6] xda = DataArray( name=dataarrays[0].name, data=merged_data, coords=coords, dims=tuple(representative_array.dims), attrs=out_attrs, ) out_nodata = nodata if nodata is not None else representative_array.rio.nodata xda.rio.write_nodata(out_nodata, inplace=True) xda.rio.write_crs(representative_array.rio.crs, inplace=True) return xda