mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
[feature] add Fill noData cells algorithm and tests
This commit is contained in:
parent
d15ce6b4c3
commit
ea7167e135
@ -88,6 +88,7 @@
|
|||||||
<file>themes/default/algorithms/mAlgorithmCollect.svg</file>
|
<file>themes/default/algorithms/mAlgorithmCollect.svg</file>
|
||||||
<file>themes/default/algorithms/mAlgorithmConvexHull.svg</file>
|
<file>themes/default/algorithms/mAlgorithmConvexHull.svg</file>
|
||||||
<file>themes/default/algorithms/mAlgorithmCreateGrid.svg</file>
|
<file>themes/default/algorithms/mAlgorithmCreateGrid.svg</file>
|
||||||
|
<file>themes/default/algorithms/mAlgorithmFillNoData.svg</file>
|
||||||
<file>themes/default/algorithms/mAlgorithmFuzzifyLinear.svg</file>
|
<file>themes/default/algorithms/mAlgorithmFuzzifyLinear.svg</file>
|
||||||
<file>themes/default/algorithms/mAlgorithmFuzzifyPower.svg</file>
|
<file>themes/default/algorithms/mAlgorithmFuzzifyPower.svg</file>
|
||||||
<file>themes/default/algorithms/mAlgorithmFuzzifyLarge.svg</file>
|
<file>themes/default/algorithms/mAlgorithmFuzzifyLarge.svg</file>
|
||||||
|
162
images/themes/default/algorithms/mAlgorithmFillNoData.svg
Normal file
162
images/themes/default/algorithms/mAlgorithmFillNoData.svg
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 6.35 6.35"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4582"
|
||||||
|
sodipodi:docname="mAlgorithmFillNoData.svg"
|
||||||
|
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||||
|
<metadata
|
||||||
|
id="metadata4588">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs4586" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1052"
|
||||||
|
id="namedview4584"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="15.806783"
|
||||||
|
inkscape:cx="-2.374488"
|
||||||
|
inkscape:cy="7.5245181"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg4582" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#7c96a8;fill-opacity:0.46320346;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617"
|
||||||
|
width="1.4848133"
|
||||||
|
height="1.4848132"
|
||||||
|
x="0.18943673"
|
||||||
|
y="0.24060294" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-1"
|
||||||
|
width="1.4848135"
|
||||||
|
height="1.4848133"
|
||||||
|
x="1.67425"
|
||||||
|
y="0.24060294" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-8"
|
||||||
|
width="1.4848135"
|
||||||
|
height="1.4848133"
|
||||||
|
x="3.1590633"
|
||||||
|
y="0.24060294" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#7c96a8;fill-opacity:0.46320346;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-5"
|
||||||
|
width="1.4848135"
|
||||||
|
height="1.4848133"
|
||||||
|
x="4.643877"
|
||||||
|
y="0.24060294" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-9"
|
||||||
|
width="1.4848135"
|
||||||
|
height="1.4848133"
|
||||||
|
x="0.18943644"
|
||||||
|
y="1.7254163" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#7c96a8;fill-opacity:0.46320346;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-1-7"
|
||||||
|
width="1.4848136"
|
||||||
|
height="1.4848135"
|
||||||
|
x="1.6742498"
|
||||||
|
y="1.7254163" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-8-5"
|
||||||
|
width="1.4848136"
|
||||||
|
height="1.4848135"
|
||||||
|
x="3.1590633"
|
||||||
|
y="1.7254163" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-5-3"
|
||||||
|
width="1.4848136"
|
||||||
|
height="1.4848135"
|
||||||
|
x="4.6438766"
|
||||||
|
y="1.7254163" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-88"
|
||||||
|
width="1.4848135"
|
||||||
|
height="1.4848133"
|
||||||
|
x="0.18943655"
|
||||||
|
y="3.2102299" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-1-3"
|
||||||
|
width="1.4848136"
|
||||||
|
height="1.4848135"
|
||||||
|
x="1.6742499"
|
||||||
|
y="3.2102299" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-8-1"
|
||||||
|
width="1.4848136"
|
||||||
|
height="1.4848135"
|
||||||
|
x="3.1590633"
|
||||||
|
y="3.2102299" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#7c96a8;fill-opacity:0.46320346;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-5-8"
|
||||||
|
width="1.4848136"
|
||||||
|
height="1.4848135"
|
||||||
|
x="4.6438766"
|
||||||
|
y="3.2102299" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-96"
|
||||||
|
width="1.4848135"
|
||||||
|
height="1.4848133"
|
||||||
|
x="0.18943644"
|
||||||
|
y="4.6950436" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#7c96a8;fill-opacity:0.46320346;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-1-4"
|
||||||
|
width="1.4848136"
|
||||||
|
height="1.4848135"
|
||||||
|
x="1.6742498"
|
||||||
|
y="4.6950436" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#7c96a8;fill-opacity:0.46320346;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-8-3"
|
||||||
|
width="1.4848136"
|
||||||
|
height="1.4848135"
|
||||||
|
x="3.1590633"
|
||||||
|
y="4.6950436" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#517083;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.14263225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4617-5-33"
|
||||||
|
width="1.4848136"
|
||||||
|
height="1.4848135"
|
||||||
|
x="4.643877"
|
||||||
|
y="4.6950436" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 6.0 KiB |
@ -64,6 +64,7 @@ SET(QGIS_ANALYSIS_SRCS
|
|||||||
processing/qgsalgorithmextractvertices.cpp
|
processing/qgsalgorithmextractvertices.cpp
|
||||||
processing/qgsalgorithmextractspecificvertices.cpp
|
processing/qgsalgorithmextractspecificvertices.cpp
|
||||||
processing/qgsalgorithmfiledownloader.cpp
|
processing/qgsalgorithmfiledownloader.cpp
|
||||||
|
processing/qgsalgorithmfillnodata.cpp
|
||||||
processing/qgsalgorithmfilter.cpp
|
processing/qgsalgorithmfilter.cpp
|
||||||
processing/qgsalgorithmfiltervertices.cpp
|
processing/qgsalgorithmfiltervertices.cpp
|
||||||
processing/qgsalgorithmfixgeometries.cpp
|
processing/qgsalgorithmfixgeometries.cpp
|
||||||
|
157
src/analysis/processing/qgsalgorithmfillnodata.cpp
Normal file
157
src/analysis/processing/qgsalgorithmfillnodata.cpp
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsalgorithmfillnodata.cpp
|
||||||
|
---------------------
|
||||||
|
begin : January 2020
|
||||||
|
copyright : (C) 2020 by Clemens Raffler
|
||||||
|
email : clemens dot raffler 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 "qgsalgorithmfillnodata.h"
|
||||||
|
#include "qgsrasterfilewriter.h"
|
||||||
|
|
||||||
|
///@cond PRIVATE
|
||||||
|
|
||||||
|
QString QgsFillNoDataAlgorithm::name() const
|
||||||
|
{
|
||||||
|
return QStringLiteral( "fillnodata" );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFillNoDataAlgorithm::displayName() const
|
||||||
|
{
|
||||||
|
return QObject::tr( "Fill noData cells" );
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList QgsFillNoDataAlgorithm::tags() const
|
||||||
|
{
|
||||||
|
return QObject::tr( "noData,nodata,data,cells,fill,set" ).split( ',' );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFillNoDataAlgorithm::group() const
|
||||||
|
{
|
||||||
|
return QObject::tr( "Raster tools" );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFillNoDataAlgorithm::groupId() const
|
||||||
|
{
|
||||||
|
return QStringLiteral( "rastertools" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFillNoDataAlgorithm::initAlgorithm( const QVariantMap & )
|
||||||
|
{
|
||||||
|
addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT" ), QStringLiteral( "Raster input" ) ) );
|
||||||
|
addParameter( new QgsProcessingParameterBand( QStringLiteral( "BAND" ), QObject::tr( "Band Number" ), 1, QStringLiteral( "INPUT" ) ) );
|
||||||
|
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "FILL_VALUE" ), QObject::tr( "Fill value" ), QgsProcessingParameterNumber::Double, 1, false, 1, 10000000 ) );
|
||||||
|
addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output raster" ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFillNoDataAlgorithm::shortHelpString() const
|
||||||
|
{
|
||||||
|
return QObject::tr( "This algorithm resets the noData values in the input raster "
|
||||||
|
"to a chosen value. This value can be set by the user or "
|
||||||
|
"evaluated as an expression." );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsFillNoDataAlgorithm *QgsFillNoDataAlgorithm::createInstance() const
|
||||||
|
{
|
||||||
|
return new QgsFillNoDataAlgorithm();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsFillNoDataAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
||||||
|
{
|
||||||
|
Q_UNUSED( feedback );
|
||||||
|
mInputRaster = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT" ), context );
|
||||||
|
mFillValue = parameterAsDouble( parameters, QStringLiteral( "FILL_VALUE" ), context );
|
||||||
|
|
||||||
|
if ( !mInputRaster )
|
||||||
|
throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT" ) ) );
|
||||||
|
|
||||||
|
mBand = parameterAsInt( parameters, QStringLiteral( "BAND" ), context );
|
||||||
|
if ( mBand < 1 || mBand > mInputRaster->bandCount() )
|
||||||
|
throw QgsProcessingException( QObject::tr( "Invalid band number for BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand ).arg( mInputRaster->bandCount() ) );
|
||||||
|
|
||||||
|
mInterface.reset( mInputRaster->dataProvider()->clone() );
|
||||||
|
mInputNoDataValue = mInputRaster->dataProvider()->sourceNoDataValue( mBand );
|
||||||
|
mExtent = mInputRaster->extent();
|
||||||
|
mLayerWidth = mInputRaster->width();
|
||||||
|
mLayerHeight = mInputRaster->height();
|
||||||
|
mCrs = mInputRaster->crs();
|
||||||
|
mNbCellsXProvider = mInterface->xSize();
|
||||||
|
mNbCellsYProvider = mInterface->ySize();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap QgsFillNoDataAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
||||||
|
{
|
||||||
|
//test if input dataset has NoData
|
||||||
|
if ( !mInputRaster->dataProvider()->sourceHasNoDataValue( mBand ) )
|
||||||
|
throw QgsProcessingException( QObject::tr( "Input raster has no NoData values. There exist no NoData cells to fill." ) );
|
||||||
|
|
||||||
|
//prepare output dataset
|
||||||
|
const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
|
||||||
|
QFileInfo fi( outputFile );
|
||||||
|
const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
|
||||||
|
std::unique_ptr< QgsRasterFileWriter > writer = qgis::make_unique< QgsRasterFileWriter >( outputFile );
|
||||||
|
writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
|
||||||
|
writer->setOutputFormat( outputFormat );
|
||||||
|
std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( mInterface->dataType( mBand ), mNbCellsXProvider, mNbCellsYProvider, mExtent, mCrs ) );
|
||||||
|
if ( !provider )
|
||||||
|
throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
|
||||||
|
if ( !provider->isValid() )
|
||||||
|
throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
|
||||||
|
|
||||||
|
//prepare output provider
|
||||||
|
mDestinationRasterProvider = provider.get();
|
||||||
|
mDestinationRasterProvider->setNoDataValue( 1, -9999 );
|
||||||
|
mDestinationRasterProvider->setEditable( true );
|
||||||
|
|
||||||
|
int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
|
||||||
|
int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
|
||||||
|
int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
|
||||||
|
int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
|
||||||
|
int nbBlocks = nbBlocksWidth * nbBlocksHeight;
|
||||||
|
|
||||||
|
QgsRasterIterator iter( mInterface.get() );
|
||||||
|
iter.startRasterRead( mBand, mLayerWidth, mLayerHeight, mExtent );
|
||||||
|
int iterLeft = 0;
|
||||||
|
int iterTop = 0;
|
||||||
|
int iterCols = 0;
|
||||||
|
int iterRows = 0;
|
||||||
|
std::unique_ptr< QgsRasterBlock > filledRasterBlock;
|
||||||
|
while ( iter.readNextRasterPart( mBand, iterCols, iterRows, filledRasterBlock, iterLeft, iterTop ) )
|
||||||
|
{
|
||||||
|
if ( feedback )
|
||||||
|
feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
|
||||||
|
if ( !filledRasterBlock->hasNoDataValue() )
|
||||||
|
continue;
|
||||||
|
for ( int row = 0; row < iterRows; row++ )
|
||||||
|
{
|
||||||
|
if ( feedback && feedback->isCanceled() )
|
||||||
|
break;
|
||||||
|
for ( int column = 0; column < iterCols; column++ )
|
||||||
|
{
|
||||||
|
if ( feedback && feedback->isCanceled() )
|
||||||
|
break;
|
||||||
|
if ( filledRasterBlock->isNoData( row, column ) )
|
||||||
|
filledRasterBlock->setValue( row, column, mFillValue );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mDestinationRasterProvider->writeBlock( filledRasterBlock.get(), mBand, iterLeft, iterTop );
|
||||||
|
}
|
||||||
|
mDestinationRasterProvider->setEditable( false );
|
||||||
|
|
||||||
|
QVariantMap outputs;
|
||||||
|
outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///@endcond
|
78
src/analysis/processing/qgsalgorithmfillnodata.h
Normal file
78
src/analysis/processing/qgsalgorithmfillnodata.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsalgorithmfillnodata.h
|
||||||
|
---------------------
|
||||||
|
begin : January 2020
|
||||||
|
copyright : (C) 2020 by Clemens Raffler
|
||||||
|
email : clemens dot raffler 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 QGSALGORITHMFILLNODATA_H
|
||||||
|
#define QGSALGORITHMFILLNODATA_H
|
||||||
|
|
||||||
|
#define SIP_NO_FILE
|
||||||
|
|
||||||
|
#include "qgis_sip.h"
|
||||||
|
#include "qgsprocessingalgorithm.h"
|
||||||
|
#include "qgsapplication.h"
|
||||||
|
|
||||||
|
///@cond PRIVATE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill NoData algorithm:
|
||||||
|
* This is an algorithm that fills the NoDataValues in a raster dataset
|
||||||
|
* based on a dynamic input parameter
|
||||||
|
*/
|
||||||
|
class QgsFillNoDataAlgorithm : public QgsProcessingAlgorithm
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
QgsFillNoDataAlgorithm() = default;
|
||||||
|
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
|
||||||
|
QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmFillNoData.svg" ) ); }
|
||||||
|
QString svgIconPath() const override { return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmFillNoData.svg" ) ); }
|
||||||
|
QString name() const override;
|
||||||
|
QString displayName() const override;
|
||||||
|
QStringList tags() const override;
|
||||||
|
QString group() const override;
|
||||||
|
QString groupId() const override;
|
||||||
|
QString shortHelpString() const override;
|
||||||
|
QgsFillNoDataAlgorithm *createInstance() const override SIP_FACTORY;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||||
|
QVariantMap processAlgorithm( const QVariantMap ¶meters,
|
||||||
|
QgsProcessingContext &context,
|
||||||
|
QgsProcessingFeedback *feedback ) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QgsRasterLayer *mInputRaster;
|
||||||
|
double mFillValue = 0;
|
||||||
|
int mBand;
|
||||||
|
std::unique_ptr< QgsRasterInterface > mInterface;
|
||||||
|
QgsRectangle mExtent;
|
||||||
|
QgsCoordinateReferenceSystem mCrs;
|
||||||
|
int mLayerWidth;
|
||||||
|
int mLayerHeight;
|
||||||
|
int mNbCellsXProvider = 0;
|
||||||
|
int mNbCellsYProvider = 0;
|
||||||
|
double mInputNoDataValue;
|
||||||
|
QgsRasterDataProvider *mDestinationRasterProvider;
|
||||||
|
|
||||||
|
bool mDynamicFillValue = false;
|
||||||
|
QgsProperty mDynamicFillValueProperty;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
///@endcond PRIVATE
|
||||||
|
|
||||||
|
#endif // QGSALGORITHMFILLNODATA_H
|
@ -59,6 +59,7 @@
|
|||||||
#include "qgsalgorithmextractspecificvertices.h"
|
#include "qgsalgorithmextractspecificvertices.h"
|
||||||
#include "qgsalgorithmextractzmvalues.h"
|
#include "qgsalgorithmextractzmvalues.h"
|
||||||
#include "qgsalgorithmfiledownloader.h"
|
#include "qgsalgorithmfiledownloader.h"
|
||||||
|
#include "qgsalgorithmfillnodata.h"
|
||||||
#include "qgsalgorithmfilter.h"
|
#include "qgsalgorithmfilter.h"
|
||||||
#include "qgsalgorithmfiltervertices.h"
|
#include "qgsalgorithmfiltervertices.h"
|
||||||
#include "qgsalgorithmfixgeometries.h"
|
#include "qgsalgorithmfixgeometries.h"
|
||||||
@ -247,6 +248,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
|
|||||||
addAlgorithm( new QgsExtractSpecificVerticesAlgorithm() );
|
addAlgorithm( new QgsExtractSpecificVerticesAlgorithm() );
|
||||||
addAlgorithm( new QgsExtractZValuesAlgorithm() );
|
addAlgorithm( new QgsExtractZValuesAlgorithm() );
|
||||||
addAlgorithm( new QgsFileDownloaderAlgorithm() );
|
addAlgorithm( new QgsFileDownloaderAlgorithm() );
|
||||||
|
addAlgorithm( new QgsFillNoDataAlgorithm() );
|
||||||
addAlgorithm( new QgsFilterAlgorithm() );
|
addAlgorithm( new QgsFilterAlgorithm() );
|
||||||
addAlgorithm( new QgsFilterVerticesByM() );
|
addAlgorithm( new QgsFilterVerticesByM() );
|
||||||
addAlgorithm( new QgsFilterVerticesByZ() );
|
addAlgorithm( new QgsFilterVerticesByZ() );
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "qgsprocessingcontext.h"
|
#include "qgsprocessingcontext.h"
|
||||||
#include "qgsprocessingmodelalgorithm.h"
|
#include "qgsprocessingmodelalgorithm.h"
|
||||||
#include "qgsnativealgorithms.h"
|
#include "qgsnativealgorithms.h"
|
||||||
|
#include "qgsalgorithmfillnodata.h"
|
||||||
#include "qgsalgorithmlinedensity.h"
|
#include "qgsalgorithmlinedensity.h"
|
||||||
#include "qgsalgorithmimportphotos.h"
|
#include "qgsalgorithmimportphotos.h"
|
||||||
#include "qgsalgorithmtransform.h"
|
#include "qgsalgorithmtransform.h"
|
||||||
@ -85,6 +86,8 @@ class TestQgsProcessingAlgs: public QObject
|
|||||||
void densifyGeometries_data();
|
void densifyGeometries_data();
|
||||||
void densifyGeometries();
|
void densifyGeometries();
|
||||||
|
|
||||||
|
void fillNoData_data();
|
||||||
|
void fillNoData();
|
||||||
void lineDensity_data();
|
void lineDensity_data();
|
||||||
void lineDensity();
|
void lineDensity();
|
||||||
|
|
||||||
@ -923,6 +926,134 @@ void TestQgsProcessingAlgs::densifyGeometries()
|
|||||||
QVERIFY2( result.geometry().equals( expectedGeometry ), QStringLiteral( "Result: %1, Expected: %2" ).arg( result.geometry().asWkt(), expectedGeometry.asWkt() ).toUtf8().constData() );
|
QVERIFY2( result.geometry().equals( expectedGeometry ), QStringLiteral( "Result: %1, Expected: %2" ).arg( result.geometry().asWkt(), expectedGeometry.asWkt() ).toUtf8().constData() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestQgsProcessingAlgs::fillNoData_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>( "inputRaster" );
|
||||||
|
QTest::addColumn<QString>( "expectedRaster" );
|
||||||
|
QTest::addColumn<int>( "inputBand" );
|
||||||
|
QTest::addColumn<double>( "fillValue" );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Testcase 1
|
||||||
|
*
|
||||||
|
* NoData raster layer
|
||||||
|
* band = 1
|
||||||
|
* fillValue = 2.0
|
||||||
|
*/
|
||||||
|
QTest::newRow( "testcase 1" )
|
||||||
|
<< "/raster/band1_int16_noct_epsg4326.tif"
|
||||||
|
<< QStringLiteral( "/fillnodata_testcase1.tif" )
|
||||||
|
<< 1
|
||||||
|
<< 2.0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Testcase 2
|
||||||
|
*
|
||||||
|
* WGS84 data without weights
|
||||||
|
* searchRadius = 3
|
||||||
|
* pixelSize = 1.8
|
||||||
|
*/
|
||||||
|
QTest::newRow( "testcase 2" )
|
||||||
|
<< "/raster/band1_int16_noct_epsg4326.tif"
|
||||||
|
<< QStringLiteral( "/fillnodata_testcase2.tif" )
|
||||||
|
<< 1
|
||||||
|
<< 1.8;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Testcase 3
|
||||||
|
*
|
||||||
|
* WGS84 data without weights
|
||||||
|
* searchRadius = 3
|
||||||
|
* pixelSize = 1.8
|
||||||
|
*/
|
||||||
|
QTest::newRow( "testcase 2" )
|
||||||
|
<< "/raster/band1_float32_noct_epsg4326.tif"
|
||||||
|
<< QStringLiteral( "/fillnodata_testcase3.tif" )
|
||||||
|
<< 1
|
||||||
|
<< 1.8;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQgsProcessingAlgs::fillNoData()
|
||||||
|
{
|
||||||
|
QFETCH( QString, inputRaster );
|
||||||
|
QFETCH( QString, expectedRaster );
|
||||||
|
QFETCH( int, inputBand );
|
||||||
|
QFETCH( double, fillValue );
|
||||||
|
|
||||||
|
//prepare input params
|
||||||
|
QgsProject p;
|
||||||
|
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:fillnodata" ) ) );
|
||||||
|
|
||||||
|
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
|
||||||
|
|
||||||
|
std::unique_ptr<QgsRasterLayer> inputRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + inputRaster, "inputDataset", "gdal" );
|
||||||
|
|
||||||
|
//set project crs and ellipsoid from input layer
|
||||||
|
p.setCrs( inputRasterLayer->crs(), true );
|
||||||
|
|
||||||
|
//set project after layer has been added so that transform context/ellipsoid from crs is also set
|
||||||
|
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
|
||||||
|
context->setProject( &p );
|
||||||
|
|
||||||
|
QVariantMap parameters;
|
||||||
|
|
||||||
|
parameters.insert( QStringLiteral( "INPUT" ), myDataPath + inputRaster );
|
||||||
|
parameters.insert( QStringLiteral( "BAND" ), inputBand );
|
||||||
|
parameters.insert( QStringLiteral( "FILL_VALUE" ), fillValue );
|
||||||
|
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
|
||||||
|
|
||||||
|
//prepare expectedRaster
|
||||||
|
std::unique_ptr<QgsRasterLayer> expectedRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + "/control_images/fillNoData/" + expectedRaster, "expectedDataset", "gdal" );
|
||||||
|
std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
|
||||||
|
QgsRasterIterator expectedIter( expectedInterface.get() );
|
||||||
|
expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
|
||||||
|
|
||||||
|
//run alg...
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
QgsProcessingFeedback feedback;
|
||||||
|
QVariantMap results;
|
||||||
|
|
||||||
|
results = alg->run( parameters, *context, &feedback, &ok );
|
||||||
|
QVERIFY( ok );
|
||||||
|
|
||||||
|
//...and check results with expected datasets
|
||||||
|
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
|
||||||
|
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
|
||||||
|
|
||||||
|
QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
|
||||||
|
QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
|
||||||
|
|
||||||
|
QgsRasterIterator outputIter( outputInterface.get() );
|
||||||
|
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
|
||||||
|
int outputIterLeft = 0;
|
||||||
|
int outputIterTop = 0;
|
||||||
|
int outputIterCols = 0;
|
||||||
|
int outputIterRows = 0;
|
||||||
|
int expectedIterLeft = 0;
|
||||||
|
int expectedIterTop = 0;
|
||||||
|
int expectedIterCols = 0;
|
||||||
|
int expectedIterRows = 0;
|
||||||
|
|
||||||
|
std::unique_ptr< QgsRasterBlock > outputRasterBlock;
|
||||||
|
std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
|
||||||
|
|
||||||
|
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
|
||||||
|
expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
|
||||||
|
{
|
||||||
|
for ( int row = 0; row < expectedIterRows; row++ )
|
||||||
|
{
|
||||||
|
for ( int column = 0; column < expectedIterCols; column++ )
|
||||||
|
{
|
||||||
|
double expectedValue = expectedRasterBlock->value( row, column );
|
||||||
|
double outputValue = outputRasterBlock->value( row, column );
|
||||||
|
QCOMPARE( outputValue, expectedValue );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestQgsProcessingAlgs::lineDensity_data()
|
void TestQgsProcessingAlgs::lineDensity_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>( "inputDataset" );
|
QTest::addColumn<QString>( "inputDataset" );
|
||||||
|
BIN
tests/testdata/control_images/fillNoData/fillnodata_testcase1.tif
vendored
Normal file
BIN
tests/testdata/control_images/fillNoData/fillnodata_testcase1.tif
vendored
Normal file
Binary file not shown.
10
tests/testdata/control_images/fillNoData/fillnodata_testcase1.tif.aux.xml
vendored
Normal file
10
tests/testdata/control_images/fillNoData/fillnodata_testcase1.tif.aux.xml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<PAMDataset>
|
||||||
|
<PAMRasterBand band="1">
|
||||||
|
<Metadata>
|
||||||
|
<MDI key="STATISTICS_MAXIMUM">32767</MDI>
|
||||||
|
<MDI key="STATISTICS_MEAN">1887.7</MDI>
|
||||||
|
<MDI key="STATISTICS_MINIMUM">-32111</MDI>
|
||||||
|
<MDI key="STATISTICS_STDDEV">17198.274992278</MDI>
|
||||||
|
</Metadata>
|
||||||
|
</PAMRasterBand>
|
||||||
|
</PAMDataset>
|
BIN
tests/testdata/control_images/fillNoData/fillnodata_testcase2.tif
vendored
Normal file
BIN
tests/testdata/control_images/fillNoData/fillnodata_testcase2.tif
vendored
Normal file
Binary file not shown.
10
tests/testdata/control_images/fillNoData/fillnodata_testcase2.tif.aux.xml
vendored
Normal file
10
tests/testdata/control_images/fillNoData/fillnodata_testcase2.tif.aux.xml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<PAMDataset>
|
||||||
|
<PAMRasterBand band="1">
|
||||||
|
<Metadata>
|
||||||
|
<MDI key="STATISTICS_MAXIMUM">32767</MDI>
|
||||||
|
<MDI key="STATISTICS_MEAN">1887.51</MDI>
|
||||||
|
<MDI key="STATISTICS_MINIMUM">-32111</MDI>
|
||||||
|
<MDI key="STATISTICS_STDDEV">17198.295829236</MDI>
|
||||||
|
</Metadata>
|
||||||
|
</PAMRasterBand>
|
||||||
|
</PAMDataset>
|
BIN
tests/testdata/control_images/fillNoData/fillnodata_testcase3.tif
vendored
Normal file
BIN
tests/testdata/control_images/fillNoData/fillnodata_testcase3.tif
vendored
Normal file
Binary file not shown.
10
tests/testdata/control_images/fillNoData/fillnodata_testcase3.tif.aux.xml
vendored
Normal file
10
tests/testdata/control_images/fillNoData/fillnodata_testcase3.tif.aux.xml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<PAMDataset>
|
||||||
|
<PAMRasterBand band="1">
|
||||||
|
<Metadata>
|
||||||
|
<MDI key="STATISTICS_MAXIMUM">3.3999999521444e+38</MDI>
|
||||||
|
<MDI key="STATISTICS_MEAN">1.9584000167522e+37</MDI>
|
||||||
|
<MDI key="STATISTICS_MINIMUM">-3.3319999287626e+38</MDI>
|
||||||
|
<MDI key="STATISTICS_STDDEV">1.7845894905146e+38</MDI>
|
||||||
|
</Metadata>
|
||||||
|
</PAMRasterBand>
|
||||||
|
</PAMDataset>
|
Loading…
x
Reference in New Issue
Block a user