Skip to content
Permalink
Browse files

Merge pull request #4053 from alexbruy/lidartools-update

[processing] fixes and new tools for LiDAR processing
  • Loading branch information
alexbruy committed Jan 25, 2017
2 parents 0de0015 + 514e55b commit 66c7129d7b5c1a0b683d8475e3ec5a037056b129
Showing with 1,073 additions and 86 deletions.
  1. +11 −1 python/plugins/processing/algs/lidar/LidarToolsAlgorithmProvider.py
  2. +1 −1 python/plugins/processing/algs/lidar/fusion/ASCII2DTM.py
  3. +29 −19 python/plugins/processing/algs/lidar/fusion/CanopyMaxima.py
  4. +28 −6 python/plugins/processing/algs/lidar/fusion/CanopyModel.py
  5. +33 −1 python/plugins/processing/algs/lidar/fusion/Catalog.py
  6. +9 −3 python/plugins/processing/algs/lidar/fusion/ClipData.py
  7. +8 −7 python/plugins/processing/algs/lidar/fusion/CloudMetrics.py
  8. +25 −7 python/plugins/processing/algs/lidar/fusion/Cover.py
  9. +3 −3 python/plugins/processing/algs/lidar/fusion/Csv2Grid.py
  10. +2 −1 python/plugins/processing/algs/lidar/fusion/DTM2ASCII.py
  11. +2 −1 python/plugins/processing/algs/lidar/fusion/DTM2TIF.py
  12. +97 −0 python/plugins/processing/algs/lidar/fusion/DensityMetrics.py
  13. +3 −2 python/plugins/processing/algs/lidar/fusion/FilterData.py
  14. +12 −0 python/plugins/processing/algs/lidar/fusion/FusionUtils.py
  15. +20 −6 python/plugins/processing/algs/lidar/fusion/GridMetrics.py
  16. +16 −15 python/plugins/processing/algs/lidar/fusion/GridSurfaceCreate.py
  17. +105 −0 python/plugins/processing/algs/lidar/fusion/GridSurfaceStats.py
  18. +2 −1 python/plugins/processing/algs/lidar/fusion/GroundFilter.py
  19. +10 −3 python/plugins/processing/algs/lidar/fusion/ImageCreate.py
  20. +10 −2 python/plugins/processing/algs/lidar/fusion/IntensityImage.py
  21. +92 −0 python/plugins/processing/algs/lidar/fusion/MergeDTM.py
  22. +3 −1 python/plugins/processing/algs/lidar/fusion/MergeData.py
  23. +80 −0 python/plugins/processing/algs/lidar/fusion/MergeRaster.py
  24. +9 −5 python/plugins/processing/algs/lidar/fusion/PolyClipData.py
  25. +97 −0 python/plugins/processing/algs/lidar/fusion/ReturnDensity.py
  26. +71 −0 python/plugins/processing/algs/lidar/fusion/SplitDTM.py
  27. +73 −0 python/plugins/processing/algs/lidar/fusion/SurfaceStats.py
  28. +2 −1 python/plugins/processing/algs/lidar/fusion/TinSurfaceCreate.py
  29. +102 −0 python/plugins/processing/algs/lidar/fusion/TopoMetrics.py
  30. +118 −0 python/plugins/processing/algs/lidar/fusion/TreeSeg.py
@@ -140,6 +140,15 @@
from .fusion.PolyClipData import PolyClipData
from .fusion.ImageCreate import ImageCreate
from .fusion.IntensityImage import IntensityImage
from .fusion.DensityMetrics import DensityMetrics
from .fusion.MergeDTM import MergeDTM
from .fusion.TopoMetrics import TopoMetrics
from .fusion.TreeSeg import TreeSeg
from .fusion.SplitDTM import SplitDTM
from .fusion.MergeRaster import MergeRaster
from .fusion.SurfaceStats import SurfaceStats
from .fusion.ReturnDensity import ReturnDensity # spellok
from .fusion.GridSurfaceStats import GridSurfaceStats
from .fusion.FusionUtils import FusionUtils


