diff --git a/CODING b/CODING index 972e9ea17a2..65d4d14b957 100644 --- a/CODING +++ b/CODING @@ -58,8 +58,14 @@ Developers guide for QGIS 3.4. The ADD_QGIS_TEST macro explained 3.5. Building your unit test 3.6. Run your tests - 4. HIG (Human Interface Guidelines) - 5. Authors + 4. Getting up and running with QtCreator and QGIS + 4.1. Installing QtCreator + 4.2. Setting up your project + 4.3. Setting up your build environment + 4.4. Setting your run environment + 4.5. Running and debugging + 5. HIG (Human Interface Guidelines) + 6. Authors ------------------------------------------------------------------------ @@ -1208,7 +1214,163 @@ works in a truly platform way. I will update this document as things progress. - 4. HIG (Human Interface Guidelines) + 4. Getting up and running with QtCreator and QGIS + ================================================= + +QtCreator is a newish IDE from the makers of the Qt library. With QtCreator you +can build any C++ project, but it's really optimised for people working on +Qt(4) based applications (including mobile apps). Everything I describe below +assumes you are running Ubuntu 11.04 'Natty'. + + + 4.1. Installing QtCreator + ========================= + +This part is easy: + + sudo apt-get install qtcreator qtcreator-doc + +After installing you should find it in your gnome menu. + + + 4.2. Setting up your project + ============================ + +I'm assuming you have already got a local Quantum-GIS clone containing the +source code, and have installed all needed build dependencies etc. There are +detailed in instructions on doing that here: + +http://github.com/qgis/Quantum-GIS/blob/master/CODING + +On my system I have checked out the code into $HOME/dev/cpp/Quantum-GIS and the +rest of the article is written assuming that, you should update these paths as +appropriate for your local system. + +On launching QtCreator do: + + File->Open File or Project + +Then use the resulting file selection dialog to browse to and open this file: + + $HOME/dev/cpp/Quantum-GIS/CMakeLists.txt + +[images/image01.jpeg] + +Next you will be prompted for a build location. I create a specific build dir +for QtCreator to work in under: + + $HOME/dev/cpp/Quantum-GIS/build-master-qtcreator + +Its probably a good idea to create separate build directories for different +branches if you can afford the disk space. + +[images/image02.jpeg] + +Next you will be asked if you have any CMake build options to pass to CMake. We +will tell CMake that we want a debug build by adding this option: + + -DCMAKE_BUILD_TYPE=Debug + +[images/image03.jpeg] + +Thats the basics of it. When you complete the Wizard, QtCreator will start +scanning the source tree for autocompletion support and do some other +housekeeping stuff in the background. We want to tweak a few things before we +start to build though. + + + 4.3. Setting up your build environment + ====================================== + +Click on the 'Projects' icon on the left of the QtCreator window. + +[images/image04.jpeg] + +Select the build settings tab (normally active by default). + +[images/image05.jpeg] + +We now want to add a custom process step. Why? Because QGIS can currently only +run from an install directory, not its build directory, so we need to ensure +that it is installed whenever we build it. Under 'Build Steps', click on the +'Add Build Step' combo button and choose 'Custom Process Step'. + +[images/image06.jpeg] + +Now we set the following details: + + Enable custom process step [yes] + Command: make + Working directory: $HOME/dev/cpp/Quantum-GIS/build-master-qtcreator + Command arguments: install + +[images/image07.jpeg] + +You are almost ready to build. Just one note: QtCreator will need write +permissions on the install prefix. By default (which I am using here) QGIS is +going to get installed to /usr/local. For my purposes on my development +machine, I just gave myself write permissions to the /usr/local directory. + +To start the build, click that big hammer icon on the bottom left of the +window. + +[images/image08.jpeg] + + + 4.4. Setting your run environment + ================================= + +As mentioned above, we cannot run QGIS from directly in the build directly, so +we need to create a custom run target to tell QtCreator to run QGIS from the +install dir (in my case /usr/local/). To do that, return to the projects +configuration screen. + +[images/image04.jpeg] + +Now select the 'Run Settings' tab + +[images/image09.jpeg] + +We need to update the default run settings from using the 'qgis' run +configuration to using a custom one. + +[images/image10.jpeg] + +Do do that, click the 'Add v' combo button next to the Run configuration +combo and choose 'Custom Executable' from the top of the list. + +[images/image11.jpeg] + +Now in the properties area set the following details: + + Executable: /usr/local/bin/qgis + Arguments : + Working directory: $HOME + Run in terminal: [no] + Debugger: C++ [yes] + Qml [no] + +Then click the 'Rename' button and give your custom executable a meaning full +name e.g. 'Installed QGIS' + +[images/image12.jpeg] + + + 4.5. Running and debugging + ========================== + +Now you are ready to run and debug QGIS. To set a break point, simply open a +source file and click in the left column. + +[images/image14.jpeg] + +Now launch QGIS under the debugger by clicking the icon with a bug on it in the +bottom left of the window. + +[images/image13.jpeg] + + + 5. HIG (Human Interface Guidelines) =================================== In order for all graphical user interface elements to appear consistant and to @@ -1257,7 +1419,7 @@ guidelines are followed in layout and design of GUIs. suffixed to the button text. - 5. Authors + 6. Authors ========== - Tim Sutton (author and editor) diff --git a/INSTALL b/INSTALL index bd84ba27b52..fa56ba51610 100644 --- a/INSTALL +++ b/INSTALL @@ -1,10 +1,10 @@ Quantum GIS (QGIS) Building QGIS from source - step by step -Sunday June 12, 2011 +Thursday August 04, 2011 -Last Updated: Sunday June 12, 2011 -Last Change : Sunday June 05, 2011 +Last Updated: Thursday August 04, 2011 +Last Change : Tuesday June 28, 2011 1. Introduction diff --git a/doc/INSTALL.html b/doc/INSTALL.html index c82b5039654..d3a64c15c5b 100644 --- a/doc/INSTALL.html +++ b/doc/INSTALL.html @@ -45,13 +45,13 @@ pre, code { font-family: monospace }

