bool QgsRasterLayerRenderer::render() { if ( !mRasterViewPort ) return true; // outside of layer extent - nothing to do //R->draw( mPainter, mRasterViewPort, &mMapToPixel ); QTime time; time.start(); // // // The goal here is to make as many decisions as possible early on (outside of the rendering loop) // so that we can maximise performance of the rendering process. So now we check which drawing // procedure to use : // QgsRasterProjector *projector = mPipe->projector(); // TODO add a method to interface to get provider and get provider // params in QgsRasterProjector if ( projector ) { projector->setCRS( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS ); } // Drawer to pipe? QgsRasterIterator iterator( mPipe->last() ); QgsRasterDrawer drawer( &iterator ); drawer.draw( mPainter, mRasterViewPort, mMapToPixel ); QgsDebugMsg( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ) ); return true; }
QByteArray* QgsWCSServer::getCoverage() { QStringList wcsLayersId = mConfigParser->wcsLayers(); QList<QgsMapLayer*> layerList; QStringList mErrors = QStringList(); //defining coverage name QString coveName = ""; //read COVERAGE QMap<QString, QString>::const_iterator cove_name_it = mParameters.find( "COVERAGE" ); if ( cove_name_it != mParameters.end() ) { coveName = cove_name_it.value(); } if ( coveName == "" ) { QMap<QString, QString>::const_iterator cove_name_it = mParameters.find( "IDENTIFIER" ); if ( cove_name_it != mParameters.end() ) { coveName = cove_name_it.value(); } } if ( coveName == "" ) { mErrors << QString( "COVERAGE is mandatory" ); } layerList = mConfigParser->mapLayerFromCoverage( coveName ); if ( layerList.size() < 1 ) { mErrors << QString( "The layer for the COVERAGE '%1' is not found" ).arg( coveName ); } bool conversionSuccess; // BBOX bool bboxOk = false; double minx = 0.0, miny = 0.0, maxx = 0.0, maxy = 0.0; // WIDTh and HEIGHT int width = 0, height = 0; // CRS QString crs = ""; // read BBOX QMap<QString, QString>::const_iterator bbIt = mParameters.find( "BBOX" ); if ( bbIt == mParameters.end() ) { minx = 0; miny = 0; maxx = 0; maxy = 0; } else { bboxOk = true; QString bbString = bbIt.value(); minx = bbString.section( ",", 0, 0 ).toDouble( &conversionSuccess ); if ( !conversionSuccess ) {bboxOk = false;} miny = bbString.section( ",", 1, 1 ).toDouble( &conversionSuccess ); if ( !conversionSuccess ) {bboxOk = false;} maxx = bbString.section( ",", 2, 2 ).toDouble( &conversionSuccess ); if ( !conversionSuccess ) {bboxOk = false;} maxy = bbString.section( ",", 3, 3 ).toDouble( &conversionSuccess ); if ( !conversionSuccess ) {bboxOk = false;} } if ( !bboxOk ) { mErrors << QString( "The BBOX is mandatory and has to be xx.xxx,yy.yyy,xx.xxx,yy.yyy" ); } // read WIDTH width = mParameters.value( "WIDTH", "0" ).toInt( &conversionSuccess ); if ( !conversionSuccess ) width = 0; // read HEIGHT height = mParameters.value( "HEIGHT", "0" ).toInt( &conversionSuccess ); if ( !conversionSuccess ) { height = 0; } if ( width < 0 || height < 0 ) { mErrors << QString( "The WIDTH and HEIGHT are mandatory and have to be integer" ); } crs = mParameters.value( "CRS", "" ); if ( crs == "" ) { mErrors << QString( "The CRS is mandatory" ); } if ( mErrors.count() != 0 ) { throw QgsMapServiceException( "RequestNotWellFormed", mErrors.join( ". " ) ); } QgsCoordinateReferenceSystem requestCRS = QgsCRSCache::instance()->crsByAuthId( crs ); if ( !requestCRS.isValid() ) { mErrors << QString( "Could not create request CRS" ); throw QgsMapServiceException( "RequestNotWellFormed", mErrors.join( ". " ) ); } QgsRectangle rect( minx, miny, maxx, maxy ); QgsMapLayer* layer = layerList.at( 0 ); QgsRasterLayer* rLayer = dynamic_cast<QgsRasterLayer*>( layer ); if ( rLayer && wcsLayersId.contains( rLayer->id() ) ) { // RESPONSE_CRS QgsCoordinateReferenceSystem responseCRS = rLayer->crs(); crs = mParameters.value( "RESPONSE_CRS", "" ); if ( crs != "" ) { responseCRS = QgsCRSCache::instance()->crsByAuthId( crs ); if ( !responseCRS.isValid() ) { responseCRS = rLayer->crs(); } } // transform rect if ( requestCRS != rLayer->crs() ) { QgsCoordinateTransform t( requestCRS, rLayer->crs() ); rect = t.transformBoundingBox( rect ); } QTemporaryFile tempFile; tempFile.open(); QgsRasterFileWriter fileWriter( tempFile.fileName() ); // clone pipe/provider QgsRasterPipe* pipe = new QgsRasterPipe(); if ( !pipe->set( rLayer->dataProvider()->clone() ) ) { mErrors << QString( "Cannot set pipe provider" ); throw QgsMapServiceException( "RequestNotWellFormed", mErrors.join( ". " ) ); } // add projector if necessary if ( responseCRS != rLayer->crs() ) { QgsRasterProjector * projector = new QgsRasterProjector; projector->setCRS( rLayer->crs(), responseCRS ); if ( !pipe->insert( 2, projector ) ) { mErrors << QString( "Cannot set pipe projector" ); throw QgsMapServiceException( "RequestNotWellFormed", mErrors.join( ". " ) ); } } QgsRasterFileWriter::WriterError err = fileWriter.writeRaster( pipe, width, height, rect, responseCRS ); if ( err != QgsRasterFileWriter::NoError ) { mErrors << QString( "Cannot write raster error code: %1" ).arg( err ); throw QgsMapServiceException( "RequestNotWellFormed", mErrors.join( ". " ) ); } delete pipe; QByteArray* ba = 0; ba = new QByteArray(); *ba = tempFile.readAll(); return ba; } return 0; }
int QgsRasterCalculator::processCalculation( QProgressDialog* p ) { //prepare search string / tree QString errorString; QgsRasterCalcNode* calcNode = QgsRasterCalcNode::parseRasterCalcString( mFormulaString, errorString ); if ( !calcNode ) { //error return static_cast<int>( ParserError ); } QMap< QString, QgsRasterBlock* > inputBlocks; QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin(); for ( ; it != mRasterEntries.constEnd(); ++it ) { if ( !it->raster ) // no raster layer in entry { delete calcNode; qDeleteAll( inputBlocks ); return static_cast< int >( InputLayerError ); } QgsRasterBlock* block = nullptr; // if crs transform needed if ( it->raster->crs() != mOutputCrs ) { QgsRasterProjector proj; proj.setCRS( it->raster->crs(), mOutputCrs ); proj.setInput( it->raster->dataProvider() ); proj.setPrecision( QgsRasterProjector::Exact ); block = proj.block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ); } else { block = it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ); } if ( block->isEmpty() ) { delete block; delete calcNode; qDeleteAll( inputBlocks ); return static_cast<int>( MemoryError ); } inputBlocks.insert( it->ref, block ); } //open output dataset for writing GDALDriverH outputDriver = openOutputDriver(); if ( !outputDriver ) { return static_cast< int >( CreateOutputError ); } GDALDatasetH outputDataset = openOutputFile( outputDriver ); GDALSetProjection( outputDataset, mOutputCrs.toWkt().toLocal8Bit().data() ); GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset, 1 ); float outputNodataValue = -FLT_MAX; GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue ); if ( p ) { p->setMaximum( mNumOutputRows ); } QgsRasterMatrix resultMatrix; resultMatrix.setNodataValue( outputNodataValue ); //read / write line by line for ( int i = 0; i < mNumOutputRows; ++i ) { if ( p ) { p->setValue( i ); } if ( p && p->wasCanceled() ) { break; } if ( calcNode->calculate( inputBlocks, resultMatrix, i ) ) { bool resultIsNumber = resultMatrix.isNumber(); float* calcData = new float[mNumOutputColumns]; for ( int j = 0; j < mNumOutputColumns; ++j ) { calcData[j] = ( float )( resultIsNumber ? resultMatrix.number() : resultMatrix.data()[j] ); } //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!" ); } delete[] calcData; } } if ( p ) { p->setValue( mNumOutputRows ); } //close datasets and release memory delete calcNode; qDeleteAll( inputBlocks ); inputBlocks.clear(); if ( p && p->wasCanceled() ) { //delete the dataset without closing (because it is faster) GDALDeleteDataset( outputDriver, TO8F( mOutputFile ) ); return static_cast< int >( Cancelled ); } GDALClose( outputDataset ); return static_cast< int >( Success ); }
QgsRasterCalculator::Result QgsRasterCalculator::processCalculation( QgsFeedback *feedback ) { mLastError.clear(); //prepare search string / tree std::unique_ptr< QgsRasterCalcNode > calcNode( QgsRasterCalcNode::parseRasterCalcString( mFormulaString, mLastError ) ); if ( !calcNode ) { //error return ParserError; } // Check input layers and bands for ( const auto &entry : qgis::as_const( mRasterEntries ) ) { if ( !entry.raster ) // no raster layer in entry { mLastError = QObject::tr( "No raster layer for entry %1" ).arg( entry.ref ); return InputLayerError; } if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() ) { mLastError = QObject::tr( "Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref ); return BandError; } } #ifdef HAVE_OPENCL // Check for matrix nodes, GPU implementation does not support them QList<const QgsRasterCalcNode *> nodeList; if ( QgsOpenClUtils::enabled() && QgsOpenClUtils::available() && calcNode->findNodes( QgsRasterCalcNode::Type::tMatrix ).isEmpty() ) return processCalculationGPU( std::move( calcNode ), feedback ); #endif //open output dataset for writing GDALDriverH outputDriver = openOutputDriver(); if ( !outputDriver ) { mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat ); return CreateOutputError; } gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) ); if ( !outputDataset ) { mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile ); return CreateOutputError; } GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt().toLocal8Bit().data() ); GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 ); float outputNodataValue = -FLT_MAX; GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue ); // Check if we need to read the raster as a whole (which is memory inefficient // and not interruptable by the user) by checking if any raster matrix nodes are // in the expression bool requiresMatrix = ! calcNode->findNodes( QgsRasterCalcNode::Type::tMatrix ).isEmpty(); // Take the fast route (process one line at a time) if we can if ( ! requiresMatrix ) { // Map of raster names -> blocks std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks; std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries; for ( const auto &r : calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) ) { QString layerRef( r->toString().remove( 0, 1 ) ); layerRef.chop( 1 ); if ( ! inputBlocks.count( layerRef ) ) { for ( const auto &ref : mRasterEntries ) { if ( ref.ref == layerRef ) { uniqueRasterEntries[layerRef] = ref; inputBlocks[layerRef ] = qgis::make_unique<QgsRasterBlock>(); } } } } //read / write line by line QMap<QString, QgsRasterBlock * > _rasterData; // Cast to float std::vector<float> castedResult; castedResult.reserve( static_cast<size_t>( mNumOutputColumns ) ); auto rowHeight = mOutputRectangle.height() / mNumOutputRows; for ( size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row ) { if ( feedback ) { feedback->setProgress( 100.0 * static_cast< double >( row ) / mNumOutputRows ); } if ( feedback && feedback->isCanceled() ) { break; } // Calculates the rect for a single row read QgsRectangle rect( mOutputRectangle ); rect.setYMaximum( rect.yMaximum() - rowHeight * row ); rect.setYMinimum( rect.yMaximum() - rowHeight ); // Read rows into input blocks for ( auto &layerRef : inputBlocks ) { QgsRasterCalculatorEntry ref = uniqueRasterEntries[layerRef.first]; if ( uniqueRasterEntries[layerRef.first].raster->crs() != mOutputCrs ) { QgsRasterProjector proj; proj.setCrs( ref.raster->crs(), mOutputCrs ); proj.setInput( ref.raster->dataProvider() ); proj.setPrecision( QgsRasterProjector::Exact ); layerRef.second.reset( proj.block( ref.bandNumber, rect, mNumOutputColumns, 1 ) ); } else { inputBlocks[layerRef.first].reset( ref.raster->dataProvider()->block( ref.bandNumber, rect, mNumOutputColumns, 1 ) ); } } QgsRasterMatrix resultMatrix; resultMatrix.setNodataValue( outputNodataValue ); _rasterData.clear(); for ( const auto &layerRef : inputBlocks ) { _rasterData.insert( layerRef.first, inputBlocks[layerRef.first].get() ); } if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) ) { // write scanline to the dataset for ( size_t i = 0; i < static_cast<size_t>( mNumOutputColumns ); i++ ) { castedResult[i] = static_cast<float>( resultMatrix.data()[i] ); } if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None ) { QgsDebugMsg( QStringLiteral( "RasterIO error!" ) ); } } } if ( feedback ) { feedback->setProgress( 100.0 ); } } else // Original code (memory inefficient route) { QMap< QString, QgsRasterBlock * > inputBlocks; QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin(); for ( ; it != mRasterEntries.constEnd(); ++it ) { std::unique_ptr< QgsRasterBlock > block; // if crs transform needed if ( it->raster->crs() != mOutputCrs ) { QgsRasterProjector proj; proj.setCrs( it->raster->crs(), mOutputCrs ); proj.setInput( it->raster->dataProvider() ); proj.setPrecision( QgsRasterProjector::Exact ); QgsRasterBlockFeedback *rasterBlockFeedback = new QgsRasterBlockFeedback(); QObject::connect( feedback, &QgsFeedback::canceled, rasterBlockFeedback, &QgsRasterBlockFeedback::cancel ); block.reset( proj.block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) ); if ( rasterBlockFeedback->isCanceled() ) { qDeleteAll( inputBlocks ); return Canceled; } } else { block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) ); } if ( block->isEmpty() ) { mLastError = QObject::tr( "Could not allocate required memory for %1" ).arg( it->ref ); qDeleteAll( inputBlocks ); return MemoryError; } inputBlocks.insert( it->ref, block.release() ); } QgsRasterMatrix resultMatrix; resultMatrix.setNodataValue( outputNodataValue ); //read / write line by line for ( int i = 0; i < mNumOutputRows; ++i ) { if ( feedback ) { feedback->setProgress( 100.0 * static_cast< double >( i ) / mNumOutputRows ); } if ( feedback && feedback->isCanceled() ) { break; } if ( calcNode->calculate( inputBlocks, resultMatrix, i ) ) { bool resultIsNumber = resultMatrix.isNumber(); float *calcData = new float[mNumOutputColumns]; for ( int j = 0; j < mNumOutputColumns; ++j ) { calcData[j] = ( float )( resultIsNumber ? resultMatrix.number() : resultMatrix.data()[j] ); } //write scanline to the dataset if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None ) { QgsDebugMsg( QStringLiteral( "RasterIO error!" ) ); } delete[] calcData; } } if ( feedback ) { feedback->setProgress( 100.0 ); } //close datasets and release memory calcNode.reset(); qDeleteAll( inputBlocks ); inputBlocks.clear(); } if ( feedback && feedback->isCanceled() ) { //delete the dataset without closing (because it is faster) gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile ); return Canceled; } return Success; }
QgsRasterCalculator::Result QgsRasterCalculator::processCalculationGPU( std::unique_ptr< QgsRasterCalcNode > calcNode, QgsFeedback *feedback ) { QString cExpression( calcNode->toString( true ) ); QList<const QgsRasterCalcNode *> nodeList( calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) ); QSet<QString> capturedTexts; for ( const auto &r : qgis::as_const( nodeList ) ) { QString s( r->toString().remove( 0, 1 ) ); s.chop( 1 ); capturedTexts.insert( s ); } // Extract all references struct LayerRef { QString name; int band; QgsRasterLayer *layer = nullptr; QString varName; QString typeName; size_t index; size_t bufferSize; size_t dataSize; }; // Collects all layers, band, name, varName and size information std::vector<LayerRef> inputRefs; size_t refCounter = 0; for ( const auto &r : capturedTexts ) { if ( r.startsWith( '"' ) ) continue; QStringList parts( r.split( '@' ) ); if ( parts.count() != 2 ) continue; bool ok = false; LayerRef entry; entry.name = r; entry.band = parts[1].toInt( &ok ); for ( const auto &ref : mRasterEntries ) { if ( ref.ref == entry.name ) entry.layer = ref.raster; } if ( !( entry.layer && entry.layer->dataProvider() && ok ) ) continue; entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band ); switch ( entry.layer->dataProvider()->dataType( entry.band ) ) { case Qgis::DataType::Byte: entry.typeName = QStringLiteral( "unsigned char" ); break; case Qgis::DataType::UInt16: entry.typeName = QStringLiteral( "unsigned int" ); break; case Qgis::DataType::Int16: entry.typeName = QStringLiteral( "short" ); break; case Qgis::DataType::UInt32: entry.typeName = QStringLiteral( "unsigned int" ); break; case Qgis::DataType::Int32: entry.typeName = QStringLiteral( "int" ); break; case Qgis::DataType::Float32: entry.typeName = QStringLiteral( "float" ); break; // FIXME: not sure all OpenCL implementations support double // maybe safer to fall back to the CPU implementation // after a compatibility check case Qgis::DataType::Float64: entry.typeName = QStringLiteral( "double" ); break; default: return BandError; } entry.bufferSize = entry.dataSize * mNumOutputColumns; entry.index = refCounter; entry.varName = QStringLiteral( "input_raster_%1_band_%2" ) .arg( refCounter++ ) .arg( entry.band ); inputRefs.push_back( entry ); } // Prepare context and queue cl::Context ctx( QgsOpenClUtils::context() ); cl::CommandQueue queue( QgsOpenClUtils::commandQueue() ); // Create the C expression std::vector<cl::Buffer> inputBuffers; inputBuffers.reserve( inputRefs.size() ); QStringList inputArgs; for ( const auto &ref : inputRefs ) { cExpression.replace( QStringLiteral( "\"%1\"" ).arg( ref.name ), QStringLiteral( "%1[i]" ).arg( ref.varName ) ); inputArgs.append( QStringLiteral( "__global %1 *%2" ) .arg( ref.typeName ) .arg( ref.varName ) ); inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize, nullptr, nullptr ) ); } //qDebug() << cExpression; // Create the program QString programTemplate( R"CL( // Inputs: ##INPUT_DESC## // Expression: ##EXPRESSION_ORIGINAL## __kernel void rasterCalculator( ##INPUT## __global float *resultLine ) { // Get the index of the current element const int i = get_global_id(0); // Expression resultLine[i] = ##EXPRESSION##; } )CL" ); QStringList inputDesc; for ( const auto &ref : inputRefs ) { inputDesc.append( QStringLiteral( " // %1 = %2" ).arg( ref.varName ).arg( ref.name ) ); } programTemplate = programTemplate.replace( QStringLiteral( "##INPUT_DESC##" ), inputDesc.join( '\n' ) ); programTemplate = programTemplate.replace( QStringLiteral( "##INPUT##" ), inputArgs.length() ? ( inputArgs.join( ',' ).append( ',' ) ) : QChar( ' ' ) ); programTemplate = programTemplate.replace( QStringLiteral( "##EXPRESSION##" ), cExpression ); programTemplate = programTemplate.replace( QStringLiteral( "##EXPRESSION_ORIGINAL##" ), calcNode->toString( ) ); // qDebug() << programTemplate; // Create a program from the kernel source cl::Program program( QgsOpenClUtils::buildProgram( programTemplate, QgsOpenClUtils::ExceptionBehavior::Throw ) ); // Create the buffers, output is float32 (4 bytes) // We assume size of float = 4 because that's the size used by OpenCL and IEEE 754 Q_ASSERT( sizeof( float ) == 4 ); std::size_t resultBufferSize( 4 * static_cast<size_t>( mNumOutputColumns ) ); cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize, nullptr, nullptr ); auto kernel = cl::Kernel( program, "rasterCalculator" ); for ( unsigned int i = 0; i < inputBuffers.size() ; i++ ) { kernel.setArg( i, inputBuffers.at( i ) ); } kernel.setArg( static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer ); QgsOpenClUtils::CPLAllocator<float> resultLine( static_cast<size_t>( mNumOutputColumns ) ); //open output dataset for writing GDALDriverH outputDriver = openOutputDriver(); if ( !outputDriver ) { mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat ); return CreateOutputError; } gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) ); if ( !outputDataset ) { mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile ); return CreateOutputError; } GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt().toLocal8Bit().data() ); GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 ); if ( !outputRasterBand ) return BandError; // Input block (buffer) std::unique_ptr<QgsRasterBlock> block; // Run kernel on all scanlines auto rowHeight = mOutputRectangle.height() / mNumOutputRows; for ( int line = 0; line < mNumOutputRows; line++ ) { if ( feedback && feedback->isCanceled() ) { break; } if ( feedback ) { feedback->setProgress( 100.0 * static_cast< double >( line ) / mNumOutputRows ); } // Read lines from rasters into the buffers for ( const auto &ref : inputRefs ) { // Read one row QgsRectangle rect( mOutputRectangle ); rect.setYMaximum( rect.yMaximum() - rowHeight * line ); rect.setYMinimum( rect.yMaximum() - rowHeight ); // TODO: check if this is too slow // if crs transform needed if ( ref.layer->crs() != mOutputCrs ) { QgsRasterProjector proj; proj.setCrs( ref.layer->crs(), mOutputCrs ); proj.setInput( ref.layer->dataProvider() ); proj.setPrecision( QgsRasterProjector::Exact ); block.reset( proj.block( ref.band, rect, mNumOutputColumns, 1 ) ); } else { block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) ); } //for ( int i = 0; i < mNumOutputColumns; i++ ) // qDebug() << "Input: " << line << i << ref.varName << " = " << block->value( 0, i ); //qDebug() << "Writing buffer " << ref.index; Q_ASSERT( ref.bufferSize == static_cast<size_t>( block->data().size( ) ) ); queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() ); } // Run the kernel queue.enqueueNDRangeKernel( kernel, 0, cl::NDRange( mNumOutputColumns ) ); // Write the result queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() ); //for ( int i = 0; i < mNumOutputColumns; i++ ) // qDebug() << "Output: " << line << i << " = " << resultLine[i]; if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None ) { return CreateOutputError; } } if ( feedback && feedback->isCanceled() ) { //delete the dataset without closing (because it is faster) gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile ); return Canceled; } inputBuffers.clear(); return Success; }
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe* pipe, QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent, const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog ) { QgsDebugMsg( "Entered" ); if ( !iter ) { return SourceProviderError; } const QgsRasterInterface* iface = pipe->last(); if ( !iface ) { return SourceProviderError; } QgsRasterDataProvider* srcProvider = const_cast<QgsRasterDataProvider*>( dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() ) ); if ( !srcProvider ) { QgsDebugMsg( "Cannot get source data provider" ); return SourceProviderError; } iter->setMaximumTileWidth( mMaxTileWidth ); iter->setMaximumTileHeight( mMaxTileHeight ); int nBands = iface->bandCount(); if ( nBands < 1 ) { return SourceProviderError; } //check if all the bands have the same data type size, otherwise we cannot write it to the provider //(at least not with the current interface) int dataTypeSize = QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) ); for ( int i = 2; i <= nBands; ++i ) { if ( QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) ) != dataTypeSize ) { return DestProviderError; } } // Output data type - source data type is preferred but it may happen that we need // to set 'no data' value (which was not set on source data) if output extent // is larger than source extent (with or without reprojection) and there is no 'free' // (not used) value available QList<bool> destHasNoDataValueList; QList<double> destNoDataValueList; QList<QGis::DataType> destDataTypeList; for ( int bandNo = 1; bandNo <= nBands; bandNo++ ) { QgsRasterNuller *nuller = pipe->nuller(); bool srcHasNoDataValue = srcProvider->srcHasNoDataValue( bandNo ); bool destHasNoDataValue = false; double destNoDataValue = std::numeric_limits<double>::quiet_NaN(); QGis::DataType destDataType = srcProvider->srcDataType( bandNo ); // TODO: verify what happens/should happen if srcNoDataValue is disabled by setUseSrcNoDataValue QgsDebugMsg( QString( "srcHasNoDataValue = %1 srcNoDataValue = %2" ).arg( srcHasNoDataValue ).arg( srcProvider->srcNoDataValue( bandNo ) ) ); if ( srcHasNoDataValue ) { // If source has no data value, it is used by provider destNoDataValue = srcProvider->srcNoDataValue( bandNo ); destHasNoDataValue = true; } else if ( nuller && nuller->noData( bandNo ).size() > 0 ) { // Use one user defined no data value destNoDataValue = nuller->noData( bandNo ).value( 0 ).min(); destHasNoDataValue = true; } else { // Verify if we realy need no data value, i.e. QgsRectangle srcExtent = outputExtent; QgsRasterProjector *projector = pipe->projector(); if ( projector && projector->destCrs() != projector->srcCrs() ) { QgsCoordinateTransform ct( projector->destCrs(), projector->srcCrs() ); srcExtent = ct.transformBoundingBox( outputExtent ); } if ( !srcProvider->extent().contains( srcExtent ) ) { // Destination extent is larger than source extent, we need destination no data values // Get src sample statistics (estimation from sample) QgsRasterBandStats stats = srcProvider->bandStatistics( bandNo, QgsRasterBandStats::Min | QgsRasterBandStats::Max, srcExtent, 250000 ); // Test if we have free (not used) values double typeMinValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) ); double typeMaxValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) ); if ( stats.minimumValue > typeMinValue ) { destNoDataValue = typeMinValue; } else if ( stats.maximumValue < typeMaxValue ) { destNoDataValue = typeMaxValue; } else { // We have to use wider type destDataType = QgsRasterBlock::typeWithNoDataValue( destDataType, &destNoDataValue ); } destHasNoDataValue = true; } } if ( nuller && destHasNoDataValue ) { nuller->setOutputNoDataValue( bandNo, destNoDataValue ); } QgsDebugMsg( QString( "bandNo = %1 destDataType = %2 destHasNoDataValue = %3 destNoDataValue = %4" ).arg( bandNo ).arg( destDataType ).arg( destHasNoDataValue ).arg( destNoDataValue ) ); destDataTypeList.append( destDataType ); destHasNoDataValueList.append( destHasNoDataValue ); destNoDataValueList.append( destNoDataValue ); } QGis::DataType destDataType = destDataTypeList.value( 0 ); // Currently write API supports one output type for dataset only -> find the widest for ( int i = 1; i < nBands; i++ ) { if ( destDataTypeList.value( i ) > destDataType ) { destDataType = destDataTypeList.value( i ); // no data value may be left per band (for future) } } //create destProvider for whole dataset here QgsRasterDataProvider* destProvider = 0; double pixelSize; double geoTransform[6]; globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize ); // initOutput() returns 0 in tile mode! destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList ); WriterError error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog ); if ( error == NoDataConflict ) { // The value used for no data was found in source data, we must use wider data type if ( destProvider ) // no tiles { destProvider->remove(); delete destProvider; destProvider = 0; } else // VRT { // TODO: remove created VRT } // But we don't know which band -> wider all for ( int i = 0; i < nBands; i++ ) { double destNoDataValue; QGis::DataType destDataType = QgsRasterBlock::typeWithNoDataValue( destDataTypeList.value( i ), &destNoDataValue ); destDataTypeList.replace( i, destDataType ); destNoDataValueList.replace( i, destNoDataValue ); } destDataType = destDataTypeList.value( 0 ); // Try again destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList ); error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog ); } if ( destProvider ) delete destProvider; return error; }