@@ -213,7 +222,8 @@ def _loadAlgorithms(self):
Csv2Grid(), Cover(), FilterData(), GridMetrics(), GroundFilter(),
GridSurfaceCreate(), MergeData(), TinSurfaceCreate(), PolyClipData(),
DTM2TIF(), DTM2ASCII(), FirstLastReturn(), ASCII2DTM(), ImageCreate(),
IntensityImage()
IntensityImage(), DensityMetrics(), MergeDTM(), TopoMetrics(), TreeSeg(),
SplitDTM(), MergeRaster(), SurfaceStats(), ReturnDensity(), GridSurfaceStats() # spellok
]
for alg in fusiontools:
alg.group, alg.i18n_group = alg.trAlgorithm('Fusion')
@@ -51,7 +51,7 @@ def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('ASCII to DTM')
self.group, self.i18n_group = self.trAlgorithm('Conversion')
self.addParameter(ParameterFile(
self.INPUT, self.tr('Input ESRI ASCII layer')))
self.INPUT, self.tr('Input ESRI ASCII layer'), optional=False))
self.addParameter(ParameterSelection(
self.XYUNITS, self.tr('XY Units'), self.UNITS))
self.addParameter(ParameterSelection(
@@ -32,7 +32,7 @@
from processing.core.parameters import ParameterFile
from processing.core.parameters import ParameterNumber
from processing.core.parameters import ParameterBoolean
from processing.core.outputs import OutputTable
from processing.core.outputs import OutputFile
from .FusionUtils import FusionUtils
from .FusionAlgorithm import FusionAlgorithm

@@ -46,43 +46,53 @@ class CanopyMaxima(FusionAlgorithm):
SUMMARY = 'SUMMARY'
PARAM_A = 'PARAM_A'
PARAM_C = 'PARAM_C'
SHAPE = 'SHAPE'

def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Canopy Maxima')
self.group, self.i18n_group = self.trAlgorithm('Points')
self.addParameter(ParameterFile(
self.INPUT, self.tr('Input FUSION canopy height model')))
self.INPUT, self.tr('Input PLANS DTM canopy height model'),
optional=False))
self.addParameter(ParameterFile(
self.GROUND, self.tr('Input ground .dtm layer [optional]')))
self.GROUND, self.tr('Input ground PLANS DTM layer [optional]')))
self.addParameter(ParameterNumber(
self.THRESHOLD, self.tr('Height threshold'), 0, None, 10.0))
# begin
self.THRESHOLD, self.tr('Limit analysis to areas above this height threshold'), 0, None, 10.0))

self.addParameter(ParameterNumber(
self.PARAM_A, self.tr('Variable window size: parameter A'), 0, None, 2.51503))
self.addParameter(ParameterNumber(
self.PARAM_C, self.tr('Parameter C'), 0, None, 0.00901))
self.addParameter(ParameterBoolean(
self.SUMMARY, self.tr('Summary (tree height summary statistics)'), False))
# end
self.addOutput(OutputTable(
self.OUTPUT, self.tr('Output file with maxima')))
summary = ParameterBoolean(
self.SUMMARY, self.tr('Tree height summary statistics'), False)
summary.isAdvanced = True
self.addParameter(summary)
shape = ParameterBoolean(
self.SHAPE, self.tr('Create output shapefiles'), False)
shape.isAdvanced = True
self.addParameter(shape)

self.addOutput(OutputFile(
self.OUTPUT, self.tr('Output file with maxima'), 'csv'))

self.addAdvancedModifiers()

