|
Quantum GIS API Documentation
master-ce49b66
|
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 "qgsproviderregistry.h" 00019 #include "qgsrasterdataprovider.h" 00020 #include "qgsrasteridentifyresult.h" 00021 #include "qgsrasterprojector.h" 00022 #include "qgslogger.h" 00023 00024 #include <QTime> 00025 #include <QMap> 00026 #include <QByteArray> 00027 #include <QVariant> 00028 00029 #include <qmath.h> 00030 00031 #define ERRMSG(message) QGS_ERROR_MESSAGE(message, "Raster provider") 00032 #define ERR(message) QgsError(message, "Raster provider") 00033 00034 void QgsRasterDataProvider::setUseSrcNoDataValue( int bandNo, bool use ) 00035 { 00036 if ( mUseSrcNoDataValue.size() < bandNo ) 00037 { 00038 for ( int i = mUseSrcNoDataValue.size(); i < bandNo; i++ ) 00039 { 00040 mUseSrcNoDataValue.append( false ); 00041 } 00042 } 00043 mUseSrcNoDataValue[bandNo-1] = use; 00044 } 00045 00046 QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle const & theExtent, int theWidth, int theHeight ) 00047 { 00048 QgsDebugMsg( QString( "theBandNo = %1 theWidth = %2 theHeight = %3" ).arg( theBandNo ).arg( theWidth ).arg( theHeight ) ); 00049 QgsDebugMsg( QString( "theExtent = %1" ).arg( theExtent.toString() ) ); 00050 00051 QgsRasterBlock *block; 00052 if ( srcHasNoDataValue( theBandNo ) && useSrcNoDataValue( theBandNo ) ) 00053 { 00054 block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight, srcNoDataValue( theBandNo ) ); 00055 } 00056 else 00057 { 00058 block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight ); 00059 } 00060 00061 if ( block->isEmpty() ) 00062 { 00063 QgsDebugMsg( "Couldn't create raster block" ); 00064 return block; 00065 } 00066 00067 // Read necessary extent only 00068 QgsRectangle tmpExtent = extent().intersect( &theExtent ); 00069 00070 if ( tmpExtent.isEmpty() ) 00071 { 00072 QgsDebugMsg( "Extent outside provider extent" ); 00073 block->setIsNoData(); 00074 return block; 00075 } 00076 00077 double xRes = theExtent.width() / theWidth; 00078 double yRes = theExtent.height() / theHeight; 00079 double tmpXRes, tmpYRes; 00080 double providerXRes = 0; 00081 double providerYRes = 0; 00082 if ( capabilities() & Size ) 00083 { 00084 providerXRes = extent().width() / xSize(); 00085 providerYRes = extent().height() / ySize(); 00086 tmpXRes = qMax( providerXRes, xRes ); 00087 tmpYRes = qMax( providerYRes, yRes ); 00088 if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes; 00089 if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes; 00090 } 00091 else 00092 { 00093 tmpXRes = xRes; 00094 tmpYRes = yRes; 00095 } 00096 00097 if ( tmpExtent != theExtent || 00098 tmpXRes > xRes || tmpYRes > yRes ) 00099 { 00100 // Read smaller extent or lower resolution 00101 00102 // Calculate row/col limits (before tmpExtent is aligned) 00103 int fromRow = qRound(( theExtent.yMaximum() - tmpExtent.yMaximum() ) / yRes ); 00104 int toRow = qRound(( theExtent.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; 00105 int fromCol = qRound(( tmpExtent.xMinimum() - theExtent.xMinimum() ) / xRes ) ; 00106 int toCol = qRound(( tmpExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1; 00107 00108 QgsDebugMsg( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ) ); 00109 00110 if ( fromRow < 0 || fromRow >= theHeight || toRow < 0 || toRow >= theHeight || 00111 fromCol < 0 || fromCol >= theWidth || toCol < 0 || toCol >= theWidth ) 00112 { 00113 // Should not happen 00114 QgsDebugMsg( "Row or column limits out of range" ); 00115 return block; 00116 } 00117 00118 // If lower source resolution is used, the extent must beS aligned to original 00119 // resolution to avoid possible shift due to resampling 00120 if ( tmpXRes > xRes ) 00121 { 00122 int col = floor(( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); 00123 tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); 00124 col = ceil(( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); 00125 tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); 00126 } 00127 if ( tmpYRes > yRes ) 00128 { 00129 int row = floor(( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); 00130 tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); 00131 row = ceil(( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); 00132 tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); 00133 } 00134 int tmpWidth = qRound( tmpExtent.width() / tmpXRes ); 00135 int tmpHeight = qRound( tmpExtent.height() / tmpYRes ); 00136 tmpXRes = tmpExtent.width() / tmpWidth; 00137 tmpYRes = tmpExtent.height() / tmpHeight; 00138 00139 QgsDebugMsg( QString( "Reading smaller block tmpWidth = %1 theHeight = %2" ).arg( tmpWidth ).arg( tmpHeight ) ); 00140 QgsDebugMsg( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ) ); 00141 00142 block->setIsNoData(); 00143 00144 QgsRasterBlock *tmpBlock; 00145 if ( srcHasNoDataValue( theBandNo ) && useSrcNoDataValue( theBandNo ) ) 00146 { 00147 tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight, srcNoDataValue( theBandNo ) ); 00148 } 00149 else 00150 { 00151 tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight ); 00152 } 00153 00154 readBlock( theBandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits() ); 00155 00156 int pixelSize = dataTypeSize( theBandNo ); 00157 00158 double xMin = theExtent.xMinimum(); 00159 double yMax = theExtent.yMaximum(); 00160 double tmpXMin = tmpExtent.xMinimum(); 00161 double tmpYMax = tmpExtent.yMaximum(); 00162 00163 for ( int row = fromRow; row <= toRow; row++ ) 00164 { 00165 double y = yMax - ( row + 0.5 ) * yRes; 00166 int tmpRow = floor(( tmpYMax - y ) / tmpYRes ); 00167 00168 for ( int col = fromCol; col <= toCol; col++ ) 00169 { 00170 double x = xMin + ( col + 0.5 ) * xRes; 00171 int tmpCol = floor(( x - tmpXMin ) / tmpXRes ); 00172 00173 if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth ) 00174 { 00175 QgsDebugMsg( "Source row or column limits out of range" ); 00176 block->setIsNoData(); // so that the problem becomes obvious and fixed 00177 delete tmpBlock; 00178 return block; 00179 } 00180 00181 size_t tmpIndex = tmpRow * tmpWidth + tmpCol; 00182 size_t index = row * theWidth + col; 00183 00184 char *tmpBits = tmpBlock->bits( tmpIndex ); 00185 char *bits = block->bits( index ); 00186 if ( !tmpBits ) 00187 { 00188 QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) ); 00189 continue; 00190 } 00191 if ( !bits ) 00192 { 00193 QgsDebugMsg( "Cannot set output block data." ); 00194 continue; 00195 } 00196 memcpy( bits, tmpBits, pixelSize ); 00197 } 00198 } 00199 00200 delete tmpBlock; 00201 } 00202 else 00203 { 00204 readBlock( theBandNo, theExtent, theWidth, theHeight, block->bits() ); 00205 } 00206 00207 // apply user no data values 00208 block->applyNoDataValues( userNoDataValues( theBandNo ) ); 00209 return block; 00210 } 00211 00212 QgsRasterDataProvider::QgsRasterDataProvider() 00213 : QgsRasterInterface( 0 ) 00214 , mDpi( -1 ) 00215 { 00216 } 00217 00218 QgsRasterDataProvider::QgsRasterDataProvider( QString const & uri ) 00219 : QgsDataProvider( uri ) 00220 , QgsRasterInterface( 0 ) 00221 , mDpi( -1 ) 00222 { 00223 } 00224 00225 // 00226 //Random Static convenience function 00227 // 00229 // convenience function for building metadata() HTML table cells 00230 // convenience function for creating a string list from a C style string list 00231 QStringList QgsRasterDataProvider::cStringList2Q_( char ** stringList ) 00232 { 00233 QStringList strings; 00234 00235 // presume null terminated string list 00236 for ( size_t i = 0; stringList[i]; ++i ) 00237 { 00238 strings.append( stringList[i] ); 00239 } 00240 00241 return strings; 00242 00243 } // cStringList2Q_ 00244 00245 QString QgsRasterDataProvider::makeTableCell( QString const & value ) 00246 { 00247 return "<p>\n" + value + "</p>\n"; 00248 } // makeTableCell_ 00249 00250 // convenience function for building metadata() HTML table cells 00251 QString QgsRasterDataProvider::makeTableCells( QStringList const & values ) 00252 { 00253 QString s( "<tr>" ); 00254 00255 for ( QStringList::const_iterator i = values.begin(); 00256 i != values.end(); 00257 ++i ) 00258 { 00259 s += QgsRasterDataProvider::makeTableCell( *i ); 00260 } 00261 00262 s += "</tr>"; 00263 00264 return s; 00265 } // makeTableCell_ 00266 00267 QString QgsRasterDataProvider::metadata() 00268 { 00269 QString s; 00270 return s; 00271 } 00272 00273 // Default implementation for values 00274 QgsRasterIdentifyResult QgsRasterDataProvider::identify( const QgsPoint & thePoint, QgsRaster::IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight ) 00275 { 00276 QgsDebugMsg( "Entered" ); 00277 QMap<int, QVariant> results; 00278 00279 if ( theFormat != QgsRaster::IdentifyFormatValue || !( capabilities() & IdentifyValue ) ) 00280 { 00281 QgsDebugMsg( "Format not supported" ); 00282 return QgsRasterIdentifyResult( ERR( tr( "Format not supported" ) ) ); 00283 } 00284 00285 if ( !extent().contains( thePoint ) ) 00286 { 00287 // Outside the raster 00288 for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ ) 00289 { 00290 results.insert( bandNo, QVariant() ); 00291 } 00292 return QgsRasterIdentifyResult( QgsRaster::IdentifyFormatValue, results ); 00293 } 00294 00295 QgsRectangle myExtent = theExtent; 00296 if ( myExtent.isEmpty() ) myExtent = extent(); 00297 00298 if ( theWidth == 0 ) 00299 { 00300 theWidth = capabilities() & Size ? xSize() : 1000; 00301 } 00302 if ( theHeight == 0 ) 00303 { 00304 theHeight = capabilities() & Size ? ySize() : 1000; 00305 } 00306 00307 // Calculate the row / column where the point falls 00308 double xres = ( myExtent.width() ) / theWidth; 00309 double yres = ( myExtent.height() ) / theHeight; 00310 00311 int col = ( int ) floor(( thePoint.x() - myExtent.xMinimum() ) / xres ); 00312 int row = ( int ) floor(( myExtent.yMaximum() - thePoint.y() ) / yres ); 00313 00314 double xMin = myExtent.xMinimum() + col * xres; 00315 double xMax = xMin + xres; 00316 double yMax = myExtent.yMaximum() - row * yres; 00317 double yMin = yMax - yres; 00318 QgsRectangle pixelExtent( xMin, yMin, xMax, yMax ); 00319 00320 for ( int i = 1; i <= bandCount(); i++ ) 00321 { 00322 QgsRasterBlock * myBlock = block( i, pixelExtent, 1, 1 ); 00323 00324 if ( myBlock ) 00325 { 00326 double value = myBlock->value( 0 ); 00327 00328 results.insert( i, value ); 00329 delete myBlock; 00330 } 00331 else 00332 { 00333 results.insert( i, QVariant() ); 00334 } 00335 } 00336 return QgsRasterIdentifyResult( QgsRaster::IdentifyFormatValue, results ); 00337 } 00338 00339 QString QgsRasterDataProvider::lastErrorFormat() 00340 { 00341 return "text/plain"; 00342 } 00343 00344 typedef QList<QPair<QString, QString> > *pyramidResamplingMethods_t(); 00345 QList<QPair<QString, QString> > QgsRasterDataProvider::pyramidResamplingMethods( QString providerKey ) 00346 { 00347 pyramidResamplingMethods_t *pPyramidResamplingMethods = ( pyramidResamplingMethods_t * ) cast_to_fptr( QgsProviderRegistry::instance()->function( providerKey, "pyramidResamplingMethods" ) ); 00348 if ( pPyramidResamplingMethods ) 00349 { 00350 QList<QPair<QString, QString> > *methods = pPyramidResamplingMethods(); 00351 if ( !methods ) 00352 { 00353 QgsDebugMsg( "provider pyramidResamplingMethods returned no methods" ); 00354 } 00355 else 00356 { 00357 return *methods; 00358 } 00359 } 00360 else 00361 { 00362 QgsDebugMsg( "Could not resolve pyramidResamplingMethods provider library" ); 00363 } 00364 return QList<QPair<QString, QString> >(); 00365 } 00366 00367 bool QgsRasterDataProvider::hasPyramids() 00368 { 00369 QList<QgsRasterPyramid> myPyramidList = buildPyramidList(); 00370 00371 if ( myPyramidList.isEmpty() ) 00372 return false; 00373 00374 QList<QgsRasterPyramid>::iterator myRasterPyramidIterator; 00375 for ( myRasterPyramidIterator = myPyramidList.begin(); 00376 myRasterPyramidIterator != myPyramidList.end(); 00377 ++myRasterPyramidIterator ) 00378 { 00379 if ( myRasterPyramidIterator->exists ) 00380 { 00381 return true; 00382 } 00383 } 00384 return false; 00385 } 00386 00387 void QgsRasterDataProvider::setUserNoDataValue( int bandNo, QgsRasterRangeList noData ) 00388 { 00389 if ( bandNo >= mUserNoDataValue.size() ) 00390 { 00391 for ( int i = mUserNoDataValue.size(); i < bandNo; i++ ) 00392 { 00393 mUserNoDataValue.append( QgsRasterRangeList() ); 00394 } 00395 } 00396 QgsDebugMsg( QString( "set %1 band %1 no data ranges" ).arg( noData.size() ) ); 00397 00398 if ( mUserNoDataValue[bandNo-1] != noData ) 00399 { 00400 // Clear statistics 00401 int i = 0; 00402 while ( i < mStatistics.size() ) 00403 { 00404 if ( mStatistics.value( i ).bandNumber == bandNo ) 00405 { 00406 mStatistics.removeAt( i ); 00407 mHistograms.removeAt( i ); 00408 } 00409 else 00410 { 00411 i++; 00412 } 00413 } 00414 mUserNoDataValue[bandNo-1] = noData; 00415 } 00416 } 00417 00418 typedef QgsRasterDataProvider * createFunction_t( const QString&, 00419 const QString&, int, 00420 QGis::DataType, 00421 int, int, double*, 00422 const QgsCoordinateReferenceSystem&, 00423 QStringList ); 00424 00425 QgsRasterDataProvider* QgsRasterDataProvider::create( const QString &providerKey, 00426 const QString &uri, 00427 const QString& format, int nBands, 00428 QGis::DataType type, 00429 int width, int height, double* geoTransform, 00430 const QgsCoordinateReferenceSystem& crs, 00431 QStringList createOptions ) 00432 { 00433 createFunction_t *createFn = ( createFunction_t* ) cast_to_fptr( QgsProviderRegistry::instance()->function( providerKey, "create" ) ); 00434 if ( !createFn ) 00435 { 00436 QgsDebugMsg( "Cannot resolve 'create' function in " + providerKey + " provider" ); 00437 // TODO: it would be good to return invalid QgsRasterDataProvider 00438 // with QgsError set, but QgsRasterDataProvider has pure virtual methods 00439 return 0; 00440 } 00441 return createFn( uri, format, nBands, type, width, height, geoTransform, crs, createOptions ); 00442 } 00443 00444 QString QgsRasterDataProvider::identifyFormatName( QgsRaster::IdentifyFormat format ) 00445 { 00446 switch ( format ) 00447 { 00448 case QgsRaster::IdentifyFormatValue: 00449 return "Value"; 00450 case QgsRaster::IdentifyFormatText: 00451 return "Text"; 00452 case QgsRaster::IdentifyFormatHtml: 00453 return "Html"; 00454 case QgsRaster::IdentifyFormatFeature: 00455 return "Feature"; 00456 default: 00457 return "Undefined"; 00458 } 00459 } 00460 00461 QString QgsRasterDataProvider::identifyFormatLabel( QgsRaster::IdentifyFormat format ) 00462 { 00463 switch ( format ) 00464 { 00465 case QgsRaster::IdentifyFormatValue: 00466 return tr( "Value" ); 00467 case QgsRaster::IdentifyFormatText: 00468 return ( "Text" ); 00469 case QgsRaster::IdentifyFormatHtml: 00470 return tr( "Html" ); 00471 case QgsRaster::IdentifyFormatFeature: 00472 return tr( "Feature" ); 00473 default: 00474 return "Undefined"; 00475 } 00476 } 00477 00478 QgsRaster::IdentifyFormat QgsRasterDataProvider::identifyFormatFromName( QString formatName ) 00479 { 00480 if ( formatName == "Value" ) return QgsRaster::IdentifyFormatValue; 00481 if ( formatName == "Text" ) return QgsRaster::IdentifyFormatText; 00482 if ( formatName == "Html" ) return QgsRaster::IdentifyFormatHtml; 00483 if ( formatName == "Feature" ) return QgsRaster::IdentifyFormatFeature; 00484 return QgsRaster::IdentifyFormatUndefined; 00485 } 00486 00487 QgsRasterInterface::Capability QgsRasterDataProvider::identifyFormatToCapability( QgsRaster::IdentifyFormat format ) 00488 { 00489 switch ( format ) 00490 { 00491 case QgsRaster::IdentifyFormatValue: 00492 return IdentifyValue; 00493 case QgsRaster::IdentifyFormatText: 00494 return IdentifyText; 00495 case QgsRaster::IdentifyFormatHtml: 00496 return IdentifyHtml; 00497 case QgsRaster::IdentifyFormatFeature: 00498 return IdentifyFeature; 00499 default: 00500 return NoCapabilities; 00501 } 00502 } 00503 00504 bool QgsRasterDataProvider::userNoDataValuesContains( int bandNo, double value ) const 00505 { 00506 QgsRasterRangeList rangeList = mUserNoDataValue.value( bandNo - 1 ); 00507 return QgsRasterRange::contains( value, rangeList ); 00508 } 00509 00510 // ENDS