def test_reclassify_cpu_equals_gpu(): import cupy bins = [10, 20, 30] new_values = [1, 2, 3] # vanilla numpy version cpu = reclassify(numpy_agg, name='numpy_result', bins=bins, new_values=new_values) # cupy cupy_agg = xr.DataArray(cupy.asarray(elevation), attrs={'res': (10.0, 10.0)}) gpu = reclassify(cupy_agg, name='cupy_result', bins=bins, new_values=new_values) assert isinstance(gpu.data, cupy.ndarray) assert np.isclose(cpu, gpu, equal_nan=True).all() # dask + cupy dask_cupy_agg = xr.DataArray(cupy.asarray(elevation), attrs={'res': (10.0, 10.0)}) dask_cupy_agg.data = da.from_array(dask_cupy_agg.data, chunks=(3, 3)) dask_gpu = reclassify(dask_cupy_agg, name='dask_cupy_result', bins=bins, new_values=new_values) assert isinstance(dask_gpu.data, da.Array) and is_cupy_backed(dask_gpu) dask_gpu.data = dask_gpu.data.compute() assert np.isclose(cpu, dask_gpu, equal_nan=True).all()
def slope(agg:xr.DataArray, name: str='slope') -> xr.DataArray: """Returns slope of input aggregate in degrees. Parameters ---------- agg : xr.DataArray name : str - name property of output xr.DataArray Returns ------- data: xr.DataArray Notes: ------ Algorithm References: - http://desktop.arcgis.com/en/arcmap/10.3/tools/spatial-analyst-toolbox/how-slope-works.htm - Burrough, P. A., and McDonell, R. A., 1998. Principles of Geographical Information Systems (Oxford University Press, New York), pp 406 """ cellsize_x, cellsize_y = get_dataarray_resolution(agg) # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data, cellsize_x, cellsize_y) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data, cellsize_x, cellsize_y) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data, cellsize_x, cellsize_y) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data, cellsize_x, cellsize_y) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def hillshade(agg, azimuth=225, angle_altitude=25, name='hillshade'): """Illuminates 2D DataArray from specific azimuth and altitude. Parameters ---------- agg : DataArray altitude : int, optional (default: 30) Altitude angle of the sun specified in degrees. azimuth : int, optional (default: 315) The angle between the north vector and the perpendicular projection of the light source down onto the horizon specified in degrees. Returns ------- Datashader Image Notes: ------ Algorithm References: - http://geoexamples.blogspot.com/2014/03/shaded-relief-images-using-gdal-python.html """ # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data, azimuth, angle_altitude) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data, azimuth, angle_altitude) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data, azimuth, angle_altitude) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data, azimuth, angle_altitude) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
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 aspect(agg: xr.DataArray, name: str = 'aspect'): """Returns downward slope direction in compass degrees (0 - 360) with 0 at 12 o'clock. Parameters ---------- agg : DataArray Returns ------- data: DataArray Notes: ------ Algorithm References: - http://desktop.arcgis.com/en/arcmap/10.3/tools/spatial-analyst-toolbox/how-aspect-works.htm#ESRI_SECTION1_4198691F8852475A9F4BC71246579FAA - Burrough, P. A., and McDonell, R. A., 1998. Principles of Geographical Information Systems (Oxford University Press, New York), pp 406 """ # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def curvature(agg, name='curvature'): """Compute the curvature (second derivatives) of a agg surface. Parameters ---------- agg: xarray.xr.DataArray 2D input agg image with shape=(height, width) Returns ------- curvature: xarray.xr.DataArray Curvature image with shape=(height, width) """ cellsize_x, cellsize_y = get_dataarray_resolution(agg) cellsize = (cellsize_x + cellsize_y) / 2 # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data, cellsize) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data, cellsize) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data, cellsize) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data, cellsize) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def _quantile(agg, k): # numpy case if isinstance(agg.data, np.ndarray): q = _run_cpu_quantile(agg.data, k) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): q = _run_cupy_quantile(agg.data, k) # dask + cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray) and is_cupy_backed(agg): q = _run_dask_cupy_quantile(agg.data, k) # dask + numpy case elif isinstance(agg.data, da.Array): q = _run_dask_numpy_quantile(agg.data, k) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return q
def hillshade(agg: xr.DataArray, azimuth: int = 225, angle_altitude: int = 25, name: Optional[str] = 'hillshade') -> xr.DataArray: """ Calculates, for all cells in the array, an illumination value of each cell based on illumination from a specific azimuth and altitude. Parameters ---------- agg : xarray.DataArray 2D NumPy, CuPy, NumPy-backed Dask, or Cupy-backed Dask array of elevation values. altitude : int, default=25 Altitude angle of the sun specified in degrees. azimuth : int, default=225 The angle between the north vector and the perpendicular projection of the light source down onto the horizon specified in degrees. name : str, default='hillshade' Name of output DataArray. Returns ------- hillshade_agg : xarray.DataArray, of same type as `agg` 2D aggregate array of illumination values. References ---------- - GeoExamples: http://geoexamples.blogspot.com/2014/03/shaded-relief-images-using-gdal-python.html # noqa Examples -------- .. plot:: :include-source: import datashader as ds import matplotlib.pyplot as plt from xrspatial import generate_terrain, hillshade # Create Canvas W = 500 H = 300 cvs = ds.Canvas(plot_width = W, plot_height = H, x_range = (-20e6, 20e6), y_range = (-20e6, 20e6)) # Generate Example Terrain terrain_agg = generate_terrain(canvas = cvs) # Edit Attributes terrain_agg = terrain_agg.assign_attrs( { 'Description': 'Example Terrain', 'units': 'km', 'Max Elevation': '4000', } ) terrain_agg = terrain_agg.rename({'x': 'lon', 'y': 'lat'}) terrain_agg = terrain_agg.rename('Elevation') # Create Hillshade Aggregate Array hillshade_agg = hillshade(agg = terrain_agg, name = 'Illumination') # Edit Attributes hillshade_agg = hillshade_agg.assign_attrs({'Description': 'Example Hillshade', 'units': ''}) # Plot Terrain terrain_agg.plot(cmap = 'terrain', aspect = 2, size = 4) plt.title("Terrain") plt.ylabel("latitude") plt.xlabel("longitude") # Plot Terrain hillshade_agg.plot(cmap = 'Greys', aspect = 2, size = 4) plt.title("Hillshade") plt.ylabel("latitude") plt.xlabel("longitude") .. sourcecode:: python >>> print(terrain_agg[200:203, 200:202]) <xarray.DataArray 'Elevation' (lat: 3, lon: 2)> array([[1264.02249454, 1261.94748873], [1285.37061171, 1282.48046696], [1306.02305679, 1303.40657515]]) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Example Terrain units: km Max Elevation: 4000 >>> print(hillshade_agg[200:203, 200:202]) <xarray.DataArray 'Illumination' (lat: 3, lon: 2)> array([[1264.02249454, 1261.94748873], [1285.37061171, 1282.48046696], [1306.02305679, 1303.40657515]]) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Example Hillshade units: Max Elevation: 4000 """ # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data, azimuth, angle_altitude) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data, azimuth, angle_altitude) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data, azimuth, angle_altitude) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data, azimuth, angle_altitude) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def hotspots(raster, kernel): """ Identify statistically significant hot spots and cold spots in an input raster. To be a statistically significant hot spot, a feature will have a high value and be surrounded by other features with high values as well. Neighborhood of a feature defined by the input kernel, which currently support a shape of circle, annulus, or custom kernel. The result should be a raster with the following 7 values: - 90 for 90% confidence high value cluster - 95 for 95% confidence high value cluster - 99 for 99% confidence high value cluster - 90 for 90% confidence low value cluster - 95 for 95% confidence low value cluster - 99 for 99% confidence low value cluster - 0 for no significance Parameters ---------- raster : xarray.DataArray 2D Input raster image with `raster.shape` = (height, width). kernel : Numpy Array 2D array where values of 1 indicate the kernel. Returns ------- hotspots_agg : xarray.DataArray of same type as `raster` 2D array of hotspots with values indicating confidence level. Examples -------- .. plot:: :include-source: import datashader as ds import matplotlib.pyplot as plt from xrspatial import generate_terrain, aspect from xrspatial.convolution import circle_kernel from xrspatial.focal import hotspots # Create Canvas W = 500 H = 300 cvs = ds.Canvas(plot_width = W, plot_height = H, x_range = (-20e6, 20e6), y_range = (-20e6, 20e6)) # Generate Example Terrain terrain_agg = generate_terrain(canvas = cvs) # Edit Attributes terrain_agg = terrain_agg.assign_attrs( { 'Description': 'Example Terrain', 'units': 'km', 'Max Elevation': '4000', } ) terrain_agg = terrain_agg.rename({'x': 'lon', 'y': 'lat'}) terrain_agg = terrain_agg.rename('Elevation') # Create Kernel kernel = circle_kernel(10, 10, 100) # Create Hotspots Aggregate array hotspots_agg = hotspots(raster = terrain_agg, kernel = kernel) # Edit Attributes hotspots_agg = hotspots_agg.rename('Significance') hotspots_agg = hotspots_agg.assign_attrs( { 'Description': 'Example Hotspots', 'units': '%', } ) # Plot Terrain terrain_agg.plot(cmap = 'terrain', aspect = 2, size = 4) plt.title("Terrain") plt.ylabel("latitude") plt.xlabel("longitude") # Plot Hotspots hotspots_agg.plot(aspect = 2, size = 4) plt.title("Hotspots") plt.ylabel("latitude") plt.xlabel("longitude") .. sourcecode:: python >>> print(terrain_agg[200:203, 200:202]) <xarray.DataArray 'Elevation' (lat: 3, lon: 2)> array([[1264.02249454, 1261.94748873], [1285.37061171, 1282.48046696], [1306.02305679, 1303.40657515]]) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Example Terrain units: km Max Elevation: 4000 >>> print(hotspots_agg[200:203, 200:202]) <xarray.DataArray 'Significance' (lat: 3, lon: 2)> array([[0, 0], [0, 0], [0, 0]], dtype=int8) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Example Hotspots units: % Max Elevation: 4000 """ # validate raster if not isinstance(raster, DataArray): raise TypeError("`raster` must be instance of DataArray") if raster.ndim != 2: raise ValueError("`raster` must be 2D") # numpy case if isinstance(raster.data, np.ndarray): out = _hotspots_numpy(raster, kernel) # cupy case elif has_cuda() and isinstance(raster.data, cupy.ndarray): out = _hotspots_cupy(raster, kernel) # dask + cupy case elif has_cuda() and isinstance(raster.data, da.Array) and \ is_cupy_backed(raster): raise NotImplementedError() # dask + numpy case elif isinstance(raster.data, da.Array): out = _hotspots_dask_numpy(raster, kernel) else: raise TypeError('Unsupported Array Type: {}'.format(type(raster.data))) return DataArray(out, coords=raster.coords, dims=raster.dims, attrs=raster.attrs)
def equal_interval(agg: xr.DataArray, k: int = 5, name: Optional[str] = 'equal_interval') -> xr.DataArray: """ Groups data for array (agg) by distributing values into at equal intervals. The result is an xarray.DataArray. Parameters: ---------- agg: xarray.DataArray 2D array of values to bin. NumPy, CuPy, NumPy-backed Dask, or Cupy-backed Dask array k: int Number of classes to be produced. name: str, optional (default = "equal_interval") Name of output aggregate. Returns: ---------- equal_interval_agg: xarray.DataArray 2D array, of the same type as the input, of class allocations. Notes: ---------- Intervals defined to have equal width: Algorithm References: ---------- PySal: - https://pysal.org/mapclassify/_modules/mapclassify/classifiers.html#EqualInterval # noqa SciKit: - https://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html#sphx-glr-auto-examples-classification-plot-classifier-comparison-py # noqa Examples: ---------- Imports >>> import numpy as np >>> import xarray as xr >>> from xrspatial.classify import equal_interval, natural_breaks Create Initial DataArray >>> np.random.seed(1) >>> agg = xr.DataArray(np.random.randint(2, 8, (4, 4)), >>> dims = ["lat", "lon"]) >>> height, width = agg.shape >>> _lon = np.linspace(0, width - 1, width) >>> _lat = np.linspace(0, height - 1, height) >>> agg["lon"] = _lon >>> agg["lat"] = _lat >>> print(agg) <xarray.DataArray (lat: 4, lon: 4)> array([[7, 5, 6, 2], [3, 5, 7, 2], [2, 3, 6, 7], [6, 3, 4, 6]]) Coordinates: * lon (lon) float64 0.0 1.0 2.0 3.0 * lat (lat) float64 0.0 1.0 2.0 3.0 Create Equal Interval DataArray >>> equal_interval_agg = equal_interval(agg, k = 5) >>> print(equal_interval_agg) <xarray.DataArray 'equal_interval' (lat: 4, lon: 4)> array([[4., 2., 3., 0.], [0., 2., 4., 0.], [0., 0., 3., 4.], [3., 0., 1., 3.]], dtype=float32) Coordinates: * lon (lon) float64 0.0 1.0 2.0 3.0 * lat (lat) float64 0.0 1.0 2.0 3.0 """ # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy_equal_interval(agg.data, k) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy_equal_interval(agg.data, k) # dask + cupy case elif has_cuda() and \ isinstance(agg.data, cupy.ndarray) and \ is_cupy_backed(agg): out = _run_dask_cupy_equal_interval(agg.data, k) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy_equal_interval(agg.data, k) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def equal_interval(agg: xr.DataArray, k: int = 5, name: Optional[str] = 'equal_interval') -> xr.DataArray: """ Reclassifies data for array `agg` into new values based on intervals of equal width. Parameters ---------- agg : xarray.DataArray 2D NumPy, CuPy, NumPy-backed Dask, or Cupy-backed Dask array of values to be reclassified. k : int, default=5 Number of classes to be produced. name : str, default='equal_interval' Name of output aggregate. Returns ------- equal_interval_agg : xarray.DataArray of the same type as `agg` 2D aggregate array of equal interval allocations. All other input attributes are preserved. References ---------- - PySAL: https://pysal.org/mapclassify/_modules/mapclassify/classifiers.html#EqualInterval # noqa - scikit-learn: https://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html#sphx-glr-auto-examples-classification-plot-classifier-comparison-py # noqa Examples -------- .. plot:: :include-source: import datashader as ds import matplotlib.pyplot as plt from xrspatial import generate_terrain from xrspatial.classify import equal_interval # Create Canvas W = 500 H = 300 cvs = ds.Canvas(plot_width = W, plot_height = H, x_range = (-20e6, 20e6), y_range = (-20e6, 20e6)) # Generate Example Terrain terrain_agg = generate_terrain(canvas = cvs) # Edit Attributes terrain_agg = terrain_agg.assign_attrs( { 'Description': 'Example Terrain', 'units': 'km', 'Max Elevation': '4000', } ) terrain_agg = terrain_agg.rename({'x': 'lon', 'y': 'lat'}) terrain_agg = terrain_agg.rename('Elevation') # Create Equal Interval Aggregate Array equal_interval_agg = equal_interval(agg = terrain_agg, name = 'Elevation') # Edit Attributes equal_interval_agg = equal_interval_agg.assign_attrs({'Description': 'Example Equal Interval'}) # Plot Terrain terrain_agg.plot(cmap = 'terrain', aspect = 2, size = 4) plt.title("Terrain") plt.ylabel("latitude") plt.xlabel("longitude") # Plot Equal Interval equal_interval_agg.plot(cmap = 'terrain', aspect = 2, size = 4) plt.title("Equal Interval") plt.ylabel("latitude") plt.xlabel("longitude") .. sourcecode:: python >>> print(terrain_agg[200:203, 200:202]) <xarray.DataArray 'Elevation' (lat: 3, lon: 2)> array([[1264.02249454, 1261.94748873], [1285.37061171, 1282.48046696], [1306.02305679, 1303.40657515]]) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Example Terrain units: km Max Elevation: 4000 .. sourcecode:: python >>> print(equal_interval_agg[200:203, 200:202]) <xarray.DataArray 'Elevation' (lat: 3, lon: 2)> array([[1., 1.], [1., 1.], [1., 1.]], dtype=float32) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Example Equal Interval units: km Max Elevation: 4000 """ # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy_equal_interval(agg.data, k) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy_equal_interval(agg.data, k) # dask + cupy case elif has_cuda() and \ isinstance(agg.data, cupy.ndarray) and \ is_cupy_backed(agg): out = _run_dask_cupy_equal_interval(agg.data, k) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy_equal_interval(agg.data, k) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def aspect(agg: xr.DataArray, name: Optional[str] = 'aspect') -> xr.DataArray: """ Calculates, for all cells in the array, the downward slope direction of each cell based on the elevation of its neighbors in a 3x3 grid. The value is measured clockwise in degrees with 0 and 360 at due north. Flat areas are given a value of -1. Values along the edges are not calculated. Parameters: ---------- agg: xarray.DataArray 2D array of elevation values. NumPy, CuPy, NumPy-backed Dask, or Cupy-backed Dask array. name: str, optional (default = "aspect") Name of ouput DataArray. Returns: ---------- xarray.DataArray 2D array, of the same type as the input, of calculated aspect values. All other input attributes are preserved. Notes: ---------- Algorithm References: - http://desktop.arcgis.com/en/arcmap/10.3/tools/spatial-analyst-toolbox/how-aspect-works.htm#ESRI_SECTION1_4198691F8852475A9F4BC71246579FAA - Burrough, P. A., and McDonell, R. A., 1998. Principles of Geographical Information Systems (Oxford University Press, New York), pp 406 Examples: ---------- Imports >>> import numpy as np >>> import xarray as xr >>> import xrspatial Create Elevation DataArray >>> agg = xr.DataArray(np.array([[0, 1, 0, 0], >>> [1, 1, 0, 0], >>> [0, 1, 2, 2], >>> [1, 0, 2, 0], >>> [0, 2, 2, 2]]), >>> dims = ["lat", "lon"]) >>> height, width = agg.shape >>> _lon = np.linspace(0, width - 1, width) >>> _lat = np.linspace(0, height - 1, height) >>> agg["lon"] = _lon >>> agg["lat"] = _lat Create Aspect DataArray >>> aspect = xrspatial.aspect(agg) >>> print(aspect) <xarray.DataArray 'aspect' (lat: 5, lon: 4)> array([[nan, nan, nan, nan], [nan, 0., 18.43494882, nan], [nan, 270., 341.56505118, nan], [nan, 288.43494882, 315., nan], [nan, nan, nan, nan]]) Coordinates: * lon (lon) float64 0.0 1.0 2.0 3.0 * lat (lat) float64 0.0 1.0 2.0 3.0 4.0 Terrain Example: https://makepath.github.io/xarray-spatial/assets/examples/user-guide.html """ # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def curvature(agg: xr.DataArray, name: Optional[str] = 'curvature') -> xr.DataArray: """ Calculates, for all cells in the array, the curvature (second derivative) of each cell based on the elevation of its neighbors in a 3x3 grid. A positive curvature indicates the surface is upwardly convex. A negative value indicates it is upwardly concave. A value of 0 indicates a flat surface. Units of the curvature output raster are one hundredth (1/100) of a z-unit. Parameters ---------- agg : xarray.DataArray 2D NumPy, CuPy, NumPy-backed Dask, or Cupy-backed Dask array of elevation values. Must contain `res` attribute. name : str, default='curvature' Name of output DataArray. Returns ------- curvature_agg : xarray.DataArray, of the same type as `agg` 2D aggregate array of curvature values. All other input attributes are preserved. References ---------- - arcgis: https://pro.arcgis.com/en/pro-app/latest/tool-reference/spatial-analyst/how-curvature-works.htm # noqa Examples -------- .. plot:: :include-source: import datashader as ds import numpy as np import matplotlib.pyplot as plt from xrspatial import generate_terrain, curvature # Create Canvas W = 500 H = 300 cvs = ds.Canvas(plot_width = W, plot_height = H, x_range = (-20e6, 20e6), y_range = (-20e6, 20e6)) # Generate Example Terrain terrain_agg = generate_terrain(canvas = cvs) # Edit Attributes terrain_agg = terrain_agg.assign_attrs( { 'Description': 'Example Terrain', 'units': 'km', 'Max Elevation': '4000', } ) terrain_agg = terrain_agg.rename({'x': 'lon', 'y': 'lat'}) terrain_agg = terrain_agg.rename('Elevation') # Create Curvature Aggregate Array curvature_agg = curvature(agg = terrain_agg, name = 'Curvature') # Edit Attributes curvature_agg = curvature_agg.assign_attrs( { 'Description': 'Curvature', 'units': 'rad', } ) # Where cells are extremely upwardly convex curvature_agg.data = np.where( np.logical_and( curvature_agg.data > 3000, curvature_agg.data < 4000, ), 1, np.nan, ) # Plot Terrain terrain_agg.plot(cmap = 'terrain', aspect = 2, size = 4) plt.title("Terrain") plt.ylabel("latitude") plt.xlabel("longitude") # Plot Curvature curvature_agg.plot(aspect = 2, size = 4) plt.title("Curvature") plt.ylabel("latitude") plt.xlabel("longitude") .. sourcecode:: python >>> print(terrain_agg[200:203, 200:202]) <xarray.DataArray 'Elevation' (lat: 3, lon: 2)> array([[1264.02249454, 1261.94748873], [1285.37061171, 1282.48046696], [1306.02305679, 1303.40657515]]) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Example Terrain units: km Max Elevation: 4000 .. sourcecode:: python >>> print(curvature_agg[200:203, 200:202]) <xarray.DataArray 'Curvature' (lat: 3, lon: 2)> array([[nan, nan], [nan, nan], [nan, nan]]) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Curvature units: rad Max Elevation: 4000 """ cellsize_x, cellsize_y = get_dataarray_resolution(agg) cellsize = (cellsize_x + cellsize_y) / 2 # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data, cellsize) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data, cellsize) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data, cellsize) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data, cellsize) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def equal_interval(agg, k=5, name='equal_interval'): """ Equal Interval Classification Parameters ---------- agg : xr.DataArray xarray.DataArray of value to classify k : int number of classes required name : str name of output aggregate Returns equal_interval_agg : xr.DataArray Notes: Intervals defined to have equal width: .. math:: bins_j = min(y)+w*(j+1) with :math:`w=\\frac{max(y)-min(j)}{k}` Algorithm References: - https://pysal.org/mapclassify/_modules/mapclassify/classifiers.html#EqualInterval - https://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html#sphx-glr-auto-examples-classification-plot-classifier-comparison-py Examples -------- >>> In []: ei = np.array([1, 1, 0, 2,4,5,6]) >>> In []: ei_array =xarray.DataArray(ei) >>> In []: xrspatial.equal_interval(ei_array) >>> Out[]: <xarray.DataArray 'equal_interval' (dim_0: 4)> array([1.5, 3. , 4.5, 6. ]) """ # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy_equal_interval(agg.data, k) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy_equal_interval(agg.data, k) # dask + cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray) and is_cupy_backed(agg): out = _run_dask_cupy_equal_interval(agg.data, k) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy_equal_interval(agg.data, k) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def aspect(agg: xr.DataArray, name: Optional[str] = 'aspect') -> xr.DataArray: """ Calculates the aspect value of an elevation aggregate. Calculates, for all cells in the array, the downward slope direction of each cell based on the elevation of its neighbors in a 3x3 grid. The value is measured clockwise in degrees with 0 and 360 at due north. Flat areas are given a value of -1. Values along the edges are not calculated. Parameters ---------- agg : xarray.DataArray 2D NumPy, CuPy, NumPy-backed Dask, or Cupy-backed Dask array of elevation values. name : str, default='aspect' Name of ouput DataArray. Returns ------- aspect_agg : xarray.DataArray of the same type as `agg` 2D aggregate array of calculated aspect values. All other input attributes are preserved. References ---------- - arcgis: http://desktop.arcgis.com/en/arcmap/10.3/tools/spatial-analyst-toolbox/how-aspect-works.htm#ESRI_SECTION1_4198691F8852475A9F4BC71246579FAA # noqa Examples -------- .. plot:: :include-source: import datashader as ds import matplotlib.pyplot as plt from xrspatial import generate_terrain, aspect # Create Canvas W = 500 H = 300 cvs = ds.Canvas(plot_width = W, plot_height = H, x_range = (-20e6, 20e6), y_range = (-20e6, 20e6)) # Generate Example Terrain terrain_agg = generate_terrain(canvas = cvs) # Edit Attributes terrain_agg = terrain_agg.assign_attrs( { 'Description': 'Example Terrain', 'units': 'km', 'Max Elevation': '4000', } ) terrain_agg = terrain_agg.rename({'x': 'lon', 'y': 'lat'}) terrain_agg = terrain_agg.rename('Elevation') # Create Aspect Aggregate Array aspect_agg = aspect(agg = terrain_agg, name = 'Aspect') # Edit Attributes aspect_agg = aspect_agg.assign_attrs( { 'Description': 'Example Aspect', 'units': 'deg', } ) # Plot Terrain terrain_agg.plot(cmap = 'terrain', aspect = 2, size = 4) plt.title("Terrain") plt.ylabel("latitude") plt.xlabel("longitude") # Plot Aspect aspect_agg.plot(aspect = 2, size = 4) plt.title("Aspect") plt.ylabel("latitude") plt.xlabel("longitude") .. sourcecode:: python >>> print(terrain_agg[200:203, 200:202]) <xarray.DataArray 'Elevation' (lat: 3, lon: 2)> array([[1264.02249454, 1261.94748873], [1285.37061171, 1282.48046696], [1306.02305679, 1303.40657515]]) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Example Terrain units: km Max Elevation: 4000 .. sourcecode:: python >>> print(aspect_agg[200:203, 200:202]) <xarray.DataArray 'Aspect' (lat: 3, lon: 2)> array([[ 8.18582638, 8.04675084], [ 5.49302641, 9.86625477], [12.04270534, 16.87079619]]) Coordinates: * lon (lon) float64 -3.96e+06 -3.88e+06 * lat (lat) float64 6.733e+06 6.867e+06 7e+06 Attributes: res: 1 Description: Example Aspect units: deg Max Elevation: 4000 """ # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def hillshade(agg: xr.DataArray, azimuth: int = 225, angle_altitude: int = 25, name: Optional[str] = 'hillshade') -> xr.DataArray: """ Calculates, for all cells in the array, an illumination value of each cell based on illumination from a specific azimuth and altitude. Parameters: ---------- agg: xarray.DataArray 2D array of elevation values: NumPy, CuPy, NumPy-backed Dask, or Cupy-backed Dask array. altitude: int (default: 30) Altitude angle of the sun specified in degrees. azimuth: int (default: 315) The angle between the north vector and the perpendicular projection of the light source down onto the horizon specified in degrees. name: str, optional (default = "hillshade") Name of output DataArray. Returns: ---------- data: xarray.DataArray 2D array, of the same type as the input of calculated illumination values. Notes: ---------- Algorithm References: http://geoexamples.blogspot.com/2014/03/shaded-relief-images-using-gdal-python.html Examples: ---------- Imports >>> import numpy as np >>> import xarray as xr >>> import xrspatial Create Initial DataArray >>> agg = xr.DataArray(np.array([[0, 1, 0, 0], >>> [1, 1, 0, 0], >>> [0, 1, 2, 2], >>> [1, 0, 2, 0], >>> [0, 2, 2, 2]]), >>> dims = ["lat", "lon"]) >>> height, width = agg.shape >>> _lon = np.linspace(0, width - 1, width) >>> _lat = np.linspace(0, height - 1, height) >>> agg["lon"] = _lon >>> agg["lat"] = _lat >>> print(agg) <xarray.DataArray (lat: 5, lon: 4)> array([[0, 1, 0, 0], [1, 1, 0, 0], [0, 1, 2, 2], [1, 0, 2, 0], [0, 2, 2, 2]]) Coordinates: * lon (lon) float64 0.0 1.0 2.0 3.0 * lat (lat) float64 0.0 1.0 2.0 3.0 4.0 Create Hillshade DataArray >>> hillshade = xrspatial.hillshade(agg) >>> print(hillshade) <xarray.DataArray 'hillshade' (lat: 5, lon: 4)> array([[ nan, nan, nan, nan], [ nan, 0.54570079, 0.32044456, nan], [ nan, 0.96130094, 0.53406336, nan], [ nan, 0.67253318, 0.71130913, nan], [ nan, nan, nan, nan]]) Coordinates: * lon (lon) float64 0.0 1.0 2.0 3.0 * lat (lat) float64 0.0 1.0 2.0 3.0 4.0 """ # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data, azimuth, angle_altitude) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data, azimuth, angle_altitude) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data, azimuth, angle_altitude) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data, azimuth, angle_altitude) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)
def curvature(agg: xr.DataArray, name: Optional[str] = 'curvature') -> xr.DataArray: """ Calculates, for all cells in the array, the curvature (second derivative) of each cell based on the elevation of its neighbors in a 3x3 grid. A positive curvature indicates the surface is upwardly convex. A negative value indicates it is upwardly concave. A value of 0 indicates a flat surface. Units of the curvature output raster are one hundredth (1/100) of a z-unit. Parameters: ---------- agg: xarray.DataArray 2D array of elevation values NumPy, CuPy, NumPy-backed Dask, or Cupy-backed Dask array. Must contain "res" attribute. name: str (default = "curvature") Name of output DataArray. Returns: ---------- curvature: xarray.DataArray 2D array, of the same type as the input, of calculated curvature values All other input attributes are preserved. Notes: ---------- Algorithm References: - https://pro.arcgis.com/en/pro-app/latest/tool-reference/spatial-analyst/how-curvature-works.htm Examples: ---------- Imports >>> import numpy as np >>> import xarray as xr >>> from xrspatial import curvature Create Initial DataArray >>> agg = xr.DataArray(np.array([[0, 1, 0, 0], >>> [1, 1, 0, 0], >>> [0, 1, 2, 2], >>> [1, 0, 2, 0], >>> [0, 2, 2, 2]]), >>> dims = ["lat", "lon"], >>> attrs = dict(res = 1)) >>> height, width = agg.shape >>> _lon = np.linspace(0, width - 1, width) >>> _lat = np.linspace(0, height - 1, height) >>> agg["lon"] = _lon >>> agg["lat"] = _lat >>> print(agg) <xarray.DataArray (lat: 5, lon: 4)> array([[0, 1, 0, 0], [1, 1, 0, 0], [0, 1, 2, 2], [1, 0, 2, 0], [0, 2, 2, 2]]) Coordinates: * lon (lon) float64 0.0 1.0 2.0 3.0 * lat (lat) float64 0.0 1.0 2.0 3.0 4.0 Attributes: res: 1 Create Curvature DataArray >>> print(curvature(agg)) <xarray.DataArray 'curvature' (lat: 5, lon: 4)> array([[ nan, nan, nan, nan], [ nan, 100., -300., nan], [ nan, 100., 300., nan], [ nan, -600., 400., nan], [ nan, nan, nan, nan]]) Coordinates: * lon (lon) float64 0.0 1.0 2.0 3.0 * lat (lat) float64 0.0 1.0 2.0 3.0 4.0 Attributes: res: 1 """ cellsize_x, cellsize_y = get_dataarray_resolution(agg) cellsize = (cellsize_x + cellsize_y) / 2 # numpy case if isinstance(agg.data, np.ndarray): out = _run_numpy(agg.data, cellsize) # cupy case elif has_cuda() and isinstance(agg.data, cupy.ndarray): out = _run_cupy(agg.data, cellsize) # dask + cupy case elif has_cuda() and isinstance(agg.data, da.Array) and is_cupy_backed(agg): out = _run_dask_cupy(agg.data, cellsize) # dask + numpy case elif isinstance(agg.data, da.Array): out = _run_dask_numpy(agg.data, cellsize) else: raise TypeError('Unsupported Array Type: {}'.format(type(agg.data))) return xr.DataArray(out, name=name, coords=agg.coords, dims=agg.dims, attrs=agg.attrs)