-Last Updated: Sunday June 12, 2011 -Last Change : Sunday June 05, 2011 +Last Updated: Thursday August 04, 2011 +Last Change : Tuesday June 28, 2011

diff --git a/src/app/qgsrasterlayerproperties.cpp b/src/app/qgsrasterlayerproperties.cpp index 7fef395fdea..81656e6b6ff 100644 --- a/src/app/qgsrasterlayerproperties.cpp +++ b/src/app/qgsrasterlayerproperties.cpp @@ -1882,7 +1882,7 @@ void QgsRasterLayerProperties::refreshHistogram() // bin in all selected layers, and the min. It then draws a scaled line between min // and max - scaled to image height. 1 line drawn per selected band // - const int BINCOUNT = 255; + const int BINCOUNT = 256; bool myIgnoreOutOfRangeFlag = true; bool myThoroughBandScanFlag = false; int myBandCountInt = mRasterLayer->bandCount(); diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 4c8115e1c08..61711877f42 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -64,6 +64,10 @@ QString QgsApplication::mBuildOutputPath; */ QgsApplication::QgsApplication( int & argc, char ** argv, bool GUIenabled, QString customConfigPath ) : QApplication( argc, argv, GUIenabled ) +{ + init( customConfigPath ); //initi can also be called directly by e.g. unit tests that dont inherit QApplication. +} +void QgsApplication::init( QString customConfigPath ) { // check if QGIS is run from build directory (not the install directory) QDir appDir( applicationDirPath() ); diff --git a/src/core/qgsapplication.h b/src/core/qgsapplication.h index 7642da1ee0c..86967004474 100644 --- a/src/core/qgsapplication.h +++ b/src/core/qgsapplication.h @@ -32,6 +32,10 @@ class CORE_EXPORT QgsApplication: public QApplication QgsApplication( int & argc, char ** argv, bool GUIenabled, QString customConfigPath = QString() ); virtual ~QgsApplication(); + /** This method initialises paths etc for QGIS. Called by the ctor or call it manually + when your app does not extend the QApplication class. */ + static void init( QString customConfigPath = QString() ); + //! Watch for QFileOpenEvent. virtual bool event( QEvent * event ); diff --git a/src/core/qgsrasterdataprovider.cpp b/src/core/qgsrasterdataprovider.cpp index 2a76a15ef5e..b7206ce4474 100644 --- a/src/core/qgsrasterdataprovider.cpp +++ b/src/core/qgsrasterdataprovider.cpp @@ -201,31 +201,31 @@ QByteArray QgsRasterDataProvider::noValueBytes( int theBandNo ) double d; switch ( type ) { - case QgsRasterDataProvider::Byte: + case Byte: uc = ( unsigned char )noval; memcpy( data, &uc, size ); break; - case QgsRasterDataProvider::UInt16: + case UInt16: us = ( unsigned short )noval; memcpy( data, &us, size ); break; - case QgsRasterDataProvider::Int16: + case Int16: s = ( short )noval; memcpy( data, &s, size ); break; - case QgsRasterDataProvider::UInt32: + case UInt32: ui = ( unsigned int )noval; memcpy( data, &ui, size ); break; - case QgsRasterDataProvider::Int32: + case Int32: i = ( int )noval; memcpy( data, &i, size ); break; - case QgsRasterDataProvider::Float32: + case Float32: f = ( float )noval; memcpy( data, &f, size ); break; - case QgsRasterDataProvider::Float64: + case Float64: d = ( double )noval; memcpy( data, &d, size ); break; @@ -235,4 +235,188 @@ QByteArray QgsRasterDataProvider::noValueBytes( int theBandNo ) return ba; } + +QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo ) +{ + double myNoDataValue = noDataValue(); + QgsRasterBandStats myRasterBandStats; + myRasterBandStats.elementCount = 0; // because we'll be counting only VALID pixels later + + 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; +} + +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; +} + // ENDS diff --git a/src/core/qgsrasterdataprovider.h b/src/core/qgsrasterdataprovider.h index 839c9e30ee5..f2f5a09c5a4 100644 --- a/src/core/qgsrasterdataprovider.h +++ b/src/core/qgsrasterdataprovider.h @@ -30,6 +30,7 @@ #include "qgscoordinatereferencesystem.h" #include "qgsrasterbandstats.h" +#include "cpl_conv.h" #include class QImage; @@ -109,10 +110,11 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider }; // Progress types - enum Progress + enum RasterProgressType { ProgressHistogram = 0, - ProgressPyramids = 1 + ProgressPyramids = 1, + ProgressStatistics = 2 }; QgsRasterDataProvider(); @@ -325,6 +327,9 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider /** read block of data using give extent and size */ virtual void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, QgsCoordinateReferenceSystem theSrcCRS, QgsCoordinateReferenceSystem theDestCRS, void *data ); + + /* Read a value from a data block at a given index. */ + virtual double readValue( void *data, int type, int index ); /** value representing null data */ virtual double noDataValue() const { return 0; } @@ -365,13 +370,16 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider */ virtual QList buildPyramidList() { return QList(); }; + /** If the provider supports it, return band stats for the + given band. Default behaviour is to blockwise read the data + and generate the stats unless the provider overloads this function. */ + virtual QgsRasterBandStats bandStatistics( int theBandNo ); /** \brief helper function to create zero padded band names */ QString generateBandName( int theBandNumber ) { return tr( "Band" ) + QString( " %1" ) .arg( theBandNumber, 1 + ( int ) log10(( float ) bandCount() ), 10, QChar( '0' ) ); - } - + }; /** * Get metadata in a format suitable for feeding directly diff --git a/src/core/raster/qgspseudocolorshader.cpp b/src/core/raster/qgspseudocolorshader.cpp index 96175a570a0..4f132418e6f 100644 --- a/src/core/raster/qgspseudocolorshader.cpp +++ b/src/core/raster/qgspseudocolorshader.cpp @@ -44,7 +44,7 @@ bool QgsPseudoColorShader::shade( double theValue, int* theReturnRedValue, int* } //check if we are in the first class break - if (( myPixelValue >= mClassBreakMin1 ) && ( myPixelValue < mClassBreakMax1 ) ) + if ( ( myPixelValue >= mClassBreakMin1 ) && ( myPixelValue < mClassBreakMax1 ) ) { *theReturnRedValue = 0; *theReturnGreenValue = static_cast < int >((( 255 / mMinimumMaximumRange ) * ( myPixelValue - mClassBreakMin1 ) ) * 3 ); diff --git a/src/core/raster/qgsrasterlayer.cpp b/src/core/raster/qgsrasterlayer.cpp index 29800556c8a..10c3b94da61 100755 --- a/src/core/raster/qgsrasterlayer.cpp +++ b/src/core/raster/qgsrasterlayer.cpp @@ -327,7 +327,6 @@ const QgsRasterBandStats QgsRasterLayer::bandStatistics( int theBandNo ) QgsRasterBandStats myNullReturnStats; return myNullReturnStats; } - // check if we have received a valid band number if (( mDataProvider->bandCount() < theBandNo ) && mRasterType != Palette ) { @@ -341,7 +340,6 @@ const QgsRasterBandStats QgsRasterLayer::bandStatistics( int theBandNo ) QgsRasterBandStats myNullReturnStats; return myNullReturnStats; } - // check if we have previously gathered stats for this band... if ( theBandNo < 1 || theBandNo > mRasterStatsList.size() ) { @@ -358,174 +356,23 @@ const QgsRasterBandStats QgsRasterLayer::bandStatistics( int theBandNo ) { return myRasterBandStats; } - // only print message if we are actually gathering the stats - emit statusChanged( tr( "Retrieving stats for %1" ).arg( name() ) ); - qApp->processEvents(); - QgsDebugMsg( "stats for band " + QString::number( theBandNo ) ); - - myRasterBandStats.elementCount = 0; // because we'll be counting only VALID pixels later - - emit statusChanged( tr( "Calculating stats for %1" ).arg( name() ) ); - //reset the main app progress bar - emit drawingProgress( 0, 0 ); - - int myDataType = mDataProvider->dataType( theBandNo ); - - int myNXBlocks, myNYBlocks, myXBlockSize, myYBlockSize; - myXBlockSize = mDataProvider->xBlockSize(); - myYBlockSize = mDataProvider->yBlockSize(); - - myNXBlocks = ( mDataProvider->xSize() + myXBlockSize - 1 ) / myXBlockSize; - myNYBlocks = ( mDataProvider->ySize() + myYBlockSize - 1 ) / myYBlockSize; - - void *myData = CPLMalloc( myXBlockSize * myYBlockSize * ( mDataProvider->dataTypeSize( theBandNo ) / 8 ) ); - - // unfortunately we need to make two passes through the data to calculate stddev - bool myFirstIterationFlag = true; - - int myBandXSize = mDataProvider->xSize(); - int myBandYSize = mDataProvider->ySize(); - for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ ) - { - emit drawingProgress( iYBlock, myNYBlocks * 2 ); - - for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ ) - { - int nXValid, nYValid; - mDataProvider->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 - mNoDataValue ) <= TINY_VALUE || myValue != myValue ) ) - { - 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++ ) - { - emit drawingProgress( iYBlock + myNYBlocks, myNYBlocks * 2 ); - - for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ ) - { - int nXValid, nYValid; - - mDataProvider->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 - mNoDataValue ) <= TINY_VALUE || myValue != myValue ) ) - { - 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", mNoDataValue, 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; + myRasterBandStats = mDataProvider->bandStatistics( theBandNo ); QgsDebugMsg( "adding stats to stats collection at position " + QString::number( theBandNo - 1 ) ); //add this band to the class stats map mRasterStatsList[theBandNo - 1] = myRasterBandStats; emit drawingProgress( mHeight, mHeight ); //reset progress - //QApplication::restoreOverrideCursor(); //restore the cursor QgsDebugMsg( "Stats collection completed returning" ); return myRasterBandStats; - } // QgsRasterLayer::bandStatistics const QgsRasterBandStats QgsRasterLayer::bandStatistics( QString const & theBandName ) { - + // only print message if we are actually gathering the stats + emit statusChanged( tr( "Retrieving stats for %1" ).arg( name() ) ); + qApp->processEvents(); + //reset the main app progress bar + emit drawingProgress( 0, 0 ); //we cant use a vector iterator because the iterator is astruct not a class //and the qvector model does not like this. for ( int i = 1; i <= mDataProvider->bandCount(); i++ ) diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 086cf844322..52524db1bfb 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -105,7 +105,9 @@ QgsGdalProvider::QgsGdalProvider( QString const & uri ) // To get buildSupportedRasterFileFilter the provider is called with empty uri if ( uri.isEmpty() ) + { return; + } mGdalDataset = NULL; @@ -287,51 +289,6 @@ QgsGdalProvider::QgsGdalProvider( QString const & uri ) QgsDebugMsg( QString( "mNoDataValue[%1] = %1" ).arg( i - 1 ).arg( mNoDataValue[i-1] ) ); } - // This block of code was in old version in QgsRasterLayer::bandStatistics - //ifdefs below to remove compiler warning about unused vars -#ifdef QGISDEBUG -#if 0 - int success; - double GDALminimum = GDALGetRasterMinimum( myGdalBand, &success ); - - if ( ! success ) - { - QgsDebugMsg( "myGdalBand->GetMinimum() failed" ); - } - - double GDALmaximum = GDALGetRasterMaximum( myGdalBand, &success ); - - if ( ! success ) - { - QgsDebugMsg( "myGdalBand->GetMaximum() failed" ); - } - - double GDALnodata = GDALGetRasterNoDataValue( myGdalBand, &success ); - - if ( ! success ) - { - QgsDebugMsg( "myGdalBand->GetNoDataValue() failed" ); - } - - QgsLogger::debug( "GDALminium: ", GDALminimum, __FILE__, __FUNCTION__, __LINE__ ); - QgsLogger::debug( "GDALmaximum: ", GDALmaximum, __FILE__, __FUNCTION__, __LINE__ ); - QgsLogger::debug( "GDALnodata: ", GDALnodata, __FILE__, __FUNCTION__, __LINE__ ); - - double GDALrange[2]; // calculated min/max, as opposed to the - // dataset provided - - GDALComputeRasterMinMax( myGdalBand, 1, GDALrange ); - QgsLogger::debug( "approximate computed GDALminium:", GDALrange[0], __FILE__, __FUNCTION__, __LINE__, 1 ); - QgsLogger::debug( "approximate computed GDALmaximum:", GDALrange[1], __FILE__, __FUNCTION__, __LINE__, 1 ); - - GDALComputeRasterMinMax( myGdalBand, 0, GDALrange ); - QgsLogger::debug( "exactly computed GDALminium:", GDALrange[0] ); - QgsLogger::debug( "exactly computed GDALmaximum:", GDALrange[1] ); - - QgsDebugMsg( "starting manual stat computation" ); -#endif -#endif - mValid = true; QgsDebugMsg( "end" ); } @@ -391,7 +348,9 @@ QgsGdalProvider::~QgsGdalProvider() void QgsGdalProvider::closeDataset() { if ( !mValid ) + { return; + } mValid = false; GDALDereferenceDataset( mGdalBaseDataset ); @@ -921,25 +880,37 @@ void QgsGdalProvider::computeMinMax( int theBandNo ) { QgsDebugMsg( QString( "theBandNo = %1 mMinMaxComputed = %2" ).arg( theBandNo ).arg( mMinMaxComputed[theBandNo-1] ) ); if ( mMinMaxComputed[theBandNo-1] ) + { return; - double GDALrange[2]; + } + int bApproxOK=false; + double pdfMin; + double pdfMax; + double pdfMean; + double pdfStdDev; GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo ); - GDALComputeRasterMinMax( myGdalBand, 1, GDALrange ); //Approximate - QgsDebugMsg( QString( "GDALrange[0] = %1 GDALrange[1] = %2" ).arg( GDALrange[0] ).arg( GDALrange[1] ) ); - mMinimum[theBandNo-1] = GDALrange[0]; - mMaximum[theBandNo-1] = GDALrange[1]; + double myerval = GDALGetRasterStatistics ( + myGdalBand, + bApproxOK, + TRUE, + &pdfMin, + &pdfMax, + &pdfMean, + &pdfStdDev + ); + Q_UNUSED(myerval); + mMinimum[theBandNo-1] = pdfMin; + mMaximum[theBandNo-1] = pdfMax; } double QgsGdalProvider::minimumValue( int theBandNo ) const { QgsDebugMsg( QString( "theBandNo = %1" ).arg( theBandNo ) ); - //computeMinMax ( theBandNo ); return mMinimum[theBandNo-1]; } double QgsGdalProvider::maximumValue( int theBandNo ) const { QgsDebugMsg( QString( "theBandNo = %1" ).arg( theBandNo ) ); - //computeMinMax ( theBandNo ); return mMaximum[theBandNo-1]; } @@ -1294,7 +1265,9 @@ void QgsGdalProvider::populateHistogram( int theBandNo, QgsRasterBandStats & t //vector is not equal to the number of bins //i.e if the histogram has never previously been generated or the user has //selected a new number of bins. - if ( theBandStats.histogramVector->size() != theBinCount || + bool myCollectHistogramFlag = true; + if ( theBandStats.histogramVector == 0 || + theBandStats.histogramVector->size() != theBinCount || theIgnoreOutOfRangeFlag != theBandStats.isHistogramOutOfRange || theHistogramEstimatedFlag != theBandStats.isHistogramEstimated ) { @@ -1865,6 +1838,74 @@ QGISEXTERN bool isValidRasterFileName( QString const & theFileNameQString, QStri } } + + +QgsRasterBandStats QgsGdalProvider::bandStatistics( int theBandNo ) +{ + GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo ); + QgsRasterBandStats myRasterBandStats; + int bApproxOK=false; + double pdfMin; + double pdfMax; + double pdfMean; + double pdfStdDev; + QgsGdalProgress myProg; + myProg.type = ProgressHistogram; + myProg.provider = this; + + // Suggested by Etienne Sky to use getrasterstatistics instead of compute + // since computerasterstatistics forces collection each time + // where as getrasterstatistics uses aux.xml cached copy if available + // Note: there is currently no progress callback in this method + double myerval = GDALGetRasterStatistics ( + myGdalBand, + bApproxOK, + TRUE, + &pdfMin, + &pdfMax, + &pdfMean, + &pdfStdDev + ); + //double myerval = GDALComputeRasterStatistics ( myGdalBand, + // bApproxOK, + // &pdfMin, + // &pdfMax, + // &pdfMean, + // &pdfStdDev, + // progressCallback, + // &myProg + //) ; + //end of first pass through data now calculate the range + myRasterBandStats.bandName = generateBandName( theBandNo ); + myRasterBandStats.bandNumber = theBandNo; + myRasterBandStats.range = pdfMax - pdfMin; + myRasterBandStats.minimumValue = pdfMin; + myRasterBandStats.maximumValue = pdfMax; + //calculate the mean + myRasterBandStats.mean = pdfMean; + myRasterBandStats.sum = 0; //not available via gdal + myRasterBandStats.elementCount = mWidth * mHeight; + myRasterBandStats.sumOfSquares = 0; //not available via gdal + myRasterBandStats.stdDev = pdfStdDev; + myRasterBandStats.statsGathered = true; + +#ifdef QGISDEBUG + QgsLogger::debug( "************ STATS **************", 1, __FILE__, __FUNCTION__, __LINE__ ); + QgsLogger::debug( "VALID NODATA", mValidNoDataValue, 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 + + myRasterBandStats.statsGathered = true; + + return myRasterBandStats; + +} // QgsGdalProvider::bandStatistics + + /** Builds the list of file filter strings to later be used by QgisApp::addRasterLayer() diff --git a/src/providers/gdal/qgsgdalprovider.h b/src/providers/gdal/qgsgdalprovider.h index 9d5a24ecd35..db6ed3d93d7 100644 --- a/src/providers/gdal/qgsgdalprovider.h +++ b/src/providers/gdal/qgsgdalprovider.h @@ -26,6 +26,7 @@ #include "qgsrasterdataprovider.h" #include "qgsrectangle.h" #include "qgscolorrampshader.h" +#include "qgsrasterbandstats.h" #include #include @@ -222,6 +223,13 @@ class QgsGdalProvider : public QgsRasterDataProvider /** \brief Returns the sublayers of this layer - Useful for providers that manage their own layers, such as WMS */ QStringList subLayers() const; + /** \brief If the provider supports it, return band stats for the + given band. + @note added in QGIS 1.7 + @note overloads virtual method from QgsRasterProvider::bandStatistics + + */ + QgsRasterBandStats bandStatistics( int theBandNo ); void populateHistogram( int theBandNoInt, QgsRasterBandStats & theBandStats, diff --git a/tests/src/core/testqgsapplication.cpp b/tests/src/core/testqgsapplication.cpp index 4490e39aac9..8527eec8d72 100644 --- a/tests/src/core/testqgsapplication.cpp +++ b/tests/src/core/testqgsapplication.cpp @@ -41,7 +41,7 @@ void TestQgsApplication::initTestCase() // init QGIS's paths - true means that all path will be inited from prefix QString qgisPath = QCoreApplication::applicationDirPath(); QgsApplication::setPrefixPath( INSTALL_PREFIX, true ); - QgsApplication::showSettings(); + qDebug( QgsApplication::showSettings().toUtf8() ); }; void TestQgsApplication::checkTheme() diff --git a/tests/src/core/testqgsrasterlayer.cpp b/tests/src/core/testqgsrasterlayer.cpp index 058b025bed5..d6e0b335035 100644 --- a/tests/src/core/testqgsrasterlayer.cpp +++ b/tests/src/core/testqgsrasterlayer.cpp @@ -57,6 +57,7 @@ class TestQgsRasterLayer: public QObject void landsatBasic(); void landsatBasic875Qml(); void checkDimensions(); + void checkStats(); void buildExternalOverviews(); void registry(); private: @@ -73,9 +74,10 @@ class TestQgsRasterLayer: public QObject void TestQgsRasterLayer::initTestCase() { // init QGIS's paths - true means that all path will be inited from prefix - QString qgisPath = QCoreApplication::applicationDirPath(); - QgsApplication::setPrefixPath( INSTALL_PREFIX, true ); - QgsApplication::showSettings(); + QgsApplication::init( QString() ); + QgsApplication::initQgis(); + QString mySettings = QgsApplication::showSettings(); + mySettings = mySettings.replace("\n","
"); //create some objects that will be used in all tests... //create a raster layer that will be used in all tests... mTestDataDir = QString( TEST_DATA_DIR ) + QDir::separator(); //defined in CmakeLists.txt @@ -96,6 +98,7 @@ void TestQgsRasterLayer::initTestCase() myLayers << mpRasterLayer->id(); mpMapRenderer->setLayerSet( myLayers ); mReport += "

Raster Layer Tests

\n"; + mReport += "

" + mySettings + "

"; } //runs after all tests void TestQgsRasterLayer::cleanupTestCase() @@ -115,6 +118,7 @@ void TestQgsRasterLayer::cleanupTestCase() void TestQgsRasterLayer::isValid() { QVERIFY( mpRasterLayer->isValid() ); + mpRasterLayer->setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum, false ); mpMapRenderer->setExtent( mpRasterLayer->extent() ); QVERIFY( render( "raster" ) ); } @@ -133,6 +137,7 @@ void TestQgsRasterLayer::pseudoColor() void TestQgsRasterLayer::landsatBasic() { + mpLandsatRasterLayer->setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum, false ); QStringList myLayers; myLayers << mpLandsatRasterLayer->id(); mpMapRenderer->setLayerSet( myLayers ); @@ -156,13 +161,26 @@ void TestQgsRasterLayer::checkDimensions() // regression check for ticket #832 // note bandStatistics call is base 1 QVERIFY( mpRasterLayer->bandStatistics( 1 ).elementCount == 100 ); + mReport += "

Check Dimensions

\n"; + mReport += "

Passed

"; +} +void TestQgsRasterLayer::checkStats() +{ + QVERIFY( mpRasterLayer->width() == 10 ); + QVERIFY( mpRasterLayer->height() == 10 ); + QVERIFY( mpRasterLayer->bandStatistics( 1 ).elementCount == 100 ); + QVERIFY( mpRasterLayer->bandStatistics( 1 ).minimumValue == 0 ); + QVERIFY( mpRasterLayer->bandStatistics( 1 ).maximumValue == 9 ); + QVERIFY( mpRasterLayer->bandStatistics( 1 ).mean == 4.5 ); + QVERIFY( mpRasterLayer->bandStatistics( 1 ).stdDev == 2.872281323269 ); + mReport += "

