BERATools 0.2.3__py3-none-any.whl → 0.2.4__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 +8 -3
- beratools/core/{algo_footprint_rel.py → algo_canopy_footprint_exp.py} +176 -139
- beratools/core/algo_centerline.py +61 -77
- beratools/core/algo_common.py +48 -57
- beratools/core/algo_cost.py +18 -25
- beratools/core/algo_dijkstra.py +37 -45
- beratools/core/algo_line_grouping.py +100 -100
- beratools/core/algo_merge_lines.py +40 -8
- beratools/core/algo_split_with_lines.py +289 -304
- beratools/core/algo_vertex_optimization.py +25 -46
- beratools/core/canopy_threshold_relative.py +755 -0
- beratools/core/constants.py +8 -9
- beratools/{tools → core}/line_footprint_functions.py +411 -258
- beratools/core/logger.py +18 -2
- beratools/core/tool_base.py +17 -75
- beratools/gui/assets/BERALogo.ico +0 -0
- beratools/gui/assets/BERA_Splash.gif +0 -0
- beratools/gui/assets/BERA_WizardImage.png +0 -0
- beratools/gui/assets/beratools.json +475 -2171
- beratools/gui/bt_data.py +585 -234
- beratools/gui/bt_gui_main.py +129 -91
- beratools/gui/main.py +4 -7
- beratools/gui/tool_widgets.py +530 -354
- beratools/tools/__init__.py +0 -7
- beratools/tools/{line_footprint_absolute.py → canopy_footprint_absolute.py} +81 -56
- beratools/tools/canopy_footprint_exp.py +113 -0
- beratools/tools/centerline.py +30 -37
- beratools/tools/check_seed_line.py +127 -0
- beratools/tools/common.py +65 -586
- beratools/tools/{line_footprint_fixed.py → ground_footprint.py} +140 -117
- beratools/tools/line_footprint_relative.py +64 -35
- beratools/tools/tool_template.py +48 -40
- beratools/tools/vertex_optimization.py +20 -34
- beratools/utility/env_checks.py +53 -0
- beratools/utility/spatial_common.py +210 -0
- beratools/utility/tool_args.py +138 -0
- beratools-0.2.4.dist-info/METADATA +134 -0
- beratools-0.2.4.dist-info/RECORD +50 -0
- {beratools-0.2.3.dist-info → beratools-0.2.4.dist-info}/WHEEL +1 -1
- beratools-0.2.4.dist-info/entry_points.txt +3 -0
- beratools-0.2.4.dist-info/licenses/LICENSE +674 -0
- beratools/core/algo_tiler.py +0 -428
- beratools/gui/__init__.py +0 -11
- beratools/gui/batch_processing_dlg.py +0 -513
- beratools/gui/map_window.py +0 -162
- beratools/tools/Beratools_r_script.r +0 -1120
- beratools/tools/Ht_metrics.py +0 -116
- beratools/tools/batch_processing.py +0 -136
- beratools/tools/canopy_threshold_relative.py +0 -672
- beratools/tools/canopycostraster.py +0 -222
- beratools/tools/fl_regen_csf.py +0 -428
- beratools/tools/forest_line_attributes.py +0 -408
- beratools/tools/line_grouping.py +0 -45
- beratools/tools/ln_relative_metrics.py +0 -615
- beratools/tools/r_cal_lpi_elai.r +0 -25
- beratools/tools/r_generate_pd_focalraster.r +0 -101
- beratools/tools/r_interface.py +0 -80
- beratools/tools/r_point_density.r +0 -9
- beratools/tools/rpy_chm2trees.py +0 -86
- beratools/tools/rpy_dsm_chm_by.py +0 -81
- beratools/tools/rpy_dtm_by.py +0 -63
- beratools/tools/rpy_find_cellsize.py +0 -43
- beratools/tools/rpy_gnd_csf.py +0 -74
- beratools/tools/rpy_hummock_hollow.py +0 -85
- beratools/tools/rpy_hummock_hollow_raster.py +0 -71
- beratools/tools/rpy_las_info.py +0 -51
- beratools/tools/rpy_laz2las.py +0 -40
- beratools/tools/rpy_lpi_elai_lascat.py +0 -466
- beratools/tools/rpy_normalized_lidar_by.py +0 -56
- beratools/tools/rpy_percent_above_dbh.py +0 -80
- beratools/tools/rpy_points2trees.py +0 -88
- beratools/tools/rpy_vegcoverage.py +0 -94
- beratools/tools/tiler.py +0 -48
- beratools/tools/zonal_threshold.py +0 -144
- beratools-0.2.3.dist-info/METADATA +0 -108
- beratools-0.2.3.dist-info/RECORD +0 -74
- beratools-0.2.3.dist-info/entry_points.txt +0 -2
- beratools-0.2.3.dist-info/licenses/LICENSE +0 -22
|
@@ -28,29 +28,25 @@
|
|
|
28
28
|
#
|
|
29
29
|
# ---------------------------------------------------------------------------
|
|
30
30
|
|
|
31
|
-
import time
|
|
32
|
-
import numpy as np
|
|
33
|
-
|
|
34
|
-
import rasterio
|
|
35
|
-
from scipy import stats, ndimage
|
|
36
31
|
from geopandas import GeoDataFrame
|
|
37
|
-
from shapely import buffer
|
|
38
32
|
from rasterio import features
|
|
39
|
-
from
|
|
40
|
-
|
|
41
|
-
import
|
|
42
|
-
from skimage.morphology import *
|
|
33
|
+
from rasterio.mask import mask
|
|
34
|
+
from shapely import LineString, MultiPolygon, Point, buffer
|
|
35
|
+
from shapely.geometry import shape
|
|
43
36
|
from skimage.graph import MCP_Flexible
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
from xrspatial import convolution
|
|
44
39
|
|
|
45
|
-
from beratools.core.constants import *
|
|
46
40
|
from beratools.core.algo_centerline import *
|
|
47
|
-
from beratools.
|
|
41
|
+
from beratools.core.canopy_threshold_relative import OperationCancelledException
|
|
42
|
+
from beratools.core.constants import *
|
|
48
43
|
from beratools.core.tool_base import *
|
|
44
|
+
from beratools.tools.common import *
|
|
45
|
+
from beratools.utility.spatial_common import *
|
|
49
46
|
|
|
50
47
|
|
|
51
48
|
def dyn_canopy_cost_raster(args):
|
|
52
|
-
|
|
53
|
-
in_chm_raster=args[0]
|
|
49
|
+
in_chm_raster = args[0]
|
|
54
50
|
DynCanTh = args[1]
|
|
55
51
|
tree_radius = args[2]
|
|
56
52
|
max_line_dist = args[3]
|
|
@@ -65,15 +61,15 @@ def dyn_canopy_cost_raster(args):
|
|
|
65
61
|
Side = args[12]
|
|
66
62
|
canopy_thresh_percentage = float(args[13]) / 100
|
|
67
63
|
line_buffer = args[14]
|
|
64
|
+
exp_shk_cell=args[15]
|
|
68
65
|
|
|
69
|
-
if Side ==
|
|
70
|
-
canopy_ht_threshold = line_df.CL_CutHt * canopy_thresh_percentage
|
|
71
|
-
elif Side ==
|
|
72
|
-
canopy_ht_threshold = line_df.CR_CutHt * canopy_thresh_percentage
|
|
73
|
-
elif Side ==
|
|
66
|
+
if Side == "Left":
|
|
67
|
+
canopy_ht_threshold = line_df.CL_CutHt.iloc[0] * canopy_thresh_percentage
|
|
68
|
+
elif Side == "Right":
|
|
69
|
+
canopy_ht_threshold = line_df.CR_CutHt.iloc[0] * canopy_thresh_percentage
|
|
70
|
+
elif Side == "Center":
|
|
74
71
|
canopy_ht_threshold = DynCanTh * canopy_thresh_percentage
|
|
75
72
|
else:
|
|
76
|
-
|
|
77
73
|
canopy_ht_threshold = 0.5
|
|
78
74
|
|
|
79
75
|
canopy_ht_threshold = float(canopy_ht_threshold)
|
|
@@ -85,47 +81,58 @@ def dyn_canopy_cost_raster(args):
|
|
|
85
81
|
cost_raster_exponent = float(exponent)
|
|
86
82
|
|
|
87
83
|
try:
|
|
88
|
-
|
|
89
84
|
clipped_rasterC, out_meta = clip_raster(in_chm_raster, line_buffer, 0)
|
|
90
85
|
|
|
91
|
-
#clipped_rasterC, out_transformC = rasterio.mask.mask(raster_obj, [line_buffer], crop=True,
|
|
92
|
-
# filled=False)
|
|
93
|
-
|
|
94
86
|
in_chm = np.squeeze(clipped_rasterC, axis=0)
|
|
87
|
+
if BT_DEBUGGING:
|
|
88
|
+
print('[Debug]: Loading CHM ...')
|
|
89
|
+
cell_x, cell_y = out_meta["transform"][0], -out_meta["transform"][4]
|
|
95
90
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
# "height": in_chm.shape[0],
|
|
100
|
-
# "width": in_chm.shape[1],
|
|
101
|
-
# "nodata": BT_NODATA,
|
|
102
|
-
# "transform": out_transformC})
|
|
103
|
-
|
|
104
|
-
# print('Loading CHM ...')
|
|
105
|
-
cell_x, cell_y = out_meta['transform'][0], -out_meta['transform'][4]
|
|
106
|
-
|
|
107
|
-
# print('Preparing Kernel window ...')
|
|
108
|
-
kernel = convolution.circle_kernel(cell_x, cell_y, tree_radius)
|
|
91
|
+
if BT_DEBUGGING:
|
|
92
|
+
print('[Debug]: Preparing Kernel window ...')
|
|
93
|
+
kernel = convolution.circle_kernel(cell_x, cell_y, int(tree_radius))
|
|
109
94
|
|
|
110
95
|
# Generate Canopy Raster and return the Canopy array
|
|
96
|
+
if BT_DEBUGGING:
|
|
97
|
+
print('[Debug]: Generate Canopy Raster ...')
|
|
111
98
|
dyn_canopy_ndarray = dyn_np_cc_map(in_chm, canopy_ht_threshold, BT_NODATA)
|
|
112
99
|
|
|
113
100
|
# Calculating focal statistic from canopy raster
|
|
101
|
+
if BT_DEBUGGING:
|
|
102
|
+
print('[Debug]: Calculating focal statistic from Canopy Raster ...')
|
|
114
103
|
cc_std, cc_mean = dyn_fs_raster_stdmean(dyn_canopy_ndarray, kernel, BT_NODATA)
|
|
115
104
|
|
|
116
105
|
# Smoothing raster
|
|
106
|
+
if BT_DEBUGGING:
|
|
107
|
+
print('[Debug]: Generating smoothed cost raster ...')
|
|
117
108
|
cc_smooth = dyn_smooth_cost(dyn_canopy_ndarray, max_line_dist, [cell_x, cell_y])
|
|
118
109
|
avoidance = max(min(float(canopy_avoid), 1), 0)
|
|
119
|
-
cost_clip = dyn_np_cost_raster(
|
|
120
|
-
|
|
121
|
-
|
|
110
|
+
cost_clip = dyn_np_cost_raster(
|
|
111
|
+
dyn_canopy_ndarray,
|
|
112
|
+
cc_mean,
|
|
113
|
+
cc_std,
|
|
114
|
+
cc_smooth,
|
|
115
|
+
avoidance,
|
|
116
|
+
cost_raster_exponent,
|
|
117
|
+
)
|
|
118
|
+
negative_cost_clip = np.where(
|
|
119
|
+
np.isnan(cost_clip), -9999, cost_clip
|
|
120
|
+
) # TODO was nodata, changed to BT_NODATA_COST
|
|
122
121
|
return (
|
|
123
|
-
line_df,
|
|
124
|
-
|
|
122
|
+
line_df,
|
|
123
|
+
dyn_canopy_ndarray,
|
|
124
|
+
negative_cost_clip,
|
|
125
|
+
out_meta,
|
|
126
|
+
max_line_dist,
|
|
127
|
+
nodata,
|
|
128
|
+
line_id,
|
|
129
|
+
Cut_Dist,
|
|
130
|
+
line_buffer,
|
|
131
|
+
exp_shk_cell
|
|
125
132
|
)
|
|
126
133
|
|
|
127
134
|
except Exception as e:
|
|
128
|
-
print("Error in
|
|
135
|
+
print("[Error]: Error in creating (dynamic) cost raster @ {}: {}".format(line_id, e))
|
|
129
136
|
return None
|
|
130
137
|
|
|
131
138
|
|
|
@@ -150,15 +157,15 @@ def split_line_npart(line):
|
|
|
150
157
|
def split_into_segments(df):
|
|
151
158
|
odf = df
|
|
152
159
|
crs = odf.crs
|
|
153
|
-
if
|
|
154
|
-
df[
|
|
160
|
+
if "OLnSEG" not in odf.columns.array:
|
|
161
|
+
df["OLnSEG"] = np.nan
|
|
155
162
|
|
|
156
163
|
df = odf.assign(geometry=odf.apply(lambda x: split_line_fc(x.geometry), axis=1))
|
|
157
164
|
df = df.explode()
|
|
158
165
|
|
|
159
|
-
df[
|
|
166
|
+
df["OLnSEG"] = df.groupby("OLnFID").cumcount()
|
|
160
167
|
gdf = GeoDataFrame(df, geometry=df.geometry, crs=crs)
|
|
161
|
-
gdf = gdf.sort_values(by=[
|
|
168
|
+
gdf = gdf.sort_values(by=["OLnFID", "OLnSEG"])
|
|
162
169
|
gdf = gdf.reset_index(drop=True)
|
|
163
170
|
return gdf
|
|
164
171
|
|
|
@@ -166,112 +173,202 @@ def split_into_segments(df):
|
|
|
166
173
|
def split_into_equal_nth_segments(df):
|
|
167
174
|
odf = df
|
|
168
175
|
crs = odf.crs
|
|
169
|
-
if
|
|
170
|
-
df[
|
|
176
|
+
if "OLnSEG" not in odf.columns.array:
|
|
177
|
+
df["OLnSEG"] = np.nan
|
|
171
178
|
df = odf.assign(geometry=odf.apply(lambda x: split_line_npart(x.geometry), axis=1))
|
|
172
179
|
df = df.explode(index_parts=True)
|
|
173
180
|
|
|
174
|
-
df[
|
|
181
|
+
df["OLnSEG"] = df.groupby("OLnFID").cumcount()
|
|
175
182
|
gdf = GeoDataFrame(df, geometry=df.geometry, crs=crs)
|
|
176
|
-
gdf = gdf.sort_values(by=[
|
|
183
|
+
gdf = gdf.sort_values(by=["OLnFID", "OLnSEG"])
|
|
177
184
|
gdf = gdf.reset_index(drop=True)
|
|
178
185
|
return gdf
|
|
179
186
|
|
|
180
187
|
|
|
181
|
-
def generate_line_args(
|
|
182
|
-
|
|
188
|
+
def generate_line_args(
|
|
189
|
+
line_seg,
|
|
190
|
+
work_in_bufferL,
|
|
191
|
+
work_in_bufferC,
|
|
192
|
+
raster,
|
|
193
|
+
tree_radius,
|
|
194
|
+
max_line_dist,
|
|
195
|
+
canopy_avoidance,
|
|
196
|
+
exponent,
|
|
197
|
+
work_in_bufferR,
|
|
198
|
+
canopy_thresh_percentage,
|
|
199
|
+
):
|
|
183
200
|
line_argsL = []
|
|
184
201
|
line_argsR = []
|
|
185
202
|
line_argsC = []
|
|
186
203
|
line_id = 0
|
|
187
204
|
for record in range(0, len(work_in_bufferL)):
|
|
188
|
-
line_bufferL = work_in_bufferL.loc[record,
|
|
189
|
-
line_bufferC = work_in_bufferC.loc[record,
|
|
190
|
-
LCut = work_in_bufferL.loc[record,
|
|
205
|
+
line_bufferL = work_in_bufferL.loc[record, "geometry"]
|
|
206
|
+
line_bufferC = work_in_bufferC.loc[record, "geometry"]
|
|
207
|
+
LCut = work_in_bufferL.loc[record, "LDist_Cut"]
|
|
191
208
|
|
|
192
|
-
clipped_rasterL, out_transformL =
|
|
193
|
-
|
|
209
|
+
clipped_rasterL, out_transformL = mask(
|
|
210
|
+
raster, [line_bufferL], crop=True, nodata=BT_NODATA, filled=True
|
|
211
|
+
)
|
|
194
212
|
clipped_rasterL = np.squeeze(clipped_rasterL, axis=0)
|
|
195
213
|
|
|
196
|
-
clipped_rasterC, out_transformC =
|
|
197
|
-
|
|
214
|
+
clipped_rasterC, out_transformC = mask(
|
|
215
|
+
raster, [line_bufferC], crop=True, nodata=BT_NODATA, filled=True
|
|
216
|
+
)
|
|
198
217
|
|
|
199
218
|
clipped_rasterC = np.squeeze(clipped_rasterC, axis=0)
|
|
200
219
|
|
|
201
220
|
# make rasterio meta for saving raster later
|
|
202
221
|
out_metaL = raster.meta.copy()
|
|
203
|
-
out_metaL.update(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
222
|
+
out_metaL.update(
|
|
223
|
+
{
|
|
224
|
+
"driver": "GTiff",
|
|
225
|
+
"height": clipped_rasterL.shape[0],
|
|
226
|
+
"width": clipped_rasterL.shape[1],
|
|
227
|
+
"nodata": BT_NODATA,
|
|
228
|
+
"transform": out_transformL,
|
|
229
|
+
}
|
|
230
|
+
)
|
|
208
231
|
|
|
209
232
|
out_metaC = raster.meta.copy()
|
|
210
|
-
out_metaC.update(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
233
|
+
out_metaC.update(
|
|
234
|
+
{
|
|
235
|
+
"driver": "GTiff",
|
|
236
|
+
"height": clipped_rasterC.shape[0],
|
|
237
|
+
"width": clipped_rasterC.shape[1],
|
|
238
|
+
"nodata": BT_NODATA,
|
|
239
|
+
"transform": out_transformC,
|
|
240
|
+
}
|
|
241
|
+
)
|
|
215
242
|
|
|
216
243
|
nodata = BT_NODATA
|
|
217
|
-
line_argsL.append(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
244
|
+
line_argsL.append(
|
|
245
|
+
[
|
|
246
|
+
clipped_rasterL,
|
|
247
|
+
float(work_in_bufferL.loc[record, "DynCanTh"]),
|
|
248
|
+
float(tree_radius),
|
|
249
|
+
float(max_line_dist),
|
|
250
|
+
float(canopy_avoidance),
|
|
251
|
+
float(exponent),
|
|
252
|
+
raster.res,
|
|
253
|
+
nodata,
|
|
254
|
+
line_seg.iloc[[record]],
|
|
255
|
+
out_metaL,
|
|
256
|
+
line_id,
|
|
257
|
+
LCut,
|
|
258
|
+
"Left",
|
|
259
|
+
canopy_thresh_percentage,
|
|
260
|
+
line_bufferC,
|
|
261
|
+
]
|
|
262
|
+
)
|
|
221
263
|
|
|
222
|
-
line_argsC.append(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
264
|
+
line_argsC.append(
|
|
265
|
+
[
|
|
266
|
+
clipped_rasterC,
|
|
267
|
+
float(work_in_bufferC.loc[record, "DynCanTh"]),
|
|
268
|
+
float(tree_radius),
|
|
269
|
+
float(max_line_dist),
|
|
270
|
+
float(canopy_avoidance),
|
|
271
|
+
float(exponent),
|
|
272
|
+
raster.res,
|
|
273
|
+
nodata,
|
|
274
|
+
line_seg.iloc[[record]],
|
|
275
|
+
out_metaC,
|
|
276
|
+
line_id,
|
|
277
|
+
10,
|
|
278
|
+
"Center",
|
|
279
|
+
canopy_thresh_percentage,
|
|
280
|
+
line_bufferC,
|
|
281
|
+
]
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
print(' "PROGRESS_LABEL Preparing line arguments... {} of {}" '.format(
|
|
285
|
+
line_id ,len(work_in_bufferL) + len(work_in_bufferR)),
|
|
286
|
+
flush=True )
|
|
287
|
+
print(" {}% ".format(
|
|
288
|
+
(line_id / (len(work_in_bufferL) + len(work_in_bufferR))) * 100)
|
|
289
|
+
,flush=True )
|
|
226
290
|
|
|
227
291
|
line_id += 1
|
|
228
292
|
|
|
229
293
|
line_id = 0
|
|
230
294
|
for record in range(0, len(work_in_bufferR)):
|
|
231
|
-
line_bufferR = work_in_bufferR.loc[record,
|
|
232
|
-
RCut = work_in_bufferR.loc[record,
|
|
233
|
-
clipped_rasterR, out_transformR =
|
|
234
|
-
|
|
295
|
+
line_bufferR = work_in_bufferR.loc[record, "geometry"]
|
|
296
|
+
RCut = work_in_bufferR.loc[record, "RDist_Cut"]
|
|
297
|
+
clipped_rasterR, out_transformR = mask(
|
|
298
|
+
raster, [line_bufferR], crop=True, nodata=BT_NODATA, filled=True
|
|
299
|
+
)
|
|
235
300
|
clipped_rasterR = np.squeeze(clipped_rasterR, axis=0)
|
|
236
301
|
|
|
237
302
|
# make rasterio meta for saving raster later
|
|
238
303
|
out_metaR = raster.meta.copy()
|
|
239
|
-
out_metaR.update(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
304
|
+
out_metaR.update(
|
|
305
|
+
{
|
|
306
|
+
"driver": "GTiff",
|
|
307
|
+
"height": clipped_rasterR.shape[0],
|
|
308
|
+
"width": clipped_rasterR.shape[1],
|
|
309
|
+
"nodata": BT_NODATA,
|
|
310
|
+
"transform": out_transformR,
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
line_bufferC = work_in_bufferC.loc[record, "geometry"]
|
|
314
|
+
clipped_rasterC, out_transformC = mask(
|
|
315
|
+
raster, [line_bufferC], crop=True, nodata=BT_NODATA, filled=True
|
|
316
|
+
)
|
|
247
317
|
|
|
248
318
|
clipped_rasterC = np.squeeze(clipped_rasterC, axis=0)
|
|
249
319
|
out_metaC = raster.meta.copy()
|
|
250
|
-
out_metaC.update(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
320
|
+
out_metaC.update(
|
|
321
|
+
{
|
|
322
|
+
"driver": "GTiff",
|
|
323
|
+
"height": clipped_rasterC.shape[0],
|
|
324
|
+
"width": clipped_rasterC.shape[1],
|
|
325
|
+
"nodata": BT_NODATA,
|
|
326
|
+
"transform": out_transformC,
|
|
327
|
+
}
|
|
328
|
+
)
|
|
255
329
|
|
|
256
330
|
nodata = BT_NODATA
|
|
257
331
|
# TODO deal with inherited nodata and BT_NODATA_COST
|
|
258
332
|
# TODO convert nodata to BT_NODATA_COST
|
|
259
|
-
line_argsR.append(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
333
|
+
line_argsR.append(
|
|
334
|
+
[
|
|
335
|
+
clipped_rasterR,
|
|
336
|
+
float(work_in_bufferR.loc[record, "DynCanTh"]),
|
|
337
|
+
float(tree_radius),
|
|
338
|
+
float(max_line_dist),
|
|
339
|
+
float(canopy_avoidance),
|
|
340
|
+
float(exponent),
|
|
341
|
+
raster.res,
|
|
342
|
+
nodata,
|
|
343
|
+
line_seg.iloc[[record]],
|
|
344
|
+
out_metaR,
|
|
345
|
+
line_id,
|
|
346
|
+
RCut,
|
|
347
|
+
"Right",
|
|
348
|
+
canopy_thresh_percentage,
|
|
349
|
+
line_bufferC,
|
|
350
|
+
]
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
print(
|
|
354
|
+
' "PROGRESS_LABEL Preparing line arguments... {} of {}" '.format(
|
|
355
|
+
line_id + 1 + len(work_in_bufferL),
|
|
356
|
+
len(work_in_bufferL) + len(work_in_bufferR),
|
|
357
|
+
),
|
|
358
|
+
flush=True,
|
|
359
|
+
)
|
|
267
360
|
print(
|
|
268
|
-
|
|
269
|
-
|
|
361
|
+
" {}% ".format(
|
|
362
|
+
(line_id + 1 + len(work_in_bufferL)) / (len(work_in_bufferL) + len(work_in_bufferR)) * 100
|
|
363
|
+
),
|
|
364
|
+
flush=True,
|
|
365
|
+
)
|
|
270
366
|
|
|
271
367
|
line_id += 1
|
|
272
368
|
|
|
273
369
|
return line_argsL, line_argsR, line_argsC
|
|
274
370
|
|
|
371
|
+
|
|
275
372
|
#
|
|
276
373
|
# def find_corridor_threshold_boundary(canopy_clip, least_cost_path, corridor_raster):
|
|
277
374
|
# threshold = -1
|
|
@@ -338,13 +435,9 @@ def find_corridor_threshold(raster):
|
|
|
338
435
|
|
|
339
436
|
|
|
340
437
|
def process_single_line_relative(segment):
|
|
341
|
-
#
|
|
342
|
-
|
|
343
|
-
#
|
|
344
|
-
#DynCanTh = segment[1]
|
|
345
|
-
|
|
346
|
-
# Segment args from mulitprocessing:
|
|
347
|
-
# [clipped_chm, float(work_in_bufferR.loc[record, 'DynCanTh']), float(tree_radius),
|
|
438
|
+
# this function takes single line to generate the line footprint
|
|
439
|
+
# Input segment arguments:
|
|
440
|
+
# [in_chm, float(work_in_bufferR.loc[record, 'DynCanTh']), float(tree_radius),
|
|
348
441
|
# float(max_line_dist), float(canopy_avoidance), float(exponent), raster.res, nodata,
|
|
349
442
|
# line_seg.iloc[[record]], out_meta, line_id,RCut,Side,canopy_thresh_percentage,line_buffer]
|
|
350
443
|
|
|
@@ -352,35 +445,39 @@ def process_single_line_relative(segment):
|
|
|
352
445
|
segment = dyn_canopy_cost_raster(segment)
|
|
353
446
|
if segment is None:
|
|
354
447
|
return None
|
|
355
|
-
#
|
|
356
|
-
|
|
448
|
+
# segment:
|
|
449
|
+
"""
|
|
450
|
+
0:line_df,
|
|
451
|
+
1:dyn_canopy_ndarray,
|
|
452
|
+
2:negative_cost_clip,
|
|
453
|
+
3:out_meta,
|
|
454
|
+
4:max_line_dist,
|
|
455
|
+
5:nodata,
|
|
456
|
+
6:line_id,
|
|
457
|
+
7:Cut_Dist,
|
|
458
|
+
8: line_buffer,
|
|
459
|
+
9: exp_shk_cell
|
|
460
|
+
"""
|
|
357
461
|
|
|
358
|
-
# this function takes single line to work the line footprint
|
|
359
462
|
# (regardless it process the whole line or individual segment)
|
|
360
463
|
df = segment[0]
|
|
361
464
|
in_canopy_r = segment[1]
|
|
362
465
|
in_cost_r = segment[2]
|
|
363
|
-
|
|
466
|
+
in_meta = segment[3]
|
|
467
|
+
no_data = segment[5]
|
|
468
|
+
Cut_Dist = segment[7]
|
|
469
|
+
exp_shk_cell=segment[9]
|
|
470
|
+
shapefile_proj = df.crs
|
|
471
|
+
in_transform = in_meta["transform"]
|
|
364
472
|
|
|
365
|
-
# in_transform = segment[3]
|
|
366
473
|
if np.isnan(in_canopy_r).all():
|
|
367
474
|
print("Canopy raster empty")
|
|
368
475
|
|
|
369
476
|
if np.isnan(in_cost_r).all():
|
|
370
477
|
print("Cost raster empty")
|
|
371
478
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
no_data = segment[5]
|
|
375
|
-
line_id = segment[6]
|
|
376
|
-
Cut_Dist = segment[7]
|
|
377
|
-
line_bufferR = segment[8]
|
|
378
|
-
|
|
379
|
-
shapefile_proj = df.crs
|
|
380
|
-
in_transform = in_meta['transform']
|
|
381
|
-
|
|
382
|
-
FID = df['OLnSEG'] # segment line feature ID
|
|
383
|
-
OID = df['OLnFID'] # original line ID for segment line
|
|
479
|
+
FID = df["OLnSEG"] # segment line feature ID
|
|
480
|
+
OID = df["OLnFID"] # original line ID for segment line
|
|
384
481
|
|
|
385
482
|
segment_list = []
|
|
386
483
|
|
|
@@ -401,7 +498,7 @@ def process_single_line_relative(segment):
|
|
|
401
498
|
if len(in_cost_r.shape) > 2:
|
|
402
499
|
in_cost_r = np.squeeze(in_cost_r, axis=0)
|
|
403
500
|
remove_nan_from_array(in_cost_r)
|
|
404
|
-
in_cost_r[in_cost_r==no_data]=np.inf
|
|
501
|
+
in_cost_r[in_cost_r == no_data] = np.inf
|
|
405
502
|
|
|
406
503
|
# generate 1m interval points along line
|
|
407
504
|
distance_delta = 1
|
|
@@ -409,9 +506,14 @@ def process_single_line_relative(segment):
|
|
|
409
506
|
multipoint_along_line = [feat.interpolate(distance) for distance in distances]
|
|
410
507
|
multipoint_along_line.append(Point(segment_list[-1]))
|
|
411
508
|
# Rasterize points along line
|
|
412
|
-
rasterized_points_Alongln = features.rasterize(
|
|
413
|
-
|
|
414
|
-
|
|
509
|
+
rasterized_points_Alongln = features.rasterize(
|
|
510
|
+
multipoint_along_line,
|
|
511
|
+
out_shape=in_cost_r.shape,
|
|
512
|
+
transform=in_transform,
|
|
513
|
+
fill=0,
|
|
514
|
+
all_touched=True,
|
|
515
|
+
default_value=1,
|
|
516
|
+
)
|
|
415
517
|
points_Alongln = np.transpose(np.nonzero(rasterized_points_Alongln))
|
|
416
518
|
|
|
417
519
|
# Find minimum cost paths through an N-d costs array.
|
|
@@ -419,11 +521,9 @@ def process_single_line_relative(segment):
|
|
|
419
521
|
flex_cost_alongLn, flex_back_alongLn = mcp_flexible1.find_costs(starts=points_Alongln)
|
|
420
522
|
|
|
421
523
|
# Generate corridor
|
|
422
|
-
# corridor = source_cost_acc + dest_cost_acc
|
|
423
524
|
corridor = flex_cost_alongLn # +flex_cost_dest #cum_cost_tosource+cum_cost_todestination
|
|
424
525
|
corridor = np.ma.masked_invalid(corridor)
|
|
425
526
|
|
|
426
|
-
|
|
427
527
|
# Calculate minimum value of corridor raster
|
|
428
528
|
if not np.ma.min(corridor) is None:
|
|
429
529
|
corr_min = float(np.ma.min(corridor))
|
|
@@ -435,9 +535,9 @@ def process_single_line_relative(segment):
|
|
|
435
535
|
|
|
436
536
|
# Set minimum as zero and save minimum file
|
|
437
537
|
# corridor_th_value = find_corridor_threshold(corridor_norm)
|
|
438
|
-
corridor_th_value =
|
|
538
|
+
corridor_th_value = Cut_Dist / cell_size_x
|
|
439
539
|
if corridor_th_value < 0: # if no threshold found, use default value
|
|
440
|
-
corridor_th_value =
|
|
540
|
+
corridor_th_value = FP_CORRIDOR_THRESHOLD / cell_size_x
|
|
441
541
|
|
|
442
542
|
# corridor_th_value = FP_CORRIDOR_THRESHOLD
|
|
443
543
|
corridor_thresh = np.ma.where(corridor_norm >= corridor_th_value, 1.0, 0.0)
|
|
@@ -449,7 +549,7 @@ def process_single_line_relative(segment):
|
|
|
449
549
|
# Process: Stamp CC and Max Line Width
|
|
450
550
|
# Original code here
|
|
451
551
|
# RasterClass = SetNull(IsNull(CorridorMin),((CorridorMin) + ((Canopy_Raster) >= 1)) > 0)
|
|
452
|
-
temp1 =
|
|
552
|
+
temp1 = corridor_thresh + in_canopy_r
|
|
453
553
|
raster_class = np.ma.where(temp1 == 0, 1, 0).data
|
|
454
554
|
|
|
455
555
|
# BERA proposed Binary morphology
|
|
@@ -471,20 +571,20 @@ def process_single_line_relative(segment):
|
|
|
471
571
|
# BERA proposed Binary morphology Shrink
|
|
472
572
|
# fileShrink = ndimage.binary_erosion((Expanded),iterations=Exp_Shk_cell,border_value=1)
|
|
473
573
|
else:
|
|
474
|
-
print(
|
|
574
|
+
print("No Expand And Shrink cell performed.")
|
|
475
575
|
|
|
476
576
|
file_shrink = raster_class
|
|
477
577
|
|
|
478
578
|
# Process: Boundary Clean
|
|
479
|
-
clean_raster = ndimage.gaussian_filter(file_shrink, sigma=0, mode=
|
|
579
|
+
clean_raster = ndimage.gaussian_filter(file_shrink, sigma=0, mode="nearest")
|
|
480
580
|
|
|
481
581
|
# creat mask for non-polygon area
|
|
482
|
-
|
|
582
|
+
polygon_mask = np.where(clean_raster == 1, True, False)
|
|
483
583
|
if clean_raster.dtype == np.int64:
|
|
484
584
|
clean_raster = clean_raster.astype(np.int32)
|
|
485
585
|
|
|
486
586
|
# Process: ndarray to shapely Polygon
|
|
487
|
-
out_polygon = features.shapes(clean_raster, mask=
|
|
587
|
+
out_polygon = features.shapes(clean_raster, mask=polygon_mask, transform=in_transform)
|
|
488
588
|
|
|
489
589
|
# create a shapely multipolygon
|
|
490
590
|
multi_polygon = []
|
|
@@ -493,13 +593,20 @@ def process_single_line_relative(segment):
|
|
|
493
593
|
poly = MultiPolygon(multi_polygon)
|
|
494
594
|
|
|
495
595
|
# create a pandas dataframe for the FP
|
|
496
|
-
out_data = pd.DataFrame(
|
|
497
|
-
|
|
596
|
+
out_data = pd.DataFrame(
|
|
597
|
+
{
|
|
598
|
+
"OLnFID": OID,
|
|
599
|
+
"OLnSEG": FID,
|
|
600
|
+
"CorriThresh": corridor_th_value,
|
|
601
|
+
"geometry": poly,
|
|
602
|
+
}
|
|
603
|
+
)
|
|
604
|
+
out_gdata = GeoDataFrame(out_data, geometry="geometry", crs=shapefile_proj)
|
|
498
605
|
|
|
499
606
|
return out_gdata, corridor_poly_gpd
|
|
500
607
|
|
|
501
608
|
except Exception as e:
|
|
502
|
-
print(
|
|
609
|
+
print("Exception: {}".format(e))
|
|
503
610
|
|
|
504
611
|
|
|
505
612
|
def multiprocessing_footprint_relative(line_args, processes):
|
|
@@ -507,35 +614,71 @@ def multiprocessing_footprint_relative(line_args, processes):
|
|
|
507
614
|
total_steps = len(line_args)
|
|
508
615
|
|
|
509
616
|
feats = []
|
|
510
|
-
# chunksize = math.ceil(total_steps / processes)
|
|
511
617
|
with Pool(processes=processes) as pool:
|
|
512
618
|
step = 0
|
|
513
619
|
# execute tasks in order, process results out of order
|
|
514
620
|
for result in pool.imap_unordered(process_single_line_relative, line_args):
|
|
515
621
|
if BT_DEBUGGING:
|
|
516
|
-
print(
|
|
622
|
+
print("Got result: {}".format(result), flush=True)
|
|
517
623
|
if result != None:
|
|
518
624
|
feats.append(result)
|
|
519
625
|
step += 1
|
|
520
|
-
print(
|
|
521
|
-
|
|
522
|
-
|
|
626
|
+
print(
|
|
627
|
+
' "PROGRESS_LABEL Dynamic Segment Line Footprint {} of {}" '.format(step, total_steps),
|
|
628
|
+
flush=True,
|
|
629
|
+
)
|
|
630
|
+
print(" {}% ".format(step / total_steps * 100), flush=True)
|
|
523
631
|
return feats
|
|
524
632
|
except OperationCancelledException:
|
|
525
633
|
print("Operation cancelled")
|
|
526
634
|
return None
|
|
527
635
|
|
|
528
636
|
|
|
529
|
-
def main_line_footprint_relative(
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
637
|
+
def main_line_footprint_relative(
|
|
638
|
+
in_line:str,
|
|
639
|
+
in_chm: str,
|
|
640
|
+
max_ln_width: float,
|
|
641
|
+
out_footprint:str,
|
|
642
|
+
out_centerline:str,
|
|
643
|
+
exp_shk_cell:int,
|
|
644
|
+
tree_radius:float,
|
|
645
|
+
max_line_dist:float,
|
|
646
|
+
canopy_avoidance:float,
|
|
647
|
+
exponent:float,
|
|
648
|
+
full_step:bool,
|
|
649
|
+
canopy_thresh_percentage:int,
|
|
650
|
+
processes:int,
|
|
651
|
+
verbose:bool,
|
|
652
|
+
debug_mode:bool=BT_DEBUGGING,
|
|
653
|
+
)-> None:
|
|
654
|
+
"""
|
|
655
|
+
This function take the centerlines with forest canopy height and distance to edge to generate
|
|
656
|
+
dynamic footprint from input CHM. An option smooth centerlines will be generated.
|
|
657
|
+
Args:
|
|
658
|
+
in_line: Path like string for input centerlines
|
|
659
|
+
in_chm: Path like string input CHM
|
|
660
|
+
max_ln_width: Maximum processing width for input lines
|
|
661
|
+
out_footprint: Path like string output footprint
|
|
662
|
+
out_centerline: (Option) Path like string output centerline
|
|
663
|
+
exp_shk_cell: Range as integer used for cell erosion
|
|
664
|
+
tree_radius: Radius of trees influence in the cost raster
|
|
665
|
+
max_line_dist: Maximum Euclidean distance from canopy
|
|
666
|
+
canopy_avoidance: 0 < Ratio < 1 of importance between canopy search radius and Euclidean distance
|
|
667
|
+
exponent: Affects the cost of vegetated areas in an exponential fashion
|
|
668
|
+
canopy_thresh_percentage: Percentage as integer range 1-100
|
|
669
|
+
|
|
670
|
+
Returns:
|
|
671
|
+
New saved footprints and/or with smoothed centerlines dataset(s).
|
|
672
|
+
"""
|
|
673
|
+
in_file, layer = decode_file_layer(in_line)
|
|
674
|
+
line_seg = GeoDataFrame.from_file(in_file, layer=layer)
|
|
675
|
+
_, processes = parallel_mode(processes)
|
|
534
676
|
|
|
535
677
|
# If Dynamic canopy threshold column not found, create one
|
|
536
|
-
if
|
|
537
|
-
print("
|
|
538
|
-
|
|
678
|
+
if "DynCanTh" not in line_seg.columns.array:
|
|
679
|
+
print("[Warning]: Field {} is not found and will be populated default values.".format("DynCanTh"))
|
|
680
|
+
line_seg['DynCanTh']=3.0
|
|
681
|
+
|
|
539
682
|
if not float(canopy_thresh_percentage):
|
|
540
683
|
canopy_thresh_percentage = 50
|
|
541
684
|
else:
|
|
@@ -546,115 +689,136 @@ def main_line_footprint_relative(callback, in_line, in_chm, max_ln_width, exp_sh
|
|
|
546
689
|
if float(exponent) <= 0.0:
|
|
547
690
|
exponent = 1.0
|
|
548
691
|
# If OLnFID column is not found, column will be created
|
|
549
|
-
if
|
|
550
|
-
print("Created {} column in input line data.".format(
|
|
551
|
-
line_seg[
|
|
692
|
+
if "OLnFID" not in line_seg.columns.array:
|
|
693
|
+
print("[info]: Created {} column in input line data.".format("OLnFID"))
|
|
694
|
+
line_seg["OLnFID"] = line_seg.index
|
|
552
695
|
|
|
553
|
-
if
|
|
554
|
-
line_seg[
|
|
696
|
+
if "OLnSEG" not in line_seg.columns.array:
|
|
697
|
+
line_seg["OLnSEG"] = 0
|
|
555
698
|
|
|
556
|
-
print(
|
|
699
|
+
print("{}%".format(10))
|
|
557
700
|
|
|
558
701
|
# check coordinate systems between line and raster features
|
|
559
702
|
with rasterio.open(in_chm) as raster:
|
|
560
703
|
line_args = []
|
|
561
704
|
|
|
562
|
-
if compare_crs(vector_crs(
|
|
705
|
+
if compare_crs(vector_crs(in_file), raster_crs(in_chm)):
|
|
563
706
|
proc_segments = False
|
|
564
707
|
if proc_segments:
|
|
565
|
-
print("Splitting lines into segments...")
|
|
708
|
+
print("[Info]: Splitting lines into segments...")
|
|
566
709
|
line_seg_split = split_into_segments(line_seg)
|
|
567
|
-
print("Splitting lines into segments...Done")
|
|
710
|
+
print("[info]: Splitting lines into segments...Done")
|
|
568
711
|
else:
|
|
569
712
|
if full_step:
|
|
570
|
-
print("Tool runs on input lines......")
|
|
713
|
+
print("[info]: Tool runs on input lines......")
|
|
571
714
|
line_seg_split = line_seg
|
|
572
715
|
else:
|
|
573
|
-
print("Tool runs on input segment lines......")
|
|
574
|
-
line_seg_split =
|
|
716
|
+
print("[info]: Tool runs on input segment lines......")
|
|
717
|
+
line_seg_split = split_into_equal_Nth_segments(line_seg, 250)
|
|
575
718
|
|
|
576
|
-
print(
|
|
719
|
+
print("{}%".format(20))
|
|
577
720
|
|
|
578
721
|
work_in_bufferL1 = GeoDataFrame.copy(line_seg_split)
|
|
579
722
|
work_in_bufferL2 = GeoDataFrame.copy(line_seg_split)
|
|
580
723
|
work_in_bufferR1 = GeoDataFrame.copy(line_seg_split)
|
|
581
724
|
work_in_bufferR2 = GeoDataFrame.copy(line_seg_split)
|
|
582
725
|
work_in_bufferC = GeoDataFrame.copy(line_seg_split)
|
|
583
|
-
work_in_bufferL1[
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
726
|
+
work_in_bufferL1["geometry"] = buffer(
|
|
727
|
+
work_in_bufferL1["geometry"],
|
|
728
|
+
distance=float(max_ln_width) + 1,
|
|
729
|
+
cap_style=3,
|
|
730
|
+
single_sided=True,
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
work_in_bufferL2["geometry"] = buffer(
|
|
734
|
+
work_in_bufferL2["geometry"],
|
|
735
|
+
distance=-1,
|
|
736
|
+
cap_style=3,
|
|
737
|
+
single_sided=True,
|
|
738
|
+
)
|
|
588
739
|
|
|
589
740
|
work_in_bufferL = GeoDataFrame(pd.concat([work_in_bufferL1, work_in_bufferL2]))
|
|
590
|
-
work_in_bufferL = work_in_bufferL.dissolve(by=[
|
|
591
|
-
|
|
592
|
-
work_in_bufferR1[
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
741
|
+
work_in_bufferL = work_in_bufferL.dissolve(by=["OLnFID", "OLnSEG"], as_index=False)
|
|
742
|
+
|
|
743
|
+
work_in_bufferR1["geometry"] = buffer(
|
|
744
|
+
work_in_bufferR1["geometry"],
|
|
745
|
+
distance=-float(max_ln_width) - 1,
|
|
746
|
+
cap_style=3,
|
|
747
|
+
single_sided=True,
|
|
748
|
+
)
|
|
749
|
+
work_in_bufferR2["geometry"] = buffer(
|
|
750
|
+
work_in_bufferR2["geometry"], distance=1, cap_style=3, single_sided=True
|
|
751
|
+
)
|
|
596
752
|
|
|
597
753
|
work_in_bufferR = GeoDataFrame(pd.concat([work_in_bufferR1, work_in_bufferR2]))
|
|
598
|
-
work_in_bufferR = work_in_bufferR.dissolve(by=[
|
|
599
|
-
|
|
600
|
-
work_in_bufferC[
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
line_argsL, line_argsR, line_argsC = generate_line_args_DFP_NoClip(
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
754
|
+
work_in_bufferR = work_in_bufferR.dissolve(by=["OLnFID", "OLnSEG"], as_index=False)
|
|
755
|
+
|
|
756
|
+
work_in_bufferC["geometry"] = buffer(
|
|
757
|
+
work_in_bufferC["geometry"],
|
|
758
|
+
distance=float(max_ln_width),
|
|
759
|
+
cap_style=3,
|
|
760
|
+
single_sided=False,
|
|
761
|
+
)
|
|
762
|
+
print("[info]: Prepare arguments for Dynamic FP ...")
|
|
763
|
+
# Just prepare arguments for multiprocessing
|
|
764
|
+
line_argsL, line_argsR, line_argsC = generate_line_args_DFP_NoClip(
|
|
765
|
+
line_seg_split,
|
|
766
|
+
work_in_bufferL,
|
|
767
|
+
work_in_bufferC,
|
|
768
|
+
raster,
|
|
769
|
+
in_chm,
|
|
770
|
+
tree_radius,
|
|
771
|
+
max_line_dist,
|
|
772
|
+
canopy_avoidance,
|
|
773
|
+
exponent,
|
|
774
|
+
work_in_bufferR,
|
|
775
|
+
canopy_thresh_percentage,
|
|
776
|
+
exp_shk_cell,
|
|
777
|
+
)
|
|
614
778
|
|
|
615
779
|
else:
|
|
616
|
-
print("Line and canopy raster spatial references are not same, please check.")
|
|
780
|
+
print("[info]: Line and canopy raster spatial references are not same, please check.")
|
|
617
781
|
exit()
|
|
618
782
|
# pass center lines for footprint
|
|
619
|
-
print("Generating Dynamic footprint ...")
|
|
783
|
+
print("[info]: Generating Dynamic footprint ...")
|
|
620
784
|
|
|
621
785
|
feat_listL = []
|
|
622
786
|
feat_listR = []
|
|
623
|
-
feat_listC = []
|
|
624
787
|
poly_listL = []
|
|
625
788
|
poly_listR = []
|
|
626
789
|
footprint_listL = []
|
|
627
790
|
footprint_listR = []
|
|
628
|
-
footprint_listC = []
|
|
629
|
-
# PARALLEL_MODE = ParallelMode.SEQUENTIAL
|
|
630
791
|
if PARALLEL_MODE == ParallelMode.MULTIPROCESSING:
|
|
631
|
-
# feat_listC = multiprocessing_footprint_relative(line_argsC, processes)
|
|
632
792
|
feat_listL = multiprocessing_footprint_relative(line_argsL, processes)
|
|
633
|
-
# feat_listL = execute_multiprocessing(process_single_line_relative,'Footprint',line_argsL, processes)
|
|
634
793
|
feat_listR = multiprocessing_footprint_relative(line_argsR, processes)
|
|
635
|
-
# feat_listR = execute_multiprocessing(process_single_line_relative, 'Footprint', line_argsR, processes)
|
|
636
794
|
|
|
637
795
|
elif PARALLEL_MODE == ParallelMode.SEQUENTIAL:
|
|
638
796
|
step = 1
|
|
639
797
|
total_steps = len(line_argsL)
|
|
640
|
-
print("There are {} result to process.".format(total_steps))
|
|
798
|
+
print("[info]: There are {} result to process.".format(total_steps))
|
|
641
799
|
for row in line_argsL:
|
|
642
800
|
feat_listL.append(process_single_line_relative(row))
|
|
643
|
-
print("Footprint (left side) for line {} is done".format(step))
|
|
644
|
-
print(
|
|
645
|
-
|
|
801
|
+
print("[info]: Footprint (left side) for line {} is done".format(step))
|
|
802
|
+
print(
|
|
803
|
+
' "PROGRESS_LABEL Dynamic Line Footprint {} of {}" '.format(step, total_steps),
|
|
804
|
+
flush=True,
|
|
805
|
+
)
|
|
806
|
+
print(" {}% ".format((step / total_steps) * 100))
|
|
646
807
|
step += 1
|
|
647
808
|
step = 1
|
|
648
809
|
total_steps = len(line_argsR)
|
|
649
810
|
for row in line_argsR:
|
|
650
811
|
feat_listR.append(process_single_line_relative(row))
|
|
651
|
-
print("Footprint for (right side) line {} is done".format(step))
|
|
652
|
-
print(
|
|
653
|
-
|
|
812
|
+
print("[info]: Footprint for (right side) line {} is done".format(step))
|
|
813
|
+
print(
|
|
814
|
+
' "PROGRESS_LABEL Dynamic Line Footprint {} of {}" '.format(step, total_steps),
|
|
815
|
+
flush=True,
|
|
816
|
+
)
|
|
817
|
+
print(" {}% ".format((step / total_steps) * 100))
|
|
654
818
|
step += 1
|
|
655
819
|
|
|
656
|
-
print(
|
|
657
|
-
|
|
820
|
+
print("{}%".format(80))
|
|
821
|
+
|
|
658
822
|
|
|
659
823
|
for feat in feat_listL:
|
|
660
824
|
if feat:
|
|
@@ -666,68 +830,57 @@ def main_line_footprint_relative(callback, in_line, in_chm, max_ln_width, exp_sh
|
|
|
666
830
|
footprint_listR.append(feat[0])
|
|
667
831
|
poly_listR.append(feat[1])
|
|
668
832
|
|
|
669
|
-
|
|
670
|
-
resultsL =
|
|
671
|
-
|
|
672
|
-
resultsR =
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
resultsR = resultsR.sort_values(by=['OLnFID', 'OLnSEG'])
|
|
833
|
+
resultsL = GeoDataFrame(pd.concat(footprint_listL,ignore_index=True))
|
|
834
|
+
resultsL["geometry"] = resultsL["geometry"].buffer(0.005)
|
|
835
|
+
resultsR = GeoDataFrame(pd.concat(footprint_listR,ignore_index=True))
|
|
836
|
+
resultsR["geometry"] = resultsR["geometry"].buffer(0.005)
|
|
837
|
+
resultsL = resultsL.sort_values(by=["OLnFID", "OLnSEG"])
|
|
838
|
+
resultsR = resultsR.sort_values(by=["OLnFID", "OLnSEG"])
|
|
676
839
|
resultsL = resultsL.reset_index(drop=True)
|
|
677
840
|
resultsR = resultsR.reset_index(drop=True)
|
|
678
841
|
#
|
|
679
842
|
|
|
680
|
-
resultsAll = GeoDataFrame(pd.concat([resultsL, resultsR]))
|
|
681
|
-
dissolved_results = resultsAll.dissolve(by=
|
|
682
|
-
dissolved_results[
|
|
683
|
-
print("Saving output ...")
|
|
684
|
-
|
|
685
|
-
|
|
843
|
+
resultsAll = GeoDataFrame(pd.concat([resultsL, resultsR],ignore_index=True))
|
|
844
|
+
dissolved_results = resultsAll.dissolve(by="OLnFID", as_index=False)
|
|
845
|
+
dissolved_results["geometry"] = dissolved_results["geometry"].buffer(-0.005)
|
|
846
|
+
print("[info]: Saving output ...")
|
|
847
|
+
out_ft_file, layer = decode_file_layer(out_footprint)
|
|
848
|
+
dissolved_results.to_file(out_ft_file, layer=layer)
|
|
849
|
+
print("[info]: Footprint file saved")
|
|
686
850
|
|
|
687
851
|
# dissolved polygon group by column 'OLnFID'
|
|
688
|
-
print("Generating centerlines from corridor polygons ...")
|
|
852
|
+
print("[info]: Generating centerlines from corridor polygons ...")
|
|
689
853
|
resultsCL = GeoDataFrame(pd.concat(poly_listL))
|
|
690
|
-
resultsCL[
|
|
854
|
+
resultsCL["geometry"] = resultsCL["geometry"].buffer(0.005)
|
|
691
855
|
resultsCR = GeoDataFrame(pd.concat(poly_listR))
|
|
692
|
-
resultsCR[
|
|
856
|
+
resultsCR["geometry"] = resultsCR["geometry"].buffer(0.005)
|
|
693
857
|
|
|
694
858
|
resultsCLR = GeoDataFrame(pd.concat([resultsCL, resultsCR]))
|
|
695
|
-
resultsCLR = resultsCLR.dissolve(by=
|
|
696
|
-
resultsCLR = resultsCLR.sort_values(by=[
|
|
859
|
+
resultsCLR = resultsCLR.dissolve(by="OLnFID", as_index=False)
|
|
860
|
+
resultsCLR = resultsCLR.sort_values(by=["OLnFID", "OLnSEG"])
|
|
697
861
|
resultsCLR = resultsCLR.reset_index(drop=True)
|
|
698
|
-
resultsCLR[
|
|
862
|
+
resultsCLR["geometry"] = resultsCLR["geometry"].buffer(-0.005)
|
|
699
863
|
|
|
700
|
-
# out_centerline=False
|
|
701
864
|
# save lines to file
|
|
702
865
|
if out_centerline:
|
|
866
|
+
out_cl_file, layer = decode_file_layer(out_centerline)
|
|
703
867
|
poly_centerline_gpd = find_centerlines(resultsCLR, line_seg, processes)
|
|
704
868
|
poly_gpd = poly_centerline_gpd.copy()
|
|
705
869
|
centerline_gpd = poly_centerline_gpd.copy()
|
|
706
870
|
|
|
707
|
-
centerline_gpd = centerline_gpd.set_geometry(
|
|
708
|
-
centerline_gpd = centerline_gpd.drop(columns=[
|
|
871
|
+
centerline_gpd = centerline_gpd.set_geometry("centerline")
|
|
872
|
+
centerline_gpd = centerline_gpd.drop(columns=["geometry"])
|
|
709
873
|
centerline_gpd.crs = poly_centerline_gpd.crs
|
|
710
|
-
centerline_gpd.to_file(out_centerline)
|
|
711
|
-
print("Centerline file saved")
|
|
712
|
-
|
|
713
|
-
# save polygons
|
|
714
|
-
path = Path(out_centerline)
|
|
715
|
-
path = path.with_stem(path.stem + '_poly')
|
|
716
|
-
poly_gpd = poly_gpd.drop(columns=['centerline'])
|
|
717
|
-
poly_gpd.to_file(path)
|
|
718
|
-
|
|
719
|
-
print('%{}'.format(100))
|
|
720
|
-
|
|
721
874
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
875
|
+
# save polygons from smooth centerline for debug
|
|
876
|
+
if debug_mode:
|
|
877
|
+
path = Path(out_cl_file)
|
|
878
|
+
path = path.with_stem(path.stem + "_poly")
|
|
879
|
+
poly_gpd = poly_gpd.drop(columns=["centerline"])
|
|
880
|
+
poly_gpd.to_file(path,layer='smoothedCL_poly')
|
|
881
|
+
print("[info]: Polygon from smoothed centerline file saved")
|
|
726
882
|
|
|
727
|
-
|
|
728
|
-
|
|
883
|
+
centerline_gpd.to_file(out_cl_file, layer=layer)
|
|
884
|
+
print("[info]: Smoothed centerline file saved")
|
|
729
885
|
|
|
730
|
-
print(
|
|
731
|
-
print('Dynamic Footprint processing finished')
|
|
732
|
-
print('Current time: {}'.format(time.strftime("%d %b %Y %H:%M:%S", time.localtime())))
|
|
733
|
-
print('Total processing time (seconds): {}'.format(round(time.time() - start_time, 3)))
|
|
886
|
+
print("{}%".format(100))
|