def test_numpy_equals_dask(): # input data data = np.asarray( [[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], [1584.8767, 1584.8767, 1585.0546, 1585.2324, 1585.2324, 1585.2324], [1585.0546, 1585.0546, 1585.2324, 1585.588, 1585.588, 1585.588], [1585.2324, 1585.4102, 1585.588, 1585.588, 1585.588, 1585.588], [1585.588, 1585.588, 1585.7659, 1585.7659, 1585.7659, 1585.7659], [1585.7659, 1585.9437, 1585.7659, 1585.7659, 1585.7659, 1585.7659], [1585.9437, 1585.9437, 1585.9437, 1585.7659, 1585.7659, 1585.7659]], dtype=np.float32) small_numpy_based_data_array = xr.DataArray(data, attrs={'res': (10.0, 10.0)}) small_das_based_data_array = xr.DataArray(da.from_array(data, chunks=(2, 2)), attrs={'res': (10.0, 10.0)}) numpy_result = aspect(small_numpy_based_data_array, name='numpy_slope', use_cuda=False) dask_result = aspect(small_das_based_data_array, name='dask_slope', use_cuda=False) dask_result.data = dask_result.data.compute() assert np.isclose(numpy_result, dask_result, equal_nan=True).all()
def test_cpu_equals_gpu(): import cupy small_da = xr.DataArray(INPUT_DATA, attrs={'res': (10.0, 10.0)}) small_da_cupy = xr.DataArray(cupy.asarray(INPUT_DATA), attrs={'res': (10.0, 10.0)}) # aspect by xrspatial cpu = aspect(small_da, name='aspect_agg') gpu = aspect(small_da_cupy, name='aspect_agg') general_output_checks(small_da_cupy, gpu) np.testing.assert_allclose(cpu.data, gpu.data.get(), equal_nan=True)
def test_numpy_equals_dask(): small_numpy_based_data_array = xr.DataArray(INPUT_DATA, attrs={'res': (10.0, 10.0)}) small_dask_based_data_array = xr.DataArray(da.from_array(INPUT_DATA, chunks=(2, 2)), attrs={'res': (10.0, 10.0)}) numpy_result = aspect(small_numpy_based_data_array, name='numpy_result') dask_result = aspect(small_dask_based_data_array, name='dask_result') general_output_checks(small_dask_based_data_array, dask_result) np.testing.assert_allclose(numpy_result.data, dask_result.data.compute(), equal_nan=True)
def test_numpy_equals_cupy(): import cupy small_da = xr.DataArray(INPUT_DATA, attrs={'res': (10.0, 10.0)}) small_da_cupy = xr.DataArray(cupy.asarray(INPUT_DATA), attrs={'res': (10.0, 10.0)}) # aspect by xrspatial cpu = aspect(small_da, name='aspect_agg') gpu = aspect(small_da_cupy, name='aspect_agg') assert isinstance(gpu.data, cupy.ndarray) assert np.isclose(cpu, gpu, equal_nan=True).all()
def test_numpy_equals_dask(): small_numpy_based_data_array = xr.DataArray(INPUT_DATA, attrs={'res': (10.0, 10.0)}) small_dask_based_data_array = xr.DataArray(da.from_array(INPUT_DATA, chunks=(2, 2)), attrs={'res': (10.0, 10.0)}) numpy_result = aspect(small_numpy_based_data_array, name='numpy_result') dask_result = aspect(small_dask_based_data_array, name='dask_result') assert isinstance(dask_result.data, da.Array) dask_result.data = dask_result.data.compute() assert np.isclose(numpy_result, dask_result, equal_nan=True).all()
def test_numpy_equals_qgis(): small_da = xr.DataArray(INPUT_DATA, attrs={'res': (10.0, 10.0)}) xrspatial_aspect = aspect(small_da, name='numpy_aspect') general_output_checks(small_da, xrspatial_aspect) assert xrspatial_aspect.name == 'numpy_aspect' # validate output values xrspatial_vals = xrspatial_aspect.data[1:-1, 1:-1] qgis_vals = QGIS_OUTPUT[1:-1, 1:-1] # aspect is nan if nan input # aspect is invalid (-1) if slope equals 0 # otherwise aspect are from 0 - 360 np.testing.assert_allclose(xrspatial_vals, qgis_vals, equal_nan=True) # nan edge effect xrspatial_edges = [ xrspatial_aspect.data[0, :], xrspatial_aspect.data[-1, :], xrspatial_aspect.data[:, 0], xrspatial_aspect.data[:, -1], ] for edge in xrspatial_edges: np.testing.assert_allclose(edge, np.full(edge.shape, np.nan), equal_nan=True)
def test_numpy_equals_qgis(): small_da = xr.DataArray(INPUT_DATA, attrs={'res': (10.0, 10.0)}) xrspatial_aspect = aspect(small_da, name='numpy_aspect') # validate output attributes assert xrspatial_aspect.dims == small_da.dims assert xrspatial_aspect.attrs == small_da.attrs assert xrspatial_aspect.shape == small_da.shape assert xrspatial_aspect.name == 'numpy_aspect' for coord in small_da.coords: assert np.all(xrspatial_aspect[coord] == small_da[coord]) # TODO: We shouldn't ignore edges! # validate output values # ignore border edges xrspatial_vals = xrspatial_aspect.values[1:-1, 1:-1] qgis_vals = QGIS_OUTPUT[1:-1, 1:-1] # TODO: use np.is_close instead # set a tolerance of 1e-4 # aspect is nan if nan input # aspect is invalid (nan) if slope equals 0 # otherwise aspect are from 0 - 360 assert np.isclose(xrspatial_vals, qgis_vals, equal_nan=True).all()
def _numpy_equals_dask_cupy(): # NOTE: Dask + GPU code paths don't currently work because of # dask casting cupy arrays to numpy arrays during # https://github.com/dask/dask/issues/4842 import cupy cupy_data = cupy.asarray(INPUT_DATA) dask_cupy_data = da.from_array(cupy_data, chunks=(3, 3)) small_da = xr.DataArray(INPUT_DATA, attrs={'res': (10.0, 10.0)}) cpu = aspect(small_da, name='numpy_result') small_dask_cupy = xr.DataArray(dask_cupy_data, attrs={'res': (10.0, 10.0)}) gpu = aspect(small_dask_cupy, name='cupy_result') assert is_cupy_backed(gpu) assert np.isclose(cpu, gpu, equal_nan=True).all()
def test_aspect_transfer_function(): """ Assert aspect transfer function """ da = xr.DataArray(data_gaussian, dims=['y', 'x'], attrs={'res': 1}) da_aspect = aspect(da) assert da_aspect.dims == da.dims assert da_aspect.coords == da.coords assert da_aspect.attrs == da.attrs assert da.shape == da_aspect.shape assert pytest.approx(da_aspect.data.max(), .1) == 360. assert pytest.approx(da_aspect.data.min(), .1) == 0.
def test_summarize_terrain(random_data): test_terrain = create_test_raster(random_data, name='myterrain') summarized_ds = summarize_terrain(test_terrain) variables = [v for v in summarized_ds] should_have = ['myterrain', 'myterrain-slope', 'myterrain-curvature', 'myterrain-aspect'] assert variables == should_have np.testing.assert_allclose(summarized_ds['myterrain-slope'], slope(test_terrain)) np.testing.assert_allclose(summarized_ds['myterrain-curvature'], curvature(test_terrain)) np.testing.assert_allclose(summarized_ds['myterrain-aspect'], aspect(test_terrain))
def test_aspect_gpu_equals_cpu(): # input data data = np.asarray( [[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], [1584.8767, 1584.8767, 1585.0546, 1585.2324, 1585.2324, 1585.2324], [1585.0546, 1585.0546, 1585.2324, 1585.588, 1585.588, 1585.588], [1585.2324, 1585.4102, 1585.588, 1585.588, 1585.588, 1585.588], [1585.588, 1585.588, 1585.7659, 1585.7659, 1585.7659, 1585.7659], [1585.7659, 1585.9437, 1585.7659, 1585.7659, 1585.7659, 1585.7659], [1585.9437, 1585.9437, 1585.9437, 1585.7659, 1585.7659, 1585.7659]], dtype=np.float32) small_da = xr.DataArray(data, attrs={'res': (10.0, 10.0)}) # aspect by xrspatial xrspatial_aspect_cpu = aspect(small_da, name='aspect_agg', use_cuda=False) xrspatial_aspect_gpu = aspect(small_da, name='aspect_agg', use_cuda=True) assert np.isclose(xrspatial_aspect_cpu, xrspatial_aspect_gpu, equal_nan=True).all()
def test_aspect_against_qgis(): # input data data = np.asarray( [[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], [1584.8767, 1584.8767, 1585.0546, 1585.2324, 1585.2324, 1585.2324], [1585.0546, 1585.0546, 1585.2324, 1585.588, 1585.588, 1585.588], [1585.2324, 1585.4102, 1585.588, 1585.588, 1585.588, 1585.588], [1585.588, 1585.588, 1585.7659, 1585.7659, 1585.7659, 1585.7659], [1585.7659, 1585.9437, 1585.7659, 1585.7659, 1585.7659, 1585.7659], [1585.9437, 1585.9437, 1585.9437, 1585.7659, 1585.7659, 1585.7659]], dtype=np.float32) small_da = xr.DataArray(data, attrs={'res': (10.0, 10.0)}) # aspect by QGIS qgis_aspect = np.asarray( [[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], [330.94687, 335.55496, 320.70786, 330.94464, 0., 0.], [333.43494, 333.43494, 329.03394, 341.56897, 0., 18.434948], [338.9621, 338.20062, 341.56506, 0., 0., 45.], [341.56506, 351.8699, 26.56505, 45., -1., 90.], [351.86676, 11.306906, 45., 45., 45., 108.431015]], dtype=np.float32) # aspect by xrspatial xrspatial_aspect = aspect(small_da, name='aspect_agg', use_cuda=False) # validate output attributes assert xrspatial_aspect.dims == small_da.dims assert xrspatial_aspect.attrs == small_da.attrs assert xrspatial_aspect.shape == small_da.shape assert xrspatial_aspect.name == 'aspect_agg' for coord in small_da.coords: assert np.all(xrspatial_aspect[coord] == small_da[coord]) # validate output values # ignore border edges xrspatial_vals = xrspatial_aspect.values[1:-1, 1:-1] qgis_vals = qgis_aspect[1:-1, 1:-1] # set a tolerance of 1e-4 # aspect is nan if nan input # aspect is invalid (nan) if slope equals 0 # otherwise aspect are from 0 - 360 assert ((np.isnan(xrspatial_vals) & np.isnan(qgis_vals)) | (np.isnan(xrspatial_vals) & (qgis_vals == -1)) | (abs(xrspatial_vals - qgis_vals) <= 1e-4)).all() assert (np.isnan(xrspatial_vals) | ((0 <= xrspatial_vals) & (xrspatial_vals <= 360) | (xrspatial_vals == -1))).all()
def test_aspect_transfer_function(): """ Assert aspect transfer function """ da = xr.DataArray(data_gaussian, dims=['y', 'x'], attrs={'res': 1}) da_aspect = aspect(da, use_cuda=False) # default name assert da_aspect.name == 'aspect' assert da_aspect.dims == da.dims assert da_aspect.attrs == da.attrs assert da.shape == da_aspect.shape for coord in da.coords: assert np.all(da[coord] == da_aspect[coord]) assert pytest.approx(np.nanmax(da_aspect.data), .1) == 360. assert pytest.approx(np.nanmin(da_aspect.data), .1) == -1.
def test_numpy_equals_qgis(qgis_output): numpy_agg = input_data() xrspatial_aspect = aspect(numpy_agg, name='numpy_aspect') general_output_checks(numpy_agg, xrspatial_aspect, verify_dtype=True) assert xrspatial_aspect.name == 'numpy_aspect' xrspatial_vals = xrspatial_aspect.data[1:-1, 1:-1] qgis_vals = qgis_output[1:-1, 1:-1] # aspect is nan if nan input # aspect is invalid (-1) if slope equals 0 # otherwise aspect are from 0 to 360 np.testing.assert_allclose(xrspatial_vals, qgis_vals, equal_nan=True) # nan edge effect assert_nan_edges_effect(xrspatial_aspect)
def test_cupy_equals_qgis(): import cupy small_da = xr.DataArray(INPUT_DATA, attrs={'res': (10.0, 10.0)}) small_da_cupy = xr.DataArray(cupy.asarray(INPUT_DATA), attrs={'res': (10.0, 10.0)}) xrspatial_aspect = aspect(small_da_cupy, name='aspect_agg') # validate output attributes assert xrspatial_aspect.dims == small_da.dims assert xrspatial_aspect.attrs == small_da.attrs assert xrspatial_aspect.shape == small_da.shape assert xrspatial_aspect.name == 'aspect_agg' for coord in small_da.coords: assert np.all(xrspatial_aspect[coord] == small_da[coord]) # TODO: We shouldn't ignore edges! # validate output values # ignore border edges xrspatial_vals = xrspatial_aspect.values[1:-1, 1:-1] qgis_vals = QGIS_OUTPUT[1:-1, 1:-1] assert np.isclose(xrspatial_vals, qgis_vals, equal_nan=True).all()
def summarize_terrain(terrain: xr.DataArray): """ Calculates slope, aspect, and curvature of an elevation terrain and return a dataset of the computed data. Parameters ---------- terrain: xarray.DataArray 2D NumPy, CuPy, or Dask with NumPy-backed xarray DataArray of elevation values. Returns ------- summarized_terrain: xarray.Dataset Dataset with slope, aspect, curvature variables with a naming convention of `terrain.name-variable_name` Examples -------- .. sourcecode:: python >>> import numpy as np >>> import xarray as xr >>> from xrspatial.analytics import summarize_terrain >>> data = np.array([ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, -1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]], dtype=np.float64) >>> raster = xr.DataArray(data, name='myraster', attrs={'res': (1, 1)}) >>> summarized_terrain = summarize_terrain(raster) >>> summarized_terrain <xarray.Dataset> Dimensions: (dim_0: 5, dim_1: 8) Dimensions without coordinates: dim_0, dim_1 Data variables: myraster (dim_0, dim_1) float64 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 myraster-slope (dim_0, dim_1) float32 nan nan nan nan ... nan nan nan myraster-curvature (dim_0, dim_1) float64 nan nan nan nan ... nan nan nan myraster-aspect (dim_0, dim_1) float32 nan nan nan nan ... nan nan nan >>> summarized_terrain['myraster-slope'] <xarray.DataArray 'myraster-slope' (dim_0: 5, dim_1: 8)> array([[ nan, nan, nan, nan, nan, nan, nan, nan], [ nan, 10.024988, 14.036243, 10.024988, 10.024988, 14.036243, 10.024988, nan], [ nan, 14.036243, 0. , 14.036243, 14.036243, 0. , 14.036243, nan], [ nan, 10.024988, 14.036243, 10.024988, 10.024988, 14.036243, 10.024988, nan], [ nan, nan, nan, nan, nan, nan, nan, nan]], dtype=float32) # noqa Dimensions without coordinates: dim_0, dim_1 Attributes: res: (1, 1) >>> summarized_terrain['myraster-curvature'] <xarray.DataArray 'myraster-curvature' (dim_0: 5, dim_1: 8)> array([[ nan, nan, nan, nan, nan, nan, nan, nan], [ nan, -0., -100., -0., -0., 100., -0., nan], [ nan, -100., 400., -100., 100., -400., 100., nan], [ nan, -0., -100., -0., -0., 100., -0., nan], [ nan, nan, nan, nan, nan, nan, nan, nan]]) Dimensions without coordinates: dim_0, dim_1 Attributes: res: (1, 1) >>> summarized_terrain['myraster-aspect'] <xarray.DataArray 'myraster-aspect' (dim_0: 5, dim_1: 8)> array([[ nan, nan, nan, nan, nan, nan, nan, nan], [ nan, 315., 0., 45., 135., 180., 225., nan], [ nan, 270., -1., 90., 90., -1., 270., nan], [ nan, 225., 180., 135., 45., 0., 315., nan], [ nan, nan, nan, nan, nan, nan, nan, nan]], dtype=float32) Dimensions without coordinates: dim_0, dim_1 Attributes: res: (1, 1) """ if terrain.name is None: raise NameError('Requires xr.DataArray.name property to be set') ds = terrain.to_dataset() ds[f'{terrain.name}-slope'] = slope(terrain) ds[f'{terrain.name}-curvature'] = curvature(terrain) ds[f'{terrain.name}-aspect'] = aspect(terrain) return ds