def processAlgorithm(self, feedback):
commands = [os.path.join(FusionUtils.FusionPath(), 'CanopyMaxima.exe')]
commands.append('/verbose')
### begin
commands.append('/wse:' + str(self.getParameterValue(self.PARAM_A)) + ',0,' + str(self.getParameterValue(self.PARAM_C)) + ',0')
commands.append('/wse:' + unicode(self.getParameterValue(self.PARAM_A)) + ',0,' + unicode(self.getParameterValue(self.PARAM_C)) + ',0')
ground = self.getParameterValue(self.GROUND)
if ground:
gfiles = self.getParameterValue(self.GROUND).split(';')
if len(gfiles) == 1:
commands.append('/ground:' + str(ground))
else:
FusionUtils.createGroundList(gfiles)
commands.append('/ground:' + str(FusionUtils.tempGroundListFilepath()))
commands.append('/threshold:' + str(self.getParameterValue(self.THRESHOLD)))
if self.getParameterValue(self.SUMMARY):
commands.append('/summary')
### end
self.addAdvancedModifiersToCommand(commands)
ground = self.getParameterValue(self.GROUND)
## here it's necessary to have the support for multiple files like for INPUT.
if str(ground).strip():
commands.append('/ground:' + str(ground))
commands.append('/threshold:'
+ str(self.getParameterValue(self.THRESHOLD)))
files = self.getParameterValue(self.INPUT).split(';')
if len(files) == 1:
commands.append(self.getParameterValue(self.INPUT))
@@ -45,6 +45,7 @@ class CanopyModel(FusionAlgorithm):

INPUT = 'INPUT'
OUTPUT_DTM = 'OUTPUT_DTM'
ASPECT = 'ASPECT'
CELLSIZE = 'CELLSIZE'
XYUNITS = 'XYUNITS'
ZUNITS = 'ZUNITS'
@@ -54,13 +55,15 @@ class CanopyModel(FusionAlgorithm):
SMOOTH = 'SMOOTH'
SLOPE = 'SLOPE'
CLASS = 'CLASS'
RETURN = 'RETURN'
ASCII = 'ASCII'

def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Canopy Model')
self.group, self.i18n_group = self.trAlgorithm('Points')
self.addParameter(ParameterFile(
self.INPUT, self.tr('Input LAS layer')))
self.INPUT, self.tr('Input LAS layer'),
optional=False))
self.addParameter(ParameterNumber(
self.CELLSIZE, self.tr('Cell Size'), 0, None, 10.0))
self.addParameter(ParameterSelection(
@@ -70,7 +73,7 @@ def defineCharacteristics(self):
self.addOutput(OutputFile(
self.OUTPUT_DTM, self.tr('.dtm output surface'), 'dtm'))
ground = ParameterFile(
self.GROUND, self.tr('Input ground DTM layer'), False, True)
self.GROUND, self.tr('Input ground PLANS DTM layer'), False, True)
ground.isAdvanced = True
self.addParameter(ground)
median = ParameterString(
@@ -82,13 +85,21 @@ def defineCharacteristics(self):
smooth.isAdvanced = True
self.addParameter(smooth)
class_var = ParameterString(
self.CLASS, self.tr('Class'), '', False, True)
self.CLASS, self.tr('Select specific class'), '', False, True)
class_var.isAdvanced = True
self.addParameter(class_var)
ret_num = ParameterString(
self.RETURN, self.tr('Select specific return'), '', False, True)
ret_num.isAdvanced = True
self.addParameter(ret_num)
slope = ParameterBoolean(
self.SLOPE, self.tr('Calculate slope'), False)
slope.isAdvanced = True
self.addParameter(slope)
aspec = ParameterBoolean(
self.ASPECT, self.tr('Calculate aspect'), False)
aspec.isAdvanced = True
self.addParameter(aspect)
self.addParameter(ParameterBoolean(
self.ASCII, self.tr('Add an ASCII output'), False))
self.addAdvancedModifiers()
@@ -98,7 +109,12 @@ def processAlgorithm(self, feedback):
commands.append('/verbose')
ground = self.getParameterValue(self.GROUND)
if str(ground).strip():
commands.append('/ground:' + str(ground))
gfiles = self.getParameterValue(self.GROUND).split(';')
if len(gfiles) == 1:
commands.append('/ground:' + str(ground))
else:
FusionUtils.createGroundList(gfiles)
commands.append('/ground:' + str(FusionUtils.tempGroundListFilepath()))
median = self.getParameterValue(self.MEDIAN)
if str(median).strip():
commands.append('/median:' + str(median))
@@ -108,11 +124,17 @@ def processAlgorithm(self, feedback):
slope = self.getParameterValue(self.SLOPE)
if slope:
commands.append('/slope')
aspect = self.getParameterValue(self.ASPECT)
if aspect:
commands.append('/aspect')
class_var = self.getParameterValue(self.CLASS)
if str(class_var).strip():
commands.append('/class:' + str(class_var))
ascii = self.getParameterValue(self.ASCII)
if ascii:
ret_num = self.getParameterValue(self.RETURN)
if str(ret_num).strip():
commands.append('/return:' + str(ret_num))
use_ascii = self.getParameterValue(self.ASCII)
if use_ascii:
commands.append('/ascii')
self.addAdvancedModifiersToCommand(commands)
commands.append(self.getOutputValue(self.OUTPUT_DTM))
@@ -29,6 +29,7 @@
import os
from processing.core.parameters import ParameterFile
from processing.core.parameters import ParameterString
from processing.core.parameters import ParameterBoolean
from processing.core.outputs import OutputFile
from .FusionUtils import FusionUtils
from .FusionAlgorithm import FusionAlgorithm
@@ -41,13 +42,19 @@ class Catalog(FusionAlgorithm):
DENSITY = 'DENSITY'
FIRSTDENSITY = 'FIRSTDENSITY'
INTENSITY = 'INTENSITY'
INDEX = 'INDEX'
IMAGE = 'IMAGE'
DRAWTILES = 'DRAWTILES'
COVERAGE = 'COVERAGE'
CRETURNS = 'CRETURNS'
ADVANCED_MODIFIERS = 'ADVANCED_MODIFIERS'

def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Catalog')
self.group, self.i18n_group = self.trAlgorithm('Points')
self.addParameter(ParameterFile(
self.INPUT, self.tr('Input LAS layer')))
self.INPUT, self.tr('Input LAS layer'),
optional=False))
self.addOutput(OutputFile(self.OUTPUT, self.tr('Output files')))
density = ParameterString(
self.DENSITY,
@@ -67,6 +74,16 @@ def defineCharacteristics(self):
'', False, True)
intensity.isAdvanced = True
self.addParameter(intensity)
self.addParameter(ParameterBoolean(self.INDEX,
self.tr('Create LIDAR data file indexes'), False))
self.addParameter(ParameterBoolean(self.IMAGE,
self.tr('Create image files showing the coverage area for each LIDAR file'), False))
self.addParameter(ParameterBoolean(self.DRAWTILES,
self.tr('Draw data file extents and names on the intensity image'), False))
self.addParameter(ParameterBoolean(self.COVERAGE,
self.tr('Create one image that shows the nominal coverage area'), False))
self.addParameter(ParameterBoolean(self.CRETURNS,
self.tr('Adds count return columns in the CSV and HTML output'), False))
advanced_modifiers = ParameterString(
self.ADVANCED_MODIFIERS,
self.tr('Additional modifiers'), '', False, True)
@@ -85,6 +102,21 @@ def processAlgorithm(self, feedback):
firstdensity = self.getParameterValue(self.FIRSTDENSITY)
if str(firstdensity).strip():
commands.append('/firstdensity:' + str(firstdensity))
index = self.getParameterValue(self.INDEX)
if str(index).strip():
commands.append('/index')
drawtiles = self.getParameterValue(self.IMAGE)
if str(drawtiles).strip():
commands.append('/drawtiles')
coverage = self.getParameterValue(self.DRAWTILES)
if str(coverage).strip():
commands.append('/coverage')
image = self.getParameterValue(self.COVERAGE)
if str(image).strip():
commands.append('/image')
creturns = self.getParameterValue(self.COVERAGE)
if str(creturns).strip():
commands.append('/countreturns')
advanced_modifiers = str(self.getParameterValue(self.ADVANCED_MODIFIERS)).strip()
if advanced_modifiers:
commands.append(advanced_modifiers)
@@ -51,10 +51,11 @@ def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Clip Data')
self.group, self.i18n_group = self.trAlgorithm('Points')
self.addParameter(ParameterFile(
self.INPUT, self.tr('Input LAS layer')))
self.INPUT, self.tr('Input LAS layer'),
optional=False))
self.addParameter(ParameterExtent(self.EXTENT, self.tr('Extent'), optional=False))
self.addParameter(ParameterSelection(
self.SHAPE, self.tr('Shape'), ['Rectangle', 'Circle']))
self.SHAPE, self.tr('Shape of the sample area'), ['Rectangle', 'Circle']))
self.addOutput(OutputFile(
self.OUTPUT, self.tr('Output clipped LAS file')))
dtm = ParameterFile(
@@ -74,7 +75,12 @@ def processAlgorithm(self, feedback):
commands.append('/shape:' + str(self.getParameterValue(self.SHAPE)))
dtm = self.getParameterValue(self.DTM)
if dtm:
commands.append('/dtm:' + str(dtm))
gfiles = self.getParameterValue(self.DTM).split(';')
if len(gfiles) == 1:
commands.append('/ground:' + str(dtm))
else:
FusionUtils.createGroundList(gfiles)
commands.append('/ground:' + str(FusionUtils.tempGroundListFilepath()))
height = self.getParameterValue(self.HEIGHT)
if height:
commands.append('/height')
@@ -32,11 +32,11 @@

import os
from processing.core.parameters import ParameterFile
from processing.core.parameters import ParameterNumber
from processing.core.parameters import ParameterBoolean
from processing.core.outputs import OutputFile
from .FusionUtils import FusionUtils
from .FusionAlgorithm import FusionAlgorithm
from processing.core.parameters import ParameterString
from processing.core.parameters import ParameterBoolean


class CloudMetrics(FusionAlgorithm):
@@ -52,10 +52,11 @@ def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Cloud Metrics')
self.group, self.i18n_group = self.trAlgorithm('Points')
self.addParameter(ParameterFile(
self.INPUT, self.tr('Input LAS layer')))
self.INPUT, self.tr('Input LAS layer'),
optional=False))
self.addOutput(OutputFile(
self.OUTPUT, self.tr('Output file with tabular metric information'), 'csv'))
above = ParameterString(self.ABOVE, self.tr('Above'), '', False)
above = ParameterNumber(self.ABOVE, self.tr('Compute cover statistics above the following heightbreak:'), 0, None, 0.0)
above.isAdvanced = True
self.addParameter(above)
firstImpulse = ParameterBoolean(
@@ -66,15 +67,15 @@ def defineCharacteristics(self):
self.FIRSTRETURN, self.tr('First Return'), False)
firstReturn.isAdvanced = True
self.addParameter(firstReturn)
htmin = ParameterString(self.HTMIN, self.tr('Htmin'), '', False, True)
htmin = ParameterNumber(self.HTMIN, self.tr('Use only returns above this minimum height:'), 0, None, 0)
htmin.isAdvanced = True
self.addParameter(htmin)

def processAlgorithm(self, feedback):
commands = [os.path.join(FusionUtils.FusionPath(), 'CloudMetrics.exe')]
commands.append('/verbose')
above = self.getParameterValue(self.ABOVE)
if str(above).strip() != '':
if above != 0.0:
commands.append('/above:' + str(above))
firstImpulse = self.getParameterValue(self.FIRSTIMPULSE)
if firstImpulse:
@@ -83,7 +84,7 @@ def processAlgorithm(self, feedback):
if firstReturn:
commands.append('/firstreturn')
htmin = self.getParameterValue(self.HTMIN)
if str(htmin).strip() != '':
if htmin != 0.0:
commands.append('/minht:' + str(htmin))
files = self.getParameterValue(self.INPUT).split(';')
if len(files) == 1:

0 comments on commit 66c7129

Please sign in to comment.
You can’t perform that action at this time.