BERATools 0.2.3__py3-none-any.whl → 0.2.5__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 (78) hide show
  1. beratools/__init__.py +8 -3
  2. beratools/core/{algo_footprint_rel.py → algo_canopy_footprint_exp.py} +176 -139
  3. beratools/core/algo_centerline.py +61 -77
  4. beratools/core/algo_common.py +48 -57
  5. beratools/core/algo_cost.py +18 -25
  6. beratools/core/algo_dijkstra.py +37 -45
  7. beratools/core/algo_line_grouping.py +100 -100
  8. beratools/core/algo_merge_lines.py +40 -8
  9. beratools/core/algo_split_with_lines.py +289 -304
  10. beratools/core/algo_vertex_optimization.py +25 -46
  11. beratools/core/canopy_threshold_relative.py +755 -0
  12. beratools/core/constants.py +8 -9
  13. beratools/{tools → core}/line_footprint_functions.py +411 -258
  14. beratools/core/logger.py +18 -2
  15. beratools/core/tool_base.py +17 -75
  16. beratools/gui/assets/BERALogo.ico +0 -0
  17. beratools/gui/assets/BERA_Splash.gif +0 -0
  18. beratools/gui/assets/BERA_WizardImage.png +0 -0
  19. beratools/gui/assets/beratools.json +475 -2171
  20. beratools/gui/bt_data.py +585 -234
  21. beratools/gui/bt_gui_main.py +129 -91
  22. beratools/gui/main.py +4 -7
  23. beratools/gui/tool_widgets.py +530 -354
  24. beratools/tools/__init__.py +0 -7
  25. beratools/tools/{line_footprint_absolute.py → canopy_footprint_absolute.py} +81 -56
  26. beratools/tools/canopy_footprint_exp.py +113 -0
  27. beratools/tools/centerline.py +30 -37
  28. beratools/tools/check_seed_line.py +127 -0
  29. beratools/tools/common.py +65 -586
  30. beratools/tools/{line_footprint_fixed.py → ground_footprint.py} +140 -117
  31. beratools/tools/line_footprint_relative.py +64 -35
  32. beratools/tools/tool_template.py +48 -40
  33. beratools/tools/vertex_optimization.py +20 -34
  34. beratools/utility/env_checks.py +53 -0
  35. beratools/utility/spatial_common.py +210 -0
  36. beratools/utility/tool_args.py +138 -0
  37. beratools-0.2.5.dist-info/METADATA +134 -0
  38. beratools-0.2.5.dist-info/RECORD +50 -0
  39. {beratools-0.2.3.dist-info → beratools-0.2.5.dist-info}/WHEEL +1 -1
  40. beratools-0.2.5.dist-info/entry_points.txt +3 -0
  41. beratools-0.2.5.dist-info/licenses/LICENSE +674 -0
  42. beratools/core/algo_tiler.py +0 -428
  43. beratools/gui/__init__.py +0 -11
  44. beratools/gui/batch_processing_dlg.py +0 -513
  45. beratools/gui/map_window.py +0 -162
  46. beratools/tools/Beratools_r_script.r +0 -1120
  47. beratools/tools/Ht_metrics.py +0 -116
  48. beratools/tools/batch_processing.py +0 -136
  49. beratools/tools/canopy_threshold_relative.py +0 -672
  50. beratools/tools/canopycostraster.py +0 -222
  51. beratools/tools/fl_regen_csf.py +0 -428
  52. beratools/tools/forest_line_attributes.py +0 -408
  53. beratools/tools/line_grouping.py +0 -45
  54. beratools/tools/ln_relative_metrics.py +0 -615
  55. beratools/tools/r_cal_lpi_elai.r +0 -25
  56. beratools/tools/r_generate_pd_focalraster.r +0 -101
  57. beratools/tools/r_interface.py +0 -80
  58. beratools/tools/r_point_density.r +0 -9
  59. beratools/tools/rpy_chm2trees.py +0 -86
  60. beratools/tools/rpy_dsm_chm_by.py +0 -81
  61. beratools/tools/rpy_dtm_by.py +0 -63
  62. beratools/tools/rpy_find_cellsize.py +0 -43
  63. beratools/tools/rpy_gnd_csf.py +0 -74
  64. beratools/tools/rpy_hummock_hollow.py +0 -85
  65. beratools/tools/rpy_hummock_hollow_raster.py +0 -71
  66. beratools/tools/rpy_las_info.py +0 -51
  67. beratools/tools/rpy_laz2las.py +0 -40
  68. beratools/tools/rpy_lpi_elai_lascat.py +0 -466
  69. beratools/tools/rpy_normalized_lidar_by.py +0 -56
  70. beratools/tools/rpy_percent_above_dbh.py +0 -80
  71. beratools/tools/rpy_points2trees.py +0 -88
  72. beratools/tools/rpy_vegcoverage.py +0 -94
  73. beratools/tools/tiler.py +0 -48
  74. beratools/tools/zonal_threshold.py +0 -144
  75. beratools-0.2.3.dist-info/METADATA +0 -108
  76. beratools-0.2.3.dist-info/RECORD +0 -74
  77. beratools-0.2.3.dist-info/entry_points.txt +0 -2
  78. beratools-0.2.3.dist-info/licenses/LICENSE +0 -22
@@ -12,6 +12,7 @@ Description:
12
12
 
13
13
  This file hosts cost raster related functions.
14
14
  """
15
+
15
16
  import numpy as np
16
17
  import scipy
17
18
 
@@ -35,7 +36,7 @@ def cost_raster(
35
36
  """
36
37
  if len(in_raster.shape) > 2:
37
38
  in_raster = np.squeeze(in_raster, axis=0)
38
-
39
+
39
40
  # regulate canopy_avoid between 0 and 1
40
41
  avoidance = max(0, min(1, canopy_avoid))
41
42
  cell_x, cell_y = meta["transform"][0], -meta["transform"][4]
@@ -45,9 +46,7 @@ def cost_raster(
45
46
  dyn_canopy_ndarray = dyn_np_cc_map(in_raster, canopy_ht_threshold)
46
47
 
47
48
  cc_std, cc_mean = cost_focal_stats(dyn_canopy_ndarray, kernel)
48
- cc_smooth = cost_norm_dist_transform(
49
- dyn_canopy_ndarray, max_line_dist, [cell_x, cell_y]
50
- )
49
+ cc_smooth = cost_norm_dist_transform(dyn_canopy_ndarray, max_line_dist, [cell_x, cell_y])
51
50
 
52
51
  cost_clip = dyn_np_cost_raster_refactor(
53
52
  dyn_canopy_ndarray, cc_mean, cc_std, cc_smooth, avoidance, cost_raster_exponent
@@ -59,62 +58,56 @@ def cost_raster(
59
58
 
60
59
  return cost_clip, dyn_canopy_ndarray
61
60
 
61
+
62
62
  def remove_nan_from_array_refactor(matrix, replacement_value=bt_const.BT_NODATA_COST):
63
63
  # Use boolean indexing to replace nan values
64
64
  matrix[np.isnan(matrix)] = replacement_value
65
65
 
66
+
66
67
  def dyn_np_cc_map(in_chm, canopy_ht_threshold):
67
68
  """
68
69
  Create a new canopy raster.
69
70
 
70
- MaskedArray based on the threshold comparison of in_chm (canopy height model)
71
- with canopy_ht_threshold. It assigns 1.0 where the condition is True (canopy)
71
+ MaskedArray based on the threshold comparison of in_chm (canopy height model)
72
+ with canopy_ht_threshold. It assigns 1.0 where the condition is True (canopy)
72
73
  and 0.0 where the condition is False (non-canopy).
73
74
 
74
75
  """
75
76
  canopy_ndarray = np.ma.where(in_chm >= canopy_ht_threshold, 1.0, 0.0).astype(float)
76
77
  return canopy_ndarray
77
78
 
79
+
78
80
  def cost_focal_stats(canopy_ndarray, kernel):
79
81
  mask = canopy_ndarray.mask
80
82
  in_ndarray = np.ma.where(mask, np.nan, canopy_ndarray)
81
83
 
82
84
  # Function to compute mean and standard deviation
83
85
  def calc_mean(arr):
84
- # Check if the array is empty or full of NaNs
85
- if arr.size == 0 or np.all(np.isnan(arr)):
86
- return np.nan # Or any other value you'd prefer for empty arrays
87
86
  return np.nanmean(arr)
88
87
 
89
88
  def calc_std(arr):
90
- # Check if the array is empty or full of NaNs
91
- if arr.size == 0 or np.all(np.isnan(arr)):
92
- return np.nan # Or any other placeholder you prefer
93
89
  return np.nanstd(arr)
94
90
 
95
91
  # Apply the generic_filter function to compute mean and std
96
- mean_array = scipy.ndimage.generic_filter(
97
- in_ndarray, calc_mean, footprint=kernel, mode="nearest"
98
- )
99
- std_array = scipy.ndimage.generic_filter(
100
- in_ndarray, calc_std, footprint=kernel, mode="nearest"
101
- )
92
+ mean_array = scipy.ndimage.generic_filter(in_ndarray, calc_mean, footprint=kernel, mode="nearest")
93
+ std_array = scipy.ndimage.generic_filter(in_ndarray, calc_std, footprint=kernel, mode="nearest")
102
94
 
103
95
  return std_array, mean_array
104
96
 
97
+
105
98
  def cost_norm_dist_transform(canopy_ndarray, max_line_dist, sampling):
106
99
  """Compute a distance-based cost map based on the proximity of valid data points."""
107
100
  # Convert masked array to a regular array and fill the masked areas with np.nan
108
101
  in_ndarray = canopy_ndarray.filled(np.nan)
109
-
102
+
110
103
  # Compute the Euclidean distance transform (edt) where the valid values are
111
104
  euc_dist_array = scipy.ndimage.distance_transform_edt(
112
105
  np.logical_not(np.isnan(in_ndarray)), sampling=sampling
113
106
  )
114
-
107
+
115
108
  # Apply the mask back to set the distances to np.nan
116
109
  euc_dist_array[canopy_ndarray.mask] = np.nan
117
-
110
+
118
111
  # Calculate the smoothness (cost) array
119
112
  normalized_cost = float(max_line_dist) - euc_dist_array
120
113
  normalized_cost[normalized_cost <= 0.0] = 0.0
@@ -122,9 +115,8 @@ def cost_norm_dist_transform(canopy_ndarray, max_line_dist, sampling):
122
115
 
123
116
  return smooth_cost_array
124
117
 
125
- def dyn_np_cost_raster_refactor(
126
- canopy_ndarray, cc_mean, cc_std, cc_smooth, avoidance, cost_raster_exponent
127
- ):
118
+
119
+ def dyn_np_cost_raster_refactor(canopy_ndarray, cc_mean, cc_std, cc_smooth, avoidance, cost_raster_exponent):
128
120
  # Calculate the lower and upper bounds for canopy cover (mean ± std deviation)
129
121
  lower_bound = cc_mean - cc_std
130
122
  upper_bound = cc_mean + cc_std
@@ -158,6 +150,7 @@ def dyn_np_cost_raster_refactor(
158
150
 
159
151
  return result_cost_raster
160
152
 
153
+
161
154
  def circle_kernel_refactor(size, radius):
162
155
  """
163
156
  Create a circular kernel using Scipy.
@@ -189,4 +182,4 @@ def circle_kernel_refactor(size, radius):
189
182
 
190
183
  # Create a circular kernel
191
184
  kernel = distance <= radius
192
- return kernel.astype(float)
185
+ return kernel.astype(float)
@@ -17,7 +17,7 @@ the Free Software Foundation; either version 2 of the License, or
17
17
  """
18
18
 
19
19
  # This will get replaced with a git SHA1 when you do a git archive
20
- __revision__ = '$Format:%H$'
20
+ __revision__ = "$Format:%H$"
21
21
 
22
22
  import math
23
23
  import queue
@@ -52,9 +52,7 @@ class MinCostPathHelper:
52
52
  def create_points_from_path(ras_transform, min_cost_path, start_point, end_point):
53
53
  path_points = list(
54
54
  map(
55
- lambda row_col: MinCostPathHelper._row_col_to_point(
56
- row_col, ras_transform
57
- ),
55
+ lambda row_col: MinCostPathHelper._row_col_to_point(row_col, ras_transform),
58
56
  min_cost_path,
59
57
  )
60
58
  )
@@ -71,7 +69,7 @@ class MinCostPathHelper:
71
69
  @staticmethod
72
70
  def block2matrix_numpy(block, nodata):
73
71
  contains_negative = False
74
- with np.nditer(block, flags=["refs_ok"], op_flags=['readwrite']) as it:
72
+ with np.nditer(block, flags=["refs_ok"], op_flags=["readwrite"]) as it:
75
73
  for x in it:
76
74
  # TODO: this speeds up a lot, but need further inspection
77
75
  # if np.isclose(x, nodata) or np.isnan(x):
@@ -90,8 +88,7 @@ class MinCostPathHelper:
90
88
  matrix = [
91
89
  [
92
90
  None
93
- if np.isclose(block[i][j], nodata)
94
- or np.isclose(block[i][j], bt_const.BT_NODATA)
91
+ if np.isclose(block[i][j], nodata) or np.isclose(block[i][j], bt_const.BT_NODATA)
95
92
  else block[i][j]
96
93
  for j in range(height)
97
94
  ]
@@ -129,8 +126,16 @@ def dijkstra(start_tuple, end_tuples, block, find_nearest, feedback=None):
129
126
 
130
127
  def neighbors(self, id):
131
128
  x, y = id
132
- results = [(x + 1, y), (x, y - 1), (x - 1, y), (x, y + 1),
133
- (x + 1, y - 1), (x + 1, y + 1), (x - 1, y - 1), (x - 1, y + 1)]
129
+ results = [
130
+ (x + 1, y),
131
+ (x, y - 1),
132
+ (x - 1, y),
133
+ (x, y + 1),
134
+ (x + 1, y - 1),
135
+ (x + 1, y + 1),
136
+ (x - 1, y - 1),
137
+ (x - 1, y + 1),
138
+ ]
134
139
  results = list(filter(self.is_valid, results))
135
140
  return results
136
141
 
@@ -142,22 +147,15 @@ def dijkstra(start_tuple, end_tuples, block, find_nearest, feedback=None):
142
147
 
143
148
  @staticmethod
144
149
  def min_manhattan(curr_node, end_nodes):
145
- return min(
146
- map(lambda node: Grid.manhattan_distance(curr_node, node), end_nodes)
147
- )
150
+ return min(map(lambda node: Grid.manhattan_distance(curr_node, node), end_nodes))
148
151
 
149
152
  @staticmethod
150
153
  def max_manhattan(curr_node, end_nodes):
151
- return max(
152
- map(lambda node: Grid.manhattan_distance(curr_node, node), end_nodes)
153
- )
154
+ return max(map(lambda node: Grid.manhattan_distance(curr_node, node), end_nodes))
154
155
 
155
156
  @staticmethod
156
157
  def all_manhattan(curr_node, end_nodes):
157
- return {
158
- end_node: Grid.manhattan_distance(curr_node, end_node)
159
- for end_node in end_nodes
160
- }
158
+ return {end_node: Grid.manhattan_distance(curr_node, end_node) for end_node in end_nodes}
161
159
 
162
160
  def simple_cost(self, cur, nex):
163
161
  cx, cy = cur
@@ -230,10 +228,7 @@ def dijkstra(start_tuple, end_tuples, block, find_nearest, feedback=None):
230
228
  bound = curr_bound
231
229
  if feedback:
232
230
  feedback.setProgress(
233
- 1
234
- + 100
235
- * (1 - bound / total_manhattan)
236
- * (1 - bound / total_manhattan)
231
+ 1 + 100 * (1 - bound / total_manhattan) * (1 - bound / total_manhattan)
237
232
  )
238
233
 
239
234
  # destination
@@ -336,6 +331,9 @@ def backtrack(initial_node, desired_node, distances):
336
331
  if path[-1][0] == initial_node[0] and path[-1][1] == initial_node[1]:
337
332
  break
338
333
 
334
+ if len(path) == 0 or path[-1][0] != initial_node[0] or path[-1][1] != initial_node[1]:
335
+ return None
336
+
339
337
  return list(reversed(path))
340
338
 
341
339
 
@@ -371,11 +369,9 @@ def dijkstra_np(start_tuple, end_tuple, matrix):
371
369
  return [(path, costs, end_tuple)]
372
370
 
373
371
 
374
- def find_least_cost_path(
375
- out_image, in_meta, line, find_nearest=True, output_linear_reference=False
376
- ):
372
+ def find_least_cost_path(out_image, in_meta, line, find_nearest=True, output_linear_reference=False):
377
373
  default_return = None
378
- ras_nodata = in_meta['nodata']
374
+ ras_nodata = in_meta["nodata"]
379
375
 
380
376
  pt_start = line.coords[0]
381
377
  pt_end = line.coords[-1]
@@ -385,24 +381,22 @@ def find_least_cost_path(
385
381
  out_image = np.squeeze(out_image, axis=0)
386
382
 
387
383
  if USE_NUMPY_FOR_DIJKSTRA:
388
- matrix, contains_negative = MinCostPathHelper.block2matrix_numpy(
389
- out_image, ras_nodata
390
- )
384
+ matrix, contains_negative = MinCostPathHelper.block2matrix_numpy(out_image, ras_nodata)
391
385
  else:
392
- matrix, contains_negative = MinCostPathHelper.block2matrix(
393
- out_image, ras_nodata
394
- )
386
+ matrix, contains_negative = MinCostPathHelper.block2matrix(out_image, ras_nodata)
395
387
 
396
388
  if contains_negative:
397
- print('ERROR: Raster has negative values.')
389
+ print("ERROR: Raster has negative values.")
398
390
  return default_return
399
391
 
400
- transformer = rasterio.transform.AffineTransformer(in_meta['transform'])
392
+ transformer = rasterio.transform.AffineTransformer(in_meta["transform"])
401
393
 
402
- if (type(pt_start[0]) is tuple or
403
- type(pt_start[1]) is tuple or
404
- type(pt_end[0]) is tuple or
405
- type(pt_end[1]) is tuple):
394
+ if (
395
+ type(pt_start[0]) is tuple
396
+ or type(pt_start[1]) is tuple
397
+ or type(pt_end[0]) is tuple
398
+ or type(pt_end[1]) is tuple
399
+ ):
406
400
  print("Point initialization error. Input is tuple.")
407
401
  return default_return
408
402
 
@@ -446,7 +440,7 @@ def find_least_cost_path(
446
440
  return default_return
447
441
 
448
442
  if len(result) == 0:
449
- print('No result returned.')
443
+ print("No result returned.")
450
444
  return default_return
451
445
 
452
446
  path_points = None
@@ -473,7 +467,7 @@ def find_least_cost_path_skimage(cost_clip, in_meta, seed_line):
473
467
  if len(cost_clip.shape) > 2:
474
468
  cost_clip = np.squeeze(cost_clip, axis=0)
475
469
 
476
- out_transform = in_meta['transform']
470
+ out_transform = in_meta["transform"]
477
471
  transformer = rasterio.transform.AffineTransformer(out_transform)
478
472
 
479
473
  x1, y1 = list(seed_line.coords)[0][:2]
@@ -482,9 +476,7 @@ def find_least_cost_path_skimage(cost_clip, in_meta, seed_line):
482
476
  row2, col2 = transformer.rowcol(x2, y2)
483
477
 
484
478
  try:
485
- path_new = sk_graph.route_through_array(
486
- cost_clip[0], [row1, col1], [row2, col2]
487
- )
479
+ path_new = sk_graph.route_through_array(cost_clip[0], [row1, col1], [row2, col2])
488
480
  except Exception as e:
489
481
  print(f"find_least_cost_path_skimage: {e}")
490
482
  return None
@@ -495,7 +487,7 @@ def find_least_cost_path_skimage(cost_clip, in_meta, seed_line):
495
487
  lc_path_new.append((x, y))
496
488
 
497
489
  if len(lc_path_new) < 2:
498
- print('No least cost path detected, pass.')
490
+ print("No least cost path detected, pass.")
499
491
  return None
500
492
  else:
501
493
  lc_path_new = sh_geom.LineString(lc_path_new)