Quantum GIS API Documentation
1.8
|
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