Quantum GIS API Documentation  1.8
src/core/qgsrasterdataprovider.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsrasterdataprovider.cpp - DataProvider Interface for raster layers
00003      --------------------------------------
00004     Date                 : Mar 11, 2005
00005     Copyright            : (C) 2005 by Brendan Morley
00006     email                : morb at ozemail dot com dot au
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgsrasterdataprovider.h"
00019 #include "qgsrasterprojector.h"
00020 #include "qgslogger.h"
00021 
00022 #include <QTime>
00023 #include <QMap>
00024 #include <QByteArray>
00025 
00026 void QgsRasterDataProvider::readBlock( int bandNo, QgsRectangle
00027                                        const & viewExtent, int width,
00028                                        int height,
00029                                        QgsCoordinateReferenceSystem theSrcCRS,
00030                                        QgsCoordinateReferenceSystem theDestCRS,
00031                                        void *data )
00032 {
00033   QgsDebugMsg( "Entered" );
00034   QgsDebugMsg( "viewExtent = " + viewExtent.toString() );
00035 
00036   if ( ! theSrcCRS.isValid() || ! theDestCRS.isValid() || theSrcCRS == theDestCRS )
00037   {
00038     readBlock( bandNo, viewExtent, width, height, data );
00039     return;
00040   }
00041 
00042   QTime time;
00043   time.start();
00044 
00045   double mMaxSrcXRes = 0;
00046   double mMaxSrcYRes = 0;
00047 
00048   if ( capabilities() & QgsRasterDataProvider::ExactResolution )
00049   {
00050     mMaxSrcXRes = extent().width() / xSize();
00051     mMaxSrcYRes = extent().height() / ySize();
00052   }
00053 
00054   QgsRasterProjector myProjector( theSrcCRS, theDestCRS, viewExtent, height, width, mMaxSrcXRes, mMaxSrcYRes, extent() );
00055 
00056   QgsDebugMsg( QString( "create projector time  (ms): %1" ).arg( time.elapsed() ) );
00057 
00058   // TODO: init data by nulls
00059 
00060   // If we zoom out too much, projector srcRows / srcCols maybe 0, which can cause problems in providers
00061   if ( myProjector.srcRows() <= 0 || myProjector.srcCols() <= 0 )
00062     return;
00063 
00064   // Allocate memory for not projected source data
00065   int mySize = dataTypeSize( bandNo ) / 8;
00066   void *mySrcData = malloc( mySize * myProjector.srcRows() * myProjector.srcCols() );
00067 
00068   time.restart();
00069 
00070   readBlock( bandNo, myProjector.srcExtent(), myProjector.srcCols(), myProjector.srcRows(), mySrcData );
00071 
00072   QgsDebugMsg( QString( "read not projected block time  (ms): %1" ).arg( time.elapsed() ) );
00073   time.restart();
00074 
00075   // Project data from source
00076   int mySrcRow;
00077   int mySrcCol;
00078   int mySrcOffset;
00079   int myDestOffset;
00080   for ( int r = 0; r < height; r++ )
00081   {
00082     for ( int c = 0; c < width; c++ )
00083     {
00084       myProjector.srcRowCol( r, c, &mySrcRow, &mySrcCol );
00085       mySrcOffset = mySize * ( mySrcRow * myProjector.srcCols() + mySrcCol );
00086       myDestOffset = mySize * ( r * width + c );
00087       // retype to char is just to avoid g++ warning
00088       memcpy(( char* ) data + myDestOffset, ( char* )mySrcData + mySrcOffset, mySize );
00089     }
00090   }
00091   QgsDebugMsg( QString( "reproject block time  (ms): %1" ).arg( time.elapsed() ) );
00092 
00093   free( mySrcData );
00094 }
00095 
00096 
00097 QgsRasterDataProvider::QgsRasterDataProvider(): mDpi( -1 )
00098 {
00099 }
00100 
00101 QgsRasterDataProvider::QgsRasterDataProvider( QString const & uri )
00102     : QgsDataProvider( uri )
00103     , mDpi( -1 )
00104 {
00105 }
00106 
00107 //
00108 //Random Static convenience function
00109 //
00111 //TODO: Change these to private function or make seprate class
00112 // convenience function for building metadata() HTML table cells
00113 // convenience function for creating a string list from a C style string list
00114 QStringList QgsRasterDataProvider::cStringList2Q_( char ** stringList )
00115 {
00116   QStringList strings;
00117 
00118   // presume null terminated string list
00119   for ( size_t i = 0; stringList[i]; ++i )
00120   {
00121     strings.append( stringList[i] );
00122   }
00123 
00124   return strings;
00125 
00126 } // cStringList2Q_
00127 
00128 
00129 QString QgsRasterDataProvider::makeTableCell( QString const & value )
00130 {
00131   return "<p>\n" + value + "</p>\n";
00132 } // makeTableCell_
00133 
00134 
00135 
00136 // convenience function for building metadata() HTML table cells
00137 QString QgsRasterDataProvider::makeTableCells( QStringList const & values )
00138 {
00139   QString s( "<tr>" );
00140 
00141   for ( QStringList::const_iterator i = values.begin();
00142         i != values.end();
00143         ++i )
00144   {
00145     s += QgsRasterDataProvider::makeTableCell( *i );
00146   }
00147 
00148   s += "</tr>";
00149 
00150   return s;
00151 } // makeTableCell_
00152 
00153 QString QgsRasterDataProvider::metadata()
00154 {
00155   QString s;
00156   return s;
00157 }
00158 
00159 QString QgsRasterDataProvider::capabilitiesString() const
00160 {
00161   QStringList abilitiesList;
00162 
00163   int abilities = capabilities();
00164 
00165   if ( abilities & QgsRasterDataProvider::Identify )
00166   {
00167     abilitiesList += tr( "Identify" );
00168   }
00169 
00170   if ( abilities & QgsRasterDataProvider::BuildPyramids )
00171   {
00172     abilitiesList += tr( "Build Pyramids" );
00173   }
00174 
00175   QgsDebugMsg( "Capability: " + abilitiesList.join( ", " ) );
00176 
00177   return abilitiesList.join( ", " );
00178 }
00179 
00180 bool QgsRasterDataProvider::identify( const QgsPoint& thePoint, QMap<QString, QString>& theResults )
00181 {
00182   Q_UNUSED( thePoint );
00183   theResults.clear();
00184   return false;
00185 }
00186 
00187 QString QgsRasterDataProvider::lastErrorFormat()
00188 {
00189   return "text/plain";
00190 }
00191 
00192 QByteArray QgsRasterDataProvider::noValueBytes( int theBandNo )
00193 {
00194   int type = dataType( theBandNo );
00195   int size = dataTypeSize( theBandNo ) / 8;
00196   QByteArray ba;
00197   ba.resize( size );
00198   char * data = ba.data();
00199   double noval = mNoDataValue[theBandNo-1];
00200   unsigned char uc;
00201   unsigned short us;
00202   short s;
00203   unsigned int ui;
00204   int i;
00205   float f;
00206   double d;
00207   switch ( type )
00208   {
00209     case Byte:
00210       uc = ( unsigned char )noval;
00211       memcpy( data, &uc, size );
00212       break;
00213     case UInt16:
00214       us = ( unsigned short )noval;
00215       memcpy( data, &us, size );
00216       break;
00217     case Int16:
00218       s = ( short )noval;
00219       memcpy( data, &s, size );
00220       break;
00221     case UInt32:
00222       ui = ( unsigned int )noval;
00223       memcpy( data, &ui, size );
00224       break;
00225     case Int32:
00226       i = ( int )noval;
00227       memcpy( data, &i, size );
00228       break;
00229     case Float32:
00230       f = ( float )noval;
00231       memcpy( data, &f, size );
00232       break;
00233     case Float64:
00234       d = ( double )noval;
00235       memcpy( data, &d, size );
00236       break;
00237     default:
00238       QgsLogger::warning( "GDAL data type is not supported" );
00239   }
00240   return ba;
00241 }
00242 
00243 
00244 QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo )
00245 {
00246   double myNoDataValue = noDataValue();
00247   QgsRasterBandStats myRasterBandStats;
00248   myRasterBandStats.elementCount = 0; // because we'll be counting only VALID pixels later
00249   myRasterBandStats.bandName = generateBandName( theBandNo );
00250   myRasterBandStats.bandNumber = theBandNo;
00251 
00252   int myDataType = dataType( theBandNo );
00253 
00254   int  myNXBlocks, myNYBlocks, myXBlockSize, myYBlockSize;
00255   myXBlockSize = xBlockSize();
00256   myYBlockSize = yBlockSize();
00257 
00258   myNXBlocks = ( xSize() + myXBlockSize - 1 ) / myXBlockSize;
00259   myNYBlocks = ( ySize() + myYBlockSize - 1 ) / myYBlockSize;
00260 
00261   void *myData = CPLMalloc( myXBlockSize * myYBlockSize * ( dataTypeSize( theBandNo ) / 8 ) );
00262 
00263   // unfortunately we need to make two passes through the data to calculate stddev
00264   bool myFirstIterationFlag = true;
00265 
00266   int myBandXSize = xSize();
00267   int myBandYSize = ySize();
00268   for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ )
00269   {
00270     for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ )
00271     {
00272       int  nXValid, nYValid;
00273       readBlock( theBandNo, iXBlock, iYBlock, myData );
00274 
00275       // Compute the portion of the block that is valid
00276       // for partial edge blocks.
00277       if (( iXBlock + 1 ) * myXBlockSize > myBandXSize )
00278         nXValid = myBandXSize - iXBlock * myXBlockSize;
00279       else
00280         nXValid = myXBlockSize;
00281 
00282       if (( iYBlock + 1 ) * myYBlockSize > myBandYSize )
00283         nYValid = myBandYSize - iYBlock * myYBlockSize;
00284       else
00285         nYValid = myYBlockSize;
00286 
00287       // Collect the histogram counts.
00288       for ( int iY = 0; iY < nYValid; iY++ )
00289       {
00290         for ( int iX = 0; iX < nXValid; iX++ )
00291         {
00292           double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) );
00293           //QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (iX).arg(iY).arg( myValue ) );
00294 
00295           if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) )
00296           {
00297             continue; // NULL
00298           }
00299 
00300           myRasterBandStats.sum += myValue;
00301           ++myRasterBandStats.elementCount;
00302           //only use this element if we have a non null element
00303           if ( myFirstIterationFlag )
00304           {
00305             //this is the first iteration so initialise vars
00306             myFirstIterationFlag = false;
00307             myRasterBandStats.minimumValue = myValue;
00308             myRasterBandStats.maximumValue = myValue;
00309           }               //end of true part for first iteration check
00310           else
00311           {
00312             //this is done for all subsequent iterations
00313             if ( myValue < myRasterBandStats.minimumValue )
00314             {
00315               myRasterBandStats.minimumValue = myValue;
00316             }
00317             if ( myValue > myRasterBandStats.maximumValue )
00318             {
00319               myRasterBandStats.maximumValue = myValue;
00320             }
00321           } //end of false part for first iteration check
00322         }
00323       }
00324     } //end of column wise loop
00325   } //end of row wise loop
00326 
00327 
00328   //end of first pass through data now calculate the range
00329   myRasterBandStats.range = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue;
00330   //calculate the mean
00331   myRasterBandStats.mean = myRasterBandStats.sum / myRasterBandStats.elementCount;
00332 
00333   //for the second pass we will get the sum of the squares / mean
00334   for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ )
00335   {
00336     for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ )
00337     {
00338       int  nXValid, nYValid;
00339 
00340       readBlock( theBandNo, iXBlock, iYBlock, myData );
00341 
00342       // Compute the portion of the block that is valid
00343       // for partial edge blocks.
00344       if (( iXBlock + 1 ) * myXBlockSize > myBandXSize )
00345         nXValid = myBandXSize - iXBlock * myXBlockSize;
00346       else
00347         nXValid = myXBlockSize;
00348 
00349       if (( iYBlock + 1 ) * myYBlockSize > myBandYSize )
00350         nYValid = myBandYSize - iYBlock * myYBlockSize;
00351       else
00352         nYValid = myYBlockSize;
00353 
00354       // Collect the histogram counts.
00355       for ( int iY = 0; iY < nYValid; iY++ )
00356       {
00357         for ( int iX = 0; iX < nXValid; iX++ )
00358         {
00359           double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) );
00360           //QgsDebugMsg ( "myValue = " + QString::number(myValue) );
00361 
00362           if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) )
00363           {
00364             continue; // NULL
00365           }
00366 
00367           myRasterBandStats.sumOfSquares += static_cast < double >
00368                                             ( pow( myValue - myRasterBandStats.mean, 2 ) );
00369         }
00370       }
00371     } //end of column wise loop
00372   } //end of row wise loop
00373 
00374   //divide result by sample size - 1 and get square root to get stdev
00375   myRasterBandStats.stdDev = static_cast < double >( sqrt( myRasterBandStats.sumOfSquares /
00376                              ( myRasterBandStats.elementCount - 1 ) ) );
00377 
00378 #ifdef QGISDEBUG
00379   QgsLogger::debug( "************ STATS **************", 1, __FILE__, __FUNCTION__, __LINE__ );
00380   QgsLogger::debug( "VALID NODATA", mValidNoDataValue, 1, __FILE__, __FUNCTION__, __LINE__ );
00381   QgsLogger::debug( "NULL", noDataValue() , 1, __FILE__, __FUNCTION__, __LINE__ );
00382   QgsLogger::debug( "MIN", myRasterBandStats.minimumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
00383   QgsLogger::debug( "MAX", myRasterBandStats.maximumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
00384   QgsLogger::debug( "RANGE", myRasterBandStats.range, 1, __FILE__, __FUNCTION__, __LINE__ );
00385   QgsLogger::debug( "MEAN", myRasterBandStats.mean, 1, __FILE__, __FUNCTION__, __LINE__ );
00386   QgsLogger::debug( "STDDEV", myRasterBandStats.stdDev, 1, __FILE__, __FUNCTION__, __LINE__ );
00387 #endif
00388 
00389   CPLFree( myData );
00390   myRasterBandStats.statsGathered = true;
00391   return myRasterBandStats;
00392 }
00393 
00394 double QgsRasterDataProvider::readValue( void *data, int type, int index )
00395 {
00396   if ( !data )
00397     return mValidNoDataValue ? noDataValue() : 0.0;
00398 
00399   switch ( type )
00400   {
00401     case Byte:
00402       return ( double )(( GByte * )data )[index];
00403       break;
00404     case UInt16:
00405       return ( double )(( GUInt16 * )data )[index];
00406       break;
00407     case Int16:
00408       return ( double )(( GInt16 * )data )[index];
00409       break;
00410     case UInt32:
00411       return ( double )(( GUInt32 * )data )[index];
00412       break;
00413     case Int32:
00414       return ( double )(( GInt32 * )data )[index];
00415       break;
00416     case Float32:
00417       return ( double )(( float * )data )[index];
00418       break;
00419     case Float64:
00420       return ( double )(( double * )data )[index];
00421       break;
00422     default:
00423       QgsLogger::warning( "GDAL data type is not supported" );
00424   }
00425 
00426   return mValidNoDataValue ? noDataValue() : 0.0;
00427 }
00428 
00429 // ENDS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines