Skip to content

Commit

Permalink
[FEATURE][processing] New algorithm for extracting binary blobs from …
Browse files Browse the repository at this point in the history
…fields

Allows users to extract binary fields to files. The filename is set via
a data defined expression, so can be taken from a column value or
a more complex expression.
  • Loading branch information
nyalldawson committed Nov 12, 2018
1 parent 5c27b7d commit e80c31e
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -41,6 +41,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmexplodehstore.cpp
processing/qgsalgorithmextendlines.cpp
processing/qgsalgorithmextenttolayer.cpp
processing/qgsalgorithmextractbinary.cpp
processing/qgsalgorithmextractbyattribute.cpp
processing/qgsalgorithmextractbyexpression.cpp
processing/qgsalgorithmextractbyextent.cpp
Expand Down
156 changes: 156 additions & 0 deletions src/analysis/processing/qgsalgorithmextractbinary.cpp
@@ -0,0 +1,156 @@
/***************************************************************************
qgsalgorithmextractbinary.cpp
-----------------------------------
begin : November 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall dot dawson 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 "qgsalgorithmextractbinary.h"
#include "qgsfeaturerequest.h"

///@cond PRIVATE

QString QgsExtractBinaryFieldAlgorithm::name() const
{
return QStringLiteral( "extractbinary" );
}

QString QgsExtractBinaryFieldAlgorithm::displayName() const
{
return QObject::tr( "Extract binary field" );
}

QString QgsExtractBinaryFieldAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm extracts contents from a binary field, saving them to individual files." );
}

QString QgsExtractBinaryFieldAlgorithm::shortDescription() const
{
return QObject::tr( "This algorithm extracts contents from a binary field, saving them to individual files." );
}

QStringList QgsExtractBinaryFieldAlgorithm::tags() const
{
return QObject::tr( "blob,binaries,save,file,contents,field,column" ).split( ',' );
}

QString QgsExtractBinaryFieldAlgorithm::group() const
{
return QObject::tr( "Vector table" );
}

QString QgsExtractBinaryFieldAlgorithm::groupId() const
{
return QStringLiteral( "vectortable" );
}

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

void QgsExtractBinaryFieldAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
QObject::tr( "Input layer" ), QList< int>() << QgsProcessing::TypeVector ) );

addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Binary field" ), QVariant(),
QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any ) );

std::unique_ptr< QgsProcessingParameterString > fileNameParam = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "FILENAME" ), QObject::tr( "File name" ) );
fileNameParam->setIsDynamic( true );
fileNameParam->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "Filename" ), QObject::tr( "File name" ), QgsPropertyDefinition::String ) );
fileNameParam->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( fileNameParam.release() );

addParameter( new QgsProcessingParameterFolderDestination( QStringLiteral( "FOLDER" ), QObject::tr( "Destination folder" ) ) );
}

QVariantMap QgsExtractBinaryFieldAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
std::unique_ptr< QgsProcessingFeatureSource > input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !input )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );

QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
int fieldIndex = input->fields().lookupField( fieldName );
if ( fieldIndex < 0 )
throw QgsProcessingException( QObject::tr( "Invalid binary field" ) );

const QString folder = parameterAsString( parameters, QStringLiteral( "FOLDER" ), context );
if ( !QFileInfo::exists( folder ) )
throw QgsProcessingException( QObject::tr( "Destination folder %1 does not exist" ).arg( folder ) );

QDir dir( folder );
const QString filename = parameterAsString( parameters, QStringLiteral( "FILENAME" ), context );
bool dynamicFilename = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "FILENAME" ) );
QgsExpressionContext expressionContext = createExpressionContext( parameters, context, input.get() );
QgsProperty filenameProperty;

QSet< QString > fields;
fields.insert( fieldName );
QgsFeatureRequest request;
if ( dynamicFilename )
{
filenameProperty = parameters.value( QStringLiteral( "FILENAME" ) ).value< QgsProperty >();
filenameProperty.prepare( expressionContext );
fields.unite( filenameProperty.referencedFields() );
}
request.setSubsetOfAttributes( fields, input->fields() ).setFlags( QgsFeatureRequest::NoGeometry );
QgsFeatureIterator features = input->getFeatures( request, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
double step = input->featureCount() > 0 ? 100.0 / input->featureCount() : 1;
int i = 0;
QgsFeature feat;
while ( features.nextFeature( feat ) )
{
i++;
if ( feedback->isCanceled() )
{
break;
}

feedback->setProgress( i * step );

QByteArray ba = feat.attribute( fieldIndex ).toByteArray();
if ( ba.isEmpty() )
continue;

QString name = filename;
if ( dynamicFilename )
{
expressionContext.setFeature( feat );
name = filenameProperty.valueAsString( expressionContext, name );
}

const QString path = dir.filePath( name );
QFile file( path );
if ( !file.open( QIODevice::WriteOnly | QFile::Truncate ) )
{
feedback->reportError( QObject::tr( "Could not open %1 for writing" ).arg( path ) );
}
else
{
file.write( ba );
file.close();
feedback->pushInfo( QObject::tr( "Extracted %1" ).arg( path ) );
}
}

QVariantMap outputs;
outputs.insert( QStringLiteral( "FOLDER" ), folder );
return outputs;
}


///@endcond
58 changes: 58 additions & 0 deletions src/analysis/processing/qgsalgorithmextractbinary.h
@@ -0,0 +1,58 @@
/***************************************************************************
qgsalgorithmextractbinary.h
---------------------------------
begin : November 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall dot dawson 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 QGSALGORITHMEXTRACTBINARY_H
#define QGSALGORITHMEXTRACTBINARY_H

#define SIP_NO_FILE

#include "qgis.h"
#include "qgsprocessingalgorithm.h"

///@cond PRIVATE

/**
* Native extract binary field algorithm.
*/
class QgsExtractBinaryFieldAlgorithm : public QgsProcessingAlgorithm
{

public:

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

protected:

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

};

///@endcond PRIVATE

#endif // QGSALGORITHMEXTRACTBINARY_H


2 changes: 2 additions & 0 deletions src/analysis/processing/qgsnativealgorithms.cpp
Expand Up @@ -36,6 +36,7 @@
#include "qgsalgorithmexplodehstore.h"
#include "qgsalgorithmextendlines.h"
#include "qgsalgorithmextenttolayer.h"
#include "qgsalgorithmextractbinary.h"
#include "qgsalgorithmextractbyattribute.h"
#include "qgsalgorithmextractbyexpression.h"
#include "qgsalgorithmextractbyextent.h"
Expand Down Expand Up @@ -163,6 +164,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsExplodeHstoreAlgorithm() );
addAlgorithm( new QgsExtendLinesAlgorithm() );
addAlgorithm( new QgsExtentToLayerAlgorithm() );
addAlgorithm( new QgsExtractBinaryFieldAlgorithm() );
addAlgorithm( new QgsExtractByAttributeAlgorithm() );
addAlgorithm( new QgsExtractByExpressionAlgorithm() );
addAlgorithm( new QgsExtractByExtentAlgorithm() );
Expand Down

0 comments on commit e80c31e

Please sign in to comment.