double QgsRasterDataProvider::readValue( void *data, int type, int index ) { if ( !data ) return mValidNoDataValue ? noDataValue() : 0.0; switch ( type ) { case Byte: return ( double )(( GByte * )data )[index]; break; case UInt16: return ( double )(( GUInt16 * )data )[index]; break; case Int16: return ( double )(( GInt16 * )data )[index]; break; case UInt32: return ( double )(( GUInt32 * )data )[index]; break; case Int32: return ( double )(( GInt32 * )data )[index]; break; case Float32: return ( double )(( float * )data )[index]; break; case Float64: return ( double )(( double * )data )[index]; break; default: QgsLogger::warning( "GDAL data type is not supported" ); } return mValidNoDataValue ? noDataValue() : 0.0; }
// Default implementation for values QMap<int, QVariant> QgsRasterDataProvider::identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight ) { QgsDebugMsg( "Entered" ); QMap<int, QVariant> results; if ( theFormat != IdentifyFormatValue || !( capabilities() & IdentifyValue ) ) { QgsDebugMsg( "Format not supported" ); return results; } if ( !extent().contains( thePoint ) ) { // Outside the raster for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ ) { results.insert( bandNo, noDataValue( bandNo ) ); } return results; } QgsRectangle myExtent = theExtent; if ( myExtent.isEmpty() ) myExtent = extent(); if ( theWidth == 0 ) { theWidth = capabilities() & Size ? xSize() : 1000; } if ( theHeight == 0 ) { theHeight = capabilities() & Size ? ySize() : 1000; } // Calculate the row / column where the point falls double xres = ( myExtent.width() ) / theWidth; double yres = ( myExtent.height() ) / theHeight; int col = ( int ) floor(( thePoint.x() - myExtent.xMinimum() ) / xres ); int row = ( int ) floor(( myExtent.yMaximum() - thePoint.y() ) / yres ); double xMin = myExtent.xMinimum() + col * xres; double xMax = xMin + xres; double yMax = myExtent.yMaximum() - row * yres; double yMin = yMax - yres; QgsRectangle pixelExtent( xMin, yMin, xMax, yMax ); for ( int i = 1; i <= bandCount(); i++ ) { QgsRasterBlock * myBlock = block( i, pixelExtent, 1, 1 ); double value = noDataValue( i ); if ( myBlock ) value = myBlock->value( 0 ); results.insert( i, value ); } return results; }
QgsRasterIdentifyResult QgsGrassRasterProvider::identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight ) { Q_UNUSED( theExtent ); Q_UNUSED( theWidth ); Q_UNUSED( theHeight ); QgsDebugMsg( "Entered" ); QMap<int, QVariant> results; if ( theFormat != IdentifyFormatValue ) { return QgsRasterIdentifyResult( ERROR( tr( "Format not supported" ) ) ); } if ( !extent().contains( thePoint ) ) { results.insert( 1, noDataValue( 1 ) ); return QgsRasterIdentifyResult( IdentifyFormatValue, results ); } // TODO: use doubles instead of strings // attention, value tool does his own tricks with grass identify() so it stops to refresh values outside extent or null values e.g. bool ok; double value = mRasterValue.value( thePoint.x(), thePoint.y(), &ok ); if ( !ok ) { return QgsRasterIdentifyResult( ERROR( tr( "Cannot read data" ) ) ); } if ( qIsNaN( value ) ) value = noDataValue( 1 ); // Apply user no data QgsRasterRangeList myNoDataRangeList = userNoDataValue( 1 ); if ( QgsRasterRange::contains( value, myNoDataRangeList ) ) { value = noDataValue( 1 ); } results.insert( 1, value ); return QgsRasterIdentifyResult( IdentifyFormatValue, results ); }
inline bool QgsRasterInterface::isNoDataValue( int bandNo, double value ) const { // More precise would be qIsNaN(value) && qIsNaN(noDataValue(bandNo)), but probably // not important and slower if ( qIsNaN( value ) || doubleNear( value, noDataValue( bandNo ) ) ) { return true; } return false; }
bool QgsRasterInterface::isNoDataValue( int bandNo, double value ) const { return QgsRasterBlock::isNoDataValue( value, noDataValue( bandNo ) ); }
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; }