BERATools 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. beratools/__init__.py +1 -7
  2. beratools/core/algo_centerline.py +491 -351
  3. beratools/core/algo_common.py +497 -0
  4. beratools/core/algo_cost.py +192 -0
  5. beratools/core/{dijkstra_algorithm.py → algo_dijkstra.py} +503 -460
  6. beratools/core/algo_footprint_rel.py +577 -0
  7. beratools/core/algo_line_grouping.py +944 -0
  8. beratools/core/algo_merge_lines.py +214 -0
  9. beratools/core/algo_split_with_lines.py +304 -0
  10. beratools/core/algo_tiler.py +428 -0
  11. beratools/core/algo_vertex_optimization.py +469 -0
  12. beratools/core/constants.py +52 -86
  13. beratools/core/logger.py +76 -85
  14. beratools/core/tool_base.py +196 -133
  15. beratools/gui/__init__.py +11 -15
  16. beratools/gui/{beratools.json → assets/beratools.json} +2185 -2300
  17. beratools/gui/batch_processing_dlg.py +513 -463
  18. beratools/gui/bt_data.py +481 -487
  19. beratools/gui/bt_gui_main.py +710 -691
  20. beratools/gui/main.py +26 -0
  21. beratools/gui/map_window.py +162 -146
  22. beratools/gui/tool_widgets.py +725 -493
  23. beratools/tools/Beratools_r_script.r +1120 -1120
  24. beratools/tools/Ht_metrics.py +116 -116
  25. beratools/tools/__init__.py +7 -7
  26. beratools/tools/batch_processing.py +136 -132
  27. beratools/tools/canopy_threshold_relative.py +672 -670
  28. beratools/tools/canopycostraster.py +222 -222
  29. beratools/tools/centerline.py +136 -176
  30. beratools/tools/common.py +857 -885
  31. beratools/tools/fl_regen_csf.py +428 -428
  32. beratools/tools/forest_line_attributes.py +408 -408
  33. beratools/tools/line_footprint_absolute.py +213 -363
  34. beratools/tools/line_footprint_fixed.py +436 -282
  35. beratools/tools/line_footprint_functions.py +733 -720
  36. beratools/tools/line_footprint_relative.py +73 -64
  37. beratools/tools/line_grouping.py +45 -0
  38. beratools/tools/ln_relative_metrics.py +615 -615
  39. beratools/tools/r_cal_lpi_elai.r +24 -24
  40. beratools/tools/r_generate_pd_focalraster.r +100 -100
  41. beratools/tools/r_interface.py +79 -79
  42. beratools/tools/r_point_density.r +8 -8
  43. beratools/tools/rpy_chm2trees.py +86 -86
  44. beratools/tools/rpy_dsm_chm_by.py +81 -81
  45. beratools/tools/rpy_dtm_by.py +63 -63
  46. beratools/tools/rpy_find_cellsize.py +43 -43
  47. beratools/tools/rpy_gnd_csf.py +74 -74
  48. beratools/tools/rpy_hummock_hollow.py +85 -85
  49. beratools/tools/rpy_hummock_hollow_raster.py +71 -71
  50. beratools/tools/rpy_las_info.py +51 -51
  51. beratools/tools/rpy_laz2las.py +40 -40
  52. beratools/tools/rpy_lpi_elai_lascat.py +466 -466
  53. beratools/tools/rpy_normalized_lidar_by.py +56 -56
  54. beratools/tools/rpy_percent_above_dbh.py +80 -80
  55. beratools/tools/rpy_points2trees.py +88 -88
  56. beratools/tools/rpy_vegcoverage.py +94 -94
  57. beratools/tools/tiler.py +48 -206
  58. beratools/tools/tool_template.py +69 -54
  59. beratools/tools/vertex_optimization.py +61 -620
  60. beratools/tools/zonal_threshold.py +144 -144
  61. beratools-0.2.2.dist-info/METADATA +108 -0
  62. beratools-0.2.2.dist-info/RECORD +74 -0
  63. {beratools-0.2.0.dist-info → beratools-0.2.2.dist-info}/WHEEL +1 -1
  64. {beratools-0.2.0.dist-info → beratools-0.2.2.dist-info}/licenses/LICENSE +22 -22
  65. beratools/gui/cli.py +0 -18
  66. beratools/gui/gui.json +0 -8
  67. beratools/gui_tk/ASCII Banners.txt +0 -248
  68. beratools/gui_tk/__init__.py +0 -20
  69. beratools/gui_tk/beratools_main.py +0 -515
  70. beratools/gui_tk/bt_widgets.py +0 -442
  71. beratools/gui_tk/cli.py +0 -18
  72. beratools/gui_tk/img/BERALogo.png +0 -0
  73. beratools/gui_tk/img/closed.gif +0 -0
  74. beratools/gui_tk/img/closed.png +0 -0
  75. beratools/gui_tk/img/open.gif +0 -0
  76. beratools/gui_tk/img/open.png +0 -0
  77. beratools/gui_tk/img/tool.gif +0 -0
  78. beratools/gui_tk/img/tool.png +0 -0
  79. beratools/gui_tk/main.py +0 -14
  80. beratools/gui_tk/map_window.py +0 -144
  81. beratools/gui_tk/runner.py +0 -1481
  82. beratools/gui_tk/tooltip.py +0 -55
  83. beratools/third_party/pyqtlet2/__init__.py +0 -9
  84. beratools/third_party/pyqtlet2/leaflet/__init__.py +0 -26
  85. beratools/third_party/pyqtlet2/leaflet/control/__init__.py +0 -6
  86. beratools/third_party/pyqtlet2/leaflet/control/control.py +0 -59
  87. beratools/third_party/pyqtlet2/leaflet/control/draw.py +0 -52
  88. beratools/third_party/pyqtlet2/leaflet/control/layers.py +0 -20
  89. beratools/third_party/pyqtlet2/leaflet/core/Parser.py +0 -24
  90. beratools/third_party/pyqtlet2/leaflet/core/__init__.py +0 -2
  91. beratools/third_party/pyqtlet2/leaflet/core/evented.py +0 -180
  92. beratools/third_party/pyqtlet2/leaflet/layer/__init__.py +0 -5
  93. beratools/third_party/pyqtlet2/leaflet/layer/featuregroup.py +0 -34
  94. beratools/third_party/pyqtlet2/leaflet/layer/icon/__init__.py +0 -1
  95. beratools/third_party/pyqtlet2/leaflet/layer/icon/icon.py +0 -30
  96. beratools/third_party/pyqtlet2/leaflet/layer/imageoverlay.py +0 -18
  97. beratools/third_party/pyqtlet2/leaflet/layer/layer.py +0 -105
  98. beratools/third_party/pyqtlet2/leaflet/layer/layergroup.py +0 -45
  99. beratools/third_party/pyqtlet2/leaflet/layer/marker/__init__.py +0 -1
  100. beratools/third_party/pyqtlet2/leaflet/layer/marker/marker.py +0 -91
  101. beratools/third_party/pyqtlet2/leaflet/layer/tile/__init__.py +0 -2
  102. beratools/third_party/pyqtlet2/leaflet/layer/tile/gridlayer.py +0 -4
  103. beratools/third_party/pyqtlet2/leaflet/layer/tile/tilelayer.py +0 -16
  104. beratools/third_party/pyqtlet2/leaflet/layer/vector/__init__.py +0 -5
  105. beratools/third_party/pyqtlet2/leaflet/layer/vector/circle.py +0 -15
  106. beratools/third_party/pyqtlet2/leaflet/layer/vector/circlemarker.py +0 -18
  107. beratools/third_party/pyqtlet2/leaflet/layer/vector/path.py +0 -5
  108. beratools/third_party/pyqtlet2/leaflet/layer/vector/polygon.py +0 -14
  109. beratools/third_party/pyqtlet2/leaflet/layer/vector/polyline.py +0 -18
  110. beratools/third_party/pyqtlet2/leaflet/layer/vector/rectangle.py +0 -14
  111. beratools/third_party/pyqtlet2/leaflet/map/__init__.py +0 -1
  112. beratools/third_party/pyqtlet2/leaflet/map/map.py +0 -220
  113. beratools/third_party/pyqtlet2/mapwidget.py +0 -45
  114. beratools/third_party/pyqtlet2/web/custom.js +0 -43
  115. beratools/third_party/pyqtlet2/web/map.html +0 -23
  116. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers-2x.png +0 -0
  117. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers.png +0 -0
  118. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon-2x.png +0 -0
  119. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon.png +0 -0
  120. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-shadow.png +0 -0
  121. beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.css +0 -656
  122. beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.js +0 -6
  123. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.codeclimate.yml +0 -14
  124. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.editorconfig +0 -4
  125. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.gitattributes +0 -22
  126. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.travis.yml +0 -43
  127. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/LICENSE +0 -20
  128. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers-2x.png +0 -0
  129. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers.png +0 -0
  130. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon-2x.png +0 -0
  131. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon.png +0 -0
  132. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-shadow.png +0 -0
  133. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet-2x.png +0 -0
  134. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.png +0 -0
  135. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.svg +0 -156
  136. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.css +0 -10
  137. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.js +0 -10
  138. beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/LICENSE +0 -22
  139. beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/leaflet.rotatedMarker.js +0 -57
  140. beratools/tools/forest_line_ecosite.py +0 -216
  141. beratools/tools/lapis_all.py +0 -103
  142. beratools/tools/least_cost_path_from_chm.py +0 -152
  143. beratools-0.2.0.dist-info/METADATA +0 -63
  144. beratools-0.2.0.dist-info/RECORD +0 -142
  145. /beratools/gui/{img → assets}/BERALogo.png +0 -0
  146. /beratools/gui/{img → assets}/closed.gif +0 -0
  147. /beratools/gui/{img → assets}/closed.png +0 -0
  148. /beratools/{gui_tk → gui/assets}/gui.json +0 -0
  149. /beratools/gui/{img → assets}/open.gif +0 -0
  150. /beratools/gui/{img → assets}/open.png +0 -0
  151. /beratools/gui/{img → assets}/tool.gif +0 -0
  152. /beratools/gui/{img → assets}/tool.png +0 -0
  153. {beratools-0.2.0.dist-info → beratools-0.2.2.dist-info}/entry_points.txt +0 -0
