Skip to content
6 changes: 6 additions & 0 deletions src/analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,12 @@ set(QGIS_ANALYSIS_SRCS
processing/pdal/qgsalgorithmpdalthinbydecimate.cpp
processing/pdal/qgsalgorithmpdalthinbyradius.cpp
processing/pdal/qgsalgorithmpdaltile.cpp
processing/pdal/qgsalgorithmpdalheightabovegroundnearestneighbour.cpp
processing/pdal/qgsalgorithmpdalheightabovegroundtriangulation.cpp
processing/pdal/qgsalgorithmpdalfilternoisestatistical.cpp
processing/pdal/qgsalgorithmpdalfilternoiseradius.cpp
processing/pdal/qgsalgorithmpdalclassifyground.cpp
processing/pdal/qgsalgorithmpdaltransform.cpp

raster/qgsalignraster.cpp
raster/qgsninecellfilter.cpp
Expand Down
120 changes: 120 additions & 0 deletions src/analysis/processing/pdal/qgsalgorithmpdalclassifyground.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/***************************************************************************
qgsalgorithmpdalclassifyground.cpp
---------------------
begin : December 2025
copyright : (C) 2025 by Jan Caha
email : jan.caha at outlook dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsalgorithmpdalclassifyground.h"

#include "qgspointcloudlayer.h"
#include "qgsrunprocess.h"

///@cond PRIVATE

QString QgsPdalClassifyGroundAlgorithm::name() const
{
return QStringLiteral( "classifyground" );
}

QString QgsPdalClassifyGroundAlgorithm::displayName() const
{
return QObject::tr( "Classify ground points" );
}

QString QgsPdalClassifyGroundAlgorithm::group() const
{
return QObject::tr( "Point cloud data management" );
}

QString QgsPdalClassifyGroundAlgorithm::groupId() const
{
return QStringLiteral( "pointclouddatamanagement" );
}

QStringList QgsPdalClassifyGroundAlgorithm::tags() const
{
return QObject::tr( "pdal,lidar,classify,ground,elevation" ).split( ',' );
}

QString QgsPdalClassifyGroundAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm classifies ground points using the Simple Morphological Filter (SMRF) algorithm." )
+ QStringLiteral( "\n\n" )
+ QObject::tr( "Cell Size is the cell size for processing grid in map units, where smaller values give finer detail but may increase noise. Scalar is the threshold for steeper slopes; a higher value is needed if the terrain is rough, otherwise real ground might be misclassified. Slope is the slope threshold measured as rise over run, indicating how much slope is tolerated as ground and should be higher for steep terrain. Threshold is the elevation threshold for separating ground from objects; higher values allow larger deviations from the ground. Window Size is the maximum filter window size, where higher values better identify large buildings or objects while smaller values protect smaller features." );
}

QString QgsPdalClassifyGroundAlgorithm::shortDescription() const
{
return QObject::tr( "Classifies ground points using the Simple Morphological Filter (SMRF) algorithm." );
}

QgsPdalClassifyGroundAlgorithm *QgsPdalClassifyGroundAlgorithm::createInstance() const
{
return new QgsPdalClassifyGroundAlgorithm();
}

void QgsPdalClassifyGroundAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterPointCloudLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );

QgsProcessingParameterNumber *cellSizeParam = new QgsProcessingParameterNumber( QStringLiteral( "CELL_SIZE" ), QObject::tr( "Grid Cell Size" ), Qgis::ProcessingNumberParameterType::Double, 1.0 );
cellSizeParam->setHelp( QObject::tr( "Cell size for processing grid (in map units). Smaller values give finer detail but may increase noise." ) );
addParameter( cellSizeParam );

QgsProcessingParameterNumber *scalarParam = new QgsProcessingParameterNumber( QStringLiteral( "SCALAR" ), QObject::tr( "Scalar" ), Qgis::ProcessingNumberParameterType::Double, 1.25 );
scalarParam->setHelp( QObject::tr( "Threshold for steeper slopes. Higher value if the terrain is rough, otherwise real ground might be misclassified." ) );
addParameter( scalarParam );

QgsProcessingParameterNumber *slopeParam = new QgsProcessingParameterNumber( QStringLiteral( "SLOPE" ), QObject::tr( "Slope" ), Qgis::ProcessingNumberParameterType::Double, 0.15 );
slopeParam->setHelp( QObject::tr( "Slope threshold (rise over run). How much slope is tolerated as ground. Should be higher for steep terrain." ) );
addParameter( slopeParam );

QgsProcessingParameterNumber *thresholdParam = new QgsProcessingParameterNumber( QStringLiteral( "THRESHOLD" ), QObject::tr( "Threshold" ), Qgis::ProcessingNumberParameterType::Double, 0.5 );
thresholdParam->setHelp( QObject::tr( "Elevation threshold for separating ground from objects. Higher values allow larger deviations from the ground." ) );
addParameter( thresholdParam );

QgsProcessingParameterNumber *windowSizeParam = new QgsProcessingParameterNumber( QStringLiteral( "WINDOW_SIZE" ), QObject::tr( "Window Size" ), Qgis::ProcessingNumberParameterType::Double, 18.0 );
windowSizeParam->setHelp( QObject::tr( "Maximum filter window size. Higher values better identify large buildings or objects, smaller values protect smaller features." ) );
addParameter( windowSizeParam );

addParameter( new QgsProcessingParameterPointCloudDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Classified Ground" ) ) );
}

QStringList QgsPdalClassifyGroundAlgorithm::createArgumentLists( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
Q_UNUSED( feedback );

QgsPointCloudLayer *layer = parameterAsPointCloudLayer( parameters, QStringLiteral( "INPUT" ), context, QgsProcessing::LayerOptionsFlag::SkipIndexGeneration );
if ( !layer )
throw QgsProcessingException( invalidPointCloudError( parameters, QStringLiteral( "INPUT" ) ) );

const QString outputName = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
QString outputFile = fixOutputFileName( layer->source(), outputName, context );
checkOutputFormat( layer->source(), outputFile );
setOutputValue( QStringLiteral( "OUTPUT" ), outputFile );

const double cellSize = parameterAsDouble( parameters, QStringLiteral( "CELL_SIZE" ), context );
const double scalar = parameterAsDouble( parameters, QStringLiteral( "SCALAR" ), context );
const double slope = parameterAsDouble( parameters, QStringLiteral( "SLOPE" ), context );
const double threshold = parameterAsDouble( parameters, QStringLiteral( "THRESHOLD" ), context );
const double windowSize = parameterAsDouble( parameters, QStringLiteral( "WINDOW_SIZE" ), context );

QStringList args = { QStringLiteral( "classify_ground" ), QStringLiteral( "--input=%1" ).arg( layer->source() ), QStringLiteral( "--output=%1" ).arg( outputFile ), QStringLiteral( "--cell-size=%1" ).arg( cellSize ), QStringLiteral( "--scalar=%1" ).arg( scalar ), QStringLiteral( "--slope=%1" ).arg( slope ), QStringLiteral( "--threshold=%1" ).arg( threshold ), QStringLiteral( "--window-size=%1" ).arg( windowSize ) };

applyCommonParameters( args, layer->crs(), parameters, context );
applyThreadsParameter( args, context );
return args;
}

///@endcond
52 changes: 52 additions & 0 deletions src/analysis/processing/pdal/qgsalgorithmpdalclassifyground.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/***************************************************************************
qgsalgorithmpdalclassifyground.h
---------------------
begin : December 2025
copyright : (C) 2025 by Jan Caha
email : jan.caha at outlook dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSALGORITHMPDALCLASSIFYGROUND_H
#define QGSALGORITHMPDALCLASSIFYGROUND_H

#define SIP_NO_FILE

#include "qgis_sip.h"
#include "qgspdalalgorithmbase.h"

///@cond PRIVATE

/**
* Native point cloud filter noise using radius algorithm.
*/
class QgsPdalClassifyGroundAlgorithm : public QgsPdalAlgorithmBase
{
public:
QgsPdalClassifyGroundAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QString name() const override;
QString displayName() const override;
QString group() const override;
QString groupId() const override;
QStringList tags() const override;
QString shortHelpString() const override;
QString shortDescription() const override;
QgsPdalClassifyGroundAlgorithm *createInstance() const override SIP_FACTORY;

QStringList createArgumentLists( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

friend class TestQgsProcessingPdalAlgs;
};

///@endcond PRIVATE

#endif // QGSALGORITHMPDALCLASSIFYGROUND_H
107 changes: 107 additions & 0 deletions src/analysis/processing/pdal/qgsalgorithmpdalfilternoiseradius.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/***************************************************************************
qgsalgorithmpdalfilternoiseradius.cpp
---------------------
begin : December 2025
copyright : (C) 2025 by Jan Caha
email : jan.caha at outlook dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsalgorithmpdalfilternoiseradius.h"

#include "qgspointcloudlayer.h"
#include "qgsrunprocess.h"

///@cond PRIVATE

QString QgsPdalFilterNoiseRadiusAlgorithm::name() const
{
return QStringLiteral( "filternoiseradius" );
}

QString QgsPdalFilterNoiseRadiusAlgorithm::displayName() const
{
return QObject::tr( "Filter noise (using radius)" );
}

QString QgsPdalFilterNoiseRadiusAlgorithm::group() const
{
return QObject::tr( "Point cloud data management" );
}

QString QgsPdalFilterNoiseRadiusAlgorithm::groupId() const
{
return QStringLiteral( "pointclouddatamanagement" );
}

QStringList QgsPdalFilterNoiseRadiusAlgorithm::tags() const
{
return QObject::tr( "pdal,lidar,filter,noise,radius" ).split( ',' );
}

QString QgsPdalFilterNoiseRadiusAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm filters noise in a point cloud using radius algorithm." )
+ QStringLiteral( "\n\n" )
+ QObject::tr( "Points are marked as noise if they have fewer than the minimum number of neighbors within the specified radius." );
}

QString QgsPdalFilterNoiseRadiusAlgorithm::shortDescription() const
{
return QObject::tr( "Filters noise in a point cloud using radius algorithm." );
}

QgsPdalFilterNoiseRadiusAlgorithm *QgsPdalFilterNoiseRadiusAlgorithm::createInstance() const
{
return new QgsPdalFilterNoiseRadiusAlgorithm();
}

void QgsPdalFilterNoiseRadiusAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterPointCloudLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "REMOVE_NOISE_POINTS" ), QObject::tr( "Remove noise points" ), false ) );

addParameter( new QgsProcessingParameterNumber( QStringLiteral( "MIN_K" ), QObject::tr( "Minimum number of neighbors in radius" ), Qgis::ProcessingNumberParameterType::Integer, 2 ) );
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "RADIUS" ), QObject::tr( "Radius" ), Qgis::ProcessingNumberParameterType::Double, 1.0 ) );

addParameter( new QgsProcessingParameterPointCloudDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Filtered (radius algorithm)" ) ) );
}

QStringList QgsPdalFilterNoiseRadiusAlgorithm::createArgumentLists( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
Q_UNUSED( feedback );

QgsPointCloudLayer *layer = parameterAsPointCloudLayer( parameters, QStringLiteral( "INPUT" ), context, QgsProcessing::LayerOptionsFlag::SkipIndexGeneration );
if ( !layer )
throw QgsProcessingException( invalidPointCloudError( parameters, QStringLiteral( "INPUT" ) ) );

const QString outputName = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
QString outputFile = fixOutputFileName( layer->source(), outputName, context );
checkOutputFormat( layer->source(), outputFile );
setOutputValue( QStringLiteral( "OUTPUT" ), outputFile );

const double minK = parameterAsDouble( parameters, QStringLiteral( "MIN_K" ), context );
const double radius = parameterAsDouble( parameters, QStringLiteral( "RADIUS" ), context );

QString removeNoisePoints = "false";
if ( parameterAsBoolean( parameters, QStringLiteral( "REMOVE_NOISE_POINTS" ), context ) )
{
removeNoisePoints = "true";
}

QStringList args = { QStringLiteral( "filter_noise" ), QStringLiteral( "--input=%1" ).arg( layer->source() ), QStringLiteral( "--output=%1" ).arg( outputFile ), QStringLiteral( "--algorithm=radius" ), QStringLiteral( "--remove-noise-points=%1" ).arg( removeNoisePoints ), QStringLiteral( "--radius-min-k=%1" ).arg( minK ), QStringLiteral( "--radius-radius=%1" ).arg( radius ) };

applyCommonParameters( args, layer->crs(), parameters, context );
applyThreadsParameter( args, context );
return args;
}

///@endcond
52 changes: 52 additions & 0 deletions src/analysis/processing/pdal/qgsalgorithmpdalfilternoiseradius.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/***************************************************************************
qgsalgorithmpdalfilternoiseradius.h
---------------------
begin : December 2025
copyright : (C) 2025 by Jan Caha
email : jan.caha at outlook dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSALGORITHMPDALFILTERNOISERADIUS_H
#define QGSALGORITHMPDALFILTERNOISERADIUS_H

#define SIP_NO_FILE

#include "qgis_sip.h"
#include "qgspdalalgorithmbase.h"

///@cond PRIVATE

/**
* Native point cloud filter noise using radius algorithm.
*/
class QgsPdalFilterNoiseRadiusAlgorithm : public QgsPdalAlgorithmBase
{
public:
QgsPdalFilterNoiseRadiusAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QString name() const override;
QString displayName() const override;
QString group() const override;
QString groupId() const override;
QStringList tags() const override;
QString shortHelpString() const override;
QString shortDescription() const override;
QgsPdalFilterNoiseRadiusAlgorithm *createInstance() const override SIP_FACTORY;

QStringList createArgumentLists( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

friend class TestQgsProcessingPdalAlgs;
};

///@endcond PRIVATE

#endif // QGSALGORITHMPDALFILTERNOISERADIUS_H
Loading
Loading