void QgsGrassRasterProvider::readBlock( int bandNo, int xBlock, int yBlock, void *block ) { Q_UNUSED( xBlock ); clearLastError(); // TODO: optimize, see extent() QgsDebugMsg( "yBlock = " + QString::number( yBlock ) ); QStringList arguments; arguments.append( "map=" + mMapName + "@" + mMapset ); QgsRectangle ext = extent(); // TODO: cut the last block double cellHeight = ext.height() / mRows; double yMaximum = ext.yMaximum() - cellHeight * yBlock * mYBlockSize; double yMinimum = yMaximum - cellHeight * mYBlockSize; QgsDebugMsg( "mYBlockSize = " + QString::number( mYBlockSize ) ); arguments.append(( QString( "window=%1,%2,%3,%4,%5,%6" ) .arg( QgsRasterBlock::printValue( ext.xMinimum() ), QgsRasterBlock::printValue( yMinimum ), QgsRasterBlock::printValue( ext.xMaximum() ), QgsRasterBlock::printValue( yMaximum ) ) .arg( mCols ).arg( mYBlockSize ) ) ); arguments.append( "format=value" ); QString cmd = QgsApplication::libexecPath() + "grass/modules/qgis.d.rast"; QByteArray data; try { data = QgsGrass::runModule( mGisdbase, mLocation, mMapset, cmd, arguments ); } catch ( QgsGrass::Exception &e ) { QString error = tr( "Cannot read raster" ) + " : " + e.what(); QgsDebugMsg( error ); appendError( error ); // We don't set mValid to false, because the raster can be recreated and work next time } QgsDebugMsg( QString( "%1 bytes read from modules stdout" ).arg( data.size() ) ); // byteCount() in Qt >= 4.6 //int size = image->byteCount() < data.size() ? image->byteCount() : data.size(); int size = mCols * mYBlockSize * dataTypeSize( bandNo ); QgsDebugMsg( QString( "mCols = %1 mYBlockSize = %2 dataTypeSize = %3" ).arg( mCols ).arg( mYBlockSize ).arg( dataTypeSize( bandNo ) ) ); if ( size != data.size() ) { QString error = tr( "%1 bytes expected but %2 byte were read from qgis.d.rast" ).arg( size ).arg( data.size() ); QgsDebugMsg( error ); appendError( error ); size = size < data.size() ? size : data.size(); } memcpy( block, data.data(), size ); }
QByteArray QgsRasterDataProvider::noValueBytes( int theBandNo ) { int type = dataType( theBandNo ); int size = dataTypeSize( theBandNo ) / 8; QByteArray ba; ba.resize( size ); char * data = ba.data(); double noval = mNoDataValue[theBandNo-1]; unsigned char uc; unsigned short us; short s; unsigned int ui; int i; float f; double d; switch ( type ) { case Byte: uc = ( unsigned char )noval; memcpy( data, &uc, size ); break; case UInt16: us = ( unsigned short )noval; memcpy( data, &us, size ); break; case Int16: s = ( short )noval; memcpy( data, &s, size ); break; case UInt32: ui = ( unsigned int )noval; memcpy( data, &ui, size ); break; case Int32: i = ( int )noval; memcpy( data, &i, size ); break; case Float32: f = ( float )noval; memcpy( data, &f, size ); break; case Float64: d = ( double )noval; memcpy( data, &d, size ); break; default: QgsLogger::warning( "GDAL data type is not supported" ); } return ba; }
void QgsGrassRasterProvider::readBlock( int bandNo, QgsRectangle const &viewExtent, int pixelWidth, int pixelHeight, void *block, QgsRasterBlockFeedback *feedback ) { Q_UNUSED( feedback ); QgsDebugMsg( "pixelWidth = " + QString::number( pixelWidth ) ); QgsDebugMsg( "pixelHeight = " + QString::number( pixelHeight ) ); QgsDebugMsg( "viewExtent: " + viewExtent.toString() ); clearLastError(); if ( pixelWidth <= 0 || pixelHeight <= 0 ) return; QStringList arguments; arguments.append( "map=" + mMapName + "@" + mMapset ); arguments.append( ( QStringLiteral( "window=%1,%2,%3,%4,%5,%6" ) .arg( QgsRasterBlock::printValue( viewExtent.xMinimum() ), QgsRasterBlock::printValue( viewExtent.yMinimum() ), QgsRasterBlock::printValue( viewExtent.xMaximum() ), QgsRasterBlock::printValue( viewExtent.yMaximum() ) ) .arg( pixelWidth ).arg( pixelHeight ) ) ); arguments.append( QStringLiteral( "format=value" ) ); QString cmd = QgsApplication::libexecPath() + "grass/modules/qgis.d.rast"; QByteArray data; try { data = QgsGrass::runModule( mGisdbase, mLocation, mMapset, cmd, arguments ); } catch ( QgsGrass::Exception &e ) { QString error = tr( "Cannot read raster" ) + " : " + e.what(); QgsDebugMsg( error ); appendError( error ); // We don't set mValid to false, because the raster can be recreated and work next time return; } QgsDebugMsg( QString( "%1 bytes read from modules stdout" ).arg( data.size() ) ); // byteCount() in Qt >= 4.6 //int size = image->byteCount() < data.size() ? image->byteCount() : data.size(); int size = pixelWidth * pixelHeight * dataTypeSize( bandNo ); if ( size != data.size() ) { QString error = tr( "%1 bytes expected but %2 byte were read from qgis.d.rast" ).arg( size ).arg( data.size() ); QgsDebugMsg( error ); appendError( error ); size = size < data.size() ? size : data.size(); } memcpy( block, data.data(), size ); }
BufferLayout::BufferLayout( int numAttribs, ... ) : m_stride( 0 ) { va_list args; va_start( args, numAttribs ); for( int i = 0; i < numAttribs; ++i ) { DataType type = va_arg( args, DataType ); m_layout.push_back( type ); } va_end( args ); for( size_t i = 0; i < m_layout.size(); ++i ) { m_stride += dataTypeSize( m_layout[i] ); } }
QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback ) { QgsDebugMsgLevel( QString( "bandNo = %1 width = %2 height = %3" ).arg( bandNo ).arg( width ).arg( height ), 4 ); QgsDebugMsgLevel( QString( "boundingBox = %1" ).arg( boundingBox.toString() ), 4 ); QgsRasterBlock *block = new QgsRasterBlock( dataType( bandNo ), width, height ); if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) ) { block->setNoDataValue( sourceNoDataValue( bandNo ) ); } if ( block->isEmpty() ) { QgsDebugMsg( "Couldn't create raster block" ); return block; } // Read necessary extent only QgsRectangle tmpExtent = extent().intersect( &boundingBox ); if ( tmpExtent.isEmpty() ) { QgsDebugMsg( "Extent outside provider extent" ); block->setIsNoData(); return block; } double xRes = boundingBox.width() / width; double yRes = boundingBox.height() / height; double tmpXRes, tmpYRes; double providerXRes = 0; double providerYRes = 0; if ( capabilities() & Size ) { providerXRes = extent().width() / xSize(); providerYRes = extent().height() / ySize(); tmpXRes = std::max( providerXRes, xRes ); tmpYRes = std::max( providerYRes, yRes ); if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes; if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes; } else { tmpXRes = xRes; tmpYRes = yRes; } if ( tmpExtent != boundingBox || tmpXRes > xRes || tmpYRes > yRes ) { // Read smaller extent or lower resolution if ( !extent().contains( boundingBox ) ) { QRect subRect = QgsRasterBlock::subRect( boundingBox, width, height, extent() ); block->setIsNoDataExcept( subRect ); } // Calculate row/col limits (before tmpExtent is aligned) int fromRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMaximum() ) / yRes ); int toRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; int fromCol = std::round( ( tmpExtent.xMinimum() - boundingBox.xMinimum() ) / xRes ); int toCol = std::round( ( tmpExtent.xMaximum() - boundingBox.xMinimum() ) / xRes ) - 1; QgsDebugMsgLevel( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ), 4 ); if ( fromRow < 0 || fromRow >= height || toRow < 0 || toRow >= height || fromCol < 0 || fromCol >= width || toCol < 0 || toCol >= width ) { // Should not happen QgsDebugMsg( "Row or column limits out of range" ); return block; } // If lower source resolution is used, the extent must beS aligned to original // resolution to avoid possible shift due to resampling if ( tmpXRes > xRes ) { int col = std::floor( ( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); col = std::ceil( ( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); } if ( tmpYRes > yRes ) { int row = std::floor( ( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); row = std::ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); } int tmpWidth = std::round( tmpExtent.width() / tmpXRes ); int tmpHeight = std::round( tmpExtent.height() / tmpYRes ); tmpXRes = tmpExtent.width() / tmpWidth; tmpYRes = tmpExtent.height() / tmpHeight; QgsDebugMsgLevel( QString( "Reading smaller block tmpWidth = %1 height = %2" ).arg( tmpWidth ).arg( tmpHeight ), 4 ); QgsDebugMsgLevel( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ), 4 ); QgsRasterBlock *tmpBlock = new QgsRasterBlock( dataType( bandNo ), tmpWidth, tmpHeight ); if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) ) { tmpBlock->setNoDataValue( sourceNoDataValue( bandNo ) ); } readBlock( bandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits(), feedback ); int pixelSize = dataTypeSize( bandNo ); double xMin = boundingBox.xMinimum(); double yMax = boundingBox.yMaximum(); double tmpXMin = tmpExtent.xMinimum(); double tmpYMax = tmpExtent.yMaximum(); for ( int row = fromRow; row <= toRow; row++ ) { double y = yMax - ( row + 0.5 ) * yRes; int tmpRow = std::floor( ( tmpYMax - y ) / tmpYRes ); for ( int col = fromCol; col <= toCol; col++ ) { double x = xMin + ( col + 0.5 ) * xRes; int tmpCol = std::floor( ( x - tmpXMin ) / tmpXRes ); if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth ) { QgsDebugMsg( "Source row or column limits out of range" ); block->setIsNoData(); // so that the problem becomes obvious and fixed delete tmpBlock; return block; } qgssize tmpIndex = static_cast< qgssize >( tmpRow ) * static_cast< qgssize >( tmpWidth ) + tmpCol; qgssize index = row * static_cast< qgssize >( width ) + col; char *tmpBits = tmpBlock->bits( tmpIndex ); char *bits = block->bits( index ); if ( !tmpBits ) { QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) ); continue; } if ( !bits ) { QgsDebugMsg( "Cannot set output block data." ); continue; } memcpy( bits, tmpBits, pixelSize ); } } delete tmpBlock; } else { readBlock( bandNo, boundingBox, width, height, block->bits(), feedback ); } // apply scale and offset block->applyScaleOffset( bandScale( bandNo ), bandOffset( bandNo ) ); // apply user no data values block->applyNoDataValues( userNoDataValues( bandNo ) ); return block; }
QgsGrassRasterProvider::QgsGrassRasterProvider( QString const &uri ) : QgsRasterDataProvider( uri ) , mNoDataValue( std::numeric_limits<double>::quiet_NaN() ) { QgsDebugMsg( "QgsGrassRasterProvider: constructing with uri '" + uri + "'." ); if ( !QgsGrass::init() ) { return; } // Parse URI, it is the same like using GDAL, i.e. path to raster cellhd, i.e. // /path/to/gisdbase/location/mapset/cellhd/map QFileInfo fileInfo( uri ); if ( !fileInfo.exists() ) // then we keep it valid forever { appendError( ERR( tr( "cellhd file %1 does not exist" ).arg( uri ) ) ); return; } mMapName = fileInfo.fileName(); QDir dir = fileInfo.dir(); QString element = dir.dirName(); if ( element != QLatin1String( "cellhd" ) ) { appendError( ERR( tr( "Groups not yet supported" ) ) ); return; } dir.cdUp(); // skip cellhd mMapset = dir.dirName(); dir.cdUp(); mLocation = dir.dirName(); dir.cdUp(); mGisdbase = dir.path(); QgsDebugMsg( QString( "gisdbase: %1" ).arg( mGisdbase ) ); QgsDebugMsg( QString( "location: %1" ).arg( mLocation ) ); QgsDebugMsg( QString( "mapset: %1" ).arg( mMapset ) ); QgsDebugMsg( QString( "mapName: %1" ).arg( mMapName ) ); mTimestamp = dataTimestamp(); mRasterValue.set( mGisdbase, mLocation, mMapset, mMapName ); //mValidNoDataValue = true; QString error; mCrs = QgsGrass::crs( mGisdbase, mLocation, error ); appendIfError( error ); QgsDebugMsg( "mCrs: " + mCrs.toWkt() ); // the block size can change of course when the raster is overridden // ibut it is only called once when statistics are calculated error.clear(); QgsGrass::size( mGisdbase, mLocation, mMapset, mMapName, &mCols, &mRows, error ); appendIfError( error ); error.clear(); mInfo = QgsGrass::info( mGisdbase, mLocation, mMapset, mMapName, QgsGrassObject::Raster, QStringLiteral( "info" ), QgsRectangle(), 0, 0, 3000, error ); appendIfError( error ); mGrassDataType = mInfo[QStringLiteral( "TYPE" )].toInt(); QgsDebugMsg( "mGrassDataType = " + QString::number( mGrassDataType ) ); // TODO: avoid showing these strange numbers in GUI // TODO: don't save no data values in project file, add a flag if value was defined by user double myInternalNoDataValue; if ( mGrassDataType == CELL_TYPE ) { myInternalNoDataValue = std::numeric_limits<int>::min(); } else if ( mGrassDataType == DCELL_TYPE ) { // Don't use numeric limits, raster layer is using // std::fabs( myValue - mNoDataValue ) <= TINY_VALUE // if the mNoDataValue would be a limit, the subtraction could overflow. // No data value is shown in GUI, use some nice number. // Choose values with small representation error. // limit: 1.7976931348623157e+308 //myInternalNoDataValue = -1e+300; myInternalNoDataValue = std::numeric_limits<double>::quiet_NaN(); } else { if ( mGrassDataType != FCELL_TYPE ) { QgsDebugMsg( "unexpected data type" ); } // limit: 3.40282347e+38 //myInternalNoDataValue = -1e+30; myInternalNoDataValue = std::numeric_limits<float>::quiet_NaN(); } mNoDataValue = myInternalNoDataValue; mSrcHasNoDataValue.append( true ); mSrcNoDataValue.append( mNoDataValue ); mUseSrcNoDataValue.append( true ); QgsDebugMsg( QString( "myInternalNoDataValue = %1" ).arg( myInternalNoDataValue ) ); // TODO: refresh mRows and mCols if raster was rewritten // We have to decide some reasonable block size, not to big to occupate too much // memory, not too small to result in too many calls to readBlock -> qgis.d.rast // for statistics int typeSize = dataTypeSize( dataType( 1 ) ); if ( mCols > 0 && typeSize > 0 ) { const int cache_size = 10000000; // ~ 10 MB mYBlockSize = cache_size / typeSize / mCols; if ( mYBlockSize > mRows ) { mYBlockSize = mRows; } QgsDebugMsg( "mYBlockSize = " + QString::number( mYBlockSize ) ); mValid = true; } }
int BufferLayout::attributeSize( size_t i ) const { assertion( i < m_layout.size(), "Layout index: %d is out of bounds", i ); return dataTypeSize( m_layout[i] ); }
void BufferLayout::pushAttribute( DataType dt ) { m_layout.push_back( dt ); m_stride += dataTypeSize( dt ); }
void QgsRasterDataProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, QgsCoordinateReferenceSystem theSrcCRS, QgsCoordinateReferenceSystem theDestCRS, void *data ) { QgsDebugMsg( "Entered" ); QgsDebugMsg( "viewExtent = " + viewExtent.toString() ); if ( ! theSrcCRS.isValid() || ! theDestCRS.isValid() || theSrcCRS == theDestCRS ) { readBlock( bandNo, viewExtent, width, height, data ); return; } QTime time; time.start(); double mMaxSrcXRes = 0; double mMaxSrcYRes = 0; if ( capabilities() & QgsRasterDataProvider::ExactResolution ) { mMaxSrcXRes = extent().width() / xSize(); mMaxSrcYRes = extent().height() / ySize(); } QgsRasterProjector myProjector( theSrcCRS, theDestCRS, viewExtent, height, width, mMaxSrcXRes, mMaxSrcYRes, extent() ); QgsDebugMsg( QString( "create projector time (ms): %1" ).arg( time.elapsed() ) ); // TODO: init data by nulls // If we zoom out too much, projector srcRows / srcCols maybe 0, which can cause problems in providers if ( myProjector.srcRows() <= 0 || myProjector.srcCols() <= 0 ) return; // Allocate memory for not projected source data int mySize = dataTypeSize( bandNo ) / 8; void *mySrcData = malloc( mySize * myProjector.srcRows() * myProjector.srcCols() ); time.restart(); readBlock( bandNo, myProjector.srcExtent(), myProjector.srcCols(), myProjector.srcRows(), mySrcData ); QgsDebugMsg( QString( "read not projected block time (ms): %1" ).arg( time.elapsed() ) ); time.restart(); // Project data from source int mySrcRow; int mySrcCol; int mySrcOffset; int myDestOffset; for ( int r = 0; r < height; r++ ) { for ( int c = 0; c < width; c++ ) { myProjector.srcRowCol( r, c, &mySrcRow, &mySrcCol ); mySrcOffset = mySize * ( mySrcRow * myProjector.srcCols() + mySrcCol ); myDestOffset = mySize * ( r * width + c ); // retype to char is just to avoid g++ warning memcpy(( char* ) data + myDestOffset, ( char* )mySrcData + mySrcOffset, mySize ); } } QgsDebugMsg( QString( "reproject block time (ms): %1" ).arg( time.elapsed() ) ); free( mySrcData ); }
QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo ) { double myNoDataValue = noDataValue(); QgsRasterBandStats myRasterBandStats; myRasterBandStats.elementCount = 0; // because we'll be counting only VALID pixels later myRasterBandStats.bandName = generateBandName( theBandNo ); myRasterBandStats.bandNumber = theBandNo; int myDataType = dataType( theBandNo ); int myNXBlocks, myNYBlocks, myXBlockSize, myYBlockSize; myXBlockSize = xBlockSize(); myYBlockSize = yBlockSize(); myNXBlocks = ( xSize() + myXBlockSize - 1 ) / myXBlockSize; myNYBlocks = ( ySize() + myYBlockSize - 1 ) / myYBlockSize; void *myData = CPLMalloc( myXBlockSize * myYBlockSize * ( dataTypeSize( theBandNo ) / 8 ) ); // unfortunately we need to make two passes through the data to calculate stddev bool myFirstIterationFlag = true; int myBandXSize = xSize(); int myBandYSize = ySize(); for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ ) { for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ ) { int nXValid, nYValid; readBlock( theBandNo, iXBlock, iYBlock, myData ); // Compute the portion of the block that is valid // for partial edge blocks. if (( iXBlock + 1 ) * myXBlockSize > myBandXSize ) nXValid = myBandXSize - iXBlock * myXBlockSize; else nXValid = myXBlockSize; if (( iYBlock + 1 ) * myYBlockSize > myBandYSize ) nYValid = myBandYSize - iYBlock * myYBlockSize; else nYValid = myYBlockSize; // Collect the histogram counts. for ( int iY = 0; iY < nYValid; iY++ ) { for ( int iX = 0; iX < nXValid; iX++ ) { double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) ); //QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (iX).arg(iY).arg( myValue ) ); if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) ) { continue; // NULL } myRasterBandStats.sum += myValue; ++myRasterBandStats.elementCount; //only use this element if we have a non null element if ( myFirstIterationFlag ) { //this is the first iteration so initialise vars myFirstIterationFlag = false; myRasterBandStats.minimumValue = myValue; myRasterBandStats.maximumValue = myValue; } //end of true part for first iteration check else { //this is done for all subsequent iterations if ( myValue < myRasterBandStats.minimumValue ) { myRasterBandStats.minimumValue = myValue; } if ( myValue > myRasterBandStats.maximumValue ) { myRasterBandStats.maximumValue = myValue; } } //end of false part for first iteration check } } } //end of column wise loop } //end of row wise loop //end of first pass through data now calculate the range myRasterBandStats.range = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue; //calculate the mean myRasterBandStats.mean = myRasterBandStats.sum / myRasterBandStats.elementCount; //for the second pass we will get the sum of the squares / mean for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ ) { for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ ) { int nXValid, nYValid; readBlock( theBandNo, iXBlock, iYBlock, myData ); // Compute the portion of the block that is valid // for partial edge blocks. if (( iXBlock + 1 ) * myXBlockSize > myBandXSize ) nXValid = myBandXSize - iXBlock * myXBlockSize; else nXValid = myXBlockSize; if (( iYBlock + 1 ) * myYBlockSize > myBandYSize ) nYValid = myBandYSize - iYBlock * myYBlockSize; else nYValid = myYBlockSize; // Collect the histogram counts. for ( int iY = 0; iY < nYValid; iY++ ) { for ( int iX = 0; iX < nXValid; iX++ ) { double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) ); //QgsDebugMsg ( "myValue = " + QString::number(myValue) ); if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) ) { continue; // NULL } myRasterBandStats.sumOfSquares += static_cast < double > ( pow( myValue - myRasterBandStats.mean, 2 ) ); } } } //end of column wise loop } //end of row wise loop //divide result by sample size - 1 and get square root to get stdev myRasterBandStats.stdDev = static_cast < double >( sqrt( myRasterBandStats.sumOfSquares / ( myRasterBandStats.elementCount - 1 ) ) ); #ifdef QGISDEBUG QgsLogger::debug( "************ STATS **************", 1, __FILE__, __FUNCTION__, __LINE__ ); QgsLogger::debug( "VALID NODATA", mValidNoDataValue, 1, __FILE__, __FUNCTION__, __LINE__ ); QgsLogger::debug( "NULL", noDataValue() , 1, __FILE__, __FUNCTION__, __LINE__ ); QgsLogger::debug( "MIN", myRasterBandStats.minimumValue, 1, __FILE__, __FUNCTION__, __LINE__ ); QgsLogger::debug( "MAX", myRasterBandStats.maximumValue, 1, __FILE__, __FUNCTION__, __LINE__ ); QgsLogger::debug( "RANGE", myRasterBandStats.range, 1, __FILE__, __FUNCTION__, __LINE__ ); QgsLogger::debug( "MEAN", myRasterBandStats.mean, 1, __FILE__, __FUNCTION__, __LINE__ ); QgsLogger::debug( "STDDEV", myRasterBandStats.stdDev, 1, __FILE__, __FUNCTION__, __LINE__ ); #endif CPLFree( myData ); myRasterBandStats.statsGathered = true; return myRasterBandStats; }
QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle const & theExtent, int theWidth, int theHeight ) { QgsDebugMsg( QString( "theBandNo = %1 theWidth = %2 theHeight = %3" ).arg( theBandNo ).arg( theWidth ).arg( theHeight ) ); QgsDebugMsg( QString( "theExtent = %1" ).arg( theExtent.toString() ) ); QgsRasterBlock *block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight, noDataValue( theBandNo ) ); if ( block->isEmpty() ) { QgsDebugMsg( "Couldn't create raster block" ); return block; } // Read necessary extent only QgsRectangle tmpExtent = extent().intersect( &theExtent ); if ( tmpExtent.isEmpty() ) { QgsDebugMsg( "Extent outside provider extent" ); block->setIsNoData(); return block; } double xRes = theExtent.width() / theWidth; double yRes = theExtent.height() / theHeight; double tmpXRes, tmpYRes; double providerXRes = 0; double providerYRes = 0; if ( capabilities() & ExactResolution ) { providerXRes = extent().width() / xSize(); providerYRes = extent().height() / ySize(); tmpXRes = qMax( providerXRes, xRes ); tmpYRes = qMax( providerYRes, yRes ); if ( doubleNear( tmpXRes, xRes ) ) tmpXRes = xRes; if ( doubleNear( tmpYRes, yRes ) ) tmpYRes = yRes; } else { tmpXRes = xRes; tmpYRes = yRes; } if ( tmpExtent != theExtent || tmpXRes > xRes || tmpYRes > yRes ) { // Read smaller extent or lower resolution // Calculate row/col limits (before tmpExtent is aligned) int fromRow = qRound(( theExtent.yMaximum() - tmpExtent.yMaximum() ) / yRes ); int toRow = qRound(( theExtent.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; int fromCol = qRound(( tmpExtent.xMinimum() - theExtent.xMinimum() ) / xRes ) ; int toCol = qRound(( tmpExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1; QgsDebugMsg( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ) ); if ( fromRow < 0 || fromRow >= theHeight || toRow < 0 || toRow >= theHeight || fromCol < 0 || fromCol >= theWidth || toCol < 0 || toCol >= theWidth ) { // Should not happen QgsDebugMsg( "Row or column limits out of range" ); return block; } // If lower source resolution is used, the extent must beS aligned to original // resolution to avoid possible shift due to resampling if ( tmpXRes > xRes ) { int col = floor(( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); col = ceil(( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); } if ( tmpYRes > yRes ) { int row = floor(( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); row = ceil(( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); } int tmpWidth = qRound( tmpExtent.width() / tmpXRes ); int tmpHeight = qRound( tmpExtent.height() / tmpYRes ); tmpXRes = tmpExtent.width() / tmpWidth; tmpYRes = tmpExtent.height() / tmpHeight; QgsDebugMsg( QString( "Reading smaller block tmpWidth = %1 theHeight = %2" ).arg( tmpWidth ).arg( tmpHeight ) ); QgsDebugMsg( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ) ); block->setIsNoData(); QgsRasterBlock *tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight, noDataValue( theBandNo ) ); readBlock( theBandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->data() ); int pixelSize = dataTypeSize( theBandNo ); double xMin = theExtent.xMinimum(); double yMax = theExtent.yMaximum(); double tmpXMin = tmpExtent.xMinimum(); double tmpYMax = tmpExtent.yMaximum(); for ( int row = fromRow; row <= toRow; row++ ) { double y = yMax - ( row + 0.5 ) * yRes; int tmpRow = floor(( tmpYMax - y ) / tmpYRes ); for ( int col = fromCol; col <= toCol; col++ ) { double x = xMin + ( col + 0.5 ) * xRes; int tmpCol = floor(( x - tmpXMin ) / tmpXRes ); if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth ) { QgsDebugMsg( "Source row or column limits out of range" ); block->setIsNoData(); // so that the problem becomes obvious and fixed delete tmpBlock; return block; } size_t tmpIndex = tmpRow * tmpWidth + tmpCol; size_t index = row * theWidth + col; char *tmpBits = tmpBlock->bits( tmpIndex ); char *bits = block->bits( index ); if ( !tmpBits ) { QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) ); continue; } if ( !bits ) { QgsDebugMsg( "Cannot set output block data." ); continue; } memcpy( bits, tmpBits, pixelSize ); } } delete tmpBlock; } else { readBlock( theBandNo, theExtent, theWidth, theHeight, block->data() ); } // apply user no data values // TODO: there are other readBlock methods where no data are not applied block->applyNodataValues( userNoDataValue( theBandNo ) ); return block; }