int QgsRasterCalculator::processCalculation( QProgressDialog* p ) { //prepare search string / tree QString errorString; QgsRasterCalcNode* calcNode = QgsRasterCalcNode::parseRasterCalcString( mFormulaString, errorString ); if ( !calcNode ) { //error } double targetGeoTransform[6]; outputGeoTransform( targetGeoTransform ); //open all input rasters for reading QMap< QString, GDALRasterBandH > mInputRasterBands; //raster references and corresponding scanline data QMap< QString, QgsRasterMatrix* > inputScanLineData; //stores raster references and corresponding scanline data QVector< GDALDatasetH > mInputDatasets; //raster references and corresponding dataset QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin(); for ( ; it != mRasterEntries.constEnd(); ++it ) { if ( !it->raster ) // no raster layer in entry { return 2; } GDALDatasetH inputDataset = GDALOpen( it->raster->source().toLocal8Bit().data(), GA_ReadOnly ); if ( inputDataset == NULL ) { return 2; } //check if the input dataset is south up or rotated. If yes, use GDALAutoCreateWarpedVRT to create a north up raster double inputGeoTransform[6]; if ( GDALGetGeoTransform( inputDataset, inputGeoTransform ) == CE_None && ( inputGeoTransform[1] < 0.0 || inputGeoTransform[2] != 0.0 || inputGeoTransform[4] != 0.0 || inputGeoTransform[5] > 0.0 ) ) { GDALDatasetH vDataset = GDALAutoCreateWarpedVRT( inputDataset, NULL, NULL, GRA_NearestNeighbour, 0.2, NULL ); mInputDatasets.push_back( vDataset ); mInputDatasets.push_back( inputDataset ); inputDataset = vDataset; } else { mInputDatasets.push_back( inputDataset ); } GDALRasterBandH inputRasterBand = GDALGetRasterBand( inputDataset, it->bandNumber ); if ( inputRasterBand == NULL ) { return 2; } int nodataSuccess; double nodataValue = GDALGetRasterNoDataValue( inputRasterBand, &nodataSuccess ); mInputRasterBands.insert( it->ref, inputRasterBand ); inputScanLineData.insert( it->ref, new QgsRasterMatrix( mNumOutputColumns, 1, new float[mNumOutputColumns], nodataValue ) ); } //open output dataset for writing GDALDriverH outputDriver = openOutputDriver(); if ( outputDriver == NULL ) { return 1; } GDALDatasetH outputDataset = openOutputFile( outputDriver ); GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset, 1 ); float outputNodataValue = -FLT_MAX; GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue ); float* resultScanLine = ( float * ) CPLMalloc( sizeof( float ) * mNumOutputColumns ); if ( p ) { p->setMaximum( mNumOutputRows ); } QgsRasterMatrix resultMatrix; //read / write line by line for ( int i = 0; i < mNumOutputRows; ++i ) { if ( p ) { p->setValue( i ); } if ( p && p->wasCanceled() ) { break; } //fill buffers QMap< QString, QgsRasterMatrix* >::iterator bufferIt = inputScanLineData.begin(); for ( ; bufferIt != inputScanLineData.end(); ++bufferIt ) { double sourceTransformation[6]; GDALRasterBandH sourceRasterBand = mInputRasterBands[bufferIt.key()]; GDALGetGeoTransform( GDALGetBandDataset( sourceRasterBand ), sourceTransformation ); //the function readRasterPart calls GDALRasterIO (and ev. does some conversion if raster transformations are not the same) readRasterPart( targetGeoTransform, 0, i, mNumOutputColumns, 1, sourceTransformation, sourceRasterBand, bufferIt.value()->data() ); } if ( calcNode->calculate( inputScanLineData, resultMatrix ) ) { bool resultIsNumber = resultMatrix.isNumber(); float* calcData; if ( resultIsNumber ) //scalar result. Insert number for every pixel { calcData = new float[mNumOutputColumns]; for ( int j = 0; j < mNumOutputColumns; ++j ) { calcData[j] = resultMatrix.number(); } } else //result is real matrix { calcData = resultMatrix.data(); } //replace all matrix nodata values with output nodatas for ( int j = 0; j < mNumOutputColumns; ++j ) { if ( calcData[j] == resultMatrix.nodataValue() ) { calcData[j] = outputNodataValue; } } //write scanline to the dataset if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None ) { qWarning( "RasterIO error!" ); } if ( resultIsNumber ) { delete[] calcData; } } } if ( p ) { p->setValue( mNumOutputRows ); } //close datasets and release memory delete calcNode; QMap< QString, QgsRasterMatrix* >::iterator bufferIt = inputScanLineData.begin(); for ( ; bufferIt != inputScanLineData.end(); ++bufferIt ) { delete bufferIt.value(); } inputScanLineData.clear(); QVector< GDALDatasetH >::iterator datasetIt = mInputDatasets.begin(); for ( ; datasetIt != mInputDatasets.end(); ++ datasetIt ) { GDALClose( *datasetIt ); } if ( p && p->wasCanceled() ) { //delete the dataset without closing (because it is faster) GDALDeleteDataset( outputDriver, mOutputFile.toLocal8Bit().data() ); return 3; } GDALClose( outputDataset ); CPLFree( resultScanLine ); return 0; }
bool QgsRasterCalcNode::calculate( QMap<QString, QgsRasterBlock* >& rasterData, QgsRasterMatrix& result, int row ) const { //if type is raster ref: return a copy of the corresponding matrix //if type is operator, call the proper matrix operations if ( mType == tRasterRef ) { QMap<QString, QgsRasterBlock*>::iterator it = rasterData.find( mRasterName ); if ( it == rasterData.end() ) { return false; } int nRows = ( row >= 0 ? 1 : ( *it )->height() ); int startRow = ( row >= 0 ? row : 0 ); int endRow = startRow + nRows; int nCols = ( *it )->width(); int nEntries = nCols * nRows; double* data = new double[nEntries]; //convert input raster values to double, also convert input no data to result no data int outRow = 0; for ( int dataRow = startRow; dataRow < endRow ; ++dataRow, ++outRow ) { for ( int dataCol = 0; dataCol < nCols; ++dataCol ) { data[ dataCol + nCols * outRow] = ( *it )->isNoData( dataRow , dataCol ) ? result.nodataValue() : ( *it )->value( dataRow, dataCol ); } } result.setData( nCols, nRows, data, result.nodataValue() ); return true; } else if ( mType == tOperator ) { QgsRasterMatrix leftMatrix, rightMatrix; leftMatrix.setNodataValue( result.nodataValue() ); rightMatrix.setNodataValue( result.nodataValue() ); if ( !mLeft || !mLeft->calculate( rasterData, leftMatrix, row ) ) { return false; } if ( mRight && !mRight->calculate( rasterData, rightMatrix, row ) ) { return false; } switch ( mOperator ) { case opPLUS: leftMatrix.add( rightMatrix ); break; case opMINUS: leftMatrix.subtract( rightMatrix ); break; case opMUL: leftMatrix.multiply( rightMatrix ); break; case opDIV: leftMatrix.divide( rightMatrix ); break; case opPOW: leftMatrix.power( rightMatrix ); break; case opEQ: leftMatrix.equal( rightMatrix ); break; case opNE: leftMatrix.notEqual( rightMatrix ); break; case opGT: leftMatrix.greaterThan( rightMatrix ); break; case opLT: leftMatrix.lesserThan( rightMatrix ); break; case opGE: leftMatrix.greaterEqual( rightMatrix ); break; case opLE: leftMatrix.lesserEqual( rightMatrix ); break; case opAND: leftMatrix.logicalAnd( rightMatrix ); break; case opOR: leftMatrix.logicalOr( rightMatrix ); break; case opSQRT: leftMatrix.squareRoot(); break; case opSIN: leftMatrix.sinus(); break; case opCOS: leftMatrix.cosinus(); break; case opTAN: leftMatrix.tangens(); break; case opASIN: leftMatrix.asinus(); break; case opACOS: leftMatrix.acosinus(); break; case opATAN: leftMatrix.atangens(); break; case opSIGN: leftMatrix.changeSign(); break; case opLOG: leftMatrix.log(); break; case opLOG10: leftMatrix.log10(); break; default: return false; } int newNColumns = leftMatrix.nColumns(); int newNRows = leftMatrix.nRows(); result.setData( newNColumns, newNRows, leftMatrix.takeData(), leftMatrix.nodataValue() ); return true; } else if ( mType == tNumber ) { double* data = new double[1]; data[0] = mNumber; result.setData( 1, 1, data, result.nodataValue() ); return true; } else if ( mType == tMatrix ) { int nEntries = mMatrix->nColumns() * mMatrix->nRows(); double* data = new double[nEntries]; for ( int i = 0; i < nEntries; ++i ) { data[i] = mMatrix->data()[i] == mMatrix->nodataValue() ? result.nodataValue() : mMatrix->data()[i]; } result.setData( mMatrix->nColumns(), mMatrix->nRows(), data, result.nodataValue() ); return true; } return false; }