@@ -1,222 +1,222 @@
1
- import os
2
- import sys
3
- import time
4
- import numpy as np
5
- from numpy.lib.stride_tricks import as_strided
6
-
7
- import rasterio
8
- import xarray as xr
9
- from xrspatial import convolution, focal
10
- from scipy import ndimage
11
-
12
- from common import *
13
-
14
-
15
- # TODO: Rolling Statistics for grid data... an alternative
16
- # by Dan Patterson
17
-
18
-
19
- def _check(a, r_c, subok=False):
20
- """Performs the array checks necessary for stride and block.
21
- : in_array - Array or list.
22
- : r_c - tuple/list/array of rows x cols.
23
- : subok - from numpy 1.12 added, keep for now
24
- :Returns:
25
- :------
26
- :Attempts will be made to ...
27
- : produce a shape at least (1*c). For a scalar, the
28
- : minimum shape will be (1*r) for 1D array or (1*c) for 2D
29
- : array if r<c. Be aware
30
- """
31
- if isinstance(r_c, (int, float)):
32
- r_c = (1, int(r_c))
33
-
34
- r, c = r_c
35
- if a.ndim == 1:
36
- a = np.atleast_2d(a)
37
-
38
- r, c = r_c = (min(r, a.shape[0]), min(c, a.shape[1]))
39
- a = np.array(a, copy=False, subok=subok)
40
- return a, r, c, tuple(r_c)
41
-
42
-
43
- def _pad(in_array, kernel):
44
- """Pad a sliding array to allow for stats"""
45
- pad_x = int(kernel.shape[0] / 2)
46
- pad_y = int(kernel.shape[0] / 2)
47
- result = np.pad(in_array, pad_width=(pad_x, pad_y), mode="constant", constant_values=(np.NaN, np.NaN))
48
-
49
- return result
50
-
51
-
52
- def stride(a, r_c):
53
- """Provide a 2D sliding/moving view of an array.
54
- : There is no edge correction for outputs.
55
- :
56
- :Requires:
57
- :--------
58
- : _check(a, r_c) ... Runs the checks on the inputs.
59
- : a - array or list, usually a 2D array. Assumes rows is >=1,
60
- : it is corrected as is the number of columns.
61
- : r_c - tuple/list/array of rows x cols. Attempts to
62
- : produce a shape at least (1*c). For a scalar, the
63
- : minimum shape will be (1*r) for 1D array or 2D
64
- : array if r<c. Be aware
65
- """
66
- a, r, c, r_c = _check(a, r_c)
67
- shape = (a.shape[0] - r + 1, a.shape[1] - c + 1) + r_c
68
- strides = a.strides * 2
69
- a_s = (as_strided(a, shape=shape, strides=strides)).squeeze()
70
- return a_s
71
-
72
-
73
- def normalize_chm(raster):
74
- n_raster = np.where(raster >= 0, raster, 0)
75
- return n_raster
76
-
77
-
78
- def np_cc_map(out_canopy_r, chm, in_array, min_ht):
79
- print('Generating Canopy Closure Raster ...')
80
-
81
- # canopy_ndarray = np.where(in_array >= min_ht, 1., 0.).astype(float)
82
- canopy_ndarray = np.ma.where(in_array > min_ht, 1., 0.).astype(float)
83
- canopy_ndarray = np.ma.filled(canopy_ndarray, chm.nodata)
84
- try:
85
- write_canopy = rasterio.open(out_canopy_r, 'w', **chm.profile)
86
- write_canopy.write(canopy_ndarray, 1)
87
- write_canopy.close()
88
- print('Generating Canopy Closure (CC) Raster ... Done')
89
- except Exception as e:
90
- print(sys.exc_info())
91
- del in_array
92
- return canopy_ndarray
93
-
94
-
95
- def fs_raster(in_ndarray, kernel):
96
- print('Generating Canopy Closure Focal Statistic ...')
97
- padded_array = _pad(in_ndarray, kernel)
98
- a_s = stride(padded_array, kernel.shape)
99
-
100
- # TODO: np.where on large ndarray fail (allocate memory error)
101
- a_s_masked = np.where(kernel == 1, a_s, np.NaN)
102
- print("Calculating Canopy Closure's Focal Statistic-Mean ...")
103
- mean_result = np.nanmean(a_s_masked, axis=(2, 3))
104
- print("Calculating Canopy Closure's Focal Statistic-Stand Deviation Raster ...")
105
- stdev_result = np.nanstd(a_s_masked, axis=(2, 3))
106
- del a_s
107
- return mean_result, stdev_result
108
-
109
-
110
- def fs_raster_stdmean(in_ndarray, kernel, nodata):
111
- # This function uses xrspatial whcih can handle large data but slow
112
- in_ndarray[in_ndarray == nodata] = np.nan
113
- result_ndarray = focal.focal_stats(xr.DataArray(in_ndarray), kernel, stats_funcs=['std', 'mean'])
114
-
115
- # Flattening the array
116
- flatten_std_result_ndarray = result_ndarray[0].data.reshape(-1)
117
- flatten_mean_result_ndarray = result_ndarray[1].data.reshape(-1)
118
-
119
- # Re-shaping the array
120
- reshape_std_ndarray = flatten_std_result_ndarray.reshape(in_ndarray.shape[0], in_ndarray.shape[1])
121
- reshape_mean_ndarray = flatten_mean_result_ndarray.reshape(in_ndarray.shape[0], in_ndarray.shape[1])
122
- return reshape_std_ndarray, reshape_mean_ndarray
123
-
124
-
125
- def smooth_cost(in_raster, search_dist, sampling):
126
- print('Generating Cost Raster ...')
127
- from tempfile import mkdtemp
128
- import os.path as path
129
- import shutil
130
-
131
- euc_dist_array = None
132
- row, col = in_raster.shape
133
- if row * col >= 30000 * 30000:
134
- filename = path.join(mkdtemp(), 'tempmmemap.dat')
135
- a_in_mat = np.memmap(filename, in_raster.dtype, 'w+', shape=in_raster.shape)
136
- a_in_mat[:] = in_raster[:]
137
- a_in_mat.flush()
138
- euc_dist_array = ndimage.distance_transform_edt(np.logical_not(a_in_mat), sampling=sampling)
139
- del a_in_mat, in_raster
140
- shutil.rmtree(path.dirname(filename))
141
- else:
142
- euc_dist_array = ndimage.distance_transform_edt(np.logical_not(in_raster), sampling=sampling)
143
-
144
- smooth1 = float(search_dist) - euc_dist_array
145
- smooth1[smooth1 <= 0.0] = 0.0
146
- smooth_cost_array = smooth1 / float(search_dist)
147
-
148
- return smooth_cost_array
149
-
150
-
151
- def np_cost_raster(canopy_ndarray, cc_mean, cc_std, cc_smooth, chm, avoidance, cost_raster_exponent, out_cost_r):
152
- print('Generating Smoothed Cost Raster ...')
153
- aM1a = (cc_mean - cc_std)
154
- aM1b = (cc_mean + cc_std)
155
- aM1 = np.divide(aM1a, aM1b, where=aM1b != 0, out=np.zeros(aM1a.shape, dtype=float))
156
- aM = (1 + aM1) / 2
157
- aaM = (cc_mean + cc_std)
158
- bM = np.where(aaM <= 0, 0, aM)
159
- cM = bM * (1 - avoidance) + (cc_smooth * avoidance)
160
- dM = np.where(canopy_ndarray == 1, 1, cM)
161
- eM = np.exp(dM)
162
- result = np.power(eM, float(cost_raster_exponent))
163
- write_cost = rasterio.open(out_cost_r, 'w+', driver='GTiff', height=chm.shape[0], width=chm.shape[1],
164
- count=1, dtype=chm.read(1).dtype, crs=chm.crs, transform=chm.transform)
165
- write_cost.write(result, 1)
166
- write_cost.close()
167
- print('Generating Smoothed Cost Raster ... Done')
168
- return
169
-
170
-
171
- # TODO: deal with NODATA
172
- def canopy_cost_raster(callback, in_chm, canopy_ht_threshold, tree_radius, max_line_dist,
173
- canopy_avoidance, exponent, out_canopy, out_cost, processes, verbose):
174
- canopy_ht_threshold = float(canopy_ht_threshold)
175
- tree_radius = float(tree_radius)
176
- max_line_dist = float(max_line_dist)
177
- canopy_avoidance = float(canopy_avoidance)
178
- cost_raster_exponent = float(exponent)
179
-
180
- print('In CHM: ' + in_chm)
181
- chm = rasterio.open(in_chm)
182
- (cell_x, cell_y) = chm.res
183
-
184
- print('Loading CHM ...')
185
- band1_ndarray = chm.read(1, masked=True)
186
- print('%{}'.format(10))
187
-
188
- print('Preparing Kernel window ...')
189
- kernel = convolution.circle_kernel(cell_x, cell_y, tree_radius)
190
- print('%{}'.format(20))
191
-
192
- # Generate Canopy Raster and return the Canopy array
193
- canopy_ndarray = np_cc_map(out_canopy, chm, band1_ndarray, canopy_ht_threshold)
194
- print('%{}'.format(40))
195
- print('Apply focal statistic on raster ...')
196
-
197
- # Calculating focal statistic from canopy raster
198
- # Alternative: (only work on large cell size
199
- if cell_y > 1 and cell_x > 1:
200
- cc_mean, cc_std = fs_raster(canopy_ndarray, kernel)
201
- else:
202
- cc_std, cc_mean = fs_raster_stdmean(canopy_ndarray, kernel, chm.nodata)
203
- print('%{}'.format(60))
204
- print('Apply focal statistic on raster ... Done')
205
-
206
- # Smoothing raster
207
- cc_smooth = smooth_cost(canopy_ndarray, max_line_dist, [cell_x, cell_y])
208
- avoidance = max(min(float(canopy_avoidance), 1), 0)
209
- np_cost_raster(canopy_ndarray, cc_mean, cc_std, cc_smooth, chm, avoidance, cost_raster_exponent, out_cost)
210
- print('%{}'.format(100))
211
-
212
-
213
- if __name__ == '__main__':
214
- in_args, in_verbose = check_arguments()
215
-
216
- start_time = time.time()
217
- print('Starting Canopy and Cost Raster processing\n @ {}'
218
- .format(time.strftime("%d %b %Y %H:%M:%S", time.localtime())))
219
-
220
- canopy_cost_raster(print, **in_args.input, processes=int(in_args.processes), verbose=in_verbose)
221
- print('Canopy and Cost Raster processing is done in {} seconds)'
222
- .format(round(time.time() - start_time, 5)))
1
+ import os
2
+ import sys
3
+ import time
4
+ import numpy as np
5
+ from numpy.lib.stride_tricks import as_strided
6
+
7
+ import rasterio
8
+ import xarray as xr
9
+ from xrspatial import convolution, focal
10
+ from scipy import ndimage
11
+
12
+ from common import *
13
+
14
+
15
+ # TODO: Rolling Statistics for grid data... an alternative
16
+ # by Dan Patterson
17
+
18
+
19
+ def _check(a, r_c, subok=False):
20
+ """Performs the array checks necessary for stride and block.
21
+ : in_array - Array or list.
22
+ : r_c - tuple/list/array of rows x cols.
23
+ : subok - from numpy 1.12 added, keep for now
24
+ :Returns:
25
+ :------
26
+ :Attempts will be made to ...
27
+ : produce a shape at least (1*c). For a scalar, the
28
+ : minimum shape will be (1*r) for 1D array or (1*c) for 2D
29
+ : array if r<c. Be aware
30
+ """
31
+ if isinstance(r_c, (int, float)):
32
+ r_c = (1, int(r_c))
33
+
34
+ r, c = r_c
35
+ if a.ndim == 1:
36
+ a = np.atleast_2d(a)
37
+
38
+ r, c = r_c = (min(r, a.shape[0]), min(c, a.shape[1]))
39
+ a = np.array(a, copy=False, subok=subok)
40
+ return a, r, c, tuple(r_c)
41
+
42
+
43
+ def _pad(in_array, kernel):
44
+ """Pad a sliding array to allow for stats"""
45
+ pad_x = int(kernel.shape[0] / 2)
46
+ pad_y = int(kernel.shape[0] / 2)
47
+ result = np.pad(in_array, pad_width=(pad_x, pad_y), mode="constant", constant_values=(np.NaN, np.NaN))
48
+
49
+ return result
50
+
51
+
52
+ def stride(a, r_c):
53
+ """Provide a 2D sliding/moving view of an array.
54
+ : There is no edge correction for outputs.
55
+ :
56
+ :Requires:
57
+ :--------
58
+ : _check(a, r_c) ... Runs the checks on the inputs.
59
+ : a - array or list, usually a 2D array. Assumes rows is >=1,
60
+ : it is corrected as is the number of columns.
61
+ : r_c - tuple/list/array of rows x cols. Attempts to
62
+ : produce a shape at least (1*c). For a scalar, the
63
+ : minimum shape will be (1*r) for 1D array or 2D
64
+ : array if r<c. Be aware
65
+ """
66
+ a, r, c, r_c = _check(a, r_c)
67
+ shape = (a.shape[0] - r + 1, a.shape[1] - c + 1) + r_c
68
+ strides = a.strides * 2
69
+ a_s = (as_strided(a, shape=shape, strides=strides)).squeeze()
70
+ return a_s
71
+
72
+
73
+ def normalize_chm(raster):
74
+ n_raster = np.where(raster >= 0, raster, 0)
75
+ return n_raster
76
+
77
+
78
+ def np_cc_map(out_canopy_r, chm, in_array, min_ht):
79
+ print('Generating Canopy Closure Raster ...')
80
+
81
+ # canopy_ndarray = np.where(in_array >= min_ht, 1., 0.).astype(float)
82
+ canopy_ndarray = np.ma.where(in_array > min_ht, 1., 0.).astype(float)
83
+ canopy_ndarray = np.ma.filled(canopy_ndarray, chm.nodata)
84
+ try:
85
+ write_canopy = rasterio.open(out_canopy_r, 'w', **chm.profile)
86
+ write_canopy.write(canopy_ndarray, 1)
87
+ write_canopy.close()
88
+ print('Generating Canopy Closure (CC) Raster ... Done')
89
+ except Exception as e:
90
+ print(sys.exc_info())
91
+ del in_array
92
+ return canopy_ndarray
93
+
94
+
95
+ def fs_raster(in_ndarray, kernel):
96
+ print('Generating Canopy Closure Focal Statistic ...')
97
+ padded_array = _pad(in_ndarray, kernel)
98
+ a_s = stride(padded_array, kernel.shape)
99
+
100
+ # TODO: np.where on large ndarray fail (allocate memory error)
101
+ a_s_masked = np.where(kernel == 1, a_s, np.NaN)
102
+ print("Calculating Canopy Closure's Focal Statistic-Mean ...")
103
+ mean_result = np.nanmean(a_s_masked, axis=(2, 3))
104
+ print("Calculating Canopy Closure's Focal Statistic-Stand Deviation Raster ...")
105
+ stdev_result = np.nanstd(a_s_masked, axis=(2, 3))
106
+ del a_s
107
+ return mean_result, stdev_result
108
+
109
+
110
+ def fs_raster_stdmean(in_ndarray, kernel, nodata):
111
+ # This function uses xrspatial whcih can handle large data but slow
112
+ in_ndarray[in_ndarray == nodata] = np.nan
113
+ result_ndarray = focal.focal_stats(xr.DataArray(in_ndarray), kernel, stats_funcs=['std', 'mean'])
114
+
115
+ # Flattening the array
116
+ flatten_std_result_ndarray = result_ndarray[0].data.reshape(-1)
117
+ flatten_mean_result_ndarray = result_ndarray[1].data.reshape(-1)
118
+
119
+ # Re-shaping the array
120
+ reshape_std_ndarray = flatten_std_result_ndarray.reshape(in_ndarray.shape[0], in_ndarray.shape[1])
121
+ reshape_mean_ndarray = flatten_mean_result_ndarray.reshape(in_ndarray.shape[0], in_ndarray.shape[1])
122
+ return reshape_std_ndarray, reshape_mean_ndarray
123
+
124
+
125
+ def smooth_cost(in_raster, search_dist, sampling):
126
+ print('Generating Cost Raster ...')
127
+ from tempfile import mkdtemp
128
+ import os.path as path
129
+ import shutil
130
+
131
+ euc_dist_array = None
132
+ row, col = in_raster.shape
133
+ if row * col >= 30000 * 30000:
134
+ filename = path.join(mkdtemp(), 'tempmmemap.dat')
135
+ a_in_mat = np.memmap(filename, in_raster.dtype, 'w+', shape=in_raster.shape)
136
+ a_in_mat[:] = in_raster[:]
137
+ a_in_mat.flush()
138
+ euc_dist_array = ndimage.distance_transform_edt(np.logical_not(a_in_mat), sampling=sampling)
139
+ del a_in_mat, in_raster
140
+ shutil.rmtree(path.dirname(filename))
141
+ else:
142
+ euc_dist_array = ndimage.distance_transform_edt(np.logical_not(in_raster), sampling=sampling)
143
+
144
+ smooth1 = float(search_dist) - euc_dist_array
145
+ smooth1[smooth1 <= 0.0] = 0.0
146
+ smooth_cost_array = smooth1 / float(search_dist)
147
+
148
+ return smooth_cost_array
149
+
150
+
151
+ def np_cost_raster(canopy_ndarray, cc_mean, cc_std, cc_smooth, chm, avoidance, cost_raster_exponent, out_cost_r):
152
+ print('Generating Smoothed Cost Raster ...')
153
+ aM1a = (cc_mean - cc_std)
154
+ aM1b = (cc_mean + cc_std)
155
+ aM1 = np.divide(aM1a, aM1b, where=aM1b != 0, out=np.zeros(aM1a.shape, dtype=float))
156
+ aM = (1 + aM1) / 2
157
+ aaM = (cc_mean + cc_std)
158
+ bM = np.where(aaM <= 0, 0, aM)
159
+ cM = bM * (1 - avoidance) + (cc_smooth * avoidance)
160
+ dM = np.where(canopy_ndarray == 1, 1, cM)
161
+ eM = np.exp(dM)
162
+ result = np.power(eM, float(cost_raster_exponent))
163
+ write_cost = rasterio.open(out_cost_r, 'w+', driver='GTiff', height=chm.shape[0], width=chm.shape[1],
164
+ count=1, dtype=chm.read(1).dtype, crs=chm.crs, transform=chm.transform)
165
+ write_cost.write(result, 1)
166
+ write_cost.close()
167
+ print('Generating Smoothed Cost Raster ... Done')
168
+ return
169
+
170
+
171
+ # TODO: deal with NODATA
172
+ def canopy_cost_raster(callback, in_chm, canopy_ht_threshold, tree_radius, max_line_dist,
173
+ canopy_avoidance, exponent, out_canopy, out_cost, processes, verbose):
174
+ canopy_ht_threshold = float(canopy_ht_threshold)
175
+ tree_radius = float(tree_radius)
176
+ max_line_dist = float(max_line_dist)
177
+ canopy_avoidance = float(canopy_avoidance)
178
+ cost_raster_exponent = float(exponent)
179
+
180
+ print('In CHM: ' + in_chm)
181
+ chm = rasterio.open(in_chm)
182
+ (cell_x, cell_y) = chm.res
183
+
184
+ print('Loading CHM ...')
185
+ band1_ndarray = chm.read(1, masked=True)
186
+ print('%{}'.format(10))
187
+
188
+ print('Preparing Kernel window ...')
189
+ kernel = convolution.circle_kernel(cell_x, cell_y, tree_radius)
190
+ print('%{}'.format(20))
191
+
192
+ # Generate Canopy Raster and return the Canopy array
193
+ canopy_ndarray = np_cc_map(out_canopy, chm, band1_ndarray, canopy_ht_threshold)
194
+ print('%{}'.format(40))
195
+ print('Apply focal statistic on raster ...')
196
+
197
+ # Calculating focal statistic from canopy raster
198
+ # Alternative: (only work on large cell size
199
+ if cell_y > 1 and cell_x > 1:
200
+ cc_mean, cc_std = fs_raster(canopy_ndarray, kernel)
201
+ else:
202
+ cc_std, cc_mean = fs_raster_stdmean(canopy_ndarray, kernel, chm.nodata)
203
+ print('%{}'.format(60))
204
+ print('Apply focal statistic on raster ... Done')
205
+
206
+ # Smoothing raster
207
+ cc_smooth = smooth_cost(canopy_ndarray, max_line_dist, [cell_x, cell_y])
208
+ avoidance = max(min(float(canopy_avoidance), 1), 0)
209
+ np_cost_raster(canopy_ndarray, cc_mean, cc_std, cc_smooth, chm, avoidance, cost_raster_exponent, out_cost)
210
+ print('%{}'.format(100))
211
+
212
+
213
+ if __name__ == '__main__':
214
+ in_args, in_verbose = check_arguments()
215
+
216
+ start_time = time.time()
217
+ print('Starting Canopy and Cost Raster processing\n @ {}'
218
+ .format(time.strftime("%d %b %Y %H:%M:%S", time.localtime())))
219
+
220
+ canopy_cost_raster(print, **in_args.input, processes=int(in_args.processes), verbose=in_verbose)
221
+ print('Canopy and Cost Raster processing is done in {} seconds)'
222
+ .format(round(time.time() - start_time, 5)))