Skip to content

Commit

Permalink
[FEATURE][processing] Export layers information algorithm (#40462)
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Dec 7, 2020
1 parent d2f95d5 commit 28b49d5
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -62,6 +62,7 @@ set(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmexecutespatialitequeryregistered.cpp
processing/qgsalgorithmexplode.cpp
processing/qgsalgorithmexplodehstore.cpp
processing/qgsalgorithmexportlayersinformation.cpp
processing/qgsalgorithmexportmesh.cpp
processing/qgsalgorithmextendlines.cpp
processing/qgsalgorithmextentfromlayer.cpp
Expand Down
174 changes: 174 additions & 0 deletions src/analysis/processing/qgsalgorithmexportlayersinformation.cpp
@@ -0,0 +1,174 @@
/***************************************************************************
qgsalgorithmexportlayersinformation.cpp
---------------------------------
begin : December 2020
copyright : (C) 2020 by Mathieu Pellerin
email : nirvn dot asia 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 "qgsalgorithmexportlayersinformation.h"
#include "qgsproviderregistry.h"

///@cond PRIVATE

QString QgsExportLayersInformationAlgorithm::name() const
{
return QStringLiteral( "exportlayersinformation" );
}

QString QgsExportLayersInformationAlgorithm::displayName() const
{
return QObject::tr( "Export layer(s) information" );
}

QStringList QgsExportLayersInformationAlgorithm::tags() const
{
return QObject::tr( "metadata,details,extent" ).split( ',' );
}

QString QgsExportLayersInformationAlgorithm::group() const
{
return QObject::tr( "Layer tools" );
}

QString QgsExportLayersInformationAlgorithm::groupId() const
{
return QStringLiteral( "layertools" );
}

void QgsExportLayersInformationAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Input layer(s)" ), QgsProcessing::TypeMapLayer ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Output" ), QgsProcessing::TypeVectorPolygon, QVariant() ) );
}

QString QgsExportLayersInformationAlgorithm::shortHelpString() const
{
return QObject::tr( "Creates a polygon layer with features corresponding to the extent of selected layer(s).\n\n"
"Additional layer details - CRS, provider name, file path, layer name, subset filer, abstract and attribution - are attached as attributes to each feature." );
}

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

bool QgsExportLayersInformationAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
for ( QgsMapLayer *layer : layers )
{
if ( !mCrs.isValid() )
{
mCrs = layer->crs();
}
else if ( mCrs.authid() != QStringLiteral( "EPSG:4326" ) )
{
if ( mCrs != layer->crs() )
{
// mixed CRSes, set output CRS to EPSG:4326
mCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
}
}
mLayers.emplace_back( layer->clone() );
}

if ( !mCrs.isValid() )
mCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );

if ( mLayers.empty() )
feedback->reportError( QObject::tr( "No layers selected" ), false );

return true;
}

QVariantMap QgsExportLayersInformationAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QgsFields outFields;
outFields.append( QgsField( QStringLiteral( "name" ), QVariant::String ) );
outFields.append( QgsField( QStringLiteral( "source" ), QVariant::String ) );
outFields.append( QgsField( QStringLiteral( "crs" ), QVariant::String ) );
outFields.append( QgsField( QStringLiteral( "provider" ), QVariant::String ) );
outFields.append( QgsField( QStringLiteral( "file_path" ), QVariant::String ) );
outFields.append( QgsField( QStringLiteral( "layer_name" ), QVariant::String ) );
outFields.append( QgsField( QStringLiteral( "subset" ), QVariant::String ) );
outFields.append( QgsField( QStringLiteral( "abstract" ), QVariant::String ) );
outFields.append( QgsField( QStringLiteral( "attribution" ), QVariant::String ) );

QString outputDest;
std::unique_ptr< QgsFeatureSink > outputSink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, outputDest, outFields,
QgsWkbTypes::Polygon, mCrs ) );

const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );

double step = layers.size() > 0 ? 100.0 / layers.size() : 1;
int i = 0;
for ( const std::unique_ptr< QgsMapLayer > &layer : mLayers )
{
i++;
if ( feedback->isCanceled() )
{
break;
}

feedback->setProgress( i * step );

QgsFeature feature;

QgsAttributes attributes;
attributes << layer->name()
<< layer->source()
<< layer->crs().authid();
if ( layer->dataProvider() )
{
const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( layer->dataProvider()->name(), layer->source() );
attributes << layer->dataProvider()->name()
<< parts[ QStringLiteral( "path" ) ]
<< parts[ QStringLiteral( "layerName" ) ]
<< parts[ QStringLiteral( "subset" ) ];
}
else
{
attributes << QVariant() << QVariant() << QVariant() << QVariant();
}
attributes << layer->metadata().rights().join( ';' )
<< layer->abstract();
feature.setAttributes( attributes );

QgsRectangle rect = layer->extent();
if ( !rect.isEmpty() )
{
if ( layer->crs() != mCrs )
{
QgsCoordinateTransform transform( layer->crs(), mCrs, context.transformContext() );
try
{
rect = transform.transformBoundingBox( rect );
}
catch ( QgsCsException &e )
{
Q_UNUSED( e )
rect = QgsRectangle();
feedback->pushInfo( QObject::tr( "Extent of layer %1 could not be reprojected" ).arg( layer->name() ) );
}
}
feature.setGeometry( QgsGeometry::fromRect( rect ) );
}
outputSink->addFeature( feature, QgsFeatureSink::FastInsert );
}

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), outputDest );
return outputs;
}

///@endcond
62 changes: 62 additions & 0 deletions src/analysis/processing/qgsalgorithmexportlayersinformation.h
@@ -0,0 +1,62 @@
/***************************************************************************
qgsalgorithmexportlayersinformation.h
---------------------------------
begin : December 2020
copyright : (C) 2020 by Mathieu Pellerin
email : nirvn dot asia 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 QGSALGORITHMEXPORTLAYERSINFORMATION_H
#define QGSALGORITHMEXPORTLAYERSINFORMATION_H

#define SIP_NO_FILE

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

///@cond PRIVATE

/**
* Native export layers information algorithm.
*/
class QgsExportLayersInformationAlgorithm : public QgsProcessingAlgorithm
{

public:

QgsExportLayersInformationAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QString shortHelpString() const override;
QgsExportLayersInformationAlgorithm *createInstance() const override SIP_FACTORY;

protected:

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

private:

std::vector< std::unique_ptr< QgsMapLayer> > mLayers;
QgsCoordinateReferenceSystem mCrs;

};

///@endcond PRIVATE

#endif // QGSALGORITHMEXPORTLAYERSINFORMATION_H
2 changes: 2 additions & 0 deletions src/analysis/processing/qgsnativealgorithms.cpp
Expand Up @@ -59,6 +59,7 @@
#include "qgsalgorithmexportmesh.h"
#include "qgsalgorithmexplode.h"
#include "qgsalgorithmexplodehstore.h"
#include "qgsalgorithmexportlayersinformation.h"
#include "qgsalgorithmextendlines.h"
#include "qgsalgorithmextentfromlayer.h"
#include "qgsalgorithmextenttolayer.h"
Expand Down Expand Up @@ -288,6 +289,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsExecuteSpatialiteQueryAlgorithm() );
addAlgorithm( new QgsExplodeAlgorithm() );
addAlgorithm( new QgsExplodeHstoreAlgorithm() );
addAlgorithm( new QgsExportLayersInformationAlgorithm() );
addAlgorithm( new QgsExportMeshVerticesAlgorithm );
addAlgorithm( new QgsExportMeshFacesAlgorithm );
addAlgorithm( new QgsExportMeshEdgesAlgorithm );
Expand Down
41 changes: 41 additions & 0 deletions tests/src/analysis/testqgsprocessingalgs.cpp
Expand Up @@ -85,6 +85,7 @@ class TestQgsProcessingAlgs: public QObject
void kmeansCluster();
void categorizeByStyle();
void extractBinary();
void exportLayersInformationAlg();
void createDirectory();
void flattenRelations();

Expand Down Expand Up @@ -318,6 +319,46 @@ void TestQgsProcessingAlgs::saveFeaturesAlg()
QCOMPARE( savedLayer->getFeature( 1 ).geometry().asPoint().x(), -83.3 );
}

void TestQgsProcessingAlgs::exportLayersInformationAlg()
{
const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
QString gpkgFileName = dataDir + "/humanbeings.gpkg";
QFileInfo gpkgFileInfo( gpkgFileName );
std::unique_ptr< QgsVectorLayer > gpkgLayer = qgis::make_unique< QgsVectorLayer >( gpkgFileInfo.filePath() + QStringLiteral( "|layername=person" ),
QStringLiteral( "person" ), QStringLiteral( "ogr" ) );

const QgsProcessingAlgorithm *exportLayersInformation( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:exportlayersinformation" ) ) );

QgsProcessingContext context;;
context.setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;

QVariantMap parameters;
parameters.insert( QStringLiteral( "LAYERS" ), QVariantList() << QVariant::fromValue( gpkgLayer.get() ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QVariantMap results = exportLayersInformation->run( parameters, context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );

QgsVectorLayer *vlayer = qobject_cast< QgsVectorLayer * >( context.getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QVERIFY( vlayer );
QCOMPARE( vlayer->featureCount(), 1L );
QCOMPARE( vlayer->crs().authid(), QStringLiteral( "EPSG:2056" ) );

parameters.insert( QStringLiteral( "LAYERS" ), QVariantList() << QVariant::fromValue( gpkgLayer.get() ) << QVariant::fromValue( mPolygonLayer ) );
ok = false;
results = exportLayersInformation->run( parameters, context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );

vlayer = qobject_cast< QgsVectorLayer * >( context.getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QVERIFY( vlayer );
QCOMPARE( vlayer->featureCount(), 2L );
// when layers have mixed CRSes, the algorithm uses WGS84
QCOMPARE( vlayer->crs().authid(), QStringLiteral( "EPSG:4326" ) );
}

void TestQgsProcessingAlgs::packageAlg()
{
QString outputGpkg = QDir::tempPath() + "/package_alg.gpkg";
Expand Down

0 comments on commit 28b49d5

Please sign in to comment.