Skip to content

Commit f40fb9c

Browse files
committed
[processing] fixes and new tools for LiDAR processing
courtesy of Niccolò Marchi
1 parent ecf1f5a commit f40fb9c

30 files changed

+1074
-87
lines changed

python/plugins/processing/algs/lidar/LidarToolsAlgorithmProvider.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,15 @@
140140
from .fusion.PolyClipData import PolyClipData
141141
from .fusion.ImageCreate import ImageCreate
142142
from .fusion.IntensityImage import IntensityImage
143+
from .fusion.DensityMetrics import DensityMetrics
144+
from .fusion.MergeDTM import MergeDTM
145+
from .fusion.TopoMetrics import TopoMetrics
146+
from .fusion.TreeSeg import TreeSeg
147+
from .fusion.SplitDTM import SplitDTM
148+
from .fusion.MergeRaster import MergeRaster
149+
from .fusion.SurfaceStats import SurfaceStats
150+
from .fusion.ReturnDensity import ReturnDensity # spellok
151+
from .fusion.GridSurfaceStats import GridSurfaceStats
143152
from .fusion.FusionUtils import FusionUtils
144153

145154

@@ -213,7 +222,8 @@ def _loadAlgorithms(self):
213222
Csv2Grid(), Cover(), FilterData(), GridMetrics(), GroundFilter(),
214223
GridSurfaceCreate(), MergeData(), TinSurfaceCreate(), PolyClipData(),
215224
DTM2TIF(), DTM2ASCII(), FirstLastReturn(), ASCII2DTM(), ImageCreate(),
216-
IntensityImage()
225+
IntensityImage(), DensityMetrics(), MergeDTM(), TopoMetrics(), TreeSeg(),
226+
SplitDTM(), MergeRaster(), SurfaceStats(), ReturnDensity(), GridSurfaceStats() # spellok
217227
]
218228
for alg in fusiontools:
219229
alg.group, alg.i18n_group = alg.trAlgorithm('Fusion')

python/plugins/processing/algs/lidar/fusion/ASCII2DTM.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def defineCharacteristics(self):
5151
self.name, self.i18n_name = self.trAlgorithm('ASCII to DTM')
5252
self.group, self.i18n_group = self.trAlgorithm('Conversion')
5353
self.addParameter(ParameterFile(
54-
self.INPUT, self.tr('Input ESRI ASCII layer')))
54+
self.INPUT, self.tr('Input ESRI ASCII layer'), optional=False))
5555
self.addParameter(ParameterSelection(
5656
self.XYUNITS, self.tr('XY Units'), self.UNITS))
5757
self.addParameter(ParameterSelection(

python/plugins/processing/algs/lidar/fusion/CanopyMaxima.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from processing.core.parameters import ParameterFile
3333
from processing.core.parameters import ParameterNumber
3434
from processing.core.parameters import ParameterBoolean
35-
from processing.core.outputs import OutputTable
35+
from processing.core.outputs import OutputFile
3636
from .FusionUtils import FusionUtils
3737
from .FusionAlgorithm import FusionAlgorithm
3838

@@ -46,43 +46,53 @@ class CanopyMaxima(FusionAlgorithm):
4646
SUMMARY = 'SUMMARY'
4747
PARAM_A = 'PARAM_A'
4848
PARAM_C = 'PARAM_C'
49+
SHAPE = 'SHAPE'
4950

5051
def defineCharacteristics(self):
5152
self.name, self.i18n_name = self.trAlgorithm('Canopy Maxima')
5253
self.group, self.i18n_group = self.trAlgorithm('Points')
5354
self.addParameter(ParameterFile(
54-
self.INPUT, self.tr('Input FUSION canopy height model')))
55+
self.INPUT, self.tr('Input PLANS DTM canopy height model'),
56+
optional=False))
5557
self.addParameter(ParameterFile(
56-
self.GROUND, self.tr('Input ground .dtm layer [optional]')))
58+
self.GROUND, self.tr('Input ground PLANS DTM layer [optional]')))
5759
self.addParameter(ParameterNumber(
58-
self.THRESHOLD, self.tr('Height threshold'), 0, None, 10.0))
59-
# begin
60+
self.THRESHOLD, self.tr('Limit analysis to areas above this height threshold'), 0, None, 10.0))
61+
6062
self.addParameter(ParameterNumber(
6163
self.PARAM_A, self.tr('Variable window size: parameter A'), 0, None, 2.51503))
6264
self.addParameter(ParameterNumber(
6365
self.PARAM_C, self.tr('Parameter C'), 0, None, 0.00901))
64-
self.addParameter(ParameterBoolean(
65-
self.SUMMARY, self.tr('Summary (tree height summary statistics)'), False))
66-
# end
67-
self.addOutput(OutputTable(
68-
self.OUTPUT, self.tr('Output file with maxima')))
66+
summary = ParameterBoolean(
67+
self.SUMMARY, self.tr('Tree height summary statistics'), False)
68+
summary.isAdvanced = True
69+
self.addParameter(summary)
70+
shape = ParameterBoolean(
71+
self.SHAPE, self.tr('Create output shapefiles'), False)
72+
shape.isAdvanced = True
73+
self.addParameter(shape)
74+
75+
self.addOutput(OutputFile(
76+
self.OUTPUT, self.tr('Output file with maxima'), 'csv'))
77+
6978
self.addAdvancedModifiers()
7079

