Quantum GIS API Documentation  master-ce49b66
src/core/raster/qgsrasterlayer.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002         qgsrasterlayer.cpp -  description
00003    -------------------
00004 begin                : Sat Jun 22 2002
00005 copyright            : (C) 2003 by Tim Sutton, Steve Halasz and Gary E.Sherman
00006 email                : tim at linfiniti.com
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 #include "qgsapplication.h"
00018 #include "qgscolorrampshader.h"
00019 #include "qgscoordinatereferencesystem.h"
00020 #include "qgscoordinatetransform.h"
00021 #include "qgsdatasourceuri.h"
00022 #include "qgslogger.h"
00023 #include "qgsmaplayerregistry.h"
00024 #include "qgsmaptopixel.h"
00025 #include "qgsmessagelog.h"
00026 #include "qgsmultibandcolorrenderer.h"
00027 #include "qgspalettedrasterrenderer.h"
00028 #include "qgsprojectfiletransform.h"
00029 #include "qgsproviderregistry.h"
00030 #include "qgspseudocolorshader.h"
00031 #include "qgsrasterdrawer.h"
00032 #include "qgsrasteriterator.h"
00033 #include "qgsrasterlayer.h"
00034 #include "qgsrasterprojector.h"
00035 #include "qgsrasterrange.h"
00036 #include "qgsrasterrendererregistry.h"
00037 #include "qgsrectangle.h"
00038 #include "qgsrendercontext.h"
00039 #include "qgssinglebandcolordatarenderer.h"
00040 #include "qgssinglebandgrayrenderer.h"
00041 #include "qgssinglebandpseudocolorrenderer.h"
00042 
00043 #include <cmath>
00044 #include <cstdio>
00045 #include <limits>
00046 #include <typeinfo>
00047 
00048 #include <QApplication>
00049 #include <QCursor>
00050 #include <QDomElement>
00051 #include <QDomNode>
00052 #include <QFile>
00053 #include <QFileInfo>
00054 #include <QFont>
00055 #include <QFontMetrics>
00056 #include <QFrame>
00057 #include <QImage>
00058 #include <QLabel>
00059 #include <QLibrary>
00060 #include <QList>
00061 #include <QMatrix>
00062 #include <QMessageBox>
00063 #include <QPainter>
00064 #include <QPixmap>
00065 #include <QRegExp>
00066 #include <QSettings>
00067 #include <QSlider>
00068 #include <QTime>
00069 
00070 // typedefs for provider plugin functions of interest
00071 typedef void buildsupportedrasterfilefilter_t( QString & theFileFiltersString );
00072 typedef bool isvalidrasterfilename_t( QString const & theFileNameQString, QString & retErrMsg );
00073 
00074 #define ERR(message) QGS_ERROR_MESSAGE(message,"Raster layer")
00075 
00076 const double QgsRasterLayer::CUMULATIVE_CUT_LOWER = 0.02;
00077 const double QgsRasterLayer::CUMULATIVE_CUT_UPPER = 0.98;
00078 const double QgsRasterLayer::SAMPLE_SIZE = 250000;
00079 
00080 QgsRasterLayer::QgsRasterLayer()
00081     : QgsMapLayer( RasterLayer )
00082     , QSTRING_NOT_SET( "Not Set" )
00083     , TRSTRING_NOT_SET( tr( "Not Set" ) )
00084     , mDataProvider( 0 )
00085 {
00086   init();
00087   mValid = false;
00088 }
00089 
00090 QgsRasterLayer::QgsRasterLayer(
00091   QString const & path,
00092   QString const & baseName,
00093   bool loadDefaultStyleFlag )
00094     : QgsMapLayer( RasterLayer, baseName, path )
00095     , QSTRING_NOT_SET( "Not Set" )
00096     , TRSTRING_NOT_SET( tr( "Not Set" ) )
00097     , mDataProvider( 0 )
00098 {
00099   QgsDebugMsg( "Entered" );
00100 
00101   // TODO, call constructor with provider key
00102   init();
00103   setDataProvider( "gdal" );
00104   if ( !mValid ) return;
00105 
00106   bool defaultLoadedFlag = false;
00107   if ( mValid && loadDefaultStyleFlag )
00108   {
00109     loadDefaultStyle( defaultLoadedFlag );
00110   }
00111   if ( !defaultLoadedFlag )
00112   {
00113     setDefaultContrastEnhancement();
00114   }
00115   return;
00116 } // QgsRasterLayer ctor
00117 
00122 QgsRasterLayer::QgsRasterLayer( const QString & uri,
00123                                 const QString & baseName,
00124                                 const QString & providerKey,
00125                                 bool loadDefaultStyleFlag )
00126     : QgsMapLayer( RasterLayer, baseName, uri )
00127     // Constant that signals property not used.
00128     , QSTRING_NOT_SET( "Not Set" )
00129     , TRSTRING_NOT_SET( tr( "Not Set" ) )
00130     , mDataProvider( 0 )
00131     , mProviderKey( providerKey )
00132 {
00133   QgsDebugMsg( "Entered" );
00134   init();
00135   setDataProvider( providerKey );
00136   if ( !mValid ) return;
00137 
00138   // load default style
00139   bool defaultLoadedFlag = false;
00140   if ( mValid && loadDefaultStyleFlag )
00141   {
00142     loadDefaultStyle( defaultLoadedFlag );
00143   }
00144   if ( !defaultLoadedFlag )
00145   {
00146     setDefaultContrastEnhancement();
00147   }
00148 
00149   // TODO: Connect signals from the dataprovider to the qgisapp
00150 
00151   emit statusChanged( tr( "QgsRasterLayer created" ) );
00152 } // QgsRasterLayer ctor
00153 
00154 QgsRasterLayer::~QgsRasterLayer()
00155 {
00156   mValid = false;
00157   // Note: provider and other interfaces are owned and deleted by pipe
00158 }
00159 
00161 //
00162 // Static Methods and members
00163 //
00165 
00173 void QgsRasterLayer::buildSupportedRasterFileFilter( QString & theFileFiltersString )
00174 {
00175   QgsDebugMsg( "Entered" );
00176   buildsupportedrasterfilefilter_t *pBuild = ( buildsupportedrasterfilefilter_t * ) cast_to_fptr( QgsProviderRegistry::instance()->function( "gdal", "buildSupportedRasterFileFilter" ) );
00177   if ( ! pBuild )
00178   {
00179     QgsDebugMsg( "Could get buildSupportedRasterFileFilter in gdal provider library" );
00180     return;
00181   }
00182 
00183   pBuild( theFileFiltersString );
00184 }
00185 
00189 bool QgsRasterLayer::isValidRasterFileName( QString const & theFileNameQString, QString & retErrMsg )
00190 {
00191   isvalidrasterfilename_t *pValid = ( isvalidrasterfilename_t * ) cast_to_fptr( QgsProviderRegistry::instance()->function( "gdal",  "isValidRasterFileName" ) );
00192   if ( ! pValid )
00193   {
00194     QgsDebugMsg( "Could not resolve isValidRasterFileName in gdal provider library" );
00195     return false;
00196   }
00197 
00198   bool myIsValid = pValid( theFileNameQString, retErrMsg );
00199   return myIsValid;
00200 }
00201 
00202 bool QgsRasterLayer::isValidRasterFileName( QString const & theFileNameQString )
00203 {
00204   QString retErrMsg;
00205   return isValidRasterFileName( theFileNameQString, retErrMsg );
00206 }
00207 
00208 QDateTime QgsRasterLayer::lastModified( QString const & name )
00209 {
00210   QgsDebugMsg( "name=" + name );
00211   QDateTime t;
00212 
00213   QFileInfo fi( name );
00214 
00215   // Is it file?
00216   if ( !fi.exists() )
00217     return t;
00218 
00219   t = fi.lastModified();
00220 
00221   QgsDebugMsg( "last modified = " + t.toString() );
00222 
00223   return t;
00224 }
00225 
00226 // typedef for the QgsDataProvider class factory
00227 typedef QgsDataProvider * classFactoryFunction_t( const QString * );
00228 
00230 //
00231 // Non Static Public methods
00232 //
00234 
00235 int QgsRasterLayer::bandCount() const
00236 {
00237   if ( !mDataProvider ) return 0;
00238   return mDataProvider->bandCount();
00239 }
00240 
00241 const QString QgsRasterLayer::bandName( int theBandNo )
00242 {
00243   return dataProvider()->generateBandName( theBandNo );
00244 }
00245 
00246 void QgsRasterLayer::setRendererForDrawingStyle( const DrawingStyle &  theDrawingStyle )
00247 {
00248   setRenderer( QgsRasterRendererRegistry::instance()->defaultRendererForDrawingStyle( theDrawingStyle, mDataProvider ) );
00249 }
00250 
00254 QgsRasterDataProvider* QgsRasterLayer::dataProvider()
00255 {
00256   return mDataProvider;
00257 }
00258 
00262 const QgsRasterDataProvider* QgsRasterLayer::dataProvider() const
00263 {
00264   return mDataProvider;
00265 }
00266 
00267 void QgsRasterLayer::reload()
00268 {
00269   if ( mDataProvider )
00270   {
00271     mDataProvider->reloadData();
00272   }
00273 }
00274 
00275 bool QgsRasterLayer::draw( QgsRenderContext& rendererContext )
00276 {
00277   QgsDebugMsg( "entered. (renderContext)" );
00278 
00279   // Don't waste time drawing if transparency is at 0 (completely transparent)
00280   if ( mTransparencyLevel == 0 )
00281     return true;
00282 
00283   QgsDebugMsg( "checking timestamp." );
00284 
00285   // Check timestamp
00286   if ( !update() )
00287   {
00288     return false;
00289   }
00290 
00291   const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
00292 
00293   QgsRectangle myProjectedViewExtent;
00294   QgsRectangle myProjectedLayerExtent;
00295 
00296   if ( rendererContext.coordinateTransform() )
00297   {
00298     QgsDebugMsg( "coordinateTransform set -> project extents." );
00299     try
00300     {
00301       myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
00302     }
00303     catch ( QgsCsException &cs )
00304     {
00305       QgsMessageLog::logMessage( tr( "Could not reproject view extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
00306       myProjectedViewExtent.setMinimal();
00307     }
00308 
00309     try
00310     {
00311       myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( extent() );
00312     }
00313     catch ( QgsCsException &cs )
00314     {
00315       QgsMessageLog::logMessage( tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
00316       myProjectedViewExtent.setMinimal();
00317     }
00318   }
00319   else
00320   {
00321     QgsDebugMsg( "coordinateTransform not set" );
00322     myProjectedViewExtent = rendererContext.extent();
00323     myProjectedLayerExtent = extent();
00324   }
00325 
00326   QPainter* theQPainter = rendererContext.painter();
00327 
00328   if ( !theQPainter )
00329   {
00330     return false;
00331   }
00332 
00333   // clip raster extent to view extent
00334   QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
00335   if ( myRasterExtent.isEmpty() )
00336   {
00337     QgsDebugMsg( "draw request outside view extent." );
00338     // nothing to do
00339     return true;
00340   }
00341 
00342   QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() );
00343   QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() );
00344   QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() );
00345   QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );
00346 
00347   //
00348   // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
00349   // relating to the size (in pixels and coordinate system units) of the raster part that is
00350   // in view in the map window. It also stores the origin.
00351   //
00352   //this is not a class level member because every time the user pans or zooms
00353   //the contents of the rasterViewPort will change
00354   QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
00355 
00356   myRasterViewPort->mDrawnExtent = myRasterExtent;
00357   if ( rendererContext.coordinateTransform() )
00358   {
00359     myRasterViewPort->mSrcCRS = crs();
00360     myRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
00361   }
00362   else
00363   {
00364     myRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
00365     myRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
00366   }
00367 
00368   // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
00369   myRasterViewPort->mTopLeftPoint = theQgsMapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
00370   myRasterViewPort->mBottomRightPoint = theQgsMapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
00371 
00372   // align to output device grid, i.e. floor/ceil to integers
00373   // TODO: this should only be done if paint device is raster - screen, image
00374   // for other devices (pdf) it can have floating point origin
00375   // we could use floating point for raster devices as well, but respecting the
00376   // output device grid should make it more effective as the resampling is done in
00377   // the provider anyway
00378   if ( true )
00379   {
00380     myRasterViewPort->mTopLeftPoint.setX( floor( myRasterViewPort->mTopLeftPoint.x() ) );
00381     myRasterViewPort->mTopLeftPoint.setY( floor( myRasterViewPort->mTopLeftPoint.y() ) );
00382     myRasterViewPort->mBottomRightPoint.setX( ceil( myRasterViewPort->mBottomRightPoint.x() ) );
00383     myRasterViewPort->mBottomRightPoint.setY( ceil( myRasterViewPort->mBottomRightPoint.y() ) );
00384     // recalc myRasterExtent to aligned values
00385     myRasterExtent.set(
00386       theQgsMapToPixel.toMapCoordinatesF( myRasterViewPort->mTopLeftPoint.x(),
00387                                           myRasterViewPort->mBottomRightPoint.y() ),
00388       theQgsMapToPixel.toMapCoordinatesF( myRasterViewPort->mBottomRightPoint.x(),
00389                                           myRasterViewPort->mTopLeftPoint.y() )
00390     );
00391 
00392   }
00393 
00394   myRasterViewPort->mWidth = static_cast<int>( qAbs(( myRasterExtent.width() / theQgsMapToPixel.mapUnitsPerPixel() ) ) );
00395   myRasterViewPort->mHeight = static_cast<int>( qAbs(( myRasterExtent.height() / theQgsMapToPixel.mapUnitsPerPixel() ) ) );
00396 
00397   //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
00398   //theQgsMapToPixel.mapUnitsPerPixel() is less then 1,
00399   //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
00400 
00401   QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( theQgsMapToPixel.mapUnitsPerPixel() ), 3 );
00402   QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( width() ), 3 );
00403   QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( height() ), 3 );
00404   QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
00405   QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
00406   QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
00407   QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
00408 
00409   QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( myRasterViewPort->mTopLeftPoint.x() ), 3 );
00410   QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( myRasterViewPort->mBottomRightPoint.x() ), 3 );
00411   QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( myRasterViewPort->mTopLeftPoint.y() ), 3 );
00412   QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( myRasterViewPort->mBottomRightPoint.y() ), 3 );
00413 
00414   QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( myRasterViewPort->mWidth ), 3 );
00415   QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( myRasterViewPort->mHeight ), 3 );
00416 
00417   // /\/\/\ - added to handle zoomed-in rasters
00418 
00419   mLastViewPort = *myRasterViewPort;
00420 
00421   // TODO: is it necessary? Probably WMS only?
00422   mDataProvider->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );
00423 
00424   draw( theQPainter, myRasterViewPort, &theQgsMapToPixel );
00425 
00426   delete myRasterViewPort;
00427   QgsDebugMsg( "exiting." );
00428 
00429   return true;
00430 
00431 }
00432 
00433 void QgsRasterLayer::draw( QPainter * theQPainter,
00434                            QgsRasterViewPort * theRasterViewPort,
00435                            const QgsMapToPixel* theQgsMapToPixel )
00436 {
00437   QgsDebugMsg( " 3 arguments" );
00438   QTime time;
00439   time.start();
00440   //
00441   //
00442   // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
00443   // so that we can maximise performance of the rendering process. So now we check which drawing
00444   // procedure to use :
00445   //
00446 
00447   QgsRasterProjector *projector = mPipe.projector();
00448 
00449   // TODO add a method to interface to get provider and get provider
00450   // params in QgsRasterProjector
00451   if ( projector )
00452   {
00453     projector->setCRS( theRasterViewPort->mSrcCRS, theRasterViewPort->mDestCRS );
00454   }
00455 
00456   // Drawer to pipe?
00457   QgsRasterIterator iterator( mPipe.last() );
00458   QgsRasterDrawer drawer( &iterator );
00459   drawer.draw( theQPainter, theRasterViewPort, theQgsMapToPixel );
00460 
00461   QgsDebugMsg( QString( "total raster draw time (ms):     %1" ).arg( time.elapsed(), 5 ) );
00462 } //end of draw method
00463 
00464 QString QgsRasterLayer::lastError()
00465 {
00466   return mError;
00467 }
00468 
00469 QString QgsRasterLayer::lastErrorTitle()
00470 {
00471   return mErrorCaption;
00472 }
00473 
00474 QList< QPair< QString, QColor > > QgsRasterLayer::legendSymbologyItems() const
00475 {
00476   QList< QPair< QString, QColor > > symbolList;
00477   QgsRasterRenderer *renderer = mPipe.renderer();
00478   if ( renderer )
00479   {
00480     renderer->legendSymbologyItems( symbolList );
00481   }
00482   return symbolList;
00483 }
00484 
00485 QString QgsRasterLayer::metadata()
00486 {
00487   QString myMetadata ;
00488   myMetadata += "<p class=\"glossy\">" + tr( "Driver" ) + "</p>\n";
00489   myMetadata += "<p>";
00490   myMetadata += mDataProvider->description();
00491   myMetadata += "</p>\n";
00492 
00493   // Insert provider-specific (e.g. WMS-specific) metadata
00494   // crashing
00495   myMetadata += mDataProvider->metadata();
00496 
00497   myMetadata += "<p class=\"glossy\">";
00498   myMetadata += tr( "No Data Value" );
00499   myMetadata += "</p>\n";
00500   myMetadata += "<p>";
00501   // TODO: all bands
00502   if ( mDataProvider->srcHasNoDataValue( 1 ) )
00503   {
00504     myMetadata += QString::number( mDataProvider->srcNoDataValue( 1 ) );
00505   }
00506   else
00507   {
00508     myMetadata += "*" + tr( "NoDataValue not set" ) + "*";
00509   }
00510   myMetadata += "</p>\n";
00511 
00512   myMetadata += "</p>\n";
00513   myMetadata += "<p class=\"glossy\">";
00514   myMetadata += tr( "Data Type" );
00515   myMetadata += "</p>\n";
00516   myMetadata += "<p>";
00517   //just use the first band
00518   switch ( mDataProvider->srcDataType( 1 ) )
00519   {
00520     case QGis::Byte:
00521       myMetadata += tr( "Byte - Eight bit unsigned integer" );
00522       break;
00523     case QGis::UInt16:
00524       myMetadata += tr( "UInt16 - Sixteen bit unsigned integer " );
00525       break;
00526     case QGis::Int16:
00527       myMetadata += tr( "Int16 - Sixteen bit signed integer " );
00528       break;
00529     case QGis::UInt32:
00530       myMetadata += tr( "UInt32 - Thirty two bit unsigned integer " );
00531       break;
00532     case QGis::Int32:
00533       myMetadata += tr( "Int32 - Thirty two bit signed integer " );
00534       break;
00535     case QGis::Float32:
00536       myMetadata += tr( "Float32 - Thirty two bit floating point " );
00537       break;
00538     case QGis::Float64:
00539       myMetadata += tr( "Float64 - Sixty four bit floating point " );
00540       break;
00541     case QGis::CInt16:
00542       myMetadata += tr( "CInt16 - Complex Int16 " );
00543       break;
00544     case QGis::CInt32:
00545       myMetadata += tr( "CInt32 - Complex Int32 " );
00546       break;
00547     case QGis::CFloat32:
00548       myMetadata += tr( "CFloat32 - Complex Float32 " );
00549       break;
00550     case QGis::CFloat64:
00551       myMetadata += tr( "CFloat64 - Complex Float64 " );
00552       break;
00553     default:
00554       myMetadata += tr( "Could not determine raster data type." );
00555   }
00556   myMetadata += "</p>\n";
00557 
00558   myMetadata += "<p class=\"glossy\">";
00559   myMetadata += tr( "Pyramid overviews" );
00560   myMetadata += "</p>\n";
00561   myMetadata += "<p>";
00562 
00563   myMetadata += "<p class=\"glossy\">";
00564   myMetadata += tr( "Layer Spatial Reference System" );
00565   myMetadata += "</p>\n";
00566   myMetadata += "<p>";
00567   myMetadata += crs().toProj4();
00568   myMetadata += "</p>\n";
00569 
00570   myMetadata += "<p class=\"glossy\">";
00571   myMetadata += tr( "Layer Extent (layer original source projection)" );
00572   myMetadata += "</p>\n";
00573   myMetadata += "<p>";
00574   myMetadata += mDataProvider->extent().toString();
00575   myMetadata += "</p>\n";
00576 
00577   // output coordinate system
00578   // TODO: this is not related to layer, to be removed? [MD]
00579 #if 0
00580   myMetadata += "<tr><td class=\"glossy\">";
00581   myMetadata += tr( "Project Spatial Reference System" );
00582   myMetadata += "</p>\n";
00583   myMetadata += "<p>";
00584   myMetadata +=  mCoordinateTransform->destCRS().toProj4();
00585   myMetadata += "</p>\n";
00586 #endif
00587 
00588   //
00589   // Add the stats for each band to the output table
00590   //
00591   int myBandCountInt = bandCount();
00592   for ( int myIteratorInt = 1; myIteratorInt <= myBandCountInt; ++myIteratorInt )
00593   {
00594     QgsDebugMsg( "Raster properties : checking if band " + QString::number( myIteratorInt ) + " has stats? " );
00595     //band name
00596     myMetadata += "<p class=\"glossy\">\n";
00597     myMetadata += tr( "Band" );
00598     myMetadata += "</p>\n";
00599     myMetadata += "<p>";
00600     myMetadata += bandName( myIteratorInt );
00601     myMetadata += "</p>\n";
00602     //band number
00603     myMetadata += "<p>";
00604     myMetadata += tr( "Band No" );
00605     myMetadata += "</p>\n";
00606     myMetadata += "<p>\n";
00607     myMetadata += QString::number( myIteratorInt );
00608     myMetadata += "</p>\n";
00609 
00610     //check if full stats for this layer have already been collected
00611     if ( !dataProvider()->hasStatistics( myIteratorInt ) )  //not collected
00612     {
00613       QgsDebugMsg( ".....no" );
00614 
00615       myMetadata += "<p>";
00616       myMetadata += tr( "No Stats" );
00617       myMetadata += "</p>\n";
00618       myMetadata += "<p>\n";
00619       myMetadata += tr( "No stats collected yet" );
00620       myMetadata += "</p>\n";
00621     }
00622     else                    // collected - show full detail
00623     {
00624       QgsDebugMsg( ".....yes" );
00625 
00626       QgsRasterBandStats myRasterBandStats = dataProvider()->bandStatistics( myIteratorInt );
00627       //Min Val
00628       myMetadata += "<p>";
00629       myMetadata += tr( "Min Val" );
00630       myMetadata += "</p>\n";
00631       myMetadata += "<p>\n";
00632       myMetadata += QString::number( myRasterBandStats.minimumValue, 'f', 10 );
00633       myMetadata += "</p>\n";
00634 
00635       // Max Val
00636       myMetadata += "<p>";
00637       myMetadata += tr( "Max Val" );
00638       myMetadata += "</p>\n";
00639       myMetadata += "<p>\n";
00640       myMetadata += QString::number( myRasterBandStats.maximumValue, 'f', 10 );
00641       myMetadata += "</p>\n";
00642 
00643       // Range
00644       myMetadata += "<p>";
00645       myMetadata += tr( "Range" );
00646       myMetadata += "</p>\n";
00647       myMetadata += "<p>\n";
00648       myMetadata += QString::number( myRasterBandStats.range, 'f', 10 );
00649       myMetadata += "</p>\n";
00650 
00651       // Mean
00652       myMetadata += "<p>";
00653       myMetadata += tr( "Mean" );
00654       myMetadata += "</p>\n";
00655       myMetadata += "<p>\n";
00656       myMetadata += QString::number( myRasterBandStats.mean, 'f', 10 );
00657       myMetadata += "</p>\n";
00658 
00659       //sum of squares
00660       myMetadata += "<p>";
00661       myMetadata += tr( "Sum of squares" );
00662       myMetadata += "</p>\n";
00663       myMetadata += "<p>\n";
00664       myMetadata += QString::number( myRasterBandStats.sumOfSquares, 'f', 10 );
00665       myMetadata += "</p>\n";
00666 
00667       //standard deviation
00668       myMetadata += "<p>";
00669       myMetadata += tr( "Standard Deviation" );
00670       myMetadata += "</p>\n";
00671       myMetadata += "<p>\n";
00672       myMetadata += QString::number( myRasterBandStats.stdDev, 'f', 10 );
00673       myMetadata += "</p>\n";
00674 
00675       //sum of all cells
00676       myMetadata += "<p>";
00677       myMetadata += tr( "Sum of all cells" );
00678       myMetadata += "</p>\n";
00679       myMetadata += "<p>\n";
00680       myMetadata += QString::number( myRasterBandStats.sum, 'f', 10 );
00681       myMetadata += "</p>\n";
00682 
00683       //number of cells
00684       myMetadata += "<p>";
00685       myMetadata += tr( "Cell Count" );
00686       myMetadata += "</p>\n";
00687       myMetadata += "<p>\n";
00688       myMetadata += QString::number( myRasterBandStats.elementCount );
00689       myMetadata += "</p>\n";
00690     }
00691   }
00692 
00693   QgsDebugMsg( myMetadata );
00694   return myMetadata;
00695 }
00696 
00701 QPixmap QgsRasterLayer::paletteAsPixmap( int theBandNumber )
00702 {
00703   //TODO: This function should take dimensions
00704   QgsDebugMsg( "entered." );
00705 
00706   // Only do this for the GDAL provider?
00707   // Maybe WMS can do this differently using QImage::numColors and QImage::color()
00708   if ( mDataProvider->colorInterpretation( theBandNumber ) == QgsRaster::PaletteIndex )
00709   {
00710     QgsDebugMsg( "....found paletted image" );
00711     QgsColorRampShader myShader;
00712     QList<QgsColorRampShader::ColorRampItem> myColorRampItemList = mDataProvider->colorTable( theBandNumber );
00713     if ( myColorRampItemList.size() > 0 )
00714     {
00715       QgsDebugMsg( "....got color ramp item list" );
00716       myShader.setColorRampItemList( myColorRampItemList );
00717       myShader.setColorRampType( QgsColorRampShader::DISCRETE );
00718       // Draw image
00719       int mySize = 100;
00720       QPixmap myPalettePixmap( mySize, mySize );
00721       QPainter myQPainter( &myPalettePixmap );
00722 
00723       QImage myQImage = QImage( mySize, mySize, QImage::Format_RGB32 );
00724       myQImage.fill( 0 );
00725       myPalettePixmap.fill();
00726 
00727       double myStep = (( double )myColorRampItemList.size() - 1 ) / ( double )( mySize * mySize );
00728       double myValue = 0.0;
00729       for ( int myRow = 0; myRow < mySize; myRow++ )
00730       {
00731         QRgb* myLineBuffer = ( QRgb* )myQImage.scanLine( myRow );
00732         for ( int myCol = 0; myCol < mySize; myCol++ )
00733         {
00734           myValue = myStep * ( double )( myCol + myRow * mySize );
00735           int c1, c2, c3;
00736           myShader.shade( myValue, &c1, &c2, &c3 );
00737           myLineBuffer[ myCol ] = qRgb( c1, c2, c3 );
00738         }
00739       }
00740 
00741       myQPainter.drawImage( 0, 0, myQImage );
00742       return myPalettePixmap;
00743     }
00744     QPixmap myNullPixmap;
00745     return myNullPixmap;
00746   }
00747   else
00748   {
00749     //invalid layer  was requested
00750     QPixmap myNullPixmap;
00751     return myNullPixmap;
00752   }
00753 }
00754 
00755 QString QgsRasterLayer::providerType() const
00756 {
00757   return mProviderKey;
00758 }
00759 
00763 double QgsRasterLayer::rasterUnitsPerPixelX()
00764 {
00765 // We return one raster pixel per map unit pixel
00766 // One raster pixel can have several raster units...
00767 
00768 // We can only use one of the mGeoTransform[], so go with the
00769 // horisontal one.
00770 
00771   if ( mDataProvider->capabilities() & QgsRasterDataProvider::Size && mDataProvider->xSize() > 0 )
00772   {
00773     return mDataProvider->extent().width() / mDataProvider->xSize();
00774   }
00775   return 1;
00776 }
00777 
00778 double QgsRasterLayer::rasterUnitsPerPixelY()
00779 {
00780   if ( mDataProvider->capabilities() & QgsRasterDataProvider::Size && mDataProvider->xSize() > 0 )
00781   {
00782     return mDataProvider->extent().height() / mDataProvider->ySize();
00783   }
00784   return 1;
00785 }
00786 
00787 void QgsRasterLayer::init()
00788 {
00789   mRasterType = QgsRasterLayer::GrayOrUndefined;
00790 
00791   setRendererForDrawingStyle( QgsRasterLayer::UndefinedDrawingStyle );
00792 
00793   //Initialize the last view port structure, should really be a class
00794   mLastViewPort.mWidth = 0;
00795   mLastViewPort.mHeight = 0;
00796 }
00797 
00798 void QgsRasterLayer::setDataProvider( QString const & provider )
00799 {
00800   QgsDebugMsg( "Entered" );
00801   mValid = false; // assume the layer is invalid until we determine otherwise
00802 
00803   mPipe.remove( mDataProvider ); // deletes if exists
00804   mDataProvider = 0;
00805 
00806   // XXX should I check for and possibly delete any pre-existing providers?
00807   // XXX How often will that scenario occur?
00808 
00809   mProviderKey = provider;
00810   // set the layer name (uppercase first character)
00811   if ( ! mLayerName.isEmpty() )   // XXX shouldn't this happen in parent?
00812   {
00813     setLayerName( mLayerName );
00814   }
00815 
00816   //mBandCount = 0;
00817 
00818   mDataProvider = ( QgsRasterDataProvider* )QgsProviderRegistry::instance()->provider( mProviderKey, mDataSource );
00819   if ( !mDataProvider )
00820   {
00821     //QgsMessageLog::logMessage( tr( "Cannot instantiate the data provider" ), tr( "Raster" ) );
00822     appendError( ERR( tr( "Cannot instantiate the '%1' data provider" ).arg( mProviderKey ) ) );
00823     return;
00824   }
00825   QgsDebugMsg( "Data provider created" );
00826 
00827   // Set data provider into pipe even if not valid so that it is deleted with pipe (with layer)
00828   mPipe.set( mDataProvider );
00829   if ( !mDataProvider->isValid() )
00830   {
00831     setError( mDataProvider->error() );
00832     appendError( ERR( tr( "Provider is not valid (provider: %1, URI: %2" ).arg( mProviderKey ).arg( mDataSource ) ) );
00833     return;
00834   }
00835 
00836   if ( provider == "gdal" )
00837   {
00838     // make sure that the /vsigzip or /vsizip is added to uri, if applicable
00839     mDataSource = mDataProvider->dataSourceUri();
00840   }
00841 
00842   // get the extent
00843   QgsRectangle mbr = mDataProvider->extent();
00844 
00845   // show the extent
00846   QString s = mbr.toString();
00847   QgsDebugMsg( "Extent of layer: " + s );
00848   // store the extent
00849   setExtent( mbr );
00850 
00851   // upper case the first letter of the layer name
00852   QgsDebugMsg( "mLayerName: " + name() );
00853 
00854   // set up the raster drawing style
00855   // Do not set any 'sensible' style here, the style is set later
00856 
00857   // Setup source CRS
00858   setCrs( QgsCoordinateReferenceSystem( mDataProvider->crs() ) );
00859 
00860   QString mySourceWkt = crs().toWkt();
00861 
00862   QgsDebugMsg( "using wkt:\n" + mySourceWkt );
00863 
00864   //defaults - Needs to be set after the Contrast list has been build
00865   //Try to read the default contrast enhancement from the config file
00866 
00867   QSettings myQSettings;
00868 
00869   //decide what type of layer this is...
00870   //TODO Change this to look at the color interp and palette interp to decide which type of layer it is
00871   QgsDebugMsg( "bandCount = " + QString::number( mDataProvider->bandCount() ) );
00872   QgsDebugMsg( "dataType = " + QString::number( mDataProvider->dataType( 1 ) ) );
00873   if (( mDataProvider->bandCount() > 1 ) )
00874   {
00875     mRasterType = Multiband;
00876   }
00877   else if ( mDataProvider->dataType( 1 ) == QGis::ARGB32
00878             ||  mDataProvider->dataType( 1 ) == QGis::ARGB32_Premultiplied )
00879   {
00880     mRasterType = ColorLayer;
00881   }
00882   else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
00883   {
00884     mRasterType = Palette;
00885   }
00886   else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
00887   {
00888     mRasterType = Palette;
00889   }
00890   else
00891   {
00892     mRasterType = GrayOrUndefined;
00893   }
00894 
00895   QgsDebugMsg( "mRasterType = " + QString::number( mRasterType ) );
00896   if ( mRasterType == ColorLayer )
00897   {
00898     QgsDebugMsg( "Setting drawing style to SingleBandColorDataStyle " + QString::number( SingleBandColorDataStyle ) );
00899     setRendererForDrawingStyle( SingleBandColorDataStyle );
00900   }
00901   else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
00902   {
00903     setRendererForDrawingStyle( PalettedColor ); //sensible default
00904   }
00905   else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
00906   {
00907     setRendererForDrawingStyle( SingleBandPseudoColor );
00908     // Load color table
00909     QList<QgsColorRampShader::ColorRampItem> colorTable = mDataProvider->colorTable( 1 );
00910     QgsSingleBandPseudoColorRenderer* r = dynamic_cast<QgsSingleBandPseudoColorRenderer*>( renderer() );
00911     if ( r )
00912     {
00913       // TODO: this should go somewhere else
00914       QgsRasterShader* shader = new QgsRasterShader();
00915       QgsColorRampShader* colorRampShader = new QgsColorRampShader();
00916       colorRampShader->setColorRampType( QgsColorRampShader::INTERPOLATED );
00917       colorRampShader->setColorRampItemList( colorTable );
00918       shader->setRasterShaderFunction( colorRampShader );
00919       r->setShader( shader );
00920     }
00921   }
00922   else if ( mRasterType == Multiband )
00923   {
00924     setRendererForDrawingStyle( MultiBandColor );  //sensible default
00925   }
00926   else                        //GrayOrUndefined
00927   {
00928     setRendererForDrawingStyle( SingleBandGray );  //sensible default
00929   }
00930 
00931   // Auto set alpha band
00932   for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
00933   {
00934     if ( mDataProvider->colorInterpretation( bandNo ) == QgsRaster::AlphaBand )
00935     {
00936       if ( mPipe.renderer() )
00937       {
00938         mPipe.renderer()->setAlphaBand( bandNo );
00939       }
00940       break;
00941     }
00942   }
00943 
00944   // brightness filter
00945   QgsBrightnessContrastFilter * brightnessFilter = new QgsBrightnessContrastFilter();
00946   mPipe.set( brightnessFilter );
00947 
00948   // hue/saturation filter
00949   QgsHueSaturationFilter * hueSaturationFilter = new QgsHueSaturationFilter();
00950   mPipe.set( hueSaturationFilter );
00951 
00952   //resampler (must be after renderer)
00953   QgsRasterResampleFilter * resampleFilter = new QgsRasterResampleFilter();
00954   mPipe.set( resampleFilter );
00955 
00956   // projector (may be anywhere in pipe)
00957   QgsRasterProjector * projector = new QgsRasterProjector;
00958   mPipe.set( projector );
00959 
00960   // Set default identify format - use the richest format available
00961   int capabilities = mDataProvider->capabilities();
00962   QgsRaster::IdentifyFormat identifyFormat = QgsRaster::IdentifyFormatUndefined;
00963   if ( capabilities & QgsRasterInterface::IdentifyHtml )
00964   {
00965     // HTML is usually richest
00966     identifyFormat = QgsRaster::IdentifyFormatHtml;
00967   }
00968   else if ( capabilities & QgsRasterInterface::IdentifyFeature )
00969   {
00970     identifyFormat = QgsRaster::IdentifyFormatFeature;
00971   }
00972   else if ( capabilities & QgsRasterInterface::IdentifyText )
00973   {
00974     identifyFormat = QgsRaster::IdentifyFormatText;
00975   }
00976   else if ( capabilities & QgsRasterInterface::IdentifyValue )
00977   {
00978     identifyFormat = QgsRaster::IdentifyFormatValue;
00979   }
00980   setCustomProperty( "identify/format", QgsRasterDataProvider::identifyFormatName( identifyFormat ) );
00981 
00982   // Store timestamp
00983   // TODO move to provider
00984   mLastModified = lastModified( mDataSource );
00985 
00986   // Connect provider signals
00987   connect(
00988     mDataProvider, SIGNAL( progress( int, double, QString ) ),
00989     this,          SLOT( onProgress( int, double, QString ) )
00990   );
00991 
00992   // Do a passthrough for the status bar text
00993   connect(
00994     mDataProvider, SIGNAL( statusChanged( QString ) ),
00995     this,          SIGNAL( statusChanged( QString ) )
00996   );
00997 
00998   //mark the layer as valid
00999   mValid = true;
01000 
01001   QgsDebugMsg( "exiting." );
01002 } // QgsRasterLayer::setDataProvider
01003 
01004 void QgsRasterLayer::closeDataProvider()
01005 {
01006   mValid = false;
01007   mPipe.remove( mDataProvider );
01008   mDataProvider = 0;
01009 }
01010 
01011 void QgsRasterLayer::setContrastEnhancement( QgsContrastEnhancement::ContrastEnhancementAlgorithm theAlgorithm, QgsRaster::ContrastEnhancementLimits theLimits, QgsRectangle theExtent, int theSampleSize, bool theGenerateLookupTableFlag )
01012 {
01013   QgsDebugMsg( QString( "theAlgorithm = %1 theLimits = %2 theExtent.isEmpty() = %3" ).arg( theAlgorithm ).arg( theLimits ).arg( theExtent.isEmpty() ) );
01014   if ( !mPipe.renderer() || !mDataProvider )
01015   {
01016     return;
01017   }
01018 
01019   QList<int> myBands;
01020   QList<QgsContrastEnhancement*> myEnhancements;
01021   QgsSingleBandGrayRenderer* myGrayRenderer = 0;
01022   QgsMultiBandColorRenderer* myMultiBandRenderer = 0;
01023   QString rendererType  = mPipe.renderer()->type();
01024   if ( rendererType == "singlebandgray" )
01025   {
01026     myGrayRenderer = dynamic_cast<QgsSingleBandGrayRenderer*>( mPipe.renderer() );
01027     if ( !myGrayRenderer ) return;
01028     myBands << myGrayRenderer->grayBand();
01029   }
01030   else if ( rendererType == "multibandcolor" )
01031   {
01032     myMultiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer*>( mPipe.renderer() );
01033     if ( !myMultiBandRenderer ) return;
01034     myBands << myMultiBandRenderer->redBand() << myMultiBandRenderer->greenBand() << myMultiBandRenderer->blueBand();
01035   }
01036 
01037   foreach ( int myBand, myBands )
01038   {
01039     if ( myBand != -1 )
01040     {
01041       QGis::DataType myType = ( QGis::DataType )mDataProvider->dataType( myBand );
01042       QgsContrastEnhancement* myEnhancement = new QgsContrastEnhancement(( QGis::DataType )myType );
01043       myEnhancement->setContrastEnhancementAlgorithm( theAlgorithm, theGenerateLookupTableFlag );
01044 
01045       double myMin = std::numeric_limits<double>::quiet_NaN();
01046       double myMax = std::numeric_limits<double>::quiet_NaN();
01047 
01048       if ( theLimits == QgsRaster::ContrastEnhancementMinMax )
01049       {
01050         QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( myBand, QgsRasterBandStats::Min | QgsRasterBandStats::Max, theExtent, theSampleSize );
01051         myMin = myRasterBandStats.minimumValue;
01052         myMax = myRasterBandStats.maximumValue;
01053       }
01054       else if ( theLimits == QgsRaster::ContrastEnhancementStdDev )
01055       {
01056         double myStdDev = 1; // make optional?
01057         QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( myBand, QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, theExtent, theSampleSize );
01058         myMin = myRasterBandStats.mean - ( myStdDev * myRasterBandStats.stdDev );
01059         myMax = myRasterBandStats.mean + ( myStdDev * myRasterBandStats.stdDev );
01060       }
01061       else if ( theLimits == QgsRaster::ContrastEnhancementCumulativeCut )
01062       {
01063         QSettings mySettings;
01064         double myLower = mySettings.value( "/Raster/cumulativeCutLower", QString::number( CUMULATIVE_CUT_LOWER ) ).toDouble();
01065         double myUpper = mySettings.value( "/Raster/cumulativeCutUpper", QString::number( CUMULATIVE_CUT_UPPER ) ).toDouble();
01066         QgsDebugMsg( QString( "myLower = %1 myUpper = %2" ).arg( myLower ).arg( myUpper ) );
01067         mDataProvider->cumulativeCut( myBand, myLower, myUpper, myMin, myMax, theExtent, theSampleSize );
01068       }
01069 
01070       QgsDebugMsg( QString( "myBand = %1 myMin = %2 myMax = %3" ).arg( myBand ).arg( myMin ).arg( myMax ) );
01071       myEnhancement->setMinimumValue( myMin );
01072       myEnhancement->setMaximumValue( myMax );
01073       myEnhancements.append( myEnhancement );
01074     }
01075     else
01076     {
01077       myEnhancements.append( 0 );
01078     }
01079   }
01080 
01081   if ( rendererType == "singlebandgray" )
01082   {
01083     if ( myEnhancements.value( 0 ) ) myGrayRenderer->setContrastEnhancement( myEnhancements.value( 0 ) );
01084   }
01085   else if ( rendererType == "multibandcolor" )
01086   {
01087     if ( myEnhancements.value( 0 ) ) myMultiBandRenderer->setRedContrastEnhancement( myEnhancements.value( 0 ) );
01088     if ( myEnhancements.value( 1 ) ) myMultiBandRenderer->setGreenContrastEnhancement( myEnhancements.value( 1 ) );
01089     if ( myEnhancements.value( 2 ) ) myMultiBandRenderer->setBlueContrastEnhancement( myEnhancements.value( 2 ) );
01090   }
01091 }
01092 
01093 void QgsRasterLayer::setDefaultContrastEnhancement()
01094 {
01095   QgsDebugMsg( "Entered" );
01096 
01097   QSettings mySettings;
01098 
01099   QString myKey;
01100   QString myDefault;
01101 
01102   // TODO: we should not test renderer class here, move it somehow to renderers
01103   if ( dynamic_cast<QgsSingleBandGrayRenderer*>( renderer() ) )
01104   {
01105     myKey = "singleBand";
01106     myDefault = "StretchToMinimumMaximum";
01107   }
01108   else if ( dynamic_cast<QgsMultiBandColorRenderer*>( renderer() ) )
01109   {
01110     if ( QgsRasterBlock::typeSize( dataProvider()->srcDataType( 1 ) ) == 1 )
01111     {
01112       myKey = "multiBandSingleByte";
01113       myDefault = "NoEnhancement";
01114     }
01115     else
01116     {
01117       myKey = "multiBandMultiByte";
01118       myDefault = "StretchToMinimumMaximum";
01119     }
01120   }
01121 
01122   if ( myKey.isEmpty() )
01123   {
01124     QgsDebugMsg( "No default contrast enhancement for this drawing style" );
01125   }
01126   QgsDebugMsg( "myKey = " + myKey );
01127 
01128   QString myAlgorithmString = mySettings.value( "/Raster/defaultContrastEnhancementAlgorithm/" + myKey, myDefault ).toString();
01129   QgsDebugMsg( "myAlgorithmString = " + myAlgorithmString );
01130 
01131   QgsContrastEnhancement::ContrastEnhancementAlgorithm myAlgorithm = QgsContrastEnhancement::contrastEnhancementAlgorithmFromString( myAlgorithmString );
01132 
01133   if ( myAlgorithm == QgsContrastEnhancement::NoEnhancement )
01134   {
01135     return;
01136   }
01137 
01138   QString myLimitsString = mySettings.value( "/Raster/defaultContrastEnhancementLimits", "CumulativeCut" ).toString();
01139   QgsRaster::ContrastEnhancementLimits myLimits = QgsRaster::contrastEnhancementLimitsFromString( myLimitsString );
01140 
01141   setContrastEnhancement( myAlgorithm, myLimits );
01142 }
01143 
01149 void QgsRasterLayer::setDrawingStyle( QString const & theDrawingStyleQString )
01150 {
01151   QgsDebugMsg( "DrawingStyle = " + theDrawingStyleQString );
01152   DrawingStyle drawingStyle;
01153   if ( theDrawingStyleQString == "SingleBandGray" )//no need to tr() this its not shown in ui
01154   {
01155     drawingStyle = SingleBandGray;
01156   }
01157   else if ( theDrawingStyleQString == "SingleBandPseudoColor" )//no need to tr() this its not shown in ui
01158   {
01159     drawingStyle = SingleBandPseudoColor;
01160   }
01161   else if ( theDrawingStyleQString == "PalettedColor" )//no need to tr() this its not shown in ui
01162   {
01163     drawingStyle = PalettedColor;
01164   }
01165   else if ( theDrawingStyleQString == "PalettedSingleBandGray" )//no need to tr() this its not shown in ui
01166   {
01167     drawingStyle = PalettedSingleBandGray;
01168   }
01169   else if ( theDrawingStyleQString == "PalettedSingleBandPseudoColor" )//no need to tr() this its not shown in ui
01170   {
01171     drawingStyle = PalettedSingleBandPseudoColor;
01172   }
01173   else if ( theDrawingStyleQString == "PalettedMultiBandColor" )//no need to tr() this its not shown in ui
01174   {
01175     drawingStyle = PalettedMultiBandColor;
01176   }
01177   else if ( theDrawingStyleQString == "MultiBandSingleBandGray" )//no need to tr() this its not shown in ui
01178   {
01179     drawingStyle = MultiBandSingleBandGray;
01180   }
01181   else if ( theDrawingStyleQString == "MultiBandSingleBandPseudoColor" )//no need to tr() this its not shown in ui
01182   {
01183     drawingStyle = MultiBandSingleBandPseudoColor;
01184   }
01185   else if ( theDrawingStyleQString == "MultiBandColor" )//no need to tr() this its not shown in ui
01186   {
01187     drawingStyle = MultiBandColor;
01188   }
01189   else if ( theDrawingStyleQString == "SingleBandColorDataStyle" )//no need to tr() this its not shown in ui
01190   {
01191     QgsDebugMsg( "Setting drawingStyle to SingleBandColorDataStyle " + QString::number( SingleBandColorDataStyle ) );
01192     drawingStyle = SingleBandColorDataStyle;
01193     QgsDebugMsg( "Setted drawingStyle to " + QString::number( drawingStyle ) );
01194   }
01195   else
01196   {
01197     drawingStyle = UndefinedDrawingStyle;
01198   }
01199   setRendererForDrawingStyle( drawingStyle );
01200 }
01201 
01202 void QgsRasterLayer::setLayerOrder( QStringList const & layers )
01203 {
01204   QgsDebugMsg( "entered." );
01205 
01206   if ( mDataProvider )
01207   {
01208     QgsDebugMsg( "About to mDataProvider->setLayerOrder(layers)." );
01209     mDataProvider->setLayerOrder( layers );
01210   }
01211 
01212 }
01213 
01214 void QgsRasterLayer::setSubLayerVisibility( QString name, bool vis )
01215 {
01216 
01217   if ( mDataProvider )
01218   {
01219     QgsDebugMsg( "About to mDataProvider->setSubLayerVisibility(name, vis)." );
01220     mDataProvider->setSubLayerVisibility( name, vis );
01221   }
01222 
01223 }
01224 
01225 void QgsRasterLayer::setRenderer( QgsRasterRenderer* theRenderer )
01226 {
01227   QgsDebugMsg( "Entered" );
01228   if ( !theRenderer ) { return; }
01229   mPipe.set( theRenderer );
01230 }
01231 
01232 void QgsRasterLayer::showProgress( int theValue )
01233 {
01234   emit progressUpdate( theValue );
01235 }
01236 
01237 
01238 void QgsRasterLayer::showStatusMessage( QString const & theMessage )
01239 {
01240   // QgsDebugMsg(QString("entered with '%1'.").arg(theMessage));
01241 
01242   // Pass-through
01243   // TODO: See if we can connect signal-to-signal.  This is a kludge according to the Qt doc.
01244   emit statusChanged( theMessage );
01245 }
01246 
01247 QStringList QgsRasterLayer::subLayers() const
01248 {
01249   return mDataProvider->subLayers();
01250 }
01251 
01252 QPixmap QgsRasterLayer::previewAsPixmap( QSize size, QColor bgColor )
01253 {
01254   QPixmap myQPixmap( size );
01255 
01256   myQPixmap.fill( bgColor );  //defaults to white, set to transparent for rendering on a map
01257 
01258   QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
01259 
01260   double myMapUnitsPerPixel;
01261   double myX = 0.0;
01262   double myY = 0.0;
01263   QgsRectangle myExtent = mDataProvider->extent();
01264   if ( myExtent.width() / myExtent.height() >=  myQPixmap.width() / myQPixmap.height() )
01265   {
01266     myMapUnitsPerPixel = myExtent.width() / myQPixmap.width();
01267     myY = ( myQPixmap.height() - myExtent.height() / myMapUnitsPerPixel ) / 2;
01268   }
01269   else
01270   {
01271     myMapUnitsPerPixel = myExtent.height() / myQPixmap.height();
01272     myX = ( myQPixmap.width() - myExtent.width() / myMapUnitsPerPixel ) / 2;
01273   }
01274 
01275   double myPixelWidth = myExtent.width() / myMapUnitsPerPixel;
01276   double myPixelHeight = myExtent.height() / myMapUnitsPerPixel;
01277 
01278   myRasterViewPort->mTopLeftPoint = QgsPoint( myX, myY );
01279   myRasterViewPort->mBottomRightPoint = QgsPoint( myPixelWidth, myPixelHeight );
01280   myRasterViewPort->mWidth = myQPixmap.width();
01281   myRasterViewPort->mHeight = myQPixmap.height();
01282 
01283   myRasterViewPort->mDrawnExtent = myExtent;
01284   myRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
01285   myRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
01286 
01287   QgsMapToPixel *myMapToPixel = new QgsMapToPixel( myMapUnitsPerPixel );
01288 
01289   QPainter * myQPainter = new QPainter( &myQPixmap );
01290   draw( myQPainter, myRasterViewPort, myMapToPixel );
01291   delete myRasterViewPort;
01292   delete myMapToPixel;
01293   myQPainter->end();
01294   delete myQPainter;
01295 
01296   return myQPixmap;
01297 }
01298 
01299 void QgsRasterLayer::triggerRepaint()
01300 {
01301   emit repaintRequested();
01302 }
01303 
01304 void QgsRasterLayer::updateProgress( int theProgress, int theMax )
01305 {
01306   //simply propogate it on!
01307   emit drawingProgress( theProgress, theMax );
01308 }
01309 
01310 void QgsRasterLayer::onProgress( int theType, double theProgress, QString theMessage )
01311 {
01312   Q_UNUSED( theType );
01313   Q_UNUSED( theMessage );
01314   QgsDebugMsg( QString( "theProgress = %1" ).arg( theProgress ) );
01315   emit progressUpdate(( int )theProgress );
01316 }
01317 
01319 //
01320 // Protected methods
01321 //
01323 /*
01324  * @param QDomNode node that will contain the symbology definition for this layer.
01325  * @param errorMessage reference to string that will be updated with any error messages
01326  * @return true in case of success.
01327  */
01328 bool QgsRasterLayer::readSymbology( const QDomNode& layer_node, QString& errorMessage )
01329 {
01330   Q_UNUSED( errorMessage );
01331   QDomElement rasterRendererElem;
01332 
01333   // pipe element was introduced in the end of 1.9 development when there were
01334   // already many project files in use so we support 1.9 backward compatibility
01335   // even it was never officialy released -> use pipe element if present, otherwise
01336   // use layer node
01337   QDomNode pipeNode = layer_node.firstChildElement( "pipe" );
01338   if ( pipeNode.isNull() ) // old project
01339   {
01340     pipeNode = layer_node;
01341   }
01342 
01343   //rasterlayerproperties element there -> old format (1.8 and early 1.9)
01344   if ( !layer_node.firstChildElement( "rasterproperties" ).isNull() )
01345   {
01346     //copy node because layer_node is const
01347     QDomNode layerNodeCopy = layer_node.cloneNode();
01348     QDomDocument doc = layerNodeCopy.ownerDocument();
01349     QDomElement rasterPropertiesElem = layerNodeCopy.firstChildElement( "rasterproperties" );
01350     QgsProjectFileTransform::convertRasterProperties( doc, layerNodeCopy, rasterPropertiesElem,
01351         this );
01352     rasterRendererElem = layerNodeCopy.firstChildElement( "rasterrenderer" );
01353     QgsDebugMsg( doc.toString() );
01354   }
01355   else
01356   {
01357     rasterRendererElem = pipeNode.firstChildElement( "rasterrenderer" );
01358   }
01359 
01360   if ( !rasterRendererElem.isNull() )
01361   {
01362     QString rendererType = rasterRendererElem.attribute( "type" );
01363     QgsRasterRendererRegistryEntry rendererEntry;
01364     if ( QgsRasterRendererRegistry::instance()->rendererData( rendererType, rendererEntry ) )
01365     {
01366       QgsRasterRenderer *renderer = rendererEntry.rendererCreateFunction( rasterRendererElem, dataProvider() );
01367       mPipe.set( renderer );
01368     }
01369   }
01370 
01371   //brightness
01372   QgsBrightnessContrastFilter * brightnessFilter = new QgsBrightnessContrastFilter();
01373   mPipe.set( brightnessFilter );
01374 
01375   //brightness coefficient
01376   QDomElement brightnessElem = pipeNode.firstChildElement( "brightnesscontrast" );
01377   if ( !brightnessElem.isNull() )
01378   {
01379     brightnessFilter->readXML( brightnessElem );
01380   }
01381 
01382   //hue/saturation
01383   QgsHueSaturationFilter * hueSaturationFilter = new QgsHueSaturationFilter();
01384   mPipe.set( hueSaturationFilter );
01385 
01386   //saturation coefficient
01387   QDomElement hueSaturationElem = pipeNode.firstChildElement( "huesaturation" );
01388   if ( !hueSaturationElem.isNull() )
01389   {
01390     hueSaturationFilter->readXML( hueSaturationElem );
01391   }
01392 
01393   //resampler
01394   QgsRasterResampleFilter * resampleFilter = new QgsRasterResampleFilter();
01395   mPipe.set( resampleFilter );
01396 
01397   //max oversampling
01398   QDomElement resampleElem = pipeNode.firstChildElement( "rasterresampler" );
01399   if ( !resampleElem.isNull() )
01400   {
01401     resampleFilter->readXML( resampleElem );
01402   }
01403 
01404   // get and set the blend mode if it exists
01405   QDomNode blendModeNode = layer_node.namedItem( "blendMode" );
01406   if ( !blendModeNode.isNull() )
01407   {
01408     QDomElement e = blendModeNode.toElement();
01409     setBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) e.text().toInt() ) );
01410   }
01411 
01412   return true;
01413 } //readSymbology
01414 
01421 bool QgsRasterLayer::readXml( const QDomNode& layer_node )
01422 {
01423   QgsDebugMsg( "Entered" );
01425 
01426   //process provider key
01427   QDomNode pkeyNode = layer_node.namedItem( "provider" );
01428 
01429   if ( pkeyNode.isNull() )
01430   {
01431     mProviderKey = "gdal";
01432   }
01433   else
01434   {
01435     QDomElement pkeyElt = pkeyNode.toElement();
01436     mProviderKey = pkeyElt.text();
01437     if ( mProviderKey.isEmpty() )
01438     {
01439       mProviderKey = "gdal";
01440     }
01441   }
01442 
01443   // Open the raster source based on provider and datasource
01444 
01445   // Go down the raster-data-provider paradigm
01446 
01447   // Collect provider-specific information
01448 
01449   QDomNode rpNode = layer_node.namedItem( "rasterproperties" );
01450 
01451   if ( mProviderKey == "wms" )
01452   {
01453     // >>> BACKWARD COMPATIBILITY < 1.9
01454     // The old WMS URI format does not contain all the informations, we add them here.
01455     if ( !mDataSource.contains( "crs=" ) && !mDataSource.contains( "format=" ) )
01456     {
01457       QgsDebugMsg( "Old WMS URI format detected -> adding params" );
01458       QgsDataSourceURI uri;
01459       uri.setEncodedUri( mDataSource );
01460       QDomElement layerElement = rpNode.firstChildElement( "wmsSublayer" );
01461       while ( !layerElement.isNull() )
01462       {
01463         // TODO: sublayer visibility - post-0.8 release timeframe
01464 
01465         // collect name for the sublayer
01466         uri.setParam( "layers",  layerElement.namedItem( "name" ).toElement().text() );
01467 
01468         // collect style for the sublayer
01469         uri.setParam( "styles", layerElement.namedItem( "style" ).toElement().text() );
01470 
01471         layerElement = layerElement.nextSiblingElement( "wmsSublayer" );
01472       }
01473 
01474       // Collect format
01475       QDomNode formatNode = rpNode.namedItem( "wmsFormat" );
01476       uri.setParam( "format", rpNode.namedItem( "wmsFormat" ).toElement().text() );
01477 
01478       // WMS CRS URL param should not be mixed with that assigned to the layer.
01479       // In the old WMS URI version there was no CRS and layer crs().authid() was used.
01480       uri.setParam( "crs", crs().authid() );
01481       mDataSource = uri.encodedUri();
01482     }
01483     // <<< BACKWARD COMPATIBILITY < 1.9
01484   }
01485 
01486   setDataProvider( mProviderKey );
01487   if ( !mValid ) return false;
01488 
01489   QString theError;
01490   bool res = readSymbology( layer_node, theError );
01491 
01492   // old wms settings we need to correct
01493   if ( res && mProviderKey == "wms" )
01494   {
01495     setRendererForDrawingStyle( SingleBandColorDataStyle );
01496   }
01497 
01498   // Check timestamp
01499   // This was probably introduced to reload completely raster if data changed and
01500   // reset completly symbology to reflect new data type etc. It creates however
01501   // problems, because user defined symbology is complete lost if data file time
01502   // changed (the content may be the same). See also 6900.
01503 #if 0
01504   QDomNode stampNode = layer_node.namedItem( "timestamp" );
01505   if ( !stampNode.isNull() )
01506   {
01507     QDateTime stamp = QDateTime::fromString( stampNode.toElement().text(), Qt::ISODate );
01508     // TODO: very bad, we have to load twice!!! Make QgsDataProvider::timestamp() static?
01509     if ( stamp < mDataProvider->dataTimestamp() )
01510     {
01511       QgsDebugMsg( "data changed, reload provider" );
01512       closeDataProvider();
01513       init();
01514       setDataProvider( mProviderKey );
01515       if ( !mValid ) return false;
01516     }
01517   }
01518 #endif
01519 
01520   // Load user no data value
01521   QDomElement noDataElement = layer_node.firstChildElement( "noData" );
01522 
01523   QDomNodeList noDataBandList = noDataElement.elementsByTagName( "noDataList" );
01524 
01525   for ( int i = 0; i < noDataBandList.size(); ++i )
01526   {
01527     QDomElement bandElement = noDataBandList.at( i ).toElement();
01528     bool ok;
01529     int bandNo = bandElement.attribute( "bandNo" ).toInt( &ok );
01530     QgsDebugMsg( QString( "bandNo = %1" ).arg( bandNo ) );
01531     if ( ok && ( bandNo > 0 ) && ( bandNo <= mDataProvider->bandCount() ) )
01532     {
01533       mDataProvider->setUseSrcNoDataValue( bandNo, bandElement.attribute( "useSrcNoData" ).toInt() );
01534       QgsRasterRangeList myNoDataRangeList;
01535 
01536       QDomNodeList rangeList = bandElement.elementsByTagName( "noDataRange" );
01537 
01538       for ( int j = 0; j < rangeList.size(); ++j )
01539       {
01540         QDomElement rangeElement = rangeList.at( j ).toElement();
01541         QgsRasterRange myNoDataRange( rangeElement.attribute( "min" ).toDouble(),
01542                                       rangeElement.attribute( "max" ).toDouble() );
01543         QgsDebugMsg( QString( "min = %1 %2" ).arg( rangeElement.attribute( "min" ) ).arg( myNoDataRange.min() ) );
01544         myNoDataRangeList << myNoDataRange;
01545       }
01546       mDataProvider->setUserNoDataValue( bandNo, myNoDataRangeList );
01547     }
01548   }
01549 
01550   return res;
01551 } // QgsRasterLayer::readXml( QDomNode & layer_node )
01552 
01553 /*
01554  * @param QDomNode the node that will have the style element added to it.
01555  * @param QDomDocument the document that will have the QDomNode added.
01556  * @param errorMessage reference to string that will be updated with any error messages
01557  * @return true in case of success.
01558  */
01559 bool QgsRasterLayer::writeSymbology( QDomNode & layer_node, QDomDocument & document, QString& errorMessage ) const
01560 {
01561   Q_UNUSED( errorMessage );
01562   QDomElement layerElem = layer_node.toElement();
01563 
01564   // Store pipe members (except provider) into pipe element, in future, it will be
01565   // possible to add custom filters into the pipe
01566   QDomElement pipeElement  = document.createElement( "pipe" );
01567 
01568   for ( int i = 1; i < mPipe.size(); i++ )
01569   {
01570     QgsRasterInterface * interface = mPipe.at( i );
01571     if ( !interface ) continue;
01572     interface->writeXML( document, pipeElement );
01573   }
01574 
01575   layer_node.appendChild( pipeElement );
01576 
01577   // add blend mode node
01578   QDomElement blendModeElement  = document.createElement( "blendMode" );
01579   QDomText blendModeText = document.createTextNode( QString::number( QgsMapRenderer::getBlendModeEnum( blendMode() ) ) );
01580   blendModeElement.appendChild( blendModeText );
01581   layer_node.appendChild( blendModeElement );
01582 
01583   return true;
01584 } // bool QgsRasterLayer::writeSymbology
01585 
01586 /*
01587  *  virtual
01588  *  @note Called by QgsMapLayer::writeXML().
01589  */
01590 bool QgsRasterLayer::writeXml( QDomNode & layer_node,
01591                                QDomDocument & document )
01592 {
01593   // first get the layer element so that we can append the type attribute
01594 
01595   QDomElement mapLayerNode = layer_node.toElement();
01596 
01597   if ( mapLayerNode.isNull() || "maplayer" != mapLayerNode.nodeName() )
01598   {
01599     QgsMessageLog::logMessage( tr( "<maplayer> not found." ), tr( "Raster" ) );
01600     return false;
01601   }
01602 
01603   mapLayerNode.setAttribute( "type", "raster" );
01604 
01605   // add provider node
01606 
01607   QDomElement provider  = document.createElement( "provider" );
01608   QDomText providerText = document.createTextNode( mProviderKey );
01609   provider.appendChild( providerText );
01610   layer_node.appendChild( provider );
01611 
01612   // User no data
01613   QDomElement noData  = document.createElement( "noData" );
01614 
01615   for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
01616   {
01617     if ( mDataProvider->userNoDataValues( bandNo ).isEmpty() ) continue;
01618 
01619     QDomElement noDataRangeList = document.createElement( "noDataList" );
01620     noDataRangeList.setAttribute( "bandNo", bandNo );
01621     noDataRangeList.setAttribute( "useSrcNoData", mDataProvider->useSrcNoDataValue( bandNo ) );
01622 
01623     foreach ( QgsRasterRange range, mDataProvider->userNoDataValues( bandNo ) )
01624     {
01625       QDomElement noDataRange =  document.createElement( "noDataRange" );
01626 
01627       noDataRange.setAttribute( "min", range.min() );
01628       noDataRange.setAttribute( "max", range.max() );
01629       noDataRangeList.appendChild( noDataRange );
01630     }
01631 
01632     noData.appendChild( noDataRangeList );
01633 
01634   }
01635   if ( noData.hasChildNodes() )
01636   {
01637     layer_node.appendChild( noData );
01638   }
01639 
01640   //write out the symbology
01641   QString errorMsg;
01642   return writeSymbology( layer_node, document, errorMsg );
01643 }
01644 
01645 int QgsRasterLayer::width() const
01646 {
01647   if ( !mDataProvider ) return 0;
01648   return mDataProvider->xSize();
01649 }
01650 
01651 int QgsRasterLayer::height() const
01652 {
01653   if ( !mDataProvider ) return 0;
01654   return mDataProvider->ySize();
01655 }
01656 
01658 //
01659 // Private methods
01660 //
01662 bool QgsRasterLayer::update()
01663 {
01664   QgsDebugMsg( "entered." );
01665   // Check if data changed
01666   if ( mDataProvider->dataTimestamp() > mDataProvider->timestamp() )
01667   {
01668     QgsDebugMsg( "reload data" );
01669     closeDataProvider();
01670     init();
01671     setDataProvider( mProviderKey );
01672     emit dataChanged();
01673   }
01674   return mValid;
01675 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines