BERATools 0.2.0__py3-none-any.whl → 0.2.1__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.1.dist-info/METADATA +109 -0
  62. beratools-0.2.1.dist-info/RECORD +74 -0
  63. {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/WHEEL +1 -1
  64. {beratools-0.2.0.dist-info → beratools-0.2.1.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.1.dist-info}/entry_points.txt +0 -0
@@ -1,176 +1,136 @@
1
- import logging
2
- import time
3
-
4
- import sys
5
- from pathlib import Path
6
- from inspect import getsourcefile
7
-
8
- if __name__ == "__main__":
9
- current_file = Path(getsourcefile(lambda: 0)).resolve()
10
- btool_dir = current_file.parents[2]
11
- sys.path.insert(0, btool_dir.as_posix())
12
-
13
- from beratools.core.logger import Logger
14
- from beratools.core.algo_centerline import *
15
- from beratools.core.dijkstra_algorithm import *
16
- from beratools.core.constants import *
17
- from common import *
18
-
19
- log = Logger('centerline', file_level=logging.INFO)
20
- logger = log.get_logger()
21
- print = log.print
22
-
23
-
24
- def process_single_line(line_args):
25
- line = line_args[0][0]
26
- prop = line_args[0][1]
27
- line_radius = line_args[1]
28
- in_raster = line_args[2]
29
- line_id = line_args[3]
30
- seed_line = shape(line) # LineString
31
- line_radius = float(line_radius)
32
-
33
- default_return = (seed_line, seed_line, prop, None)
34
-
35
- cost_clip, out_meta = clip_raster(in_raster, seed_line, line_radius)
36
-
37
- if not HAS_COST_RASTER:
38
- cost_clip, _ = cost_raster(cost_clip, out_meta)
39
-
40
- lc_path = line
41
- try:
42
- if CL_USE_SKIMAGE_GRAPH:
43
- # skimage shortest path
44
- lc_path = find_least_cost_path_skimage(cost_clip, out_meta, seed_line)
45
- else:
46
- lc_path = find_least_cost_path(cost_clip, out_meta, seed_line)
47
- except Exception as e:
48
- print(e)
49
- return default_return
50
-
51
- if lc_path:
52
- lc_path_coords = lc_path.coords
53
- else:
54
- lc_path_coords = []
55
-
56
- # search for centerline
57
- if len(lc_path_coords) < 2:
58
- print('No least cost path detected, use input line.')
59
- prop['status'] = CenterlineStatus.FAILED.value
60
- return default_return
61
-
62
- # get corridor raster
63
- lc_path = LineString(lc_path_coords)
64
- cost_clip, out_meta = clip_raster(in_raster, lc_path, line_radius * 0.9)
65
- if not HAS_COST_RASTER:
66
- cost_clip, _ = cost_raster(cost_clip, out_meta)
67
-
68
- out_transform = out_meta['transform']
69
- transformer = rasterio.transform.AffineTransformer(out_transform)
70
- cell_size = (out_transform[0], -out_transform[4])
71
-
72
- x1, y1 = lc_path_coords[0]
73
- x2, y2 = lc_path_coords[-1]
74
- source = [transformer.rowcol(x1, y1)]
75
- destination = [transformer.rowcol(x2, y2)]
76
- corridor_thresh_cl = corridor_raster(cost_clip, out_meta, source, destination,
77
- cell_size, FP_CORRIDOR_THRESHOLD)
78
-
79
- # find contiguous corridor polygon and extract centerline
80
- df = gpd.GeoDataFrame(geometry=[seed_line], crs=out_meta['crs'])
81
- corridor_poly_gpd = find_corridor_polygon(corridor_thresh_cl, out_transform, df)
82
- center_line, status = find_centerline(corridor_poly_gpd.geometry.iloc[0], lc_path)
83
- prop['status'] = status.value
84
-
85
- print(" Searching centerline: line {} ".format(line_id), flush=True)
86
- return center_line, lc_path, prop, corridor_poly_gpd
87
-
88
-
89
- def centerline(callback, in_line, in_raster, line_radius,
90
- proc_segments, out_line, processes, verbose):
91
- if not compare_crs(vector_crs(in_line), raster_crs(in_raster)):
92
- print("Line and CHM have different spatial references, please check.")
93
- return
94
-
95
- # Read input line features
96
- layer_crs = None
97
- schema = None
98
- input_lines = []
99
-
100
- with fiona.open(in_line) as open_line_file:
101
- layer_crs = open_line_file.crs
102
- schema = open_line_file.meta['schema']
103
- for line in open_line_file:
104
- if line.geometry:
105
- if line.geometry['type'] != 'MultiLineString':
106
- input_lines.append([line.geometry, line.properties])
107
- else:
108
- print('MultiLineString found.')
109
- geoms = shape(line['geometry']).geoms
110
- for item in geoms:
111
- line_part = fiona.Geometry.from_dict(item)
112
- if line_part:
113
- input_lines.append([line_part, line.properties])
114
- else:
115
- print(f'Line {line.id} has empty geometry.')
116
-
117
- if proc_segments:
118
- # split line segments at vertices
119
- input_lines_temp = []
120
- for line in input_lines:
121
- line_seg = line[0]
122
- line_prop = line[1]
123
- line_segs = segments(line_seg.coordinates)
124
- line_feats = [(line, line_prop) for line in line_segs]
125
- if line_segs:
126
- input_lines_temp.extend(line_feats)
127
-
128
- input_lines = input_lines_temp
129
-
130
- # Process lines
131
- all_lines = []
132
- i = 0
133
- for line in input_lines:
134
- all_lines.append((line, line_radius, in_raster, i))
135
- i += 1
136
-
137
- print('{} lines to be processed.'.format(len(all_lines)))
138
-
139
- lc_path_geoms = []
140
- feat_props = []
141
- center_line_geoms = []
142
- corridor_poly_list = []
143
- result = execute_multiprocessing(process_single_line, all_lines, 'Centerline',
144
- processes, 1, verbose=verbose)
145
-
146
- for item in result:
147
- center_line = item[0]
148
- lc_path = item[1]
149
- prop = item[2]
150
- corridor_poly = item[3]
151
-
152
- if lc_path and prop:
153
- lc_path_geoms.append(lc_path)
154
- feat_props.append(prop)
155
- center_line_geoms.append(center_line)
156
- corridor_poly_list.append(corridor_poly)
157
-
158
- out_centerline_path = Path(out_line)
159
- schema['properties']['status'] = 'int'
160
- save_features_to_shapefile(out_centerline_path.as_posix(), layer_crs, center_line_geoms, feat_props, schema)
161
-
162
- out_least_cost_path = out_centerline_path.with_stem(out_centerline_path.stem + '_least_cost_path')
163
- save_features_to_shapefile(out_least_cost_path.as_posix(), layer_crs, lc_path_geoms, feat_props, schema)
164
-
165
- # save corridor polygons
166
- corridor_polys = pd.concat(corridor_poly_list)
167
- out_corridor_poly_path = Path(out_line)
168
- out_corridor_poly_path = out_corridor_poly_path.with_stem(out_corridor_poly_path.stem + '_corridor_poly')
169
- corridor_polys.to_file(out_corridor_poly_path.as_posix())
170
-
171
-
172
- if __name__ == '__main__':
173
- in_args, in_verbose = check_arguments()
174
- start_time = time.time()
175
- centerline(print, **in_args.input, processes=int(in_args.processes), verbose=in_verbose)
176
- print('Elapsed time: {}'.format(time.time() - start_time))
1
+ """
2
+ Copyright (C) 2025 Applied Geospatial Research Group.
3
+
4
+ This script is licensed under the GNU General Public License v3.0.
5
+ See <https://gnu.org/licenses/gpl-3.0> for full license details.
6
+
7
+ Author: Richard Zeng
8
+
9
+ Description:
10
+ This script is part of the BERA Tools.
11
+ Webpage: https://github.com/appliedgrg/beratools
12
+
13
+ The purpose of this script is to provide main interface for centerline tool.
14
+ """
15
+
16
+ import logging
17
+ import time
18
+ from pathlib import Path
19
+
20
+ import pandas as pd
21
+
22
+ import beratools.core.algo_centerline as algo_centerline
23
+ import beratools.core.algo_common as algo_common
24
+ import beratools.core.constants as bt_const
25
+ import beratools.tools.common as bt_common
26
+ from beratools.core.logger import Logger
27
+ from beratools.core.tool_base import execute_multiprocessing
28
+
29
+ log = Logger("centerline", file_level=logging.INFO)
30
+ logger = log.get_logger()
31
+ print = log.print
32
+
33
+ def generate_line_class_list(
34
+ in_vector, in_raster, line_radius, layer=None, proc_segments=True
35
+ ) -> list:
36
+ line_classes = []
37
+ line_list = algo_common.prepare_lines_gdf(in_vector, layer, proc_segments)
38
+
39
+ for item in line_list:
40
+ line_classes.append(
41
+ algo_centerline.SeedLine(item, in_raster, proc_segments, line_radius)
42
+ )
43
+
44
+ return line_classes
45
+
46
+
47
+ def process_single_line_class(seed_line):
48
+ seed_line.compute()
49
+ return seed_line
50
+
51
+
52
+ def centerline(
53
+ in_line,
54
+ in_raster,
55
+ line_radius,
56
+ proc_segments,
57
+ out_line,
58
+ processes,
59
+ verbose,
60
+ in_layer=None,
61
+ out_layer=None,
62
+ parallel_mode=bt_const.ParallelMode.MULTIPROCESSING
63
+ ):
64
+ if not bt_common.compare_crs(
65
+ bt_common.vector_crs(in_line), bt_common.raster_crs(in_raster)
66
+ ):
67
+ print("Line and CHM have different spatial references, please check.")
68
+ return
69
+
70
+ line_class_list = generate_line_class_list(
71
+ in_line,
72
+ in_raster,
73
+ line_radius=float(line_radius),
74
+ layer=in_layer,
75
+ proc_segments=proc_segments,
76
+ )
77
+
78
+ print("{} lines to be processed.".format(len(line_class_list)))
79
+
80
+ lc_path_list = []
81
+ centerline_list = []
82
+ corridor_poly_list = []
83
+ result = execute_multiprocessing(
84
+ process_single_line_class,
85
+ line_class_list,
86
+ "Centerline",
87
+ processes,
88
+ verbose=verbose,
89
+ mode=parallel_mode,
90
+ )
91
+ if not result:
92
+ print("No centerlines found.")
93
+ return
94
+
95
+ for item in result:
96
+ lc_path_list.append(item.lc_path)
97
+ centerline_list.append(item.centerline)
98
+ corridor_poly_list.append(item.corridor_poly_gpd)
99
+
100
+ # Concatenate the lists of GeoDataFrames into single GeoDataFrames
101
+ if (
102
+ len(lc_path_list) == 0
103
+ or len(centerline_list) == 0
104
+ or len(corridor_poly_list) == 0
105
+ ):
106
+ print("No centerline generated.")
107
+ return 1
108
+
109
+ lc_path_list = pd.concat(lc_path_list, ignore_index=True)
110
+ centerline_list = pd.concat(centerline_list, ignore_index=True)
111
+ corridor_polys = pd.concat(corridor_poly_list, ignore_index=True)
112
+
113
+ # Save the concatenated GeoDataFrames to the shapefile/gpkg
114
+ centerline_list.to_file(out_line, layer=out_layer)
115
+
116
+ # Check if the output file is a shapefile
117
+ out_line_path = Path(out_line)
118
+
119
+ if out_line_path.suffix == ".shp":
120
+ # Generate the new file name for the GeoPackage with '_aux' appended
121
+ aux_file = out_line_path.with_name(out_line_path.stem + "_aux.gpkg")
122
+ print(f"Saved auxiliary data to: {aux_file}")
123
+ else:
124
+ aux_file = out_line # continue using out_line (gpkg)
125
+
126
+ # Save lc_path_list and corridor_polys to the new GeoPackage with '_aux' suffix
127
+ lc_path_list.to_file(aux_file, layer="least_cost_path")
128
+ corridor_polys.to_file(aux_file, layer="corridor_polygon")
129
+
130
+
131
+ # TODO: fix geometries when job done
132
+ if __name__ == "__main__":
133
+ in_args, in_verbose = bt_common.check_arguments()
134
+ start_time = time.time()
135
+ centerline(**in_args.input, processes=int(in_args.processes), verbose=in_verbose)
136
+ print("Elapsed time: {}".format(time.time() - start_time))