Quantum GIS API Documentation  master-ce49b66
src/core/raster/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 "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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines