def fill_arr(a, win=(3, 3)): """try filling an array""" fd = np.array([[32, 64, 128], [16, 0, 1], [8, 4, 2]]) # flow direction # if (zone < a.min()) or (zone > a.max()) or (zone is None): # print("\nYou need a zone that is within the range of values.") # return a, None if win[0] == 3: pr = 1 else: pr = 0 ap = np.pad(a, pad_width=(1, pr), mode="constant", constant_values=(0, 0)) if win == (2, 2): a_c = ap[1:, 1:] # for 2x2 even w, h = win elif win == (3, 3): w, h = win a_c = ap[1:-1, 1:-1] # for 3x3 odd a_s = stride(a_c, win=win) # stride the array r, c = a_s.shape[:2] out = [] x = a_s.shape[0] y = a_s.shape[1] for i in range(x): for j in range(y): # do stuff sub = a_s[i, j].ravel() edges = np.asarray([sub[:4], sub[5:]]).ravel() e_min = edges[np.argmin(edges)] if sub[4] < e_min: out.append(e_min) else: out.append(sub[4]) out = np.asarray(out).reshape(r, c) return out # , a_s, ap, a_c
def fill_arr(a, win=(3, 3)): """try filling an array as in fill, sinks """ #fd = np.array([[32, 64, 128], [16, 0, 1], [8, 4, 2]]) # flow direction # if (zone < a.min()) or (zone > a.max()) or (zone is None): # print("\nYou need a zone that is within the range of values.") # return a, None if win[0] == 3: pr = 1 else: pr = 0 ap = np.pad(a, pad_width=(1, pr), mode="constant", constant_values=(0, 0)) if win == (2, 2): a_c = ap[1:, 1:] # for 2x2 even elif win == (3, 3): a_c = ap[1:-1, 1:-1] # for 3x3 odd a_s = stride(a_c, win=win) # stride the array r, c = a_s.shape[:2] out = [] x = a_s.shape[0] y = a_s.shape[1] for i in range(x): for j in range(y): # do stuff sub = a_s[i, j].ravel() edges = np.asarray([sub[:4], sub[5:]]).ravel() e_min = edges[np.argmax(edges)] # argmax or argmin??? if sub[4] < e_min: out.append(e_min) else: out.append(sub[4]) out = np.asarray(out).reshape(r, c) return out # , a_s, ap, a_c
def _slope_aspect_demo_(cell_size=2): """Demo of the data set below""" a = np.array([[0, 1, 2, 3, 3, 3, 2, 1, 0], [1, 2, 3, 4, 4, 4, 3, 2, 1], [2, 3, 4, 5, 5, 5, 4, 3, 2], [3, 4, 5, 5, 5, 5, 5, 4, 3], [3, 4, 5, 5, 5, 5, 5, 4, 3], [3, 4, 5, 5, 5, 5, 5, 4, 3], [2, 3, 4, 5, 5, 5, 4, 3, 2], [1, 2, 3, 4, 4, 4, 3, 2, 1], [0, 1, 2, 3, 3, 3, 2, 1, 0]]) r, c = a.shape data = stride(a, win=(3, 3), stepby=(1, 1)) # produce strided array slope = slope_a(a, cell_size=cell_size) slope = np.array(slope) aspect = [ aspect_a(data[i][j]) for i in range(data.shape[0]) for j in range(data.shape[1]) ] aspect = np.array(aspect).reshape((r - 2, c - 2)) frmt = """ :---- Slope, Aspect Demo ------------------------------------------------ :Sample DEM with a cell size of {} units. {}\n Slope (degrees) ... {}\n Aspect (degrees) ... {} """ args = [cell_size] pre = ' ..' args.extend([indent(str(i), pre) for i in [a, slope, aspect]]) print(dedent(frmt).format(*args)) return a, slope, aspect
def _slope_aspect_demo_(cell_size=2): """Demo of the data set below""" a = np.array([[0, 1, 2, 3, 3, 3, 2, 1, 0], [1, 2, 3, 4, 4, 4, 3, 2, 1], [2, 3, 4, 5, 5, 5, 4, 3, 2], [3, 4, 5, 5, 5, 5, 5, 4, 3], [3, 4, 5, 5, 5, 5, 5, 4, 3], [3, 4, 5, 5, 5, 5, 5, 4, 3], [2, 3, 4, 5, 5, 5, 4, 3, 2], [1, 2, 3, 4, 4, 4, 3, 2, 1], [0, 1, 2, 3, 3, 3, 2, 1, 0]]) r, c = a.shape data = stride(a, win=(3, 3), stepby=(1, 1)) # produce strided array slope = slope_a(a, cell_size=cell_size) slope = np.array(slope) aspect = [aspect_a(data[i][j]) for i in range(data.shape[0]) for j in range(data.shape[1])] aspect = np.array(aspect).reshape((r-2, c-2)) frmt = """ :---- Slope, Aspect Demo ------------------------------------------------ :Sample DEM with a cell size of {} units. {}\n Slope (degrees) ... {}\n Aspect (degrees) ... {} """ args = [cell_size] pre = ' ..' args.extend([indent(str(i), pre) for i in [a, slope, aspect]]) print(dedent(frmt).format(*args)) return a, slope, aspect
def aspect_a(a, cell_size=1, flat=0.1, degrees=True, keepdims=False): """Return the aspect of a slope in degrees from North. :Requires: :-------- : a - an input 2d array. X and Y represent coordinates of the Z values : cell_size - needed to proper flat calculation : flat - degree value, e.g. flat surface <= 0.05 deg : 0.05 deg => 8.7e-04 rad 0.10 deg => 1.7e-02 rad : """ if not isinstance(flat, (int, float)): flat = 0.1 a_s = stride(a, win=(3, 3), stepby=(1, 1)) if a_s.ndim < 4: new_shape = (1, ) * (4 - len(a_s.shape)) + a_s.shape a_s = a_s.reshape(new_shape) f_dxyz = np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]], dtype="float64") a_s = a_s * f_dxyz # dz_dx, dz_dy = filter_a(a_s, a_filter=f_dxyz, cell_size=1) # asp = np.arctan2(dz_dy, -dz_dx) # relative to East # get the slope s = np.sqrt((dz_dx * cell_size)**2 + (dz_dy * cell_size)**2) asp = np.rad2deg(asp) asp = np.mod((450.0 - asp), 360.) # simplest way to get azimuth asp = np.where(s <= flat, -1, asp) if not keepdims: asp = np.squeeze(asp) if not degrees: asp = np.deg2rad(asp) return asp
def slope_a(a, cell_size=1, kern=None, degrees=True, verb=False, keep=False): """Return slope in degrees for an input array using 3rd order finite difference method for a 3x3 moing window view into the array. Requires: --------- - a : an input 2d array. X and Y represent coordinates of the Z values - cell_size : cell size, must be in the same units as X and Y - kern : kernel to use - degrees : True, returns degrees otherwise radians - verb : True, to print results - keep : False, to remove/squeeze extra dimensions - filter : np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]]) **current default Notes: ------ :: dzdx: sum(col2 - col0)/8*cellsize dzdy: sum(row2 - row0)/8*celsize Assert the array is ndim=4 even if (1,z,y,x) general dzdx + dzdy = dxyz [[a, b, c], [[1, 0, 1], [[1, 2, 1] [[1, 2, 1] [d, e, f] [2, 0, 2], + [0, 0, 0] = [2, 0, 2], [g, h, i] [1, 0, 1]] [1, 2, 1]] [1, 2, 1]] """ frmt = """\n :----------------------------------------: :{}\n :input array...\n {}\n :slope values...\n {!r:} :----------------------------------------: """ # ---- stride the data and calculate slope for 3x3 sliding windows ---- np.set_printoptions(edgeitems=10, linewidth=100, precision=1) a_s = stride(a, win=(3, 3), stepby=(1, 1)) if a_s.ndim < 4: new_shape = (1,) * (4-len(a_s.shape)) + a_s.shape a_s = a_s.reshape(new_shape) # kern = kernels(kern) # return the kernel if specified # ---- default filter, apply the filter to the array ---- # dz_dx, dz_dy = filter_a(a_s, a_filter=kern, cell_size=cell_size) # s = np.sqrt(dz_dx**2 + dz_dy**2) if degrees: s = np.rad2deg(np.arctan(s)) if not keep: s = np.squeeze(s) if verb: p = " " args = ["Results for slope_a... ", indent(str(a), p), indent(str(s), p)] print(dedent(frmt).format(*args)) return s
def slope_a(a, cell_size=1, degrees=True, verbose=False, keepdims=False): """Return slope in degrees for an input array using 3rd order : finite difference method for a 3x3 moing window view into the array. : :Requires: :-------- : a - an input 2d array. X and Y represent coordinates of the Z values : cell_size - must be in the same units as X and Y : degrees - True, returns degrees otherwise radians : verbose - True, to print results : keepdims - False, to remove/squeeze extra dimensions : filter - np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]]) **current default : :Notes: dzdx: sum(col2 - col0)/8*cellsize :----- dzdy: sum(row2 - row0)/8*celsize : Assert the array is ndim=4 even if (1,z,y,x) : general dzdx + dzdy = dxyz : [[a, b, c], [[1, 0, 1], [[1, 2, 1] [[1, 2, 1] : [d, e, f] [2, 0, 2], + [0, 0, 0] = [2, 0, 2], : [g, h, i] [1, 0, 1]] [1, 2, 1]] [1, 2, 1]] : """ frmt = """\n :----------------------------------------: :{}\n :input array...\n {}\n :slope values...\n {!r:} :----------------------------------------: """ # ---- stride the data and calculate slope for 3x3 sliding windows ---- np.set_printoptions(edgeitems=10, linewidth=100, precision=1) a_s = stride(a, win=(3, 3), stepby=(1, 1)) if a_s.ndim < 4: new_shape = (1, ) * (4 - len(a_s.shape)) + a_s.shape a_s = a_s.reshape(new_shape) f_dxyz = np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]], dtype="float64") # ---- default filter, apply the filter to the array ---- # dz_dx, dz_dy = filter_a(a_s, a_filter=f_dxyz, cell_size=cell_size) # s = np.sqrt(dz_dx**2 + dz_dy**2) if degrees: s = np.rad2deg(np.arctan(s)) if not keepdims: s = np.squeeze(s) if verbose: from textwrap import indent, dedent # if not imported p = " " args = [ "Results for slope_a... ", indent(str(a), p), indent(str(s), p) ] print(dedent(frmt).format(*args)) return s
def expand_zone(a, zone=None, win=2): """Expand a value (zone) in a 2D array, normally assumed to represent a raster surface. zone : number The value/class to expand into the surrounding cells win : list/tuple select a (2, 2) or (3, 3) moving window """ msg = "\nYou need a zone that is within the range of values." if zone is None: print(msg) return a, None if (zone < a.min()) or (zone > a.max()): print(msg) return a, None if win not in (2, 3): win = 2 p = [1, 0][win == 2] # check for 2 or 3 in win ap = np.pad(a, pad_width=(1, p), mode="constant", constant_values=(0, 0)) # n, m = ap.shape if win == 2: a_c = ap[1:, 1:] # for 2x2 even elif win == 3: a_c = ap[1:-1, 1:-1] # for 3x3 odd a_s = stride(ap, win=(win, win), stepby=(win, win)) # stride the array r, c = a_s.shape[:2] out = [] x = a_s.shape[0] y = a_s.shape[1] for i in range(x): for j in range(y): if zone in a_s[i, j]: out.append(1) else: out.append(0) out1 = np.asarray(out).reshape(r, c) out = np.repeat(np.repeat(out1, 2, axis=1), 2, axis=0) dx, dy = np.array(out.shape) - np.array(a.shape) if dx != 0: out = out[:dx, :dy] final = np.where(out == 1, zone, a_c) return final
def expand_zone(a, zone=None, win=2): """Expand a value (zone) in a 2D array, normally assumed to represent a raster surface. zone : number The value/class to expand into the surrounding cells win : list/tuple select a (2, 2) or (3, 3) moving window """ msg = "\nYou need a zone that is within the range of values." if zone is None: print(msg) return a, None if (zone < a.min()) or (zone > a.max()): print(msg) return a, None if win not in (2, 3): win = 2 p = [1, 0][win == 2] # check for 2 or 3 in win ap = np.pad(a, pad_width=(1, p), mode="constant", constant_values=(0, 0)) # n, m = ap.shape if win == 2: a_c = ap[1:, 1:] # for 2x2 even elif win == 3: a_c = ap[1:-1, 1:-1] # for 3x3 odd a_s = stride(ap, win=(win, win), stepby=(win, win)) # stride the array r, c = a_s.shape[:2] out = [] x = a_s.shape[0] y = a_s.shape[1] for i in range(x): for j in range(y): if zone in a_s[i, j]: out.append(1) else: out.append(0) out1 = np.asarray(out).reshape(r, c) out = np.repeat(np.repeat(out1, 2, axis=1), 2, axis=0) dx, dy = np.array(out.shape) - np.array(a.shape) if dx != 0: out = out[:dx, :dy] final = np.where(out == 1, zone, a_c) return final
def aspect_a(a, cell_size=1, flat=0.1, degrees=True, keepdims=False): """Return the aspect of a slope in degrees from North. Requires: -------- - a : an input 2d array. X and Y represent coordinates of the Z values - cell_size : needed to proper flat calculation - flat : degree value, e.g. flat surface <= 0.05 deg 0.05 deg => 8.7e-04 rad 0.10 deg => 1.7e-02 rad """ if not isinstance(flat, (int, float)): flat = 0.1 a_s = stride(a, win=(3, 3), stepby=(1, 1)) if a_s.ndim < 4: new_shape = (1,) * (4-len(a_s.shape)) + a_s.shape a_s = a_s.reshape(new_shape) f_dxyz = np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]], dtype="float64") a_s = a_s * f_dxyz # dz_dx, dz_dy = filter_a(a_s, a_filter=f_dxyz, cell_size=1) # asp = np.arctan2(dz_dy, -dz_dx) # relative to East # get the slope s = np.sqrt((dz_dx*cell_size)**2 + (dz_dy*cell_size)**2) asp = np.rad2deg(asp) asp = np.mod((450.0 - asp), 360.) # simplest way to get azimuth asp = np.where(s <= flat, -1, asp) if not keepdims: asp = np.squeeze(asp) if not degrees: asp = np.deg2rad(asp) return asp
def a_filter(a, mode=1, pad_output=True, ignore_nodata=True, nodata=None): """Various filters applied to an array. Requires: -------- stride : function the `stride` function is used internally in this function a : array an array that will be strided using a 3x3 window pad_output : boolean True, produces a masked array padded so that the shape is the same as the input ignore_nodata : boolean True, all values used, False, array contains nodata nodata : None or number None : max int or float used value : use this value in integer or float form otherwise mode : a dictionary containing a choice from :: 1. `all_f` : all 1's 2. `no_cnt` : no center 3. `cross_f` : cross filter, corners 4. `plus_f` : up down, left right 5. `gradient` : `6 7 8 9 10` directional gradients 11. `lap_33` : laplacian 12. `line_h` : line detection, horizonal 13. `line_ld` : line detection, left diagonal 14. `line_rd` : line detection, right diagonal 15. `line_v` : line detection, vertical 16. `high` : 17. `sob_hor` : Sobel horizontal 18. `sob_vert` : Sobel vertical 19. `emboss` : 20. `sharp1` : sharpen 1 22. `sharp2` : sharpen 2 23. `sharp3` : sharpen 3 highpass 3x3 24. `lowpass` : lowpass filter Notes: ----- Only 3x3 filters covered here. The output array is padded with np.nan and the array is returned as a masked array. >>> a0 = pyramid(core=4, steps=5, incr=(1, 1)) >>> a0 = a0 * 2 # multiply by a number to increase slope >>> a0 = (pyramid(core=4, steps=5, incr=(1, 1)) + 1) * 2 # is also good! References: ---------- .. [1] http://desktop.arcgis.com/en/arcmap/latest/tools/spatial-analyst-toolbox/how-filter-works.htm [2] http://desktop.arcgis.com/en/arcmap/latest/manage-data/raster-and-images/convolution-function.htm [3] https://github.com/scikit-image/scikit-image/tree/master/skimage/filters """ n = np.nan all_f = [1, 1, 1, 1, 1, 1, 1, 1, 1] no_cnt = [1, 1, 1, 1, n, 1, 1, 1, 1] cross_f = [1, 0, 1, 0, 0, 0, 1, 0, 1] plus_f = [0, 1, 0, 1, 0, 1, 0, 1, 0] grad_e = [1, 0, -1, 2, 0, -2, 1, 0, -1] grad_n = [-1, -2, -1, 0, 0, 0, 1, 2, 1] grad_ne = [0, -1, -2, 1, 0, -1, 2, 1, 0] grad_nw = [2, -1, 0, -1, 0, 1, 0, 1, 2] grad_s = [1, 2, 1, 0, 0, 0, -1, -2, -1] grad_w = [-1, 0, 1, -2, 0, 2, -1, 0, 1] lap_33 = [0, -1, 0, -1, 4, -1, 0, -1, 0] line_h = [-1, -1, -1, 2, 2, 2, -1, -1, -1] line_ld = [2, -1, -1, -1, 2, -1, -1, -1, 2] line_rd = [-1, -1, 2, -1, 2, -1, 2, -1, -1] line_v = [-1, 0, -1, -1, 2, -1, -1, 2, -1] high = [-0.7, -1.0, -0.7, -1.0, 6.8, -1.0, -0.7, -1.0, -0.7] # arc sob_hor = [1, 2, 1, 0, 0, 0, -1, -2, -1] # sobel y /4.0 weights sob_vert = [1, 0, -1, 2, 0, -2, 1, 0, -1] # sobel x /4 emboss = [-1, -1, 0, -1, 0, 1, 0, 1, 1] sharp1 = [0., -0.25, 0., -0.25, 2.0, -0.25, 0., -0.25, 0.] # arc sharp2 = [-0.25, -0.25, -0.25, -0.25, 3.0, -0.25, -0.25, -0.25, -0.25] sharp3 = [-1, -1, -1, -1, 9, -1, -1, -1, -1] # arc lowpass = [1, 2, 1, 2, 4, 2, 1, 2, 1] # arc # ---- assemble the dictionary ---- d = {1: all_f, 2: no_cnt, 3: cross_f, 4: plus_f, 5: grad_e, 6: grad_n, 7: grad_ne, 8: grad_nw, 9: grad_s, 10: grad_w, 11: lap_33, 12: line_h, 13: line_ld, 14: line_rd, 15: line_v, 16: high, 17: sob_hor, 18: sob_vert, 19: emboss, 20: sharp1, 21: sharp2, 23: sharp3, 24: lowpass} filter_ = np.array(d[mode]).reshape(3, 3) # ---- stride the input array ---- a_strided = stride(a) if ignore_nodata: c = np.sum(a_strided * filter_, axis=(2, 3)) else: c = np.nansum(a_strided * filter_, axis=(2, 3)) if pad_output: pad_ = nodata if nodata is None: if c.dtype.name in ('int', 'int32', 'int64'): pad_ = min([0, -1, a.min()-1]) else: pad_ = min([0.0, -1.0, a.min()-1]) c = np.lib.pad(c, (1, 1), "constant", constant_values=(pad_, pad_)) m = np.where(c == pad_, 1, 0) c = np.ma.array(c, mask=m, fill_value=None) return c
def a_filter(a, mode=1, pad_output=True, ignore_nodata=True, nodata=None): """Various filters applied to an array. Requires: -------- stride : function the `stride` function is used internally in this function a : array an array that will be strided using a 3x3 window pad_output : boolean True, produces a masked array padded so that the shape is the same as the input ignore_nodata : boolean True, all values used, False, array contains nodata nodata : None or number None : max int or float used value : use this value in integer or float form otherwise mode : a dictionary containing a choice from :: 1. `all_f` : all 1's 2. `no_cnt` : no center 3. `cross_f` : cross filter, corners 4. `plus_f` : up down, left right 5. `gradient` : `6 7 8 9 10` directional gradients 11. `lap_33` : laplacian 12. `line_h` : line detection, horizonal 13. `line_ld` : line detection, left diagonal 14. `line_rd` : line detection, right diagonal 15. `line_v` : line detection, vertical 16. `high` : 17. `sob_hor` : Sobel horizontal 18. `sob_vert` : Sobel vertical 19. `emboss` : 20. `sharp1` : sharpen 1 22. `sharp2` : sharpen 2 23. `sharp3` : sharpen 3 highpass 3x3 24. `lowpass` : lowpass filter Notes: ----- Only 3x3 filters covered here. The output array is padded with np.nan and the array is returned as a masked array. >>> a0 = pyramid(core=4, steps=5, incr=(1, 1)) >>> a0 = a0 * 2 # multiply by a number to increase slope >>> a0 = (pyramid(core=4, steps=5, incr=(1, 1)) + 1) * 2 # is also good! References: ---------- .. [1] http://desktop.arcgis.com/en/arcmap/latest/tools/spatial-analyst-toolbox/how-filter-works.htm [2] http://desktop.arcgis.com/en/arcmap/latest/manage-data/raster-and-images/convolution-function.htm [3] https://github.com/scikit-image/scikit-image/tree/master/skimage/filters """ n = np.nan all_f = [1, 1, 1, 1, 1, 1, 1, 1, 1] no_cnt = [1, 1, 1, 1, n, 1, 1, 1, 1] cross_f = [1, 0, 1, 0, 0, 0, 1, 0, 1] plus_f = [0, 1, 0, 1, 0, 1, 0, 1, 0] grad_e = [1, 0, -1, 2, 0, -2, 1, 0, -1] grad_n = [-1, -2, -1, 0, 0, 0, 1, 2, 1] grad_ne = [0, -1, -2, 1, 0, -1, 2, 1, 0] grad_nw = [2, -1, 0, -1, 0, 1, 0, 1, 2] grad_s = [1, 2, 1, 0, 0, 0, -1, -2, -1] grad_w = [-1, 0, 1, -2, 0, 2, -1, 0, 1] lap_33 = [0, -1, 0, -1, 4, -1, 0, -1, 0] line_h = [-1, -1, -1, 2, 2, 2, -1, -1, -1] line_ld = [2, -1, -1, -1, 2, -1, -1, -1, 2] line_rd = [-1, -1, 2, -1, 2, -1, 2, -1, -1] line_v = [-1, 0, -1, -1, 2, -1, -1, 2, -1] high = [-0.7, -1.0, -0.7, -1.0, 6.8, -1.0, -0.7, -1.0, -0.7] # arc sob_hor = [1, 2, 1, 0, 0, 0, -1, -2, -1] # sobel y /4.0 weights sob_vert = [1, 0, -1, 2, 0, -2, 1, 0, -1] # sobel x /4 emboss = [-1, -1, 0, -1, 0, 1, 0, 1, 1] sharp1 = [0., -0.25, 0., -0.25, 2.0, -0.25, 0., -0.25, 0.] # arc sharp2 = [-0.25, -0.25, -0.25, -0.25, 3.0, -0.25, -0.25, -0.25, -0.25] sharp3 = [-1, -1, -1, -1, 9, -1, -1, -1, -1] # arc lowpass = [1, 2, 1, 2, 4, 2, 1, 2, 1] # arc # ---- assemble the dictionary ---- d = { 1: all_f, 2: no_cnt, 3: cross_f, 4: plus_f, 5: grad_e, 6: grad_n, 7: grad_ne, 8: grad_nw, 9: grad_s, 10: grad_w, 11: lap_33, 12: line_h, 13: line_ld, 14: line_rd, 15: line_v, 16: high, 17: sob_hor, 18: sob_vert, 19: emboss, 20: sharp1, 21: sharp2, 23: sharp3, 24: lowpass } filter_ = np.array(d[mode]).reshape(3, 3) # ---- stride the input array ---- a_strided = stride(a) if ignore_nodata: c = np.sum(a_strided * filter_, axis=(2, 3)) else: c = np.nansum(a_strided * filter_, axis=(2, 3)) if pad_output: pad_ = nodata if nodata is None: if c.dtype.name in ('int', 'int32', 'int64'): pad_ = min([0, -1, a.min() - 1]) else: pad_ = min([0.0, -1.0, a.min() - 1]) c = np.lib.pad(c, (1, 1), "constant", constant_values=(pad_, pad_)) m = np.where(c == pad_, 1, 0) c = np.ma.array(c, mask=m, fill_value=None) return c