7180
def processAlgorithm(self, feedback):
7281
commands = [os.path.join(FusionUtils.FusionPath(), 'CanopyMaxima.exe')]
7382
commands.append('/verbose')
74-
### begin
75-
commands.append('/wse:' + str(self.getParameterValue(self.PARAM_A)) + ',0,' + str(self.getParameterValue(self.PARAM_C)) + ',0')
83+
commands.append('/wse:' + unicode(self.getParameterValue(self.PARAM_A)) + ',0,' + unicode(self.getParameterValue(self.PARAM_C)) + ',0')
84+
ground = self.getParameterValue(self.GROUND)
85+
if ground:
86+
gfiles = self.getParameterValue(self.GROUND).split(';')
87+
if len(gfiles) == 1:
88+
commands.append('/ground:' + str(ground))
89+
else:
90+
FusionUtils.createGroundList(gfiles)
91+
commands.append('/ground:' + str(FusionUtils.tempGroundListFilepath()))
92+
commands.append('/threshold:' + str(self.getParameterValue(self.THRESHOLD)))
7693
if self.getParameterValue(self.SUMMARY):
7794
commands.append('/summary')
78-
### end
7995
self.addAdvancedModifiersToCommand(commands)
80-
ground = self.getParameterValue(self.GROUND)
81-
## here it's necessary to have the support for multiple files like for INPUT.
82-
if str(ground).strip():
83-
commands.append('/ground:' + str(ground))
84-
commands.append('/threshold:'
85-
+ str(self.getParameterValue(self.THRESHOLD)))
8696
files = self.getParameterValue(self.INPUT).split(';')
8797
if len(files) == 1:
8898
commands.append(self.getParameterValue(self.INPUT))

python/plugins/processing/algs/lidar/fusion/CanopyModel.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class CanopyModel(FusionAlgorithm):
4545

4646
INPUT = 'INPUT'
4747
OUTPUT_DTM = 'OUTPUT_DTM'
48+
ASPECT = 'ASPECT'
4849
CELLSIZE = 'CELLSIZE'
4950
XYUNITS = 'XYUNITS'
5051
ZUNITS = 'ZUNITS'
@@ -54,13 +55,15 @@ class CanopyModel(FusionAlgorithm):
5455
SMOOTH = 'SMOOTH'
5556
SLOPE = 'SLOPE'
5657
CLASS = 'CLASS'
58+
RETURN = 'RETURN'
5759
ASCII = 'ASCII'
5860

