void QgsZonalStatistics::statisticsFromMiddlePointTest( const QgsGeometry &poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle &rasterBBox, FeatureStats &stats ) { double cellCenterX, cellCenterY; cellCenterY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2; stats.reset(); GEOSGeometry *polyGeos = poly.exportToGeos(); if ( !polyGeos ) { return; } GEOSContextHandle_t geosctxt = QgsGeometry::getGEOSHandler(); const GEOSPreparedGeometry *polyGeosPrepared = GEOSPrepare_r( geosctxt, polyGeos ); if ( !polyGeosPrepared ) { GEOSGeom_destroy_r( geosctxt, polyGeos ); return; } GEOSCoordSequence *cellCenterCoords = nullptr; GEOSGeometry *currentCellCenter = nullptr; QgsRectangle featureBBox = poly.boundingBox().intersect( &rasterBBox ); QgsRectangle intersectBBox = rasterBBox.intersect( &featureBBox ); QgsRasterBlock *block = mRasterProvider->block( mRasterBand, intersectBBox, nCellsX, nCellsY ); for ( int i = 0; i < nCellsY; ++i ) { cellCenterX = rasterBBox.xMinimum() + pixelOffsetX * cellSizeX + cellSizeX / 2; for ( int j = 0; j < nCellsX; ++j ) { if ( validPixel( block->value( i, j ) ) ) { GEOSGeom_destroy_r( geosctxt, currentCellCenter ); cellCenterCoords = GEOSCoordSeq_create_r( geosctxt, 1, 2 ); GEOSCoordSeq_setX_r( geosctxt, cellCenterCoords, 0, cellCenterX ); GEOSCoordSeq_setY_r( geosctxt, cellCenterCoords, 0, cellCenterY ); currentCellCenter = GEOSGeom_createPoint_r( geosctxt, cellCenterCoords ); if ( GEOSPreparedContains_r( geosctxt, polyGeosPrepared, currentCellCenter ) ) { stats.addValue( block->value( i, j ) ); } } cellCenterX += cellSizeX; } cellCenterY -= cellSizeY; } GEOSGeom_destroy_r( geosctxt, currentCellCenter ); GEOSPreparedGeom_destroy_r( geosctxt, polyGeosPrepared ); GEOSGeom_destroy_r( geosctxt, polyGeos ); delete block; }
void QgsZonalStatistics::statisticsFromPreciseIntersection( const QgsGeometry &poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle &rasterBBox, FeatureStats &stats ) { stats.reset(); double currentY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2; QgsGeometry pixelRectGeometry; double hCellSizeX = cellSizeX / 2.0; double hCellSizeY = cellSizeY / 2.0; double pixelArea = cellSizeX * cellSizeY; double weight = 0; QgsRectangle featureBBox = poly.boundingBox().intersect( &rasterBBox ); QgsRectangle intersectBBox = rasterBBox.intersect( &featureBBox ); QgsRasterBlock *block = mRasterProvider->block( mRasterBand, intersectBBox, nCellsX, nCellsY ); for ( int i = 0; i < nCellsY; ++i ) { double currentX = rasterBBox.xMinimum() + cellSizeX / 2.0 + pixelOffsetX * cellSizeX; for ( int j = 0; j < nCellsX; ++j ) { if ( !validPixel( block->value( i, j ) ) ) { continue; } pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) ); if ( !pixelRectGeometry.isNull() ) { //intersection QgsGeometry intersectGeometry = pixelRectGeometry.intersection( poly ); if ( !intersectGeometry.isNull() ) { double intersectionArea = intersectGeometry.area(); if ( intersectionArea >= 0.0 ) { weight = intersectionArea / pixelArea; stats.addValue( block->value( i, j ), weight ); } } pixelRectGeometry = QgsGeometry(); } currentX += cellSizeX; } currentY -= cellSizeY; } delete block; }
// 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; }
QgsRasterBlock * QgsRasterNuller::block( int bandNo, QgsRectangle const & extent, int width, int height ) { QgsDebugMsg( "Entered" ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { return outputBlock; } //void * rasterData = mInput->block( bandNo, extent, width, height ); QgsRasterBlock *inputBlock = mInput->block( bandNo, extent, width, height ); // Input may be without no data value //double noDataValue = mInput->noDataValue( bandNo ); double noDataValue = mOutputNoData; for ( int i = 0; i < height; i++ ) { for ( int j = 0; j < width; j++ ) { //int index = i * width + j; //double value = readValue( rasterData, dataType, index ); double value = inputBlock->value( i, j ); foreach ( NoData noData, mNoData ) { if (( value >= noData.min && value <= noData.max ) || doubleNear( value, noData.min ) || doubleNear( value, noData.max ) ) { inputBlock->setValue( i, j, noDataValue ); } } } } return inputBlock; }
QgsRasterBlock* QgsSingleBandGrayRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height ) { Q_UNUSED( bandNo ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mGrayBand, extent, width, height ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } QgsRasterBlock *alphaBlock = 0; if ( mAlphaBand > 0 && mGrayBand != mAlphaBand ) { alphaBlock = mInput->block( mAlphaBand, extent, width, height ); if ( !alphaBlock || alphaBlock->isEmpty() ) { // TODO: better to render without alpha delete inputBlock; delete alphaBlock; return outputBlock; } } else if ( mAlphaBand > 0 ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; delete alphaBlock; return outputBlock; } QRgb myDefaultColor = NODATA_COLOR; for ( qgssize i = 0; i < ( qgssize )width*height; i++ ) { if ( inputBlock->isNoData( i ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } double grayVal = inputBlock->value( i ); double currentAlpha = mOpacity; if ( mRasterTransparency ) { currentAlpha = mRasterTransparency->alphaValue( grayVal, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentAlpha *= alphaBlock->value( i ) / 255.0; } if ( mContrastEnhancement ) { if ( !mContrastEnhancement->isValueInDisplayableRange( grayVal ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } grayVal = mContrastEnhancement->enhanceContrast( grayVal ); } if ( mGradient == WhiteToBlack ) { grayVal = 255 - grayVal; } if ( qgsDoubleNear( currentAlpha, 1.0 ) ) { outputBlock->setColor( i, qRgba( grayVal, grayVal, grayVal, 255 ) ); } else { outputBlock->setColor( i, qRgba( currentAlpha * grayVal, currentAlpha * grayVal, currentAlpha * grayVal, currentAlpha * 255 ) ); } } delete inputBlock; if ( mAlphaBand > 0 && mGrayBand != mAlphaBand ) { delete alphaBlock; } return outputBlock; }
QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback ) { Q_UNUSED( bandNo ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { QgsDebugMsg( "No input raster!" ); return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height, feedback ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } QgsRasterBlock *alphaBlock = nullptr; if ( mAlphaBand > 0 && mBand != mAlphaBand ) { alphaBlock = mInput->block( mAlphaBand, extent, width, height, feedback ); if ( !alphaBlock || alphaBlock->isEmpty() ) { // TODO: better to render without alpha delete inputBlock; delete alphaBlock; return outputBlock; } } else if ( mAlphaBand > 0 ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; delete alphaBlock; return outputBlock; } double cellXSize = extent.width() / double( width ); double cellYSize = extent.height() / double( height ); double zenithRad = qMax( 0.0, 90 - mLightAngle ) * M_PI / 180.0; double azimuthRad = -1 * mLightAzimuth * M_PI / 180.0; double cosZenithRad = cos( zenithRad ); double sinZenithRad = sin( zenithRad ); // Multi direction hillshade: http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf double angle0Rad = ( -1 * mLightAzimuth - 45 - 45 * 0.5 ) * M_PI / 180.0; double angle1Rad = ( -1 * mLightAzimuth - 45 * 0.5 ) * M_PI / 180.0; double angle2Rad = ( -1 * mLightAzimuth + 45 * 0.5 ) * M_PI / 180.0; double angle3Rad = ( -1 * mLightAzimuth + 45 + 45 * 0.5 ) * M_PI / 180.0; QRgb myDefaultColor = NODATA_COLOR; for ( qgssize i = 0; i < ( qgssize )height; i++ ) { for ( qgssize j = 0; j < ( qgssize )width; j++ ) { if ( inputBlock->isNoData( i, j ) ) { outputBlock->setColor( i, j, myDefaultColor ); continue; } qgssize iUp, iDown, jLeft, jRight; if ( i == 0 ) { iUp = i; iDown = i + 1; } else if ( i < ( qgssize )height - 1 ) { iUp = i - 1; iDown = i + 1; } else { iUp = i - 1; iDown = i; } if ( j == 0 ) { jLeft = j; jRight = j + 1; } else if ( j < ( qgssize )width - 1 ) { jLeft = j - 1; jRight = j + 1; } else { jLeft = j - 1; jRight = j; } double x11; double x21; double x31; double x12; double x22; // Working cell double x32; double x13; double x23; double x33; // This is center cell. It is not nodata. Use this in place of nodata neighbors x22 = inputBlock->value( i, j ); x11 = inputBlock->isNoData( iUp, jLeft ) ? x22 : inputBlock->value( iUp, jLeft ); x21 = inputBlock->isNoData( i, jLeft ) ? x22 : inputBlock->value( i, jLeft ); x31 = inputBlock->isNoData( iDown, jLeft ) ? x22 : inputBlock->value( iDown, jLeft ); x12 = inputBlock->isNoData( iUp, j ) ? x22 : inputBlock->value( iUp, j ); // x22 x32 = inputBlock->isNoData( iDown, j ) ? x22 : inputBlock->value( iDown, j ); x13 = inputBlock->isNoData( iUp, jRight ) ? x22 : inputBlock->value( iUp, jRight ); x23 = inputBlock->isNoData( i, jRight ) ? x22 : inputBlock->value( i, jRight ); x33 = inputBlock->isNoData( iDown, jRight ) ? x22 : inputBlock->value( iDown, jRight ); double derX = calcFirstDerX( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellXSize ); double derY = calcFirstDerY( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellYSize ); double slopeRad = atan( mZFactor * sqrt( derX * derX + derY * derY ) ); double aspectRad = atan2( derX, -derY ); double grayValue; if ( !mMultiDirectional ) { // Standard single direction hillshade grayValue = qBound( 0.0, 255.0 * ( cosZenithRad * cos( slopeRad ) + sinZenithRad * sin( slopeRad ) * cos( azimuthRad - aspectRad ) ) , 255.0 ); } else { // Weighted multi direction as in http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf double weight0 = sin( aspectRad - angle0Rad ); double weight1 = sin( aspectRad - angle1Rad ); double weight2 = sin( aspectRad - angle2Rad ); double weight3 = sin( aspectRad - angle3Rad ); weight0 = weight0 * weight0; weight1 = weight1 * weight1; weight2 = weight2 * weight2; weight3 = weight3 * weight3; double cosSlope = cosZenithRad * cos( slopeRad ); double sinSlope = sinZenithRad * sin( slopeRad ); double color0 = cosSlope + sinSlope * cos( angle0Rad - aspectRad ) ; double color1 = cosSlope + sinSlope * cos( angle1Rad - aspectRad ) ; double color2 = cosSlope + sinSlope * cos( angle2Rad - aspectRad ) ; double color3 = cosSlope + sinSlope * cos( angle3Rad - aspectRad ) ; grayValue = qBound( 0.0, 255 * ( weight0 * color0 + weight1 * color1 + weight2 * color2 + weight3 * color3 ) * 0.5, 255.0 ); } double currentAlpha = mOpacity; if ( mRasterTransparency ) { currentAlpha = mRasterTransparency->alphaValue( x22, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentAlpha *= alphaBlock->value( i ) / 255.0; } if ( qgsDoubleNear( currentAlpha, 1.0 ) ) { outputBlock->setColor( i, j, qRgba( grayValue, grayValue, grayValue, 255 ) ); } else { outputBlock->setColor( i, j, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) ); } } } delete inputBlock; if ( mAlphaBand > 0 && mBand != mAlphaBand ) { delete alphaBlock; } return outputBlock; }
QgsRasterBlock* QgsSingleBandPseudoColorRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback ) { Q_UNUSED( bandNo ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput || !mShader ) { return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height, feedback ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } //rendering is faster without considering user-defined transparency bool hasTransparency = usesTransparency(); QgsRasterBlock *alphaBlock = nullptr; if ( mAlphaBand > 0 && mAlphaBand != mBand ) { alphaBlock = mInput->block( mAlphaBand, extent, width, height, feedback ); if ( !alphaBlock || alphaBlock->isEmpty() ) { delete inputBlock; delete alphaBlock; return outputBlock; } } else if ( mAlphaBand == mBand ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; delete alphaBlock; return outputBlock; } QRgb myDefaultColor = NODATA_COLOR; for ( qgssize i = 0; i < ( qgssize )width*height; i++ ) { if ( inputBlock->isNoData( i ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } double val = inputBlock->value( i ); int red, green, blue, alpha; if ( !mShader->shade( val, &red, &green, &blue, &alpha ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } if ( alpha < 255 ) { // Working with premultiplied colors, so multiply values by alpha red *= ( alpha / 255.0 ); blue *= ( alpha / 255.0 ); green *= ( alpha / 255.0 ); } if ( !hasTransparency ) { outputBlock->setColor( i, qRgba( red, green, blue, alpha ) ); } else { //opacity double currentOpacity = mOpacity; if ( mRasterTransparency ) { currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentOpacity *= alphaBlock->value( i ) / 255.0; } outputBlock->setColor( i, qRgba( currentOpacity * red, currentOpacity * green, currentOpacity * blue, currentOpacity * alpha ) ); } } delete inputBlock; if ( mAlphaBand > 0 && mBand != mAlphaBand ) { delete alphaBlock; } return outputBlock; }
QgsRasterHistogram QgsRasterInterface::histogram( int theBandNo, int theBinCount, double theMinimum, double theMaximum, const QgsRectangle & theExtent, int theSampleSize, bool theIncludeOutOfRange ) { QgsDebugMsg( QString( "theBandNo = %1 theBinCount = %2 theMinimum = %3 theMaximum = %4 theSampleSize = %5" ).arg( theBandNo ).arg( theBinCount ).arg( theMinimum ).arg( theMaximum ).arg( theSampleSize ) ); QgsRasterHistogram myHistogram; initHistogram( myHistogram, theBandNo, theBinCount, theMinimum, theMaximum, theExtent, theSampleSize, theIncludeOutOfRange ); // Find cached foreach ( QgsRasterHistogram histogram, mHistograms ) { if ( histogram == myHistogram ) { QgsDebugMsg( "Using cached histogram." ); return histogram; } } int myBinCount = myHistogram.binCount; int myWidth = myHistogram.width; int myHeight = myHistogram.height; QgsRectangle myExtent = myHistogram.extent; myHistogram.histogramVector.resize( myBinCount ); int myXBlockSize = xBlockSize(); int myYBlockSize = yBlockSize(); if ( myXBlockSize == 0 ) // should not happen, but happens { myXBlockSize = 500; } if ( myYBlockSize == 0 ) // should not happen, but happens { myYBlockSize = 500; } int myNXBlocks = ( myWidth + myXBlockSize - 1 ) / myXBlockSize; int myNYBlocks = ( myHeight + myYBlockSize - 1 ) / myYBlockSize; double myXRes = myExtent.width() / myWidth; double myYRes = myExtent.height() / myHeight; double myMinimum = myHistogram.minimum; double myMaximum = myHistogram.maximum; // To avoid rounding errors // TODO: check this double myerval = ( myMaximum - myMinimum ) / myHistogram.binCount; myMinimum -= 0.1 * myerval; myMaximum += 0.1 * myerval; QgsDebugMsg( QString( "binCount = %1 myMinimum = %2 myMaximum = %3" ).arg( myHistogram.binCount ).arg( myMinimum ).arg( myMaximum ) ); double myBinSize = ( myMaximum - myMinimum ) / myBinCount; // TODO: progress signals for ( int myYBlock = 0; myYBlock < myNYBlocks; myYBlock++ ) { for ( int myXBlock = 0; myXBlock < myNXBlocks; myXBlock++ ) { int myBlockWidth = qMin( myXBlockSize, myWidth - myXBlock * myXBlockSize ); int myBlockHeight = qMin( myYBlockSize, myHeight - myYBlock * myYBlockSize ); double xmin = myExtent.xMinimum() + myXBlock * myXBlockSize * myXRes; double xmax = xmin + myBlockWidth * myXRes; double ymin = myExtent.yMaximum() - myYBlock * myYBlockSize * myYRes; double ymax = ymin - myBlockHeight * myYRes; QgsRectangle myPartExtent( xmin, ymin, xmax, ymax ); QgsRasterBlock* blk = block( theBandNo, myPartExtent, myBlockWidth, myBlockHeight ); // Collect the histogram counts. for ( qgssize i = 0; i < (( qgssize ) myBlockHeight ) * myBlockWidth; i++ ) { if ( blk->isNoData( i ) ) { continue; // NULL } double myValue = blk->value( i ); int myBinIndex = static_cast <int>( qFloor(( myValue - myMinimum ) / myBinSize ) ) ; if (( myBinIndex < 0 || myBinIndex > ( myBinCount - 1 ) ) && !theIncludeOutOfRange ) { continue; } if ( myBinIndex < 0 ) myBinIndex = 0; if ( myBinIndex > ( myBinCount - 1 ) ) myBinIndex = myBinCount - 1; myHistogram.histogramVector[myBinIndex] += 1; myHistogram.nonNullCount++; } delete blk; } } myHistogram.valid = true; mHistograms.append( myHistogram ); #ifdef QGISDEBUG QString hist; for ( int i = 0; i < qMin( myHistogram.histogramVector.size(), 500 ); i++ ) { hist += QString::number( myHistogram.histogramVector.value( i ) ) + " "; } QgsDebugMsg( "Histogram (max first 500 bins): " + hist ); #endif return myHistogram; }
QgsRasterBlock * QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height ) { QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( bandNo, extent, width, height ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } double currentOpacity = mOpacity; //rendering is faster without considering user-defined transparency bool hasTransparency = usesTransparency(); QgsRasterBlock *alphaBlock = 0; if ( mAlphaBand > 0 && mAlphaBand != mBand ) { alphaBlock = mInput->block( mAlphaBand, extent, width, height ); if ( !alphaBlock || alphaBlock->isEmpty() ) { delete inputBlock; delete alphaBlock; return outputBlock; } } else if ( mAlphaBand == mBand ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; delete alphaBlock; return outputBlock; } QRgb myDefaultColor = NODATA_COLOR; //use direct data access instead of QgsRasterBlock::setValue //because of performance unsigned int* outputData = ( unsigned int* )( outputBlock->bits() ); qgssize rasterSize = ( qgssize )width * height; for ( qgssize i = 0; i < rasterSize; ++i ) { if ( inputBlock->isNoData( i ) ) { outputData[i] = myDefaultColor; continue; } int val = ( int ) inputBlock->value( i ); if ( !hasTransparency ) { outputData[i] = mColors[val]; } else { currentOpacity = mOpacity; if ( mRasterTransparency ) { currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentOpacity *= alphaBlock->value( i ) / 255.0; } QColor currentColor = QColor( mColors[val] ); outputData[i] = qRgba( currentOpacity * currentColor.red(), currentOpacity * currentColor.green(), currentOpacity * currentColor.blue(), currentOpacity * 255 ); } } delete inputBlock; if ( mAlphaBand > 0 && mBand != mAlphaBand ) { delete alphaBlock; } return outputBlock; }
QgsRasterBlock *QgsMultiBandColorRenderer::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback ) { Q_UNUSED( bandNo ); std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() ); if ( !mInput ) { return outputBlock.release(); } //In some (common) cases, we can simplify the drawing loop considerably and save render time bool fastDraw = ( !usesTransparency() && mRedBand > 0 && mGreenBand > 0 && mBlueBand > 0 && mAlphaBand < 1 ); QSet<int> bands; if ( mRedBand > 0 ) { bands << mRedBand; } if ( mGreenBand > 0 ) { bands << mGreenBand; } if ( mBlueBand > 0 ) { bands << mBlueBand; } if ( bands.empty() ) { // no need to draw anything if no band is set // TODO:: we should probably return default color block return outputBlock.release(); } if ( mAlphaBand > 0 ) { bands << mAlphaBand; } QMap<int, QgsRasterBlock *> bandBlocks; QgsRasterBlock *defaultPointer = nullptr; QSet<int>::const_iterator bandIt = bands.constBegin(); for ( ; bandIt != bands.constEnd(); ++bandIt ) { bandBlocks.insert( *bandIt, defaultPointer ); } QgsRasterBlock *redBlock = nullptr; QgsRasterBlock *greenBlock = nullptr; QgsRasterBlock *blueBlock = nullptr; QgsRasterBlock *alphaBlock = nullptr; bandIt = bands.constBegin(); for ( ; bandIt != bands.constEnd(); ++bandIt ) { bandBlocks[*bandIt] = mInput->block( *bandIt, extent, width, height, feedback ); if ( !bandBlocks[*bandIt] ) { // We should free the alloced mem from block(). QgsDebugMsg( QStringLiteral( "No input band" ) ); --bandIt; for ( ; bandIt != bands.constBegin(); --bandIt ) { delete bandBlocks[*bandIt]; } return outputBlock.release(); } } if ( mRedBand > 0 ) { redBlock = bandBlocks[mRedBand]; } if ( mGreenBand > 0 ) { greenBlock = bandBlocks[mGreenBand]; } if ( mBlueBand > 0 ) { blueBlock = bandBlocks[mBlueBand]; } if ( mAlphaBand > 0 ) { alphaBlock = bandBlocks[mAlphaBand]; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { for ( int i = 0; i < bandBlocks.size(); i++ ) { delete bandBlocks.value( i ); } return outputBlock.release(); } QRgb *outputBlockColorData = outputBlock->colorData(); // faster data access to data for the common case that input data are coming from RGB image with 8-bit bands bool hasByteRgb = ( redBlock && greenBlock && blueBlock && redBlock->dataType() == Qgis::Byte && greenBlock->dataType() == Qgis::Byte && blueBlock->dataType() == Qgis::Byte ); const quint8 *redData = nullptr, *greenData = nullptr, *blueData = nullptr; if ( hasByteRgb ) { redData = redBlock->byteData(); greenData = greenBlock->byteData(); blueData = blueBlock->byteData(); } QRgb myDefaultColor = NODATA_COLOR; if ( fastDraw ) { // By default RGB raster layers have contrast enhancement assigned and normally that requires us to take the slow // route that applies the enhancement. However if the algorithm type is "no enhancement" and all input bands are byte-sized, // no transform would be applied to the input values and we can take the fast route. bool hasEnhancement; if ( hasByteRgb ) { hasEnhancement = ( mRedContrastEnhancement && mRedContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement ) || ( mGreenContrastEnhancement && mGreenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement ) || ( mBlueContrastEnhancement && mBlueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement ); } else { hasEnhancement = mRedContrastEnhancement || mGreenContrastEnhancement || mBlueContrastEnhancement; } if ( hasEnhancement ) fastDraw = false; } qgssize count = ( qgssize )width * height; for ( qgssize i = 0; i < count; i++ ) { if ( fastDraw ) //fast rendering if no transparency, stretching, color inversion, etc. { if ( redBlock->isNoData( i ) || greenBlock->isNoData( i ) || blueBlock->isNoData( i ) ) { outputBlock->setColor( i, myDefaultColor ); } else { if ( hasByteRgb ) { outputBlockColorData[i] = qRgb( redData[i], greenData[i], blueData[i] ); } else { int redVal = static_cast<int>( redBlock->value( i ) ); int greenVal = static_cast<int>( greenBlock->value( i ) ); int blueVal = static_cast<int>( blueBlock->value( i ) ); outputBlockColorData[i] = qRgb( redVal, greenVal, blueVal ); } } continue; } bool isNoData = false; double redVal = 0; double greenVal = 0; double blueVal = 0; if ( mRedBand > 0 ) { redVal = redBlock->value( i ); if ( redBlock->isNoData( i ) ) isNoData = true; } if ( !isNoData && mGreenBand > 0 ) { greenVal = greenBlock->value( i ); if ( greenBlock->isNoData( i ) ) isNoData = true; } if ( !isNoData && mBlueBand > 0 ) { blueVal = blueBlock->value( i ); if ( blueBlock->isNoData( i ) ) isNoData = true; } if ( isNoData ) { outputBlock->setColor( i, myDefaultColor ); continue; } //apply default color if red, green or blue not in displayable range if ( ( mRedContrastEnhancement && !mRedContrastEnhancement->isValueInDisplayableRange( redVal ) ) || ( mGreenContrastEnhancement && !mGreenContrastEnhancement->isValueInDisplayableRange( redVal ) ) || ( mBlueContrastEnhancement && !mBlueContrastEnhancement->isValueInDisplayableRange( redVal ) ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } //stretch color values if ( mRedContrastEnhancement ) { redVal = mRedContrastEnhancement->enhanceContrast( redVal ); } if ( mGreenContrastEnhancement ) { greenVal = mGreenContrastEnhancement->enhanceContrast( greenVal ); } if ( mBlueContrastEnhancement ) { blueVal = mBlueContrastEnhancement->enhanceContrast( blueVal ); } //opacity double currentOpacity = mOpacity; if ( mRasterTransparency ) { currentOpacity = mRasterTransparency->alphaValue( redVal, greenVal, blueVal, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentOpacity *= alphaBlock->value( i ) / 255.0; } if ( qgsDoubleNear( currentOpacity, 1.0 ) ) { outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) ); } else { outputBlock->setColor( i, qRgba( currentOpacity * redVal, currentOpacity * greenVal, currentOpacity * blueVal, currentOpacity * 255 ) ); } } //delete input blocks QMap<int, QgsRasterBlock *>::const_iterator bandDelIt = bandBlocks.constBegin(); for ( ; bandDelIt != bandBlocks.constEnd(); ++bandDelIt ) { delete bandDelIt.value(); } return outputBlock.release(); }
QgsRasterBandStats QgsRasterInterface::bandStatistics( int theBandNo, int theStats, const QgsRectangle & theExtent, int theSampleSize ) { QgsDebugMsg( QString( "theBandNo = %1 theStats = %2 theSampleSize = %3" ).arg( theBandNo ).arg( theStats ).arg( theSampleSize ) ); // TODO: null values set on raster layer!!! QgsRasterBandStats myRasterBandStats; initStatistics( myRasterBandStats, theBandNo, theStats, theExtent, theSampleSize ); foreach ( QgsRasterBandStats stats, mStatistics ) { if ( stats.contains( myRasterBandStats ) ) { QgsDebugMsg( "Using cached statistics." ); return stats; } } QgsRectangle myExtent = myRasterBandStats.extent; int myWidth = myRasterBandStats.width; int myHeight = myRasterBandStats.height; //int myDataType = dataType( theBandNo ); int myXBlockSize = xBlockSize(); int myYBlockSize = yBlockSize(); if ( myXBlockSize == 0 ) // should not happen, but happens { myXBlockSize = 500; } if ( myYBlockSize == 0 ) // should not happen, but happens { myYBlockSize = 500; } int myNXBlocks = ( myWidth + myXBlockSize - 1 ) / myXBlockSize; int myNYBlocks = ( myHeight + myYBlockSize - 1 ) / myYBlockSize; double myXRes = myExtent.width() / myWidth; double myYRes = myExtent.height() / myHeight; // TODO: progress signals // used by single pass stdev double myMean = 0; double mySumOfSquares = 0; bool myFirstIterationFlag = true; for ( int myYBlock = 0; myYBlock < myNYBlocks; myYBlock++ ) { for ( int myXBlock = 0; myXBlock < myNXBlocks; myXBlock++ ) { QgsDebugMsg( QString( "myYBlock = %1 myXBlock = %2" ).arg( myYBlock ).arg( myXBlock ) ); int myBlockWidth = qMin( myXBlockSize, myWidth - myXBlock * myXBlockSize ); int myBlockHeight = qMin( myYBlockSize, myHeight - myYBlock * myYBlockSize ); double xmin = myExtent.xMinimum() + myXBlock * myXBlockSize * myXRes; double xmax = xmin + myBlockWidth * myXRes; double ymin = myExtent.yMaximum() - myYBlock * myYBlockSize * myYRes; double ymax = ymin - myBlockHeight * myYRes; QgsRectangle myPartExtent( xmin, ymin, xmax, ymax ); QgsRasterBlock* blk = block( theBandNo, myPartExtent, myBlockWidth, myBlockHeight ); // Collect the histogram counts. for ( qgssize i = 0; i < (( qgssize ) myBlockHeight ) * myBlockWidth; i++ ) { if ( blk->isNoData( i ) ) continue; // NULL double myValue = blk->value( i ); myRasterBandStats.sum += myValue; myRasterBandStats.elementCount++; if ( myFirstIterationFlag ) { myFirstIterationFlag = false; myRasterBandStats.minimumValue = myValue; myRasterBandStats.maximumValue = myValue; } else { if ( myValue < myRasterBandStats.minimumValue ) { myRasterBandStats.minimumValue = myValue; } if ( myValue > myRasterBandStats.maximumValue ) { myRasterBandStats.maximumValue = myValue; } } // Single pass stdev double myDelta = myValue - myMean; myMean += myDelta / myRasterBandStats.elementCount; mySumOfSquares += myDelta * ( myValue - myMean ); } delete blk; } } myRasterBandStats.range = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue; myRasterBandStats.mean = myRasterBandStats.sum / myRasterBandStats.elementCount; myRasterBandStats.sumOfSquares = mySumOfSquares; // OK with single pass? // stdDev may differ from GDAL stats, because GDAL is using naive single pass // algorithm which is more error prone (because of rounding errors) // Divide result by sample size - 1 and get square root to get stdev myRasterBandStats.stdDev = sqrt( mySumOfSquares / ( myRasterBandStats.elementCount - 1 ) ); QgsDebugMsg( "************ STATS **************" ); QgsDebugMsg( QString( "MIN %1" ).arg( myRasterBandStats.minimumValue ) ); QgsDebugMsg( QString( "MAX %1" ).arg( myRasterBandStats.maximumValue ) ); QgsDebugMsg( QString( "RANGE %1" ).arg( myRasterBandStats.range ) ); QgsDebugMsg( QString( "MEAN %1" ).arg( myRasterBandStats.mean ) ); QgsDebugMsg( QString( "STDDEV %1" ).arg( myRasterBandStats.stdDev ) ); myRasterBandStats.statsGathered = QgsRasterBandStats::All; mStatistics.append( myRasterBandStats ); return myRasterBandStats; }
QgsRasterBlock * QgsRasterNuller::block( int bandNo, QgsRectangle const & extent, int width, int height ) { QgsDebugMsg( "Entered" ); if ( !mInput ) { return new QgsRasterBlock(); } QgsRasterBlock *inputBlock = mInput->block( bandNo, extent, width, height ); if ( !inputBlock ) { return new QgsRasterBlock(); } // We don't support nuller for color types if ( QgsRasterBlock::typeIsColor( inputBlock->dataType() ) ) { return inputBlock; } QgsRasterBlock *outputBlock = 0; if ( mHasOutputNoData.value( bandNo - 1 ) || inputBlock->hasNoDataValue() ) { double noDataValue; if ( mHasOutputNoData.value( bandNo - 1 ) ) { noDataValue = mOutputNoData.value( bandNo - 1 ); } else { noDataValue = inputBlock->noDataValue(); } outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height, noDataValue ); } else { outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height ); } for ( int i = 0; i < height; i++ ) { for ( int j = 0; j < width; j++ ) { double value = inputBlock->value( i, j ); bool isNoData = inputBlock->isNoData( i, j ); if ( QgsRasterRange::contains( value, mNoData.value( bandNo - 1 ) ) ) { isNoData = true; } outputBlock->setValue( i, j, inputBlock->value( i, j ) ); if ( isNoData ) { outputBlock->setIsNoData( i, j ); } else { outputBlock->setValue( i, j, value ); } } } delete inputBlock; return outputBlock; }
QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height ) { Q_UNUSED( bandNo ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { QgsDebugMsg( "No input raster!" ); return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } double cellXSize = extent.width() / double( width ); double cellYSize = extent.height() / double( height ); double zenithRad = qMax( 0.0, 90 - mLightAngle ) * M_PI / 180.0; double azimuthRad = -1 * mLightAzimuth * M_PI / 180.0; double cosZenithRad = cos( zenithRad ); double sinZenithRad = sin( zenithRad ); QRgb myDefaultColor = NODATA_COLOR; for ( qgssize i = 0; i < ( qgssize )height; i++ ) { for ( qgssize j = 0; j < ( qgssize )width; j++ ) { if ( inputBlock->isNoData( i, j ) ) { outputBlock->setColor( i, j, myDefaultColor ); continue; } qgssize iUp, iDown, jLeft, jRight; if ( i == 0 ) { iUp = i; iDown = i + 1; } else if ( i < ( qgssize )height - 1 ) { iUp = i - 1; iDown = i + 1; } else { iUp = i - 1; iDown = i; } if ( j == 0 ) { jLeft = j; jRight = j + 1; } else if ( j < ( qgssize )width - 1 ) { jLeft = j - 1; jRight = j + 1; } else { jLeft = j - 1; jRight = j; } double x11; double x21; double x31; double x12; double x22; // Working cell double x32; double x13; double x23; double x33; // This is center cell. It is not nodata. Use this in place of nodata neighbors x22 = inputBlock->value( i, j ); x11 = inputBlock->isNoData( iUp, jLeft ) ? x22 : inputBlock->value( iUp, jLeft ); x21 = inputBlock->isNoData( i, jLeft ) ? x22 : inputBlock->value( i, jLeft ); x31 = inputBlock->isNoData( iDown, jLeft ) ? x22 : inputBlock->value( iDown, jLeft ); x12 = inputBlock->isNoData( iUp, j ) ? x22 : inputBlock->value( iUp, j ); // x22 x32 = inputBlock->isNoData( iDown, j ) ? x22 : inputBlock->value( iDown, j ); x13 = inputBlock->isNoData( iUp, jRight ) ? x22 : inputBlock->value( iUp, jRight ); x23 = inputBlock->isNoData( i, jRight ) ? x22 : inputBlock->value( i, jRight ); x33 = inputBlock->isNoData( iDown, jRight ) ? x22 : inputBlock->value( iDown, jRight ); double derX = calcFirstDerX( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellXSize ); double derY = calcFirstDerY( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellYSize ); double slope_rad = atan( mZFactor * sqrt( derX * derX + derY * derY ) ); double aspectRad = atan2( derX, -derY ); double colorvalue = qBound( 0.0, 255.0 * (( cosZenithRad * cos( slope_rad ) ) + ( sinZenithRad * sin( slope_rad ) * cos( azimuthRad - aspectRad ) ) ), 255.0 ); outputBlock->setColor( i, j, qRgb( colorvalue, colorvalue, colorvalue ) ); } } return outputBlock; }
QgsRasterBlock *QgsMultiBandColorRenderer::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback ) { Q_UNUSED( bandNo ); std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() ); if ( !mInput ) { return outputBlock.release(); } //In some (common) cases, we can simplify the drawing loop considerably and save render time bool fastDraw = ( !usesTransparency() && mRedBand > 0 && mGreenBand > 0 && mBlueBand > 0 && mAlphaBand < 1 && !mRedContrastEnhancement && !mGreenContrastEnhancement && !mBlueContrastEnhancement ); QSet<int> bands; if ( mRedBand > 0 ) { bands << mRedBand; } if ( mGreenBand > 0 ) { bands << mGreenBand; } if ( mBlueBand > 0 ) { bands << mBlueBand; } if ( bands.size() < 1 ) { // no need to draw anything if no band is set // TODO:: we should probably return default color block return outputBlock.release(); } if ( mAlphaBand > 0 ) { bands << mAlphaBand; } QMap<int, QgsRasterBlock *> bandBlocks; QgsRasterBlock *defaultPointer = nullptr; QSet<int>::const_iterator bandIt = bands.constBegin(); for ( ; bandIt != bands.constEnd(); ++bandIt ) { bandBlocks.insert( *bandIt, defaultPointer ); } QgsRasterBlock *redBlock = nullptr; QgsRasterBlock *greenBlock = nullptr; QgsRasterBlock *blueBlock = nullptr; QgsRasterBlock *alphaBlock = nullptr; bandIt = bands.constBegin(); for ( ; bandIt != bands.constEnd(); ++bandIt ) { bandBlocks[*bandIt] = mInput->block( *bandIt, extent, width, height, feedback ); if ( !bandBlocks[*bandIt] ) { // We should free the alloced mem from block(). QgsDebugMsg( "No input band" ); --bandIt; for ( ; bandIt != bands.constBegin(); --bandIt ) { delete bandBlocks[*bandIt]; } return outputBlock.release(); } } if ( mRedBand > 0 ) { redBlock = bandBlocks[mRedBand]; } if ( mGreenBand > 0 ) { greenBlock = bandBlocks[mGreenBand]; } if ( mBlueBand > 0 ) { blueBlock = bandBlocks[mBlueBand]; } if ( mAlphaBand > 0 ) { alphaBlock = bandBlocks[mAlphaBand]; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { for ( int i = 0; i < bandBlocks.size(); i++ ) { delete bandBlocks.value( i ); } return outputBlock.release(); } QRgb myDefaultColor = NODATA_COLOR; for ( qgssize i = 0; i < ( qgssize )width * height; i++ ) { if ( fastDraw ) //fast rendering if no transparency, stretching, color inversion, etc. { if ( redBlock->isNoData( i ) || greenBlock->isNoData( i ) || blueBlock->isNoData( i ) ) { outputBlock->setColor( i, myDefaultColor ); } else { int redVal = ( int )redBlock->value( i ); int greenVal = ( int )greenBlock->value( i ); int blueVal = ( int )blueBlock->value( i ); outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) ); } continue; } bool isNoData = false; double redVal = 0; double greenVal = 0; double blueVal = 0; if ( mRedBand > 0 ) { redVal = redBlock->value( i ); if ( redBlock->isNoData( i ) ) isNoData = true; } if ( !isNoData && mGreenBand > 0 ) { greenVal = greenBlock->value( i ); if ( greenBlock->isNoData( i ) ) isNoData = true; } if ( !isNoData && mBlueBand > 0 ) { blueVal = blueBlock->value( i ); if ( blueBlock->isNoData( i ) ) isNoData = true; } if ( isNoData ) { outputBlock->setColor( i, myDefaultColor ); continue; } //apply default color if red, green or blue not in displayable range if ( ( mRedContrastEnhancement && !mRedContrastEnhancement->isValueInDisplayableRange( redVal ) ) || ( mGreenContrastEnhancement && !mGreenContrastEnhancement->isValueInDisplayableRange( redVal ) ) || ( mBlueContrastEnhancement && !mBlueContrastEnhancement->isValueInDisplayableRange( redVal ) ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } //stretch color values if ( mRedContrastEnhancement ) { redVal = mRedContrastEnhancement->enhanceContrast( redVal ); } if ( mGreenContrastEnhancement ) { greenVal = mGreenContrastEnhancement->enhanceContrast( greenVal ); } if ( mBlueContrastEnhancement ) { blueVal = mBlueContrastEnhancement->enhanceContrast( blueVal ); } //opacity double currentOpacity = mOpacity; if ( mRasterTransparency ) { currentOpacity = mRasterTransparency->alphaValue( redVal, greenVal, blueVal, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentOpacity *= alphaBlock->value( i ) / 255.0; } if ( qgsDoubleNear( currentOpacity, 1.0 ) ) { outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) ); } else { outputBlock->setColor( i, qRgba( currentOpacity * redVal, currentOpacity * greenVal, currentOpacity * blueVal, currentOpacity * 255 ) ); } } //delete input blocks QMap<int, QgsRasterBlock *>::const_iterator bandDelIt = bandBlocks.constBegin(); for ( ; bandDelIt != bandBlocks.constEnd(); ++bandDelIt ) { delete bandDelIt.value(); } return outputBlock.release(); }
bool QgsRasterChecker::runTest( QString theVerifiedKey, QString theVerifiedUri, QString theExpectedKey, QString theExpectedUri ) { bool ok = true; mReport += "\n\n"; //QgsRasterDataProvider* verifiedProvider = QgsRasterLayer::loadProvider( theVerifiedKey, theVerifiedUri ); QgsRasterDataProvider* verifiedProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( theVerifiedKey, theVerifiedUri ); if ( !verifiedProvider || !verifiedProvider->isValid() ) { error( QString( "Cannot load provider %1 with URI: %2" ).arg( theVerifiedKey ).arg( theVerifiedUri ), mReport ); ok = false; } //QgsRasterDataProvider* expectedProvider = QgsRasterLayer::loadProvider( theExpectedKey, theExpectedUri ); QgsRasterDataProvider* expectedProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( theExpectedKey, theExpectedUri ); if ( !expectedProvider || !expectedProvider->isValid() ) { error( QString( "Cannot load provider %1 with URI: %2" ).arg( theExpectedKey ).arg( theExpectedUri ), mReport ); ok = false; } if ( !ok ) return false; mReport += QString( "Verified URI: %1<br>" ).arg( theVerifiedUri.replace( "&", "&" ) ); mReport += QString( "Expected URI: %1<br>" ).arg( theExpectedUri.replace( "&", "&" ) ); mReport += "<br>"; mReport += QString( "<table style='%1'>\n" ).arg( mTabStyle ); mReport += compareHead(); compare( "Band count", verifiedProvider->bandCount(), expectedProvider->bandCount(), mReport, ok ); compare( "Width", verifiedProvider->xSize(), expectedProvider->xSize(), mReport, ok ); compare( "Height", verifiedProvider->ySize(), expectedProvider->ySize(), mReport, ok ); compareRow( "Extent", verifiedProvider->extent().toString(), expectedProvider->extent().toString(), mReport, verifiedProvider->extent() == expectedProvider->extent() ); if ( verifiedProvider->extent() != expectedProvider->extent() ) ok = false; mReport += "</table>\n"; if ( !ok ) return false; bool allOk = true; for ( int band = 1; band <= expectedProvider->bandCount(); band++ ) { bool bandOk = true; mReport += QString( "<h3>Band %1</h3>\n" ).arg( band ); mReport += QString( "<table style='%1'>\n" ).arg( mTabStyle ); mReport += compareHead(); // Data types may differ (?) bool typesOk = true; compare( "Source data type", verifiedProvider->srcDataType( band ), expectedProvider->srcDataType( band ), mReport, typesOk ); compare( "Data type", verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk ) ; // TODO: not yet sure if noDataValue() should exist at all //compare( "No data (NULL) value", verifiedProvider->noDataValue( band ), expectedProvider->noDataValue( band ), mReport, typesOk ); bool statsOk = true; QgsRasterBandStats verifiedStats = verifiedProvider->bandStatistics( band ); QgsRasterBandStats expectedStats = expectedProvider->bandStatistics( band ); // Min/max may 'slightly' differ, for big numbers however, the difference may // be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24 double tol = tolerance( expectedStats.minimumValue ); compare( "Minimum value", verifiedStats.minimumValue, expectedStats.minimumValue, mReport, statsOk, tol ); tol = tolerance( expectedStats.maximumValue ); compare( "Maximum value", verifiedStats.maximumValue, expectedStats.maximumValue, mReport, statsOk, tol ); // TODO: enable once fixed (WCS excludes nulls but GDAL does not) //compare( "Cells count", verifiedStats.elementCount, expectedStats.elementCount, mReport, statsOk ); tol = tolerance( expectedStats.mean ); compare( "Mean", verifiedStats.mean, expectedStats.mean, mReport, statsOk, tol ); // stdDev usually differ significantly tol = tolerance( expectedStats.stdDev, 1 ); compare( "Standard deviation", verifiedStats.stdDev, expectedStats.stdDev, mReport, statsOk, tol ); mReport += "</table>"; mReport += "<br>"; if ( !bandOk ) { allOk = false; continue; } if ( !statsOk || !typesOk ) { allOk = false; // create values table anyway so that values are available } mReport += "<table><tr>"; mReport += "<td>Data comparison</td>"; mReport += QString( "<td style='%1 %2 border: 1px solid'>correct value</td>" ).arg( mCellStyle ).arg( mOkStyle ); mReport += "<td></td>"; mReport += QString( "<td style='%1 %2 border: 1px solid'>wrong value<br>expected value</td></tr>" ).arg( mCellStyle ).arg( mErrStyle ); mReport += "</tr></table>"; mReport += "<br>"; int width = expectedProvider->xSize(); int height = expectedProvider->ySize(); QgsRasterBlock *expectedBlock = expectedProvider->block( band, expectedProvider->extent(), width, height ); QgsRasterBlock *verifiedBlock = verifiedProvider->block( band, expectedProvider->extent(), width, height ); if ( !expectedBlock || !expectedBlock->isValid() || !verifiedBlock || !verifiedBlock->isValid() ) { allOk = false; mReport += "cannot read raster block"; continue; } // compare data values QString htmlTable = QString( "<table style='%1'>" ).arg( mTabStyle ); for ( int row = 0; row < height; row ++ ) { htmlTable += "<tr>"; for ( int col = 0; col < width; col ++ ) { bool cellOk = true; double verifiedVal = verifiedBlock->value( row, col ); double expectedVal = expectedBlock->value( row, col ); QString valStr; if ( compare( verifiedVal, expectedVal, 0 ) ) { valStr = QString( "%1" ).arg( verifiedVal ); } else { cellOk = false; allOk = false; valStr = QString( "%1<br>%2" ).arg( verifiedVal ).arg( expectedVal ); } htmlTable += QString( "<td style='%1 %2'>%3</td>" ).arg( mCellStyle ).arg( cellOk ? mOkStyle : mErrStyle ).arg( valStr ); } htmlTable += "</tr>"; } htmlTable += "</table>"; mReport += htmlTable; delete expectedBlock; delete verifiedBlock; } delete verifiedProvider; delete expectedProvider; return allOk; }
int QgsGrassGisLib::readRasterRow( int fd, void * buf, int row, RASTER_MAP_TYPE data_type, bool noDataAsZero ) { if ( row < 0 || row >= mRows ) { QgsDebugMsg( QString( "row %1 out of range 0 - %2" ).arg( row ).arg( mRows ) ); return 0; } // TODO: use cached block with more rows Raster raster = mRasters.value( fd ); //if ( !raster.provider ) return -1; if ( !raster.input ) return -1; // Create extent for current row QgsRectangle blockRect = mExtent; double yRes = mExtent.height() / mRows; double yMax = mExtent.yMaximum() - yRes * row; //QgsDebugMsg( QString( "height = %1 mRows = %2" ).arg( mExtent.height() ).arg( mRows ) ); //QgsDebugMsg( QString( "row = %1 yRes = %2 yRes * row = %3" ).arg( row ).arg( yRes ).arg( yRes * row ) ); //QgsDebugMsg( QString( "mExtent.yMaximum() = %1 yMax = %2" ).arg( mExtent.yMaximum() ).arg( yMax ) ); blockRect.setYMaximum( yMax ); blockRect.setYMinimum( yMax - yRes ); QgsRasterBlock *block = raster.input->block( raster.band, blockRect, mColumns, 1 ); if ( !block ) return -1; QGis::DataType requestedType = qgisRasterType( data_type ); //QgsDebugMsg( QString("data_type = %1").arg(data_type) ); //QgsDebugMsg( QString("requestedType = %1").arg(requestedType) ); //QgsDebugMsg( QString("requestedType size = %1").arg( QgsRasterBlock::typeSize( requestedType ) ) ); //QgsDebugMsg( QString("block->dataType = %1").arg( block->dataType() ) ); block->convert( requestedType ); memcpy( buf, block->bits( 0 ), QgsRasterBlock::typeSize( requestedType ) * mColumns ); for ( int i = 0; i < mColumns; i++ ) { QgsDebugMsgLevel( QString( "row = %1 i = %2 val = %3 isNoData = %4" ).arg( row ).arg( i ).arg( block->value( i ) ).arg( block->isNoData( i ) ), 5 ); //(( CELL * ) buf )[i] = i; if ( block->isNoData( 0, i ) ) { if ( noDataAsZero ) { switch ( data_type ) { case CELL_TYPE: G_zero(( char * ) &(( CELL * ) buf )[i], G_raster_size( data_type ) ); break; case FCELL_TYPE: G_zero(( char * ) &(( FCELL * ) buf )[i], G_raster_size( data_type ) ); break; case DCELL_TYPE: G_zero(( char * ) &(( DCELL * ) buf )[i], G_raster_size( data_type ) ); break; default: break; } } else { switch ( data_type ) { case CELL_TYPE: G_set_c_null_value( &(( CELL * ) buf )[i], 1 ); break; case FCELL_TYPE: G_set_f_null_value( &(( FCELL * ) buf )[i], 1 ); break; case DCELL_TYPE: G_set_d_null_value( &(( DCELL * ) buf )[i], 1 ); break; default: break; } } } //else //{ //memcpy( &( buf[i] ), block->bits( 0, i ), 4 ); //buf[i] = (int) block->value( 0, i); //QgsDebugMsg( QString("buf[i] = %1").arg(buf[i])); //} } delete block; return 1; }
// Default implementation for values QgsRasterIdentifyResult QgsRasterDataProvider::identify( const QgsPointXY &point, QgsRaster::IdentifyFormat format, const QgsRectangle &boundingBox, int width, int height, int /*dpi*/ ) { QgsDebugMsgLevel( "Entered", 4 ); QMap<int, QVariant> results; if ( format != QgsRaster::IdentifyFormatValue || !( capabilities() & IdentifyValue ) ) { QgsDebugMsg( "Format not supported" ); return QgsRasterIdentifyResult( ERR( tr( "Format not supported" ) ) ); } if ( !extent().contains( point ) ) { // Outside the raster for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ ) { results.insert( bandNo, QVariant() ); } return QgsRasterIdentifyResult( QgsRaster::IdentifyFormatValue, results ); } QgsRectangle finalExtent = boundingBox; if ( finalExtent.isEmpty() ) finalExtent = extent(); if ( width == 0 ) { width = capabilities() & Size ? xSize() : 1000; } if ( height == 0 ) { height = capabilities() & Size ? ySize() : 1000; } // Calculate the row / column where the point falls double xres = ( finalExtent.width() ) / width; double yres = ( finalExtent.height() ) / height; int col = static_cast< int >( std::floor( ( point.x() - finalExtent.xMinimum() ) / xres ) ); int row = static_cast< int >( std::floor( ( finalExtent.yMaximum() - point.y() ) / yres ) ); double xMin = finalExtent.xMinimum() + col * xres; double xMax = xMin + xres; double yMax = finalExtent.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 ); if ( myBlock ) { double value = myBlock->value( 0 ); results.insert( i, value ); delete myBlock; } else { results.insert( i, QVariant() ); } } return QgsRasterIdentifyResult( QgsRaster::IdentifyFormatValue, results ); }
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 QList<QgsRasterBlock::Range> myNoDataRangeList = userNoDataValue( theBandNo ); if ( !myNoDataRangeList.isEmpty() ) { double myNoDataValue = noDataValue( theBandNo ); size_t size = theWidth * theHeight; for ( size_t i = 0; i < size; i++ ) { double value = block->value( i ); if ( QgsRasterBlock::valueInRange( value, myNoDataRangeList ) ) { block->setValue( i, myNoDataValue ); } } } return block; }