Skip to content

Commit

Permalink
Do not add duplicates in raster calc layers list
Browse files Browse the repository at this point in the history
Related to #20601
  • Loading branch information
elpaso committed Nov 29, 2018
1 parent 318c246 commit 18854dc
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 21 deletions.
Expand Up @@ -23,6 +23,13 @@ Represents an individual raster layer/band number entry within a raster calculat
%End
public:

static QList<QgsRasterCalculatorEntry> rasterEntries();
%Docstring
Creates a list of raster entries from the current project

:return: the list of raster entries form the current project
%End

QString ref;

QgsRasterLayer *raster;
Expand Down
Expand Up @@ -44,9 +44,10 @@
from processing.tools import dataobjects
from processing.tools.system import userFolder


from processing.gui.wrappers import InvalidParameterValue

from qgis.analysis import QgsRasterCalculatorEntry

pluginPath = os.path.dirname(__file__)
WIDGET_ADD_NEW, BASE_ADD_NEW = uic.loadUiType(
os.path.join(pluginPath, 'AddNewExpressionDialog.ui'))
Expand Down Expand Up @@ -203,12 +204,10 @@ def _panel(self, options):

def createWidget(self):
if self.dialogType == DIALOG_STANDARD:
layers = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False)
entries = QgsRasterCalculatorEntry.rasterEntries()
options = {}
for lyr in layers:
for n in range(lyr.bandCount()):
name = '{:s}@{:d}'.format(lyr.name(), n + 1)
options[name] = name
for entry in entries:
options[entry.ref] = entry.ref
return self._panel(options)
elif self.dialogType == DIALOG_BATCH:
return QLineEdit()
Expand Down
55 changes: 55 additions & 0 deletions src/analysis/raster/qgsrastercalculator.cpp
Expand Up @@ -25,6 +25,7 @@
#include "qgsrasterprojector.h"
#include "qgsfeedback.h"
#include "qgsogrutils.h"
#include "qgsproject.h"

#include <QFile>

Expand Down Expand Up @@ -349,3 +350,57 @@ QString QgsRasterCalculator::lastError() const
{
return mLastError;
}

QList<QgsRasterCalculatorEntry> QgsRasterCalculatorEntry::rasterEntries()
{
QList<QgsRasterCalculatorEntry> availableEntries;
const QMap<QString, QgsMapLayer *> &layers = QgsProject::instance()->mapLayers();

auto uniqueRasterBandIdentifier = [ & ]( QgsRasterCalculatorEntry & entry ) -> bool
{
unsigned int i( 1 );
entry.ref = QStringLiteral( "%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
while ( true )
{
bool unique( true );
for ( const auto &ref : qgis::as_const( availableEntries ) )
{
// Safety belt
if ( !( entry.raster && ref.raster ) )
continue;
// Check if a layer with the same data source was already added to the list
if ( ref.raster->publicSource() == entry.raster->publicSource() )
return false;
// If same name but different source
if ( ref.ref == entry.ref )
{
unique = false;
entry.ref = QStringLiteral( "%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
}
}
if ( unique )
return true;
}
};

QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
for ( ; layerIt != layers.constEnd(); ++layerIt )
{
QgsRasterLayer *rlayer = dynamic_cast<QgsRasterLayer *>( layerIt.value() );
if ( rlayer && rlayer->dataProvider() && rlayer->dataProvider()->name() == QLatin1String( "gdal" ) )
{

//get number of bands
for ( int i = 0; i < rlayer->bandCount(); ++i )
{
QgsRasterCalculatorEntry entry;
entry.raster = rlayer;
entry.bandNumber = i + 1;
if ( ! uniqueRasterBandIdentifier( entry ) )
continue;
availableEntries.push_back( entry );
}
}
}
return availableEntries;
}
6 changes: 6 additions & 0 deletions src/analysis/raster/qgsrastercalculator.h
Expand Up @@ -40,6 +40,12 @@ class ANALYSIS_EXPORT QgsRasterCalculatorEntry

public:

/**
* Creates a list of raster entries from the current project
* \return the list of raster entries form the current project
*/
static QList<QgsRasterCalculatorEntry> rasterEntries();

/**
* Name of entry.
*/
Expand Down
22 changes: 7 additions & 15 deletions src/app/qgsrastercalcdialog.cpp
Expand Up @@ -164,31 +164,23 @@ void QgsRasterCalcDialog::setExtentSize( int width, int height, QgsRectangle bbo
mExtentSizeSet = true;
}


void QgsRasterCalcDialog::insertAvailableRasterBands()
{
const QMap<QString, QgsMapLayer *> &layers = QgsProject::instance()->mapLayers();
QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();

for ( ; layerIt != layers.constEnd(); ++layerIt )
mAvailableRasterBands = QgsRasterCalculatorEntry::rasterEntries();
for ( const auto &entry : qgis::as_const( mAvailableRasterBands ) )
{
QgsRasterLayer *rlayer = dynamic_cast<QgsRasterLayer *>( layerIt.value() );
QgsRasterLayer *rlayer = entry.raster;
if ( rlayer && rlayer->dataProvider() && rlayer->dataProvider()->name() == QLatin1String( "gdal" ) )
{
if ( !mExtentSizeSet ) //set bounding box / resolution of output to the values of the first possible input layer
{
setExtentSize( rlayer->width(), rlayer->height(), rlayer->extent() );
mCrsSelector->setCrs( rlayer->crs() );
}
//get number of bands
for ( int i = 0; i < rlayer->bandCount(); ++i )
{
QgsRasterCalculatorEntry entry;
entry.raster = rlayer;
entry.bandNumber = i + 1;
entry.ref = rlayer->name() + '@' + QString::number( i + 1 );
mAvailableRasterBands.push_back( entry );
mRasterBandsListWidget->addItem( entry.ref );
}
QListWidgetItem *item = new QListWidgetItem( entry.ref, mRasterBandsListWidget );
item->setData( Qt::ToolTipRole, rlayer->publicSource() );
mRasterBandsListWidget->addItem( item );
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/app/qgsrastercalcdialog.h
Expand Up @@ -28,6 +28,13 @@ class APP_EXPORT QgsRasterCalcDialog: public QDialog, private Ui::QgsRasterCalcD
{
Q_OBJECT
public:

/**
* Constructor for raster calculator dialog
* \param rasterLayer main raster layer, will be used for default extent and projection
* \param parent widget
* \param f window flags
*/
QgsRasterCalcDialog( QgsRasterLayer *rasterLayer = nullptr, QWidget *parent = nullptr, Qt::WindowFlags f = nullptr );

QString formulaString() const;
Expand All @@ -43,6 +50,10 @@ class APP_EXPORT QgsRasterCalcDialog: public QDialog, private Ui::QgsRasterCalcD
//! Number of pixels in y-direction
int numberOfRows() const;

/**
* Extract raster layer information from the current project
* \return a vector of raster entries from the current project
*/
QVector<QgsRasterCalculatorEntry> rasterEntries() const;

private slots:
Expand Down Expand Up @@ -102,6 +113,8 @@ class APP_EXPORT QgsRasterCalcDialog: public QDialog, private Ui::QgsRasterCalcD
QList<QgsRasterCalculatorEntry> mAvailableRasterBands;

bool mExtentSizeSet = false;

friend class TestQgsRasterCalcDialog;
};

#endif // QGSRASTERCALCDIALOG_H
32 changes: 32 additions & 0 deletions tests/src/analysis/testqgsrastercalculator.cpp
Expand Up @@ -58,6 +58,8 @@ class TestQgsRasterCalculator : public QObject
void toString();
void findNodes();

void testRasterEntries();

private:

QgsRasterLayer *mpLandsatRasterLayer = nullptr;
Expand Down Expand Up @@ -554,6 +556,36 @@ void TestQgsRasterCalculator::findNodes()

}

void TestQgsRasterCalculator::testRasterEntries()
{
// Create some test layers
QList<QgsMapLayer *> layers;
QgsRasterLayer *rlayer = new QgsRasterLayer( QStringLiteral( TEST_DATA_DIR ) + "/analysis/dem.tif", QStringLiteral( "dem" ) );
layers << rlayer;
// Duplicate name, same source
rlayer = new QgsRasterLayer( QStringLiteral( TEST_DATA_DIR ) + "/analysis/dem.tif", QStringLiteral( "dem" ) );
layers << rlayer;
// Duplicated name different source
rlayer = new QgsRasterLayer( QStringLiteral( TEST_DATA_DIR ) + "/analysis/dem_int16.tif", QStringLiteral( "dem" ) );
layers << rlayer;
// Different name and different source
rlayer = new QgsRasterLayer( QStringLiteral( TEST_DATA_DIR ) + "/analysis/slope.tif", QStringLiteral( "slope" ) );
layers << rlayer ;
// Different name and same source
rlayer = new QgsRasterLayer( QStringLiteral( TEST_DATA_DIR ) + "/analysis/slope.tif", QStringLiteral( "slope2" ) );
layers << rlayer ;
QgsProject::instance()->addMapLayers( layers );
QList<QgsRasterCalculatorEntry> availableRasterBands = QgsRasterCalculatorEntry::rasterEntries();
QMap<QString, QgsRasterCalculatorEntry> entryMap;
for ( const auto &rb : qgis::as_const( availableRasterBands ) )
{
entryMap[rb.ref] = rb;
}
QStringList keys( entryMap.keys() );
keys.sort();
QCOMPARE( keys.join( ',' ), QStringLiteral( "dem@1,dem_1@1,landsat@1,landsat_4326@1,slope2@1" ) );
}

void TestQgsRasterCalculator::errors( )
{
QgsRasterCalculatorEntry entry1;
Expand Down

0 comments on commit 18854dc

Please sign in to comment.