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.
- beratools/__init__.py +1 -7
- beratools/core/algo_centerline.py +491 -351
- beratools/core/algo_common.py +497 -0
- beratools/core/algo_cost.py +192 -0
- beratools/core/{dijkstra_algorithm.py → algo_dijkstra.py} +503 -460
- beratools/core/algo_footprint_rel.py +577 -0
- beratools/core/algo_line_grouping.py +944 -0
- beratools/core/algo_merge_lines.py +214 -0
- beratools/core/algo_split_with_lines.py +304 -0
- beratools/core/algo_tiler.py +428 -0
- beratools/core/algo_vertex_optimization.py +469 -0
- beratools/core/constants.py +52 -86
- beratools/core/logger.py +76 -85
- beratools/core/tool_base.py +196 -133
- beratools/gui/__init__.py +11 -15
- beratools/gui/{beratools.json → assets/beratools.json} +2185 -2300
- beratools/gui/batch_processing_dlg.py +513 -463
- beratools/gui/bt_data.py +481 -487
- beratools/gui/bt_gui_main.py +710 -691
- beratools/gui/main.py +26 -0
- beratools/gui/map_window.py +162 -146
- beratools/gui/tool_widgets.py +725 -493
- beratools/tools/Beratools_r_script.r +1120 -1120
- beratools/tools/Ht_metrics.py +116 -116
- beratools/tools/__init__.py +7 -7
- beratools/tools/batch_processing.py +136 -132
- beratools/tools/canopy_threshold_relative.py +672 -670
- beratools/tools/canopycostraster.py +222 -222
- beratools/tools/centerline.py +136 -176
- beratools/tools/common.py +857 -885
- beratools/tools/fl_regen_csf.py +428 -428
- beratools/tools/forest_line_attributes.py +408 -408
- beratools/tools/line_footprint_absolute.py +213 -363
- beratools/tools/line_footprint_fixed.py +436 -282
- beratools/tools/line_footprint_functions.py +733 -720
- beratools/tools/line_footprint_relative.py +73 -64
- beratools/tools/line_grouping.py +45 -0
- beratools/tools/ln_relative_metrics.py +615 -615
- beratools/tools/r_cal_lpi_elai.r +24 -24
- beratools/tools/r_generate_pd_focalraster.r +100 -100
- beratools/tools/r_interface.py +79 -79
- beratools/tools/r_point_density.r +8 -8
- beratools/tools/rpy_chm2trees.py +86 -86
- beratools/tools/rpy_dsm_chm_by.py +81 -81
- beratools/tools/rpy_dtm_by.py +63 -63
- beratools/tools/rpy_find_cellsize.py +43 -43
- beratools/tools/rpy_gnd_csf.py +74 -74
- beratools/tools/rpy_hummock_hollow.py +85 -85
- beratools/tools/rpy_hummock_hollow_raster.py +71 -71
- beratools/tools/rpy_las_info.py +51 -51
- beratools/tools/rpy_laz2las.py +40 -40
- beratools/tools/rpy_lpi_elai_lascat.py +466 -466
- beratools/tools/rpy_normalized_lidar_by.py +56 -56
- beratools/tools/rpy_percent_above_dbh.py +80 -80
- beratools/tools/rpy_points2trees.py +88 -88
- beratools/tools/rpy_vegcoverage.py +94 -94
- beratools/tools/tiler.py +48 -206
- beratools/tools/tool_template.py +69 -54
- beratools/tools/vertex_optimization.py +61 -620
- beratools/tools/zonal_threshold.py +144 -144
- beratools-0.2.1.dist-info/METADATA +109 -0
- beratools-0.2.1.dist-info/RECORD +74 -0
- {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/WHEEL +1 -1
- {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/licenses/LICENSE +22 -22
- beratools/gui/cli.py +0 -18
- beratools/gui/gui.json +0 -8
- beratools/gui_tk/ASCII Banners.txt +0 -248
- beratools/gui_tk/__init__.py +0 -20
- beratools/gui_tk/beratools_main.py +0 -515
- beratools/gui_tk/bt_widgets.py +0 -442
- beratools/gui_tk/cli.py +0 -18
- beratools/gui_tk/img/BERALogo.png +0 -0
- beratools/gui_tk/img/closed.gif +0 -0
- beratools/gui_tk/img/closed.png +0 -0
- beratools/gui_tk/img/open.gif +0 -0
- beratools/gui_tk/img/open.png +0 -0
- beratools/gui_tk/img/tool.gif +0 -0
- beratools/gui_tk/img/tool.png +0 -0
- beratools/gui_tk/main.py +0 -14
- beratools/gui_tk/map_window.py +0 -144
- beratools/gui_tk/runner.py +0 -1481
- beratools/gui_tk/tooltip.py +0 -55
- beratools/third_party/pyqtlet2/__init__.py +0 -9
- beratools/third_party/pyqtlet2/leaflet/__init__.py +0 -26
- beratools/third_party/pyqtlet2/leaflet/control/__init__.py +0 -6
- beratools/third_party/pyqtlet2/leaflet/control/control.py +0 -59
- beratools/third_party/pyqtlet2/leaflet/control/draw.py +0 -52
- beratools/third_party/pyqtlet2/leaflet/control/layers.py +0 -20
- beratools/third_party/pyqtlet2/leaflet/core/Parser.py +0 -24
- beratools/third_party/pyqtlet2/leaflet/core/__init__.py +0 -2
- beratools/third_party/pyqtlet2/leaflet/core/evented.py +0 -180
- beratools/third_party/pyqtlet2/leaflet/layer/__init__.py +0 -5
- beratools/third_party/pyqtlet2/leaflet/layer/featuregroup.py +0 -34
- beratools/third_party/pyqtlet2/leaflet/layer/icon/__init__.py +0 -1
- beratools/third_party/pyqtlet2/leaflet/layer/icon/icon.py +0 -30
- beratools/third_party/pyqtlet2/leaflet/layer/imageoverlay.py +0 -18
- beratools/third_party/pyqtlet2/leaflet/layer/layer.py +0 -105
- beratools/third_party/pyqtlet2/leaflet/layer/layergroup.py +0 -45
- beratools/third_party/pyqtlet2/leaflet/layer/marker/__init__.py +0 -1
- beratools/third_party/pyqtlet2/leaflet/layer/marker/marker.py +0 -91
- beratools/third_party/pyqtlet2/leaflet/layer/tile/__init__.py +0 -2
- beratools/third_party/pyqtlet2/leaflet/layer/tile/gridlayer.py +0 -4
- beratools/third_party/pyqtlet2/leaflet/layer/tile/tilelayer.py +0 -16
- beratools/third_party/pyqtlet2/leaflet/layer/vector/__init__.py +0 -5
- beratools/third_party/pyqtlet2/leaflet/layer/vector/circle.py +0 -15
- beratools/third_party/pyqtlet2/leaflet/layer/vector/circlemarker.py +0 -18
- beratools/third_party/pyqtlet2/leaflet/layer/vector/path.py +0 -5
- beratools/third_party/pyqtlet2/leaflet/layer/vector/polygon.py +0 -14
- beratools/third_party/pyqtlet2/leaflet/layer/vector/polyline.py +0 -18
- beratools/third_party/pyqtlet2/leaflet/layer/vector/rectangle.py +0 -14
- beratools/third_party/pyqtlet2/leaflet/map/__init__.py +0 -1
- beratools/third_party/pyqtlet2/leaflet/map/map.py +0 -220
- beratools/third_party/pyqtlet2/mapwidget.py +0 -45
- beratools/third_party/pyqtlet2/web/custom.js +0 -43
- beratools/third_party/pyqtlet2/web/map.html +0 -23
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-shadow.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.css +0 -656
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.js +0 -6
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.codeclimate.yml +0 -14
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.editorconfig +0 -4
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.gitattributes +0 -22
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.travis.yml +0 -43
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/LICENSE +0 -20
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-shadow.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.svg +0 -156
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.css +0 -10
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.js +0 -10
- beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/LICENSE +0 -22
- beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/leaflet.rotatedMarker.js +0 -57
- beratools/tools/forest_line_ecosite.py +0 -216
- beratools/tools/lapis_all.py +0 -103
- beratools/tools/least_cost_path_from_chm.py +0 -152
- beratools-0.2.0.dist-info/METADATA +0 -63
- beratools-0.2.0.dist-info/RECORD +0 -142
- /beratools/gui/{img → assets}/BERALogo.png +0 -0
- /beratools/gui/{img → assets}/closed.gif +0 -0
- /beratools/gui/{img → assets}/closed.png +0 -0
- /beratools/{gui_tk → gui/assets}/gui.json +0 -0
- /beratools/gui/{img → assets}/open.gif +0 -0
- /beratools/gui/{img → assets}/open.png +0 -0
- /beratools/gui/{img → assets}/tool.gif +0 -0
- /beratools/gui/{img → assets}/tool.png +0 -0
- {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/entry_points.txt +0 -0
beratools/tools/common.py
CHANGED
|
@@ -1,885 +1,857 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import
|
|
19
|
-
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
import
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
import
|
|
28
|
-
import shapely
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
from
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
#
|
|
48
|
-
warnings.simplefilter(
|
|
49
|
-
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
out_image[out_image
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
#
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
#
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
def
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
Returns
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
(
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
# "nodata": BT_NODATA,
|
|
859
|
-
# "transform": out_transformC})
|
|
860
|
-
|
|
861
|
-
nodata = BT_NODATA
|
|
862
|
-
# TODO deal with inherited nodata and BT_NODATA_COST
|
|
863
|
-
# TODO convert nodata to BT_NODATA_COST
|
|
864
|
-
line_argsR.append([in_chm, float(work_in_bufferR.loc[record, 'DynCanTh']), float(tree_radius),
|
|
865
|
-
float(max_line_dist), float(canopy_avoidance), float(exponent), in_chm_obj.res, nodata,
|
|
866
|
-
line_seg.iloc[[record]], in_chm_obj.meta.copy(), line_id, RCut, 'Right',
|
|
867
|
-
canopy_thresh_percentage, line_bufferR])
|
|
868
|
-
|
|
869
|
-
step = line_id + 1 + len(work_in_bufferL)
|
|
870
|
-
total = len(work_in_bufferL) + len(work_in_bufferR)
|
|
871
|
-
print(f' "PROGRESS_LABEL Preparing... {step} of {total}" ', flush=True)
|
|
872
|
-
print(f' %{step / total * 100} ', flush=True)
|
|
873
|
-
|
|
874
|
-
line_id += 1
|
|
875
|
-
|
|
876
|
-
return line_argsL, line_argsR, line_argsC
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
def chk_null_geometry(in_data):
|
|
880
|
-
find = False
|
|
881
|
-
if isinstance(in_data, gpd.GeoDataFrame):
|
|
882
|
-
if len(in_data[(in_data.is_empty | in_data.isna())]) > 0:
|
|
883
|
-
find = True
|
|
884
|
-
|
|
885
|
-
return find
|
|
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, Maverick Fong
|
|
8
|
+
|
|
9
|
+
Description:
|
|
10
|
+
This script is part of the BERA Tools.
|
|
11
|
+
Webpage: https://github.com/appliedgrg/beratools
|
|
12
|
+
|
|
13
|
+
This file is intended to be hosting common classes/functions for BERA Tools
|
|
14
|
+
"""
|
|
15
|
+
import argparse
|
|
16
|
+
import json
|
|
17
|
+
import shlex
|
|
18
|
+
import warnings
|
|
19
|
+
|
|
20
|
+
import geopandas as gpd
|
|
21
|
+
import numpy as np
|
|
22
|
+
import osgeo
|
|
23
|
+
import pyogrio
|
|
24
|
+
import pyproj
|
|
25
|
+
import rasterio
|
|
26
|
+
import shapely
|
|
27
|
+
import shapely.geometry as sh_geom
|
|
28
|
+
import shapely.ops as sh_ops
|
|
29
|
+
import xarray as xr
|
|
30
|
+
import xrspatial
|
|
31
|
+
from osgeo import gdal
|
|
32
|
+
from rasterio import mask
|
|
33
|
+
from scipy import ndimage
|
|
34
|
+
|
|
35
|
+
import beratools.core.constants as bt_const
|
|
36
|
+
|
|
37
|
+
# suppress pandas UserWarning: Geometry column contains no geometry when splitting lines
|
|
38
|
+
warnings.simplefilter(action="ignore", category=UserWarning)
|
|
39
|
+
|
|
40
|
+
# restore .shx for shapefile for using GDAL or pyogrio
|
|
41
|
+
gdal.SetConfigOption("SHAPE_RESTORE_SHX", "YES")
|
|
42
|
+
pyogrio.set_gdal_config_options({"SHAPE_RESTORE_SHX": "YES"})
|
|
43
|
+
|
|
44
|
+
# suppress all kinds of warnings
|
|
45
|
+
if not bt_const.BT_DEBUGGING:
|
|
46
|
+
gdal.SetConfigOption("CPL_LOG", "NUL") # GDAL warning
|
|
47
|
+
warnings.filterwarnings("ignore") # suppress warnings
|
|
48
|
+
warnings.simplefilter(
|
|
49
|
+
action="ignore", category=UserWarning
|
|
50
|
+
) # suppress Pandas UserWarning
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def clip_raster(
|
|
54
|
+
in_raster_file,
|
|
55
|
+
clip_geom,
|
|
56
|
+
buffer=0.0,
|
|
57
|
+
out_raster_file=None,
|
|
58
|
+
default_nodata=bt_const.BT_NODATA,
|
|
59
|
+
):
|
|
60
|
+
out_meta = None
|
|
61
|
+
with rasterio.open(in_raster_file) as raster_file:
|
|
62
|
+
out_meta = raster_file.meta
|
|
63
|
+
ras_nodata = out_meta["nodata"]
|
|
64
|
+
if ras_nodata is None:
|
|
65
|
+
ras_nodata = default_nodata
|
|
66
|
+
|
|
67
|
+
clip_geo_buffer = [clip_geom.buffer(buffer)]
|
|
68
|
+
out_image: np.ndarray
|
|
69
|
+
out_image, out_transform = mask.mask(
|
|
70
|
+
raster_file, clip_geo_buffer, crop=True, nodata=ras_nodata, filled=True
|
|
71
|
+
)
|
|
72
|
+
if np.isnan(ras_nodata):
|
|
73
|
+
out_image[np.isnan(out_image)] = default_nodata
|
|
74
|
+
|
|
75
|
+
elif np.isinf(ras_nodata):
|
|
76
|
+
out_image[np.isinf(out_image)] = default_nodata
|
|
77
|
+
else:
|
|
78
|
+
out_image[out_image == ras_nodata] = default_nodata
|
|
79
|
+
|
|
80
|
+
out_image = np.ma.masked_where(out_image == default_nodata, out_image)
|
|
81
|
+
out_image.fill_value = default_nodata
|
|
82
|
+
ras_nodata = default_nodata
|
|
83
|
+
|
|
84
|
+
height, width = out_image.shape[1:]
|
|
85
|
+
|
|
86
|
+
out_meta.update(
|
|
87
|
+
{
|
|
88
|
+
"driver": "GTiff",
|
|
89
|
+
"height": height,
|
|
90
|
+
"width": width,
|
|
91
|
+
"transform": out_transform,
|
|
92
|
+
"nodata": ras_nodata,
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if out_raster_file:
|
|
97
|
+
with rasterio.open(out_raster_file, "w", **out_meta) as dest:
|
|
98
|
+
dest.write(out_image)
|
|
99
|
+
print("[Clip raster]: data saved to {}.".format(out_raster_file))
|
|
100
|
+
|
|
101
|
+
return out_image, out_meta
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# def clip_lines(clip_geom, buffer, in_line_file, out_line_file):
|
|
105
|
+
# in_line = gpd.read_file(in_line_file)
|
|
106
|
+
# out_line = in_line.clip(clip_geom.buffer(buffer * bt_const.BT_BUFFER_RATIO))
|
|
107
|
+
|
|
108
|
+
# if out_line_file and len(out_line) > 0:
|
|
109
|
+
# out_line.to_file(out_line_file)
|
|
110
|
+
# print("[Clip lines]: data saved to {}.".format(out_line_file))
|
|
111
|
+
|
|
112
|
+
# return out_line
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# def read_geoms_from_shapefile(in_file):
|
|
116
|
+
# geoms = []
|
|
117
|
+
# with fiona.open(in_file) as open_file:
|
|
118
|
+
# for geom in open_file:
|
|
119
|
+
# geoms.append(geom['geometry'])
|
|
120
|
+
|
|
121
|
+
# return geoms
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# def read_feature_from_shapefile(in_file):
|
|
125
|
+
# """ Read feature from shapefile
|
|
126
|
+
|
|
127
|
+
# Args:
|
|
128
|
+
# in_file (str): file name
|
|
129
|
+
|
|
130
|
+
# Returns:
|
|
131
|
+
# list: list of features
|
|
132
|
+
# """
|
|
133
|
+
# shapes = []
|
|
134
|
+
# with fiona.open(in_file) as open_file:
|
|
135
|
+
# for feat in open_file:
|
|
136
|
+
# shapes.append([shape(feat.geometry), feat.properties])
|
|
137
|
+
|
|
138
|
+
# return shapes
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def remove_nan_from_array(matrix):
|
|
142
|
+
with np.nditer(matrix, op_flags=["readwrite"]) as it:
|
|
143
|
+
for x in it:
|
|
144
|
+
if np.isnan(x[...]):
|
|
145
|
+
x[...] = bt_const.BT_NODATA_COST
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# def replace_Nodata2NaN(matrix, nodata):
|
|
149
|
+
# with np.nditer(matrix, op_flags=["readwrite"]) as it:
|
|
150
|
+
# for x in it:
|
|
151
|
+
# if x[...] == nodata:
|
|
152
|
+
# x[...] = np.NaN
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# def replace_Nodata2Inf(matrix, nodata):
|
|
156
|
+
# with np.nditer(matrix, op_flags=["readwrite"]) as it:
|
|
157
|
+
# for x in it:
|
|
158
|
+
# if x[...] == nodata:
|
|
159
|
+
# x[...] = np.Inf
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# Split LineString to segments at vertices
|
|
163
|
+
# def segments(line_coords):
|
|
164
|
+
# if len(line_coords) < 2:
|
|
165
|
+
# return None
|
|
166
|
+
# elif len(line_coords) == 2:
|
|
167
|
+
# return [fiona.Geometry.from_dict({'type': 'LineString', 'coordinates': line_coords})]
|
|
168
|
+
# else:
|
|
169
|
+
# seg_list = zip(line_coords[:-1], line_coords[1:])
|
|
170
|
+
# line_list = [{'type': 'LineString', 'coordinates': coords} for coords in seg_list]
|
|
171
|
+
# return [fiona.Geometry.from_dict(line) for line in line_list]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def extract_string_from_printout(str_print, str_extract):
|
|
175
|
+
str_array = shlex.split(str_print) # keep string in double quotes
|
|
176
|
+
str_array_enum = enumerate(str_array)
|
|
177
|
+
index = 0
|
|
178
|
+
for item in str_array_enum:
|
|
179
|
+
if str_extract in item[1]:
|
|
180
|
+
index = item[0]
|
|
181
|
+
break
|
|
182
|
+
str_out = str_array[index]
|
|
183
|
+
return str_out.strip()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def check_arguments():
|
|
187
|
+
# Get tool arguments
|
|
188
|
+
parser = argparse.ArgumentParser()
|
|
189
|
+
parser.add_argument("-i", "--input", type=json.loads)
|
|
190
|
+
parser.add_argument("-p", "--processes")
|
|
191
|
+
parser.add_argument("-v", "--verbose")
|
|
192
|
+
args = parser.parse_args()
|
|
193
|
+
|
|
194
|
+
verbose = True if args.verbose == "True" else False
|
|
195
|
+
for item in args.input:
|
|
196
|
+
if args.input[item].lower() == "false":
|
|
197
|
+
args.input[item] = False
|
|
198
|
+
elif args.input[item].lower() == "true":
|
|
199
|
+
args.input[item] = True
|
|
200
|
+
|
|
201
|
+
return args, verbose
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# def save_features_to_file(out_file, crs, geoms, properties=None, schema=None,
|
|
205
|
+
# driver='ESRI Shapefile', layer=None):
|
|
206
|
+
# """
|
|
207
|
+
|
|
208
|
+
# Args:
|
|
209
|
+
# out_file :
|
|
210
|
+
# crs :
|
|
211
|
+
# geoms : shapely geometry objects
|
|
212
|
+
# schema :
|
|
213
|
+
# properties :
|
|
214
|
+
# driver:
|
|
215
|
+
# layer:
|
|
216
|
+
# """
|
|
217
|
+
# # remove all None items
|
|
218
|
+
# # TODO: check geom type consistency
|
|
219
|
+
# if len(geoms) < 1:
|
|
220
|
+
# return
|
|
221
|
+
|
|
222
|
+
# try:
|
|
223
|
+
# geom_type = mapping(geoms[0])['type']
|
|
224
|
+
# except Exception as e:
|
|
225
|
+
# print(e)
|
|
226
|
+
|
|
227
|
+
# if not schema:
|
|
228
|
+
# props_tuple = zip([], []) # if lengths are not the same, ValueError raises
|
|
229
|
+
# props_schema = [(item, type(value).__name__) for item, value in props_tuple]
|
|
230
|
+
|
|
231
|
+
# schema = {
|
|
232
|
+
# 'geometry': geom_type,
|
|
233
|
+
# 'properties': OrderedDict([])
|
|
234
|
+
# }
|
|
235
|
+
|
|
236
|
+
# properties = None
|
|
237
|
+
|
|
238
|
+
# print('Writing to file {}'.format(out_file), flush=True)
|
|
239
|
+
|
|
240
|
+
# try:
|
|
241
|
+
# out_line_file = fiona.open(out_file, 'w', driver, schema, crs, layer=layer)
|
|
242
|
+
# except Exception as e:
|
|
243
|
+
# print(e)
|
|
244
|
+
# out_line_file.close()
|
|
245
|
+
# return
|
|
246
|
+
|
|
247
|
+
# if properties:
|
|
248
|
+
# feat_tuple = zip_longest(geoms, properties)
|
|
249
|
+
# else: # properties are None
|
|
250
|
+
# feat_tuple = [(item, None) for item in geoms]
|
|
251
|
+
|
|
252
|
+
# try:
|
|
253
|
+
# for geom, prop in feat_tuple:
|
|
254
|
+
# if geom:
|
|
255
|
+
# feature = {
|
|
256
|
+
# 'geometry': mapping(geom),
|
|
257
|
+
# 'properties': prop
|
|
258
|
+
# }
|
|
259
|
+
|
|
260
|
+
# out_line_file.write(feature)
|
|
261
|
+
# except Exception as e:
|
|
262
|
+
# print(e)
|
|
263
|
+
|
|
264
|
+
# out_line_file.close()
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def vector_crs(in_vector):
|
|
268
|
+
osr_crs = osgeo.osr.SpatialReference()
|
|
269
|
+
from pyproj.enums import WktVersion
|
|
270
|
+
|
|
271
|
+
vec_crs = None
|
|
272
|
+
# open input vector data as GeoDataFrame
|
|
273
|
+
gpd_vector = gpd.GeoDataFrame.from_file(in_vector)
|
|
274
|
+
try:
|
|
275
|
+
if gpd_vector.crs is not None:
|
|
276
|
+
vec_crs = gpd_vector.crs
|
|
277
|
+
if osgeo.version_info.major < 3:
|
|
278
|
+
osr_crs.ImportFromWkt(vec_crs.to_wkt(WktVersion.WKT1_GDAL))
|
|
279
|
+
else:
|
|
280
|
+
osr_crs.ImportFromEPSG(vec_crs.to_epsg())
|
|
281
|
+
return osr_crs
|
|
282
|
+
else:
|
|
283
|
+
print(
|
|
284
|
+
"No CRS found in the input feature, please check!"
|
|
285
|
+
)
|
|
286
|
+
exit()
|
|
287
|
+
except Exception as e:
|
|
288
|
+
print(e)
|
|
289
|
+
exit()
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# def df_crs(in_df):
|
|
293
|
+
# vec_crs = None
|
|
294
|
+
# osr_crs = osgeo.osr.SpatialReference()
|
|
295
|
+
# from pyproj.enums import WktVersion
|
|
296
|
+
|
|
297
|
+
# try:
|
|
298
|
+
# if in_df.crs is not None:
|
|
299
|
+
# vec_crs = in_df.crs
|
|
300
|
+
# if osgeo.version_info.major < 3:
|
|
301
|
+
# osr_crs.ImportFromWkt(vec_crs.to_wkt(WktVersion.WKT1_GDAL))
|
|
302
|
+
# else:
|
|
303
|
+
# osr_crs.ImportFromEPSG(vec_crs.to_epsg())
|
|
304
|
+
# return osr_crs
|
|
305
|
+
# else:
|
|
306
|
+
# print(
|
|
307
|
+
# "No Coordinate Reference System (CRS) find in the input feature, please check!"
|
|
308
|
+
# )
|
|
309
|
+
# exit()
|
|
310
|
+
# except Exception as e:
|
|
311
|
+
# print(e)
|
|
312
|
+
# exit()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def raster_crs(in_raster):
|
|
316
|
+
osr_crs = osgeo.osr.SpatialReference()
|
|
317
|
+
with rasterio.open(in_raster) as raster_file:
|
|
318
|
+
from pyproj.enums import WktVersion
|
|
319
|
+
|
|
320
|
+
try:
|
|
321
|
+
if raster_file.crs is not None:
|
|
322
|
+
vec_crs = raster_file.crs
|
|
323
|
+
if osgeo.version_info.major < 3:
|
|
324
|
+
osr_crs.ImportFromWkt(vec_crs.to_wkt(WktVersion.WKT1_GDAL))
|
|
325
|
+
else:
|
|
326
|
+
osr_crs.ImportFromEPSG(vec_crs.to_epsg())
|
|
327
|
+
return osr_crs
|
|
328
|
+
else:
|
|
329
|
+
print(
|
|
330
|
+
"No Coordinate Reference System (CRS) find in the input feature, please check!"
|
|
331
|
+
)
|
|
332
|
+
exit()
|
|
333
|
+
except Exception as e:
|
|
334
|
+
print(e)
|
|
335
|
+
exit()
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def compare_crs(crs_org, crs_dst):
|
|
339
|
+
if crs_org and crs_dst:
|
|
340
|
+
if crs_org.IsSameGeogCS(crs_dst):
|
|
341
|
+
print("Check: Input file Spatial Reference are the same, continue.")
|
|
342
|
+
return True
|
|
343
|
+
else:
|
|
344
|
+
crs_org_norm = pyproj.CRS(crs_org.ExportToWkt())
|
|
345
|
+
crs_dst_norm = pyproj.CRS(crs_dst.ExportToWkt())
|
|
346
|
+
if crs_org_norm.is_compound:
|
|
347
|
+
crs_org_proj = crs_org_norm.sub_crs_list[0].coordinate_operation.name
|
|
348
|
+
elif crs_org_norm.name == "unnamed":
|
|
349
|
+
return False
|
|
350
|
+
else:
|
|
351
|
+
crs_org_proj = crs_org_norm.coordinate_operation.name
|
|
352
|
+
|
|
353
|
+
if crs_dst_norm.is_compound:
|
|
354
|
+
crs_dst_proj = crs_dst_norm.sub_crs_list[0].coordinate_operation.name
|
|
355
|
+
elif crs_org_norm.name == "unnamed":
|
|
356
|
+
return False
|
|
357
|
+
else:
|
|
358
|
+
crs_dst_proj = crs_dst_norm.coordinate_operation.name
|
|
359
|
+
|
|
360
|
+
if crs_org_proj == crs_dst_proj:
|
|
361
|
+
if crs_org_norm.name == crs_dst_norm.name:
|
|
362
|
+
print("Input files Spatial Reference are the same, continue.")
|
|
363
|
+
return True
|
|
364
|
+
else:
|
|
365
|
+
print(
|
|
366
|
+
"""Checked: Data are on the same projected Zone but using
|
|
367
|
+
different Spatial Reference. \n Consider to re-project
|
|
368
|
+
all data onto same spatial reference system.\n Process Stop."""
|
|
369
|
+
)
|
|
370
|
+
exit()
|
|
371
|
+
else:
|
|
372
|
+
return False
|
|
373
|
+
|
|
374
|
+
return False
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def identity_polygon(line_args):
|
|
378
|
+
"""
|
|
379
|
+
Return polygon of line segment.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
line_args : list[GeoDataFrame]
|
|
383
|
+
0 : GeoDataFrame line segment, one item
|
|
384
|
+
1 : GeoDataFrame line buffer, one item
|
|
385
|
+
2 : GeoDataFrame polygons returned by spatial search
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
line, identity : tuple of line and associated footprint
|
|
389
|
+
|
|
390
|
+
"""
|
|
391
|
+
line = line_args[0]
|
|
392
|
+
in_cl_buffer = line_args[1][["geometry", "OLnFID"]]
|
|
393
|
+
in_fp_polygon = line_args[2]
|
|
394
|
+
|
|
395
|
+
identity = None
|
|
396
|
+
try:
|
|
397
|
+
# drop polygons not intersecting with line segment
|
|
398
|
+
line_geom = line.iloc[0].geometry
|
|
399
|
+
drop_list = []
|
|
400
|
+
for i in in_fp_polygon.index:
|
|
401
|
+
if not in_fp_polygon.loc[i].geometry.intersects(line_geom):
|
|
402
|
+
drop_list.append(i)
|
|
403
|
+
elif (
|
|
404
|
+
line_geom.intersection(in_fp_polygon.loc[i].geometry).length
|
|
405
|
+
/ line_geom.length
|
|
406
|
+
< 0.30
|
|
407
|
+
):
|
|
408
|
+
drop_list.append(
|
|
409
|
+
i
|
|
410
|
+
) # if less the 1/5 of line is inside of polygon, ignore
|
|
411
|
+
|
|
412
|
+
# drop all polygons not used
|
|
413
|
+
in_fp_polygon = in_fp_polygon.drop(index=drop_list)
|
|
414
|
+
|
|
415
|
+
if not in_fp_polygon.empty:
|
|
416
|
+
identity = in_fp_polygon.overlay(in_cl_buffer, how="intersection")
|
|
417
|
+
except Exception as e:
|
|
418
|
+
print(e)
|
|
419
|
+
|
|
420
|
+
return line, identity
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def line_split2(in_ln_shp, seg_length):
|
|
424
|
+
# Check the OLnFID column in data. If it is not, column will be created
|
|
425
|
+
if "OLnFID" not in in_ln_shp.columns.array:
|
|
426
|
+
if bt_const.BT_DEBUGGING:
|
|
427
|
+
print("Cannot find {} column in input line data")
|
|
428
|
+
|
|
429
|
+
print(f"New column created: {'OLnFID'}, {'OLnFID'}")
|
|
430
|
+
in_ln_shp["OLnFID"] = in_ln_shp.index
|
|
431
|
+
line_seg = split_into_Equal_Nth_segments(in_ln_shp, seg_length)
|
|
432
|
+
|
|
433
|
+
return line_seg
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def split_into_Equal_Nth_segments(df, seg_length):
|
|
437
|
+
odf = df
|
|
438
|
+
crs = odf.crs
|
|
439
|
+
if "OLnSEG" not in odf.columns.array:
|
|
440
|
+
df["OLnSEG"] = np.nan
|
|
441
|
+
df = odf.assign(
|
|
442
|
+
geometry=odf.apply(lambda x: cut_line_by_length(x.geometry, seg_length), axis=1)
|
|
443
|
+
)
|
|
444
|
+
df = df.explode()
|
|
445
|
+
|
|
446
|
+
df["OLnSEG"] = df.groupby("OLnFID").cumcount()
|
|
447
|
+
gdf = gpd.GeoDataFrame(df, geometry=df.geometry, crs=crs)
|
|
448
|
+
gdf = gdf.sort_values(by=["OLnFID", "OLnSEG"])
|
|
449
|
+
gdf = gdf.reset_index(drop=True)
|
|
450
|
+
|
|
451
|
+
if "shape_leng" in gdf.columns.array:
|
|
452
|
+
gdf["shape_leng"] = gdf.geometry.length
|
|
453
|
+
elif "LENGTH" in gdf.columns.array:
|
|
454
|
+
gdf["LENGTH"] = gdf.geometry.length
|
|
455
|
+
else:
|
|
456
|
+
gdf["shape_leng"] = gdf.geometry.length
|
|
457
|
+
return gdf
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def split_line_nPart(line, seg_length):
|
|
461
|
+
seg_line = shapely.segmentize(line, seg_length)
|
|
462
|
+
distances = np.arange(seg_length, line.length, seg_length)
|
|
463
|
+
|
|
464
|
+
if len(distances) > 0:
|
|
465
|
+
points = [
|
|
466
|
+
shapely.line_interpolate_point(seg_line, distance) for distance in distances
|
|
467
|
+
]
|
|
468
|
+
|
|
469
|
+
split_points = shapely.multipoints(points)
|
|
470
|
+
mline = sh_ops.split(seg_line, split_points)
|
|
471
|
+
else:
|
|
472
|
+
mline = seg_line
|
|
473
|
+
|
|
474
|
+
return mline
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def cut_line_by_length(line, length, merge_threshold=0.5):
|
|
478
|
+
"""
|
|
479
|
+
Split line into segments of equal length.
|
|
480
|
+
|
|
481
|
+
Merge the last segment with the second-to-last if its length
|
|
482
|
+
is smaller than the given threshold.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
line : LineString
|
|
486
|
+
Line to be split by distance along the line.
|
|
487
|
+
length : float
|
|
488
|
+
Length of each segment to cut.
|
|
489
|
+
merge_threshold : float, optional
|
|
490
|
+
Threshold below which the last segment is merged with the previous one. Default is 0.5.
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
List of LineString objects
|
|
494
|
+
A list containing the resulting line segments.
|
|
495
|
+
|
|
496
|
+
Example:
|
|
497
|
+
>>> from shapely.geometry import LineString
|
|
498
|
+
>>> line = LineString([(0, 0), (10, 0)])
|
|
499
|
+
>>> segments = cut_line_by_length(line, 3, merge_threshold=1)
|
|
500
|
+
>>> for segment in segments:
|
|
501
|
+
>>> print(f"Segment: {segment}, Length: {segment.length}")
|
|
502
|
+
|
|
503
|
+
Output:
|
|
504
|
+
Segment: LINESTRING (0 0, 3 0), Length: 3.0
|
|
505
|
+
Segment: LINESTRING (3 0, 6 0), Length: 3.0
|
|
506
|
+
Segment: LINESTRING (6 0, 9 0), Length: 3.0
|
|
507
|
+
Segment: LINESTRING (9 0, 10 0), Length: 1.0
|
|
508
|
+
|
|
509
|
+
After merging the last segment with the second-to-last segment:
|
|
510
|
+
|
|
511
|
+
Output:
|
|
512
|
+
Segment: LINESTRING (0 0, 3 0), Length: 3.0
|
|
513
|
+
Segment: LINESTRING (3 0, 6 0), Length: 3.0
|
|
514
|
+
Segment: LINESTRING (6 0, 10 0), Length: 4.0
|
|
515
|
+
|
|
516
|
+
"""
|
|
517
|
+
if line.has_z:
|
|
518
|
+
# Remove the Z component of the line if it exists
|
|
519
|
+
line = sh_ops.transform(lambda x, y, z=None: (x, y), line)
|
|
520
|
+
|
|
521
|
+
if shapely.is_empty(line):
|
|
522
|
+
return []
|
|
523
|
+
|
|
524
|
+
# Segment the line based on the specified distance
|
|
525
|
+
line = shapely.segmentize(line, length)
|
|
526
|
+
lines = []
|
|
527
|
+
end_pt = None
|
|
528
|
+
|
|
529
|
+
while line.length > length:
|
|
530
|
+
coords = list(line.coords)
|
|
531
|
+
|
|
532
|
+
for i, p in enumerate(coords):
|
|
533
|
+
p_dist = line.project(sh_geom.Point(p))
|
|
534
|
+
|
|
535
|
+
# Check if the distance matches closely and split the line
|
|
536
|
+
if abs(p_dist - length) < 1e-9: # Use a small epsilon value
|
|
537
|
+
lines.append(sh_geom.LineString(coords[:i + 1]))
|
|
538
|
+
line = sh_geom.LineString(coords[i:])
|
|
539
|
+
end_pt = None
|
|
540
|
+
break
|
|
541
|
+
elif p_dist > length:
|
|
542
|
+
end_pt = line.interpolate(length)
|
|
543
|
+
lines.append(sh_geom.LineString(coords[:i] + list(end_pt.coords)))
|
|
544
|
+
line = sh_geom.LineString(list(end_pt.coords) + coords[i:])
|
|
545
|
+
break
|
|
546
|
+
|
|
547
|
+
if end_pt:
|
|
548
|
+
lines.append(line)
|
|
549
|
+
|
|
550
|
+
# Handle the threshold condition: merge the last segment if its length is below the threshold
|
|
551
|
+
if len(lines) > 1:
|
|
552
|
+
if lines[-1].length < merge_threshold:
|
|
553
|
+
# Merge the last segment with the second-to-last one
|
|
554
|
+
lines[-2] = sh_geom.LineString(list(lines[-2].coords) + list(lines[-1].coords))
|
|
555
|
+
lines.pop() # Remove the last segment after merging
|
|
556
|
+
|
|
557
|
+
return lines
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
# def LCP_skimage_mcp_connect(cost_clip, in_meta, seed_line):
|
|
561
|
+
# lc_path_new = []
|
|
562
|
+
# if len(cost_clip.shape) > 2:
|
|
563
|
+
# cost_clip = np.squeeze(cost_clip, axis=0)
|
|
564
|
+
|
|
565
|
+
# out_transform = in_meta["transform"]
|
|
566
|
+
# transformer = rasterio.transform.AffineTransformer(out_transform)
|
|
567
|
+
|
|
568
|
+
# x1, y1 = list(seed_line.coords)[0][:2]
|
|
569
|
+
# x2, y2 = list(seed_line.coords)[-1][:2]
|
|
570
|
+
# source = [transformer.rowcol(x1, y1)]
|
|
571
|
+
# destination = [transformer.rowcol(x2, y2)]
|
|
572
|
+
|
|
573
|
+
# try:
|
|
574
|
+
# init_obj1 = sk_graph.MCP_Connect(cost_clip)
|
|
575
|
+
# path = []
|
|
576
|
+
# for end in destination:
|
|
577
|
+
# path.append(init_obj1.traceback(end))
|
|
578
|
+
# for row, col in path[0]:
|
|
579
|
+
# x, y = transformer.xy(row, col)
|
|
580
|
+
# lc_path_new.append((x, y))
|
|
581
|
+
# except Exception as e:
|
|
582
|
+
# print(e)
|
|
583
|
+
# return None
|
|
584
|
+
|
|
585
|
+
# if len(lc_path_new) < 2:
|
|
586
|
+
# print("No least cost path detected, pass.")
|
|
587
|
+
# return None
|
|
588
|
+
# else:
|
|
589
|
+
# lc_path_new = sh_geom.LineString(lc_path_new)
|
|
590
|
+
|
|
591
|
+
# return lc_path_new
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def chk_df_multipart(df, chk_shp_in_string):
|
|
595
|
+
try:
|
|
596
|
+
found = False
|
|
597
|
+
if str.upper(chk_shp_in_string) in [x.upper() for x in df.geom_type.values]:
|
|
598
|
+
found = True
|
|
599
|
+
df = df.explode()
|
|
600
|
+
if type(df) is gpd.geodataframe.GeoDataFrame:
|
|
601
|
+
df["OLnSEG"] = df.groupby("OLnFID").cumcount()
|
|
602
|
+
df = df.sort_values(by=["OLnFID", "OLnSEG"])
|
|
603
|
+
df = df.reset_index(drop=True)
|
|
604
|
+
else:
|
|
605
|
+
found = False
|
|
606
|
+
return df, found
|
|
607
|
+
except Exception as e:
|
|
608
|
+
print(e)
|
|
609
|
+
return df, True
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def dyn_fs_raster_stdmean(canopy_ndarray, kernel, nodata):
|
|
613
|
+
# This function uses xrspatial which can handle large data but slow
|
|
614
|
+
mask = canopy_ndarray.mask
|
|
615
|
+
in_ndarray = np.ma.where(mask == True, np.NaN, canopy_ndarray)
|
|
616
|
+
result_ndarray = xrspatial.focal.focal_stats(
|
|
617
|
+
xr.DataArray(in_ndarray.data), kernel, stats_funcs=["std", "mean"]
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
# Assign std and mean ndarray (return array contain NaN value)
|
|
621
|
+
reshape_std_ndarray = result_ndarray[0].data
|
|
622
|
+
reshape_mean_ndarray = result_ndarray[1].data
|
|
623
|
+
|
|
624
|
+
return reshape_std_ndarray, reshape_mean_ndarray
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
def dyn_smooth_cost(canopy_ndarray, max_line_dist, sampling):
|
|
628
|
+
mask = canopy_ndarray.mask
|
|
629
|
+
in_ndarray = np.ma.where(mask == True, np.NaN, canopy_ndarray)
|
|
630
|
+
# scipy way to do Euclidean distance transform
|
|
631
|
+
euc_dist_array = ndimage.distance_transform_edt(
|
|
632
|
+
np.logical_not(np.isnan(in_ndarray.data)), sampling=sampling
|
|
633
|
+
)
|
|
634
|
+
euc_dist_array[mask == True] = np.NaN
|
|
635
|
+
smooth1 = float(max_line_dist) - euc_dist_array
|
|
636
|
+
smooth1[smooth1 <= 0.0] = 0.0
|
|
637
|
+
smooth_cost_array = smooth1 / float(max_line_dist)
|
|
638
|
+
|
|
639
|
+
return smooth_cost_array
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def dyn_np_cost_raster(
|
|
643
|
+
canopy_ndarray, cc_mean, cc_std, cc_smooth, avoidance, cost_raster_exponent
|
|
644
|
+
):
|
|
645
|
+
aM1a = cc_mean - cc_std
|
|
646
|
+
aM1b = cc_mean + cc_std
|
|
647
|
+
aM1 = np.divide(aM1a, aM1b, where=aM1b != 0, out=np.zeros(aM1a.shape, dtype=float))
|
|
648
|
+
aM = (1 + aM1) / 2
|
|
649
|
+
aaM = cc_mean + cc_std
|
|
650
|
+
bM = np.where(aaM <= 0, 0, aM)
|
|
651
|
+
cM = bM * (1 - avoidance) + (cc_smooth * avoidance)
|
|
652
|
+
dM = np.where(canopy_ndarray.data == 1, 1, cM)
|
|
653
|
+
eM = np.exp(dM)
|
|
654
|
+
result = np.power(eM, float(cost_raster_exponent))
|
|
655
|
+
|
|
656
|
+
return result
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def dyn_np_cc_map(in_chm, canopy_ht_threshold, nodata):
|
|
660
|
+
canopy_ndarray = np.ma.where(in_chm >= canopy_ht_threshold, 1.0, 0.0).astype(float)
|
|
661
|
+
canopy_ndarray.fill_value = nodata
|
|
662
|
+
|
|
663
|
+
return canopy_ndarray
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
# def morph_raster(corridor_thresh, canopy_raster, exp_shk_cell, cell_size_x):
|
|
667
|
+
# # Process: Stamp CC and Max Line Width
|
|
668
|
+
# ras_sum = corridor_thresh + canopy_raster
|
|
669
|
+
# raster_class = np.ma.where(ras_sum == 0, 1, 0).data
|
|
670
|
+
|
|
671
|
+
# if exp_shk_cell > 0 and cell_size_x < 1:
|
|
672
|
+
# # Process: Expand
|
|
673
|
+
# # FLM original Expand equivalent
|
|
674
|
+
# cell_size = int(exp_shk_cell * 2 + 1)
|
|
675
|
+
# expanded = ndimage.grey_dilation(raster_class, size=(cell_size, cell_size))
|
|
676
|
+
|
|
677
|
+
# # Process: Shrink
|
|
678
|
+
# # FLM original Shrink equivalent
|
|
679
|
+
# file_shrink = ndimage.grey_erosion(expanded, size=(cell_size, cell_size))
|
|
680
|
+
|
|
681
|
+
# else:
|
|
682
|
+
# if bt_const.BT_DEBUGGING:
|
|
683
|
+
# print("No Expand And Shrink cell performed.")
|
|
684
|
+
# file_shrink = raster_class
|
|
685
|
+
|
|
686
|
+
# # Process: Boundary Clean
|
|
687
|
+
# clean_raster = ndimage.gaussian_filter(file_shrink, sigma=0, mode="nearest")
|
|
688
|
+
|
|
689
|
+
# return clean_raster
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
# def generate_line_args_NoClipraster(
|
|
693
|
+
# line_seg,
|
|
694
|
+
# work_in_buffer,
|
|
695
|
+
# in_chm_obj,
|
|
696
|
+
# in_chm,
|
|
697
|
+
# tree_radius,
|
|
698
|
+
# max_line_dist,
|
|
699
|
+
# canopy_avoidance,
|
|
700
|
+
# exponent,
|
|
701
|
+
# canopy_thresh_percentage,
|
|
702
|
+
# ):
|
|
703
|
+
# line_argsC = []
|
|
704
|
+
|
|
705
|
+
# for record in range(0, len(work_in_buffer)):
|
|
706
|
+
# try:
|
|
707
|
+
# line_bufferC = work_in_buffer.loc[record, "geometry"]
|
|
708
|
+
|
|
709
|
+
# nodata = bt_const.BT_NODATA
|
|
710
|
+
# line_argsC.append(
|
|
711
|
+
# [
|
|
712
|
+
# in_chm,
|
|
713
|
+
# float(work_in_buffer.loc[record, "DynCanTh"]),
|
|
714
|
+
# float(tree_radius),
|
|
715
|
+
# float(max_line_dist),
|
|
716
|
+
# float(canopy_avoidance),
|
|
717
|
+
# float(exponent),
|
|
718
|
+
# in_chm_obj.res,
|
|
719
|
+
# nodata,
|
|
720
|
+
# line_seg.iloc[[record]],
|
|
721
|
+
# in_chm_obj.meta.copy(),
|
|
722
|
+
# record,
|
|
723
|
+
# 10,
|
|
724
|
+
# "Center",
|
|
725
|
+
# canopy_thresh_percentage,
|
|
726
|
+
# line_bufferC,
|
|
727
|
+
# ]
|
|
728
|
+
# )
|
|
729
|
+
# except Exception as e:
|
|
730
|
+
# print(e)
|
|
731
|
+
|
|
732
|
+
# step = record + 1
|
|
733
|
+
# total = len(work_in_buffer)
|
|
734
|
+
|
|
735
|
+
# print(f' "PROGRESS_LABEL Preparing lines {step} of {total}" ', flush=True)
|
|
736
|
+
# print(f" %{step / total * 100} ", flush=True)
|
|
737
|
+
|
|
738
|
+
# return line_argsC
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
def generate_line_args_DFP_NoClip(
|
|
742
|
+
line_seg,
|
|
743
|
+
work_in_bufferL,
|
|
744
|
+
work_in_bufferC,
|
|
745
|
+
in_chm_obj,
|
|
746
|
+
in_chm,
|
|
747
|
+
tree_radius,
|
|
748
|
+
max_line_dist,
|
|
749
|
+
canopy_avoidance,
|
|
750
|
+
exponent,
|
|
751
|
+
work_in_bufferR,
|
|
752
|
+
canopy_thresh_percentage,
|
|
753
|
+
):
|
|
754
|
+
line_argsL = []
|
|
755
|
+
line_argsR = []
|
|
756
|
+
line_argsC = []
|
|
757
|
+
line_id = 0
|
|
758
|
+
for record in range(0, len(work_in_bufferL)):
|
|
759
|
+
line_bufferL = work_in_bufferL.loc[record, "geometry"]
|
|
760
|
+
line_bufferC = work_in_bufferC.loc[record, "geometry"]
|
|
761
|
+
LCut = work_in_bufferL.loc[record, "LDist_Cut"]
|
|
762
|
+
|
|
763
|
+
nodata = bt_const.BT_NODATA
|
|
764
|
+
line_argsL.append(
|
|
765
|
+
[
|
|
766
|
+
in_chm,
|
|
767
|
+
float(work_in_bufferL.loc[record, "DynCanTh"]),
|
|
768
|
+
float(tree_radius),
|
|
769
|
+
float(max_line_dist),
|
|
770
|
+
float(canopy_avoidance),
|
|
771
|
+
float(exponent),
|
|
772
|
+
in_chm_obj.res,
|
|
773
|
+
nodata,
|
|
774
|
+
line_seg.iloc[[record]],
|
|
775
|
+
in_chm_obj.meta.copy(),
|
|
776
|
+
line_id,
|
|
777
|
+
LCut,
|
|
778
|
+
"Left",
|
|
779
|
+
canopy_thresh_percentage,
|
|
780
|
+
line_bufferL,
|
|
781
|
+
]
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
line_argsC.append(
|
|
785
|
+
[
|
|
786
|
+
in_chm,
|
|
787
|
+
float(work_in_bufferC.loc[record, "DynCanTh"]),
|
|
788
|
+
float(tree_radius),
|
|
789
|
+
float(max_line_dist),
|
|
790
|
+
float(canopy_avoidance),
|
|
791
|
+
float(exponent),
|
|
792
|
+
in_chm_obj.res,
|
|
793
|
+
nodata,
|
|
794
|
+
line_seg.iloc[[record]],
|
|
795
|
+
in_chm_obj.meta.copy(),
|
|
796
|
+
line_id,
|
|
797
|
+
10,
|
|
798
|
+
"Center",
|
|
799
|
+
canopy_thresh_percentage,
|
|
800
|
+
line_bufferC,
|
|
801
|
+
]
|
|
802
|
+
)
|
|
803
|
+
|
|
804
|
+
line_id += 1
|
|
805
|
+
|
|
806
|
+
line_id = 0
|
|
807
|
+
for record in range(0, len(work_in_bufferR)):
|
|
808
|
+
line_bufferR = work_in_bufferR.loc[record, "geometry"]
|
|
809
|
+
RCut = work_in_bufferR.loc[record, "RDist_Cut"]
|
|
810
|
+
line_bufferC = work_in_bufferC.loc[record, "geometry"]
|
|
811
|
+
|
|
812
|
+
nodata = bt_const.BT_NODATA
|
|
813
|
+
# TODO deal with inherited nodata and BT_NODATA_COST
|
|
814
|
+
# TODO convert nodata to BT_NODATA_COST
|
|
815
|
+
line_argsR.append(
|
|
816
|
+
[
|
|
817
|
+
in_chm,
|
|
818
|
+
float(work_in_bufferR.loc[record, "DynCanTh"]),
|
|
819
|
+
float(tree_radius),
|
|
820
|
+
float(max_line_dist),
|
|
821
|
+
float(canopy_avoidance),
|
|
822
|
+
float(exponent),
|
|
823
|
+
in_chm_obj.res,
|
|
824
|
+
nodata,
|
|
825
|
+
line_seg.iloc[[record]],
|
|
826
|
+
in_chm_obj.meta.copy(),
|
|
827
|
+
line_id,
|
|
828
|
+
RCut,
|
|
829
|
+
"Right",
|
|
830
|
+
canopy_thresh_percentage,
|
|
831
|
+
line_bufferR,
|
|
832
|
+
]
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
step = line_id + 1 + len(work_in_bufferL)
|
|
836
|
+
total = len(work_in_bufferL) + len(work_in_bufferR)
|
|
837
|
+
print(f' "PROGRESS_LABEL Preparing... {step} of {total}" ', flush=True)
|
|
838
|
+
print(f" %{step / total * 100} ", flush=True)
|
|
839
|
+
|
|
840
|
+
line_id += 1
|
|
841
|
+
|
|
842
|
+
return line_argsL, line_argsR, line_argsC
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
# def chk_null_geometry(in_data):
|
|
846
|
+
# find = False
|
|
847
|
+
# if isinstance(in_data, gpd.GeoDataFrame):
|
|
848
|
+
# if len(in_data[(in_data.is_empty | in_data.isna())]) > 0:
|
|
849
|
+
# find = True
|
|
850
|
+
#
|
|
851
|
+
# return find
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
# def read_data2gpd(in_data):
|
|
855
|
+
# print("Reading data.......")
|
|
856
|
+
# out_gpd_obj = gpd.GeoDataFrame.from_file(in_data)
|
|
857
|
+
# return out_gpd_obj
|