Check Stats

\n"; + mReport += "

Passed

"; } void TestQgsRasterLayer::buildExternalOverviews() { //before we begin delete any old ovr file (if it exists) //and make a copy of the landsat raster into the temp dir - QString myTempPath = QDir::tempPath() + QDir::separator(); QFile::remove( myTempPath + "landsat.tif.ovr" ); QFile::copy( mTestDataDir + "landsat.tif", myTempPath + "landsat.tif" ); @@ -205,6 +223,8 @@ void TestQgsRasterLayer::buildExternalOverviews() QVERIFY( QFile::exists( myTempPath + "landsat.tif.ovr" ) ); //cleanup delete mypLayer; + mReport += "

Check Overviews

\n"; + mReport += "

Passed

"; } diff --git a/tests/testdata/expected_landsat_875.png b/tests/testdata/expected_landsat_875.png index 7ff32ba87ac..ef2a77b4738 100644 Binary files a/tests/testdata/expected_landsat_875.png and b/tests/testdata/expected_landsat_875.png differ diff --git a/tests/testdata/expected_landsat_basic.png b/tests/testdata/expected_landsat_basic.png index b7ed1421ae9..966df40f769 100644 Binary files a/tests/testdata/expected_landsat_basic.png and b/tests/testdata/expected_landsat_basic.png differ diff --git a/tests/testdata/landsat_875.qml b/tests/testdata/landsat_875.qml index bdfec09ffaf..c3663289f84 100644 --- a/tests/testdata/landsat_875.qml +++ b/tests/testdata/landsat_875.qml @@ -1,73 +1,64 @@ - - - landsat_clip20080125220410500 - landsat_clip.tif - landsat_clip - - - +proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs - 2267 - 32633 - 32633 - WGS 84 / UTM zone 33N - utm - WGS84 - true - - - 255 - - - - MULTI_BAND_COLOR - UNDEFINED_SHADING_ALGORITHM - - 8 : Undefined - 7 : Undefined - 5 : Undefined - Not Set - 0 - STRETCH_TO_MINMAX - - - 122 - 130 - - - 133 - 148 - - - 57 - 157 - - - 0 - 255 - - - 61 - 117 - - - 0 - 255 - - - 92 - 213 - - - 122 - 255 - - - 0 - 255 - - - -9999.000000 - - + + 255 + + MultiBandColor + UndefinedShader + + Band 8 + Band 7 + Band 5 + Band 1 + 0 + + + + + StretchToMinimumMaximum + + + 0 + 255 + + + 0 + 255 + + + 0 + 255 + + + 0 + 255 + + + 61 + 123 + + + 0 + 255 + + + 92 + 232 + + + 122 + 255 + + + 0 + 255 + + + -32768.000000 + + + + + + +