Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
algorithm to create COPC files for input point clouds
  • Loading branch information
alexbruy authored and wonder-sk committed May 8, 2023
1 parent 7685c9a commit 51c34ba
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -419,6 +419,8 @@ endif()

if (WITH_PDAL AND PDAL_2_5_OR_HIGHER)
set(QGIS_ANALYSIS_SRCS ${QGIS_ANALYSIS_SRCS}
${CMAKE_SOURCE_DIR}/external/untwine/api/QgisUntwine.cpp

processing/pdal/qgspdalalgorithms.cpp

processing/pdal/qgspdalalgorithmbase.cpp
Expand All @@ -428,6 +430,7 @@ if (WITH_PDAL AND PDAL_2_5_OR_HIGHER)
processing/pdal/qgsalgorithmpdalbuildvpc.cpp
processing/pdal/qgsalgorithmpdalclip.cpp
processing/pdal/qgsalgorithmpdalconvertformat.cpp
processing/pdal/qgsalgorithmpdalcreatecopc.cpp
processing/pdal/qgsalgorithmpdaldensity.cpp
processing/pdal/qgsalgorithmpdalexportraster.cpp
processing/pdal/qgsalgorithmpdalexportrastertin.cpp
Expand All @@ -442,8 +445,14 @@ if (WITH_PDAL AND PDAL_2_5_OR_HIGHER)
)

set(QGIS_ANALYSIS_HDRS ${QGIS_ANALYSIS_HDRS}
${CMAKE_SOURCE_DIR}/external/untwine/api/QgisUntwine.hpp

processing/pdal/qgspdalalgorithms.h
)

include_directories(
${CMAKE_SOURCE_DIR}/external/untwine/api
)
endif()

include_directories(SYSTEM ${SPATIALITE_INCLUDE_DIR})
Expand Down
175 changes: 175 additions & 0 deletions src/analysis/processing/pdal/qgsalgorithmpdalcreatecopc.cpp
@@ -0,0 +1,175 @@
/***************************************************************************
qgsalgorithmpdalcreatecopc.cpp
---------------------
begin : May 2023
copyright : (C) 2023 by Alexander Bruy
email : alexander dot bruy at gmail 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 "qgsalgorithmpdalcreatecopc.h"

#include <QProcessEnvironment>
#include <QThread>
#include <QFileInfo>
#include <QDir>

#include "QgisUntwine.hpp"
#include "qgsapplication.h"
#include "qgspointcloudlayer.h"

///@cond PRIVATE

QString QgsPdalCreateCopcAlgorithm::name() const
{
return QStringLiteral( "createcopc" );
}

QString QgsPdalCreateCopcAlgorithm::displayName() const
{
return QObject::tr( "Create COPC" );
}

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

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

QStringList QgsPdalCreateCopcAlgorithm::tags() const
{
return QObject::tr( "pdal,lidar,copc,convert,format,translate" ).split( ',' );
}

QString QgsPdalCreateCopcAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm creates a COPC file for each input point cloud file." );
}

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

void QgsPdalCreateCopcAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Input layers" ), QgsProcessing::TypePointCloud ) );
addOutput( new QgsProcessingOutputMultipleLayers( QStringLiteral( "OUTPUT_LAYERS" ), QObject::tr( "Output layers" ) ) );
}

QVariantMap QgsPdalCreateCopcAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context, QgsProcessing::LayerOptionsFlag::SkipIndexGeneration );
if ( layers.empty() )
{
feedback->reportError( QObject::tr( "No layers selected" ), true );
}

QString untwineExecutable = QProcessEnvironment::systemEnvironment().value( QStringLiteral( "QGIS_UNTWINE_EXECUTABLE" ) );
if ( untwineExecutable.isEmpty() )
{
#if defined(Q_OS_WIN)
untwineExecutable = QgsApplication::libexecPath() + "untwine.exe";
#else
untwineExecutable = QgsApplication::libexecPath() + "untwine";
#endif
}
const QFileInfo executable( untwineExecutable );
if ( !executable.isExecutable() )
{
throw QgsProcessingException( QObject::tr( "Untwine executable not found %1" ).arg( untwineExecutable ) );
}

QgsProcessingMultiStepFeedback multiStepFeedback( layers.size(), feedback );
QStringList outputLayers;

int i = 0;
for ( QgsMapLayer *layer : layers )
{
if ( feedback->isCanceled() )
break;

multiStepFeedback.setCurrentStep( i );
i++;

if ( !layer )
continue;

QgsPointCloudLayer *pcl = qobject_cast< QgsPointCloudLayer * >( layer );
if ( !pcl )
continue;

feedback->pushInfo( QObject::tr( "Processing layer %1/%2: %3" ).arg( i ).arg( layers.count() ).arg( layer ? layer->name() : QString() ) );

const QFileInfo fi( pcl->source() );
const QDir directory = fi.absoluteDir();
const QString outputFile = QStringLiteral( "%1/%2.copc.laz" ).arg( directory.absolutePath() ).arg( fi.completeBaseName() );

const QFileInfo outputFileInfo( outputFile );
if ( outputFileInfo.exists() )
{
feedback->pushInfo( QObject::tr( "File %1 is already indexed" ).arg( pcl->source() ) );
continue;
}
QString tmpDir = outputFile + QStringLiteral( "_tmp" );
if ( QDir( tmpDir ).exists() )
{
feedback->pushInfo( QObject::tr( "Another indexing process is running (or finished with crash) in directory %1" ).arg( tmpDir ) );
continue;
}

untwine::QgisUntwine untwineProcess( untwineExecutable.toStdString() );
untwine::QgisUntwine::Options options;
// calculate stats for attributes
options.push_back( { "stats", std::string() } );
// generate COPC files
options.push_back( { "single_file", std::string() } );

const std::vector<std::string> files = {pcl->source().toStdString()};
untwineProcess.start( files, outputFile.toStdString(), options );
const int lastPercent = 0;
while ( true )
{
if ( feedback->isCanceled() )
break;

QThread::msleep( 100 );
const int percent = untwineProcess.progressPercent();
if ( lastPercent != percent )
{
multiStepFeedback.setProgress( percent );
}

if ( !untwineProcess.running() )
{
if ( !untwineProcess.errorMessage().empty() )
{
feedback->pushWarning( QObject::tr( "Failed to index file %1: %2" ).arg( outputFile, QString::fromStdString( untwineProcess.errorMessage() ) ) );
}
break;
}
}

QDir( tmpDir ).removeRecursively();

outputLayers << outputFile;
}

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT_LAYERS" ), outputLayers );
return outputs;
}

///@endcond
56 changes: 56 additions & 0 deletions src/analysis/processing/pdal/qgsalgorithmpdalcreatecopc.h
@@ -0,0 +1,56 @@
/***************************************************************************
qgsalgorithmpdalcreatecopc.h
---------------------
begin : May 2023
copyright : (C) 2023 by Alexander Bruy
email : alexander dot bruy at gmail 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 QGSALGORITHMPDALCREATECOPC_H
#define QGSALGORITHMPDALCREATECOPC_H

#define SIP_NO_FILE

#include "qgis_sip.h"
#include "qgsprocessingalgorithm.h"

///@cond PRIVATE

/**
* Native point cloud create COPC algorithm.
*/
class QgsPdalCreateCopcAlgorithm : public QgsProcessingAlgorithm
{

public:

QgsPdalCreateCopcAlgorithm() = 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;
QgsPdalCreateCopcAlgorithm *createInstance() const override SIP_FACTORY;

protected:

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

friend class TestQgsProcessingPdalAlgs;
};

///@endcond PRIVATE

#endif // QGSALGORITHMPDALCREATECOPC_H
2 changes: 2 additions & 0 deletions src/analysis/processing/pdal/qgspdalalgorithms.cpp
Expand Up @@ -24,6 +24,7 @@
#include "qgsalgorithmpdalbuildvpc.h"
#include "qgsalgorithmpdalclip.h"
#include "qgsalgorithmpdalconvertformat.h"
#include "qgsalgorithmpdalcreatecopc.h"
#include "qgsalgorithmpdaldensity.h"
#include "qgsalgorithmpdalexportraster.h"
#include "qgsalgorithmpdalexportrastertin.h"
Expand Down Expand Up @@ -96,6 +97,7 @@ void QgsPdalAlgorithms::loadAlgorithms()
addAlgorithm( new QgsPdalBuildVpcAlgorithm() );
addAlgorithm( new QgsPdalClipAlgorithm() );
addAlgorithm( new QgsPdalConvertFormatAlgorithm() );
addAlgorithm( new QgsPdalCreateCopcAlgorithm() );
addAlgorithm( new QgsPdalDensityAlgorithm() );
addAlgorithm( new QgsPdalExportRasterAlgorithm() );
addAlgorithm( new QgsPdalExportRasterTinAlgorithm() );
Expand Down

0 comments on commit 51c34ba

Please sign in to comment.