5961
def defineCharacteristics(self):
6062
self.name, self.i18n_name = self.trAlgorithm('Canopy Model')
6163
self.group, self.i18n_group = self.trAlgorithm('Points')
6264
self.addParameter(ParameterFile(
63-
self.INPUT, self.tr('Input LAS layer')))
65+
self.INPUT, self.tr('Input LAS layer'),
66+
optional=False))
6467
self.addParameter(ParameterNumber(
6568
self.CELLSIZE, self.tr('Cell Size'), 0, None, 10.0))
6669
self.addParameter(ParameterSelection(
@@ -70,7 +73,7 @@ def defineCharacteristics(self):
7073
self.addOutput(OutputFile(
7174
self.OUTPUT_DTM, self.tr('.dtm output surface'), 'dtm'))
7275
ground = ParameterFile(
73-
self.GROUND, self.tr('Input ground DTM layer'), False, True)
76+
self.GROUND, self.tr('Input ground PLANS DTM layer'), False, True)
7477
ground.isAdvanced = True
7578
self.addParameter(ground)
7679
median = ParameterString(
@@ -82,13 +85,21 @@ def defineCharacteristics(self):
8285
smooth.isAdvanced = True
8386
self.addParameter(smooth)
8487
class_var = ParameterString(
85-
self.CLASS, self.tr('Class'), '', False, True)
88+
self.CLASS, self.tr('Select specific class'), '', False, True)
8689
class_var.isAdvanced = True
8790
self.addParameter(class_var)
91+
ret_num = ParameterString(
92+
self.RETURN, self.tr('Select specific return'), '', False, True)
93+
ret_num.isAdvanced = True
94+
self.addParameter(ret_num)
8895
slope = ParameterBoolean(
8996
self.SLOPE, self.tr('Calculate slope'), False)
9097
slope.isAdvanced = True
9198
self.addParameter(slope)
99+
aspec = ParameterBoolean(
100+
self.ASPECT, self.tr('Calculate aspect'), False)
101+
aspec.isAdvanced = True
102+
self.addParameter(aspect)
92103
self.addParameter(ParameterBoolean(
93104
self.ASCII, self.tr('Add an ASCII output'), False))
94105
self.addAdvancedModifiers()
@@ -98,7 +109,12 @@ def processAlgorithm(self, feedback):
98109
commands.append('/verbose')
99110
ground = self.getParameterValue(self.GROUND)
100111
if str(ground).strip():
101-
commands.append('/ground:' + str(ground))
112+
gfiles = self.getParameterValue(self.GROUND).split(';')
113+
if len(gfiles) == 1:
114+
commands.append('/ground:' + str(ground))
115+
else:
116+
FusionUtils.createGroundList(gfiles)
117+
commands.append('/ground:' + str(FusionUtils.tempGroundListFilepath()))
102118
median = self.getParameterValue(self.MEDIAN)
103119
if str(median).strip():
104120
commands.append('/median:' + str(median))
@@ -108,11 +124,17 @@ def processAlgorithm(self, feedback):
108124
slope = self.getParameterValue(self.SLOPE)
109125
if slope:
110126
commands.append('/slope')
127+
aspect = self.getParameterValue(self.ASPECT)
128+
if aspect:
129+
commands.append('/aspect')
111130
class_var = self.getParameterValue(self.CLASS)
112131
if str(class_var).strip():
113132
commands.append('/class:' + str(class_var))
114-
ascii = self.getParameterValue(self.ASCII)
115-
if ascii:
133+
ret_num = self.getParameterValue(self.RETURN)
134+
if str(ret_num).strip():
135+
commands.append('/return:' + str(ret_num))
136+
use_ascii = self.getParameterValue(self.ASCII)
137+
if use_ascii:
116138
commands.append('/ascii')
117139
self.addAdvancedModifiersToCommand(commands)
118140
commands.append(self.getOutputValue(self.OUTPUT_DTM))

python/plugins/processing/algs/lidar/fusion/Catalog.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import os
3030
from processing.core.parameters import ParameterFile
3131
from processing.core.parameters import ParameterString
32+
from processing.core.parameters import ParameterBoolean
3233
from processing.core.outputs import OutputFile
3334
from .FusionUtils import FusionUtils
3435
from .FusionAlgorithm import FusionAlgorithm
@@ -41,13 +42,19 @@ class Catalog(FusionAlgorithm):
4142
DENSITY = 'DENSITY'
4243
FIRSTDENSITY = 'FIRSTDENSITY'
4344
INTENSITY = 'INTENSITY'
45+
INDEX = 'INDEX'
46+
IMAGE = 'IMAGE'
47+
DRAWTILES = 'DRAWTILES'
48+
COVERAGE = 'COVERAGE'
49+
CRETURNS = 'CRETURNS'
4450
ADVANCED_MODIFIERS = 'ADVANCED_MODIFIERS'
4551

4652
def defineCharacteristics(self):
4753
self.name, self.i18n_name = self.trAlgorithm('Catalog')
4854
self.group, self.i18n_group = self.trAlgorithm('Points')
4955
self.addParameter(ParameterFile(
50-
self.INPUT, self.tr('Input LAS layer')))
56+
self.INPUT, self.tr('Input LAS layer'),
57+
optional=False))
5158
self.addOutput(OutputFile(self.OUTPUT, self.tr('Output files')))
5259
density = ParameterString(
5360
self.DENSITY,
@@ -67,6 +74,16 @@ def defineCharacteristics(self):
6774
'', False, True)
6875
intensity.isAdvanced = True
6976
self.addParameter(intensity)
77+
self.addParameter(ParameterBoolean(self.INDEX,
78+
self.tr('Create LIDAR data file indexes'), False))
79+
self.addParameter(ParameterBoolean(self.IMAGE,
80+
self.tr('Create image files showing the coverage area for each LIDAR file'), False))
81+
self.addParameter(ParameterBoolean(self.DRAWTILES,
82+
self.tr('Draw data file extents and names on the intensity image'), False))
83+
self.addParameter(ParameterBoolean(self.COVERAGE,
84+
self.tr('Create one image that shows the nominal coverage area'), False))
85+
self.addParameter(ParameterBoolean(self.CRETURNS,
86+
self.tr('Adds count return columns in the CSV and HTML output'), False))
7087
advanced_modifiers = ParameterString(
7188
self.ADVANCED_MODIFIERS,
7289
self.tr('Additional modifiers'), '', False, True)
@@ -85,6 +102,21 @@ def processAlgorithm(self, feedback):
85102
firstdensity = self.getParameterValue(self.FIRSTDENSITY)
86103
if str(firstdensity).strip():
87104
commands.append('/firstdensity:' + str(firstdensity))
105+
index = self.getParameterValue(self.INDEX)
106+
if str(index).strip():
107+
commands.append('/index')
108+
drawtiles = self.getParameterValue(self.IMAGE)
109+
if str(drawtiles).strip():
110+
commands.append('/drawtiles')
111+
coverage = self.getParameterValue(self.DRAWTILES)
112+
if str(coverage).strip():
113+
commands.append('/coverage')
114+
image = self.getParameterValue(self.COVERAGE)
115+
if str(image).strip():
116+
commands.append('/image')
117+
creturns = self.getParameterValue(self.COVERAGE)
118+
if str(creturns).strip():
119+
commands.append('/countreturns')
88120
advanced_modifiers = str(self.getParameterValue(self.ADVANCED_MODIFIERS)).strip()
89121
if advanced_modifiers:
90122
commands.append(advanced_modifiers)

python/plugins/processing/algs/lidar/fusion/ClipData.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ def defineCharacteristics(self):
5151
self.name, self.i18n_name = self.trAlgorithm('Clip Data')
5252
self.group, self.i18n_group = self.trAlgorithm('Points')
5353
self.addParameter(ParameterFile(
54-
self.INPUT, self.tr('Input LAS layer')))
54+
self.INPUT, self.tr('Input LAS layer'),
55+
optional=False))
5556
self.addParameter(ParameterExtent(self.EXTENT, self.tr('Extent'), optional=False))
5657
self.addParameter(ParameterSelection(
57-
self.SHAPE, self.tr('Shape'), ['Rectangle', 'Circle']))
58+
self.SHAPE, self.tr('Shape of the sample area'), ['Rectangle', 'Circle']))
5859
self.addOutput(OutputFile(
5960
self.OUTPUT, self.tr('Output clipped LAS file')))
6061
dtm = ParameterFile(
@@ -74,7 +75,12 @@ def processAlgorithm(self, feedback):
7475
commands.append('/shape:' + str(self.getParameterValue(self.SHAPE)))
7576
dtm = self.getParameterValue(self.DTM)
7677
if dtm:
77-
commands.append('/dtm:' + str(dtm))
78+
gfiles = self.getParameterValue(self.DTM).split(';')
79+
if len(gfiles) == 1:
80+
commands.append('/ground:' + str(dtm))
81+
else:
82+
FusionUtils.createGroundList(gfiles)
83+
commands.append('/ground:' + str(FusionUtils.tempGroundListFilepath()))
7884
height = self.getParameterValue(self.HEIGHT)
7985
if height:
8086
commands.append('/height')

python/plugins/processing/algs/lidar/fusion/CloudMetrics.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@
3232

3333
import os
3434
from processing.core.parameters import ParameterFile
35+
from processing.core.parameters import ParameterNumber
36+
from processing.core.parameters import ParameterBoolean
3537
from processing.core.outputs import OutputFile
3638
from .FusionUtils import FusionUtils
3739
from .FusionAlgorithm import FusionAlgorithm
38-
from processing.core.parameters import ParameterString
39-
from processing.core.parameters import ParameterBoolean
4040

4141

4242
class CloudMetrics(FusionAlgorithm):
@@ -52,10 +52,11 @@ def defineCharacteristics(self):
5252
self.name, self.i18n_name = self.trAlgorithm('Cloud Metrics')
5353
self.group, self.i18n_group = self.trAlgorithm('Points')
5454
self.addParameter(ParameterFile(
55-
self.INPUT, self.tr('Input LAS layer')))
55+
self.INPUT, self.tr('Input LAS layer'),
56+
optional=False))
5657
self.addOutput(OutputFile(
5758
self.OUTPUT, self.tr('Output file with tabular metric information'), 'csv'))
58-
above = ParameterString(self.ABOVE, self.tr('Above'), '', False)
59+
above = ParameterNumber(self.ABOVE, self.tr('Compute cover statistics above the following heightbreak:'), 0, None, 0.0)
5960
above.isAdvanced = True
6061
self.addParameter(above)
6162
firstImpulse = ParameterBoolean(
@@ -66,15 +67,15 @@ def defineCharacteristics(self):
6667
self.FIRSTRETURN, self.tr('First Return'), False)
6768
firstReturn.isAdvanced = True
6869
self.addParameter(firstReturn)
69-
htmin = ParameterString(self.HTMIN, self.tr('Htmin'), '', False, True)
70+
htmin = ParameterNumber(self.HTMIN, self.tr('Use only returns above this minimum height:'), 0, None, 0)
7071
htmin.isAdvanced = True
7172
self.addParameter(htmin)
7273

7374
def processAlgorithm(self, feedback):
7475
commands = [os.path.join(FusionUtils.FusionPath(), 'CloudMetrics.exe')]
7576
commands.append('/verbose')
7677
above = self.getParameterValue(self.ABOVE)
77-
if str(above).strip() != '':
78+
if above != 0.0:
7879
commands.append('/above:' + str(above))
7980
firstImpulse = self.getParameterValue(self.FIRSTIMPULSE)
8081
if firstImpulse:
@@ -83,7 +84,7 @@ def processAlgorithm(self, feedback):
8384
if firstReturn:
8485
commands.append('/firstreturn')
8586
htmin = self.getParameterValue(self.HTMIN)
86-
if str(htmin).strip() != '':
87+
if htmin != 0.0:
8788
commands.append('/minht:' + str(htmin))
8889
files = self.getParameterValue(self.INPUT).split(';')
8990
if len(files) == 1:

0 commit comments

Comments
 (0)