QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsrasterlayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterlayer.cpp - description
3  -------------------
4 begin : Sat Jun 22 2002
5 copyright : (C) 2003 by Tim Sutton, Steve Halasz and Gary E.Sherman
6 email : tim at linfiniti.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 #include "qgsapplication.h"
18 #include "qgscolorrampshader.h"
20 #include "qgscoordinatetransform.h"
21 #include "qgsdatasourceuri.h"
22 #include "qgslogger.h"
23 #include "qgsmaplayerregistry.h"
24 #include "qgsmaptopixel.h"
25 #include "qgsmessagelog.h"
29 #include "qgsproviderregistry.h"
30 #include "qgspseudocolorshader.h"
31 #include "qgsrasterdrawer.h"
32 #include "qgsrasteriterator.h"
33 #include "qgsrasterlayer.h"
34 #include "qgsrasterprojector.h"
35 #include "qgsrasterrange.h"
37 #include "qgsrectangle.h"
38 #include "qgsrendercontext.h"
42 
43 #include <cmath>
44 #include <cstdio>
45 #include <limits>
46 #include <typeinfo>
47 
48 #include <QApplication>
49 #include <QCursor>
50 #include <QDomElement>
51 #include <QDomNode>
52 #include <QFile>
53 #include <QFileInfo>
54 #include <QFont>
55 #include <QFontMetrics>
56 #include <QFrame>
57 #include <QImage>
58 #include <QLabel>
59 #include <QLibrary>
60 #include <QList>
61 #include <QMatrix>
62 #include <QMessageBox>
63 #include <QPainter>
64 #include <QPixmap>
65 #include <QRegExp>
66 #include <QSettings>
67 #include <QSlider>
68 #include <QTime>
69 
70 // typedefs for provider plugin functions of interest
71 typedef bool isvalidrasterfilename_t( QString const & theFileNameQString, QString & retErrMsg );
72 
73 #define ERR(message) QGS_ERROR_MESSAGE(message,"Raster layer")
74 
75 const double QgsRasterLayer::CUMULATIVE_CUT_LOWER = 0.02;
76 const double QgsRasterLayer::CUMULATIVE_CUT_UPPER = 0.98;
77 const double QgsRasterLayer::SAMPLE_SIZE = 250000;
78 
80  : QgsMapLayer( RasterLayer )
81  , QSTRING_NOT_SET( "Not Set" )
82  , TRSTRING_NOT_SET( tr( "Not Set" ) )
83  , mDataProvider( 0 )
84 {
85  init();
86  mValid = false;
87 }
88 
90  QString const & path,
91  QString const & baseName,
92  bool loadDefaultStyleFlag )
93  : QgsMapLayer( RasterLayer, baseName, path )
94  , QSTRING_NOT_SET( "Not Set" )
95  , TRSTRING_NOT_SET( tr( "Not Set" ) )
96  , mDataProvider( 0 )
97 {
98  QgsDebugMsg( "Entered" );
99 
100  // TODO, call constructor with provider key
101  init();
102  setDataProvider( "gdal" );
103  if ( !mValid ) return;
104 
105  bool defaultLoadedFlag = false;
106  if ( mValid && loadDefaultStyleFlag )
107  {
108  loadDefaultStyle( defaultLoadedFlag );
109  }
110  if ( !defaultLoadedFlag )
111  {
113  }
114  return;
115 } // QgsRasterLayer ctor
116 
121 QgsRasterLayer::QgsRasterLayer( const QString & uri,
122  const QString & baseName,
123  const QString & providerKey,
124  bool loadDefaultStyleFlag )
125  : QgsMapLayer( RasterLayer, baseName, uri )
126  // Constant that signals property not used.
127  , QSTRING_NOT_SET( "Not Set" )
128  , TRSTRING_NOT_SET( tr( "Not Set" ) )
129  , mDataProvider( 0 )
130  , mProviderKey( providerKey )
131 {
132  QgsDebugMsg( "Entered" );
133  init();
134  setDataProvider( providerKey );
135  if ( !mValid ) return;
136 
137  // load default style
138  bool defaultLoadedFlag = false;
139  if ( mValid && loadDefaultStyleFlag )
140  {
141  loadDefaultStyle( defaultLoadedFlag );
142  }
143  if ( !defaultLoadedFlag )
144  {
146  }
147 
148  // TODO: Connect signals from the dataprovider to the qgisapp
149 
150  emit statusChanged( tr( "QgsRasterLayer created" ) );
151 } // QgsRasterLayer ctor
152 
154 {
155  mValid = false;
156  // Note: provider and other interfaces are owned and deleted by pipe
157 }
158 
160 //
161 // Static Methods and members
162 //
164 
168 bool QgsRasterLayer::isValidRasterFileName( QString const & theFileNameQString, QString & retErrMsg )
169 {
170  isvalidrasterfilename_t *pValid = ( isvalidrasterfilename_t * ) cast_to_fptr( QgsProviderRegistry::instance()->function( "gdal", "isValidRasterFileName" ) );
171  if ( ! pValid )
172  {
173  QgsDebugMsg( "Could not resolve isValidRasterFileName in gdal provider library" );
174  return false;
175  }
176 
177  bool myIsValid = pValid( theFileNameQString, retErrMsg );
178  return myIsValid;
179 }
180 
181 bool QgsRasterLayer::isValidRasterFileName( QString const & theFileNameQString )
182 {
183  QString retErrMsg;
184  return isValidRasterFileName( theFileNameQString, retErrMsg );
185 }
186 
187 QDateTime QgsRasterLayer::lastModified( QString const & name )
188 {
189  QgsDebugMsg( "name=" + name );
190  QDateTime t;
191 
192  QFileInfo fi( name );
193 
194  // Is it file?
195  if ( !fi.exists() )
196  return t;
197 
198  t = fi.lastModified();
199 
200  QgsDebugMsg( "last modified = " + t.toString() );
201 
202  return t;
203 }
204 
205 // typedef for the QgsDataProvider class factory
206 typedef QgsDataProvider * classFactoryFunction_t( const QString * );
207 
209 //
210 // Non Static Public methods
211 //
213 
215 {
216  if ( !mDataProvider ) return 0;
217  return mDataProvider->bandCount();
218 }
219 
220 const QString QgsRasterLayer::bandName( int theBandNo )
221 {
222  return dataProvider()->generateBandName( theBandNo );
223 }
224 
226 {
227  setRenderer( QgsRasterRendererRegistry::instance()->defaultRendererForDrawingStyle( theDrawingStyle, mDataProvider ) );
228 }
229 
234 {
235  return mDataProvider;
236 }
237 
242 {
243  return mDataProvider;
244 }
245 
247 {
248  if ( mDataProvider )
249  {
251  }
252 }
253 
254 bool QgsRasterLayer::draw( QgsRenderContext& rendererContext )
255 {
256  QgsDebugMsg( "entered. (renderContext)" );
257 
258  // Don't waste time drawing if transparency is at 0 (completely transparent)
259  if ( mTransparencyLevel == 0 )
260  return true;
261 
262  QgsDebugMsg( "checking timestamp." );
263 
264  // Check timestamp
265  if ( !update() )
266  {
267  return false;
268  }
269 
270  const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
271 
272  QgsRectangle myProjectedViewExtent;
273  QgsRectangle myProjectedLayerExtent;
274 
275  if ( rendererContext.coordinateTransform() )
276  {
277  QgsDebugMsg( "coordinateTransform set -> project extents." );
278  try
279  {
280  myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
281  }
282  catch ( QgsCsException &cs )
283  {
284  QgsMessageLog::logMessage( tr( "Could not reproject view extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
285  myProjectedViewExtent.setMinimal();
286  }
287 
288  try
289  {
290  myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( extent() );
291  }
292  catch ( QgsCsException &cs )
293  {
294  QgsMessageLog::logMessage( tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
295  myProjectedViewExtent.setMinimal();
296  }
297  }
298  else
299  {
300  QgsDebugMsg( "coordinateTransform not set" );
301  myProjectedViewExtent = rendererContext.extent();
302  myProjectedLayerExtent = extent();
303  }
304 
305  QPainter* theQPainter = rendererContext.painter();
306 
307  if ( !theQPainter )
308  {
309  return false;
310  }
311 
312  // clip raster extent to view extent
313  QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
314  if ( myRasterExtent.isEmpty() )
315  {
316  QgsDebugMsg( "draw request outside view extent." );
317  // nothing to do
318  return true;
319  }
320 
321  QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() );
322  QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() );
323  QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() );
324  QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );
325 
326  //
327  // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
328  // relating to the size (in pixels and coordinate system units) of the raster part that is
329  // in view in the map window. It also stores the origin.
330  //
331  //this is not a class level member because every time the user pans or zooms
332  //the contents of the rasterViewPort will change
333  QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
334 
335  myRasterViewPort->mDrawnExtent = myRasterExtent;
336  if ( rendererContext.coordinateTransform() )
337  {
338  myRasterViewPort->mSrcCRS = crs();
339  myRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
340  }
341  else
342  {
343  myRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
344  myRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
345  }
346 
347  // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
348  myRasterViewPort->mTopLeftPoint = theQgsMapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
349  myRasterViewPort->mBottomRightPoint = theQgsMapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
350 
351  // align to output device grid, i.e. floor/ceil to integers
352  // TODO: this should only be done if paint device is raster - screen, image
353  // for other devices (pdf) it can have floating point origin
354  // we could use floating point for raster devices as well, but respecting the
355  // output device grid should make it more effective as the resampling is done in
356  // the provider anyway
357  myRasterViewPort->mTopLeftPoint.setX( floor( myRasterViewPort->mTopLeftPoint.x() ) );
358  myRasterViewPort->mTopLeftPoint.setY( floor( myRasterViewPort->mTopLeftPoint.y() ) );
359  myRasterViewPort->mBottomRightPoint.setX( ceil( myRasterViewPort->mBottomRightPoint.x() ) );
360  myRasterViewPort->mBottomRightPoint.setY( ceil( myRasterViewPort->mBottomRightPoint.y() ) );
361  // recalc myRasterExtent to aligned values
362  myRasterExtent.set(
363  theQgsMapToPixel.toMapCoordinatesF( myRasterViewPort->mTopLeftPoint.x(),
364  myRasterViewPort->mBottomRightPoint.y() ),
365  theQgsMapToPixel.toMapCoordinatesF( myRasterViewPort->mBottomRightPoint.x(),
366  myRasterViewPort->mTopLeftPoint.y() )
367  );
368 
369  //raster viewport top left / bottom right are already rounded to int
370  myRasterViewPort->mWidth = static_cast<int>( myRasterViewPort->mBottomRightPoint.x() - myRasterViewPort->mTopLeftPoint.x() );
371  myRasterViewPort->mHeight = static_cast<int>( myRasterViewPort->mBottomRightPoint.y() - myRasterViewPort->mTopLeftPoint.y() );
372 
373 
374  //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
375  //theQgsMapToPixel.mapUnitsPerPixel() is less then 1,
376  //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
377 
378  QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( theQgsMapToPixel.mapUnitsPerPixel() ), 3 );
379  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( width() ), 3 );
380  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( height() ), 3 );
381  QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
382  QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
383  QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
384  QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
385 
386  QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( myRasterViewPort->mTopLeftPoint.x() ), 3 );
387  QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( myRasterViewPort->mBottomRightPoint.x() ), 3 );
388  QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( myRasterViewPort->mTopLeftPoint.y() ), 3 );
389  QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( myRasterViewPort->mBottomRightPoint.y() ), 3 );
390 
391  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( myRasterViewPort->mWidth ), 3 );
392  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( myRasterViewPort->mHeight ), 3 );
393 
394  // /\/\/\ - added to handle zoomed-in rasters
395 
396  mLastViewPort = *myRasterViewPort;
397 
398  // TODO: is it necessary? Probably WMS only?
399  mDataProvider->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );
400 
401  draw( theQPainter, myRasterViewPort, &theQgsMapToPixel );
402 
403  delete myRasterViewPort;
404  QgsDebugMsg( "exiting." );
405 
406  return true;
407 
408 }
409 
410 void QgsRasterLayer::draw( QPainter * theQPainter,
411  QgsRasterViewPort * theRasterViewPort,
412  const QgsMapToPixel* theQgsMapToPixel )
413 {
414  QgsDebugMsg( " 3 arguments" );
415  QTime time;
416  time.start();
417  //
418  //
419  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
420  // so that we can maximise performance of the rendering process. So now we check which drawing
421  // procedure to use :
422  //
423 
424  QgsRasterProjector *projector = mPipe.projector();
425 
426  // TODO add a method to interface to get provider and get provider
427  // params in QgsRasterProjector
428  if ( projector )
429  {
430  projector->setCRS( theRasterViewPort->mSrcCRS, theRasterViewPort->mDestCRS );
431  }
432 
433  // Drawer to pipe?
434  QgsRasterIterator iterator( mPipe.last() );
435  QgsRasterDrawer drawer( &iterator );
436  drawer.draw( theQPainter, theRasterViewPort, theQgsMapToPixel );
437 
438  QgsDebugMsg( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ) );
439 } //end of draw method
440 
442 {
443  return mError;
444 }
445 
447 {
448  return mErrorCaption;
449 }
450 
451 QList< QPair< QString, QColor > > QgsRasterLayer::legendSymbologyItems() const
452 {
453  QList< QPair< QString, QColor > > symbolList;
455  if ( renderer )
456  {
457  renderer->legendSymbologyItems( symbolList );
458  }
459  return symbolList;
460 }
461 
463 {
464  QString myMetadata ;
465  myMetadata += "<p class=\"glossy\">" + tr( "Driver" ) + "</p>\n";
466  myMetadata += "<p>";
467  myMetadata += mDataProvider->description();
468  myMetadata += "</p>\n";
469 
470  // Insert provider-specific (e.g. WMS-specific) metadata
471  // crashing
472  myMetadata += mDataProvider->metadata();
473 
474  myMetadata += "<p class=\"glossy\">";
475  myMetadata += tr( "No Data Value" );
476  myMetadata += "</p>\n";
477  myMetadata += "<p>";
478  // TODO: all bands
479  if ( mDataProvider->srcHasNoDataValue( 1 ) )
480  {
481  myMetadata += QString::number( mDataProvider->srcNoDataValue( 1 ) );
482  }
483  else
484  {
485  myMetadata += "*" + tr( "NoDataValue not set" ) + "*";
486  }
487  myMetadata += "</p>\n";
488 
489  myMetadata += "</p>\n";
490  myMetadata += "<p class=\"glossy\">";
491  myMetadata += tr( "Data Type" );
492  myMetadata += "</p>\n";
493  myMetadata += "<p>";
494  //just use the first band
495  switch ( mDataProvider->srcDataType( 1 ) )
496  {
497  case QGis::Byte:
498  myMetadata += tr( "Byte - Eight bit unsigned integer" );
499  break;
500  case QGis::UInt16:
501  myMetadata += tr( "UInt16 - Sixteen bit unsigned integer " );
502  break;
503  case QGis::Int16:
504  myMetadata += tr( "Int16 - Sixteen bit signed integer " );
505  break;
506  case QGis::UInt32:
507  myMetadata += tr( "UInt32 - Thirty two bit unsigned integer " );
508  break;
509  case QGis::Int32:
510  myMetadata += tr( "Int32 - Thirty two bit signed integer " );
511  break;
512  case QGis::Float32:
513  myMetadata += tr( "Float32 - Thirty two bit floating point " );
514  break;
515  case QGis::Float64:
516  myMetadata += tr( "Float64 - Sixty four bit floating point " );
517  break;
518  case QGis::CInt16:
519  myMetadata += tr( "CInt16 - Complex Int16 " );
520  break;
521  case QGis::CInt32:
522  myMetadata += tr( "CInt32 - Complex Int32 " );
523  break;
524  case QGis::CFloat32:
525  myMetadata += tr( "CFloat32 - Complex Float32 " );
526  break;
527  case QGis::CFloat64:
528  myMetadata += tr( "CFloat64 - Complex Float64 " );
529  break;
530  default:
531  myMetadata += tr( "Could not determine raster data type." );
532  }
533  myMetadata += "</p>\n";
534 
535  myMetadata += "<p class=\"glossy\">";
536  myMetadata += tr( "Pyramid overviews" );
537  myMetadata += "</p>\n";
538  myMetadata += "<p>";
539 
540  myMetadata += "<p class=\"glossy\">";
541  myMetadata += tr( "Layer Spatial Reference System" );
542  myMetadata += "</p>\n";
543  myMetadata += "<p>";
544  myMetadata += crs().toProj4();
545  myMetadata += "</p>\n";
546 
547  myMetadata += "<p class=\"glossy\">";
548  myMetadata += tr( "Layer Extent (layer original source projection)" );
549  myMetadata += "</p>\n";
550  myMetadata += "<p>";
551  myMetadata += mDataProvider->extent().toString();
552  myMetadata += "</p>\n";
553 
554  // output coordinate system
555  // TODO: this is not related to layer, to be removed? [MD]
556 #if 0
557  myMetadata += "<tr><td class=\"glossy\">";
558  myMetadata += tr( "Project Spatial Reference System" );
559  myMetadata += "</p>\n";
560  myMetadata += "<p>";
561  myMetadata += mCoordinateTransform->destCRS().toProj4();
562  myMetadata += "</p>\n";
563 #endif
564 
565  //
566  // Add the stats for each band to the output table
567  //
568  int myBandCountInt = bandCount();
569  for ( int myIteratorInt = 1; myIteratorInt <= myBandCountInt; ++myIteratorInt )
570  {
571  QgsDebugMsg( "Raster properties : checking if band " + QString::number( myIteratorInt ) + " has stats? " );
572  //band name
573  myMetadata += "<p class=\"glossy\">\n";
574  myMetadata += tr( "Band" );
575  myMetadata += "</p>\n";
576  myMetadata += "<p>";
577  myMetadata += bandName( myIteratorInt );
578  myMetadata += "</p>\n";
579  //band number
580  myMetadata += "<p>";
581  myMetadata += tr( "Band No" );
582  myMetadata += "</p>\n";
583  myMetadata += "<p>\n";
584  myMetadata += QString::number( myIteratorInt );
585  myMetadata += "</p>\n";
586 
587  //check if full stats for this layer have already been collected
588  if ( !dataProvider()->hasStatistics( myIteratorInt ) ) //not collected
589  {
590  QgsDebugMsg( ".....no" );
591 
592  myMetadata += "<p>";
593  myMetadata += tr( "No Stats" );
594  myMetadata += "</p>\n";
595  myMetadata += "<p>\n";
596  myMetadata += tr( "No stats collected yet" );
597  myMetadata += "</p>\n";
598  }
599  else // collected - show full detail
600  {
601  QgsDebugMsg( ".....yes" );
602 
603  QgsRasterBandStats myRasterBandStats = dataProvider()->bandStatistics( myIteratorInt );
604  //Min Val
605  myMetadata += "<p>";
606  myMetadata += tr( "Min Val" );
607  myMetadata += "</p>\n";
608  myMetadata += "<p>\n";
609  myMetadata += QString::number( myRasterBandStats.minimumValue, 'f', 10 );
610  myMetadata += "</p>\n";
611 
612  // Max Val
613  myMetadata += "<p>";
614  myMetadata += tr( "Max Val" );
615  myMetadata += "</p>\n";
616  myMetadata += "<p>\n";
617  myMetadata += QString::number( myRasterBandStats.maximumValue, 'f', 10 );
618  myMetadata += "</p>\n";
619 
620  // Range
621  myMetadata += "<p>";
622  myMetadata += tr( "Range" );
623  myMetadata += "</p>\n";
624  myMetadata += "<p>\n";
625  myMetadata += QString::number( myRasterBandStats.range, 'f', 10 );
626  myMetadata += "</p>\n";
627 
628  // Mean
629  myMetadata += "<p>";
630  myMetadata += tr( "Mean" );
631  myMetadata += "</p>\n";
632  myMetadata += "<p>\n";
633  myMetadata += QString::number( myRasterBandStats.mean, 'f', 10 );
634  myMetadata += "</p>\n";
635 
636  //sum of squares
637  myMetadata += "<p>";
638  myMetadata += tr( "Sum of squares" );
639  myMetadata += "</p>\n";
640  myMetadata += "<p>\n";
641  myMetadata += QString::number( myRasterBandStats.sumOfSquares, 'f', 10 );
642  myMetadata += "</p>\n";
643 
644  //standard deviation
645  myMetadata += "<p>";
646  myMetadata += tr( "Standard Deviation" );
647  myMetadata += "</p>\n";
648  myMetadata += "<p>\n";
649  myMetadata += QString::number( myRasterBandStats.stdDev, 'f', 10 );
650  myMetadata += "</p>\n";
651 
652  //sum of all cells
653  myMetadata += "<p>";
654  myMetadata += tr( "Sum of all cells" );
655  myMetadata += "</p>\n";
656  myMetadata += "<p>\n";
657  myMetadata += QString::number( myRasterBandStats.sum, 'f', 10 );
658  myMetadata += "</p>\n";
659 
660  //number of cells
661  myMetadata += "<p>";
662  myMetadata += tr( "Cell Count" );
663  myMetadata += "</p>\n";
664  myMetadata += "<p>\n";
665  myMetadata += QString::number( myRasterBandStats.elementCount );
666  myMetadata += "</p>\n";
667  }
668  }
669 
670  QgsDebugMsg( myMetadata );
671  return myMetadata;
672 }
673 
678 QPixmap QgsRasterLayer::paletteAsPixmap( int theBandNumber )
679 {
680  //TODO: This function should take dimensions
681  QgsDebugMsg( "entered." );
682 
683  // Only do this for the GDAL provider?
684  // Maybe WMS can do this differently using QImage::numColors and QImage::color()
685  if ( mDataProvider->colorInterpretation( theBandNumber ) == QgsRaster::PaletteIndex )
686  {
687  QgsDebugMsg( "....found paletted image" );
688  QgsColorRampShader myShader;
689  QList<QgsColorRampShader::ColorRampItem> myColorRampItemList = mDataProvider->colorTable( theBandNumber );
690  if ( myColorRampItemList.size() > 0 )
691  {
692  QgsDebugMsg( "....got color ramp item list" );
693  myShader.setColorRampItemList( myColorRampItemList );
695  // Draw image
696  int mySize = 100;
697  QPixmap myPalettePixmap( mySize, mySize );
698  QPainter myQPainter( &myPalettePixmap );
699 
700  QImage myQImage = QImage( mySize, mySize, QImage::Format_RGB32 );
701  myQImage.fill( 0 );
702  myPalettePixmap.fill();
703 
704  double myStep = (( double )myColorRampItemList.size() - 1 ) / ( double )( mySize * mySize );
705  double myValue = 0.0;
706  for ( int myRow = 0; myRow < mySize; myRow++ )
707  {
708  QRgb* myLineBuffer = ( QRgb* )myQImage.scanLine( myRow );
709  for ( int myCol = 0; myCol < mySize; myCol++ )
710  {
711  myValue = myStep * ( double )( myCol + myRow * mySize );
712  int c1, c2, c3, c4;
713  myShader.shade( myValue, &c1, &c2, &c3, &c4 );
714  myLineBuffer[ myCol ] = qRgba( c1, c2, c3, c4 );
715  }
716  }
717 
718  myQPainter.drawImage( 0, 0, myQImage );
719  return myPalettePixmap;
720  }
721  QPixmap myNullPixmap;
722  return myNullPixmap;
723  }
724  else
725  {
726  //invalid layer was requested
727  QPixmap myNullPixmap;
728  return myNullPixmap;
729  }
730 }
731 
733 {
734  return mProviderKey;
735 }
736 
741 {
742 // We return one raster pixel per map unit pixel
743 // One raster pixel can have several raster units...
744 
745 // We can only use one of the mGeoTransform[], so go with the
746 // horisontal one.
747 
749  {
750  return mDataProvider->extent().width() / mDataProvider->xSize();
751  }
752  return 1;
753 }
754 
756 {
758  {
759  return mDataProvider->extent().height() / mDataProvider->ySize();
760  }
761  return 1;
762 }
763 
765 {
767 
769 
770  //Initialize the last view port structure, should really be a class
771  mLastViewPort.mWidth = 0;
773 }
774 
775 void QgsRasterLayer::setDataProvider( QString const & provider )
776 {
777  QgsDebugMsg( "Entered" );
778  mValid = false; // assume the layer is invalid until we determine otherwise
779 
780  mPipe.remove( mDataProvider ); // deletes if exists
781  mDataProvider = 0;
782 
783  // XXX should I check for and possibly delete any pre-existing providers?
784  // XXX How often will that scenario occur?
785 
786  mProviderKey = provider;
787  // set the layer name (uppercase first character)
788  if ( ! mLayerName.isEmpty() ) // XXX shouldn't this happen in parent?
789  {
791  }
792 
793  //mBandCount = 0;
794 
796  if ( !mDataProvider )
797  {
798  //QgsMessageLog::logMessage( tr( "Cannot instantiate the data provider" ), tr( "Raster" ) );
799  appendError( ERR( tr( "Cannot instantiate the '%1' data provider" ).arg( mProviderKey ) ) );
800  return;
801  }
802  QgsDebugMsg( "Data provider created" );
803 
804  // Set data provider into pipe even if not valid so that it is deleted with pipe (with layer)
806  if ( !mDataProvider->isValid() )
807  {
809  appendError( ERR( tr( "Provider is not valid (provider: %1, URI: %2" ).arg( mProviderKey ).arg( mDataSource ) ) );
810  return;
811  }
812 
813  if ( provider == "gdal" )
814  {
815  // make sure that the /vsigzip or /vsizip is added to uri, if applicable
817  }
818 
819  // get the extent
821 
822  // show the extent
823  QString s = mbr.toString();
824  QgsDebugMsg( "Extent of layer: " + s );
825  // store the extent
826  setExtent( mbr );
827 
828  // upper case the first letter of the layer name
829  QgsDebugMsg( "mLayerName: " + name() );
830 
831  // set up the raster drawing style
832  // Do not set any 'sensible' style here, the style is set later
833 
834  // Setup source CRS
836 
837  QString mySourceWkt = crs().toWkt();
838 
839  QgsDebugMsg( "using wkt:\n" + mySourceWkt );
840 
841  //defaults - Needs to be set after the Contrast list has been build
842  //Try to read the default contrast enhancement from the config file
843 
844  QSettings myQSettings;
845 
846  //decide what type of layer this is...
847  //TODO Change this to look at the color interp and palette interp to decide which type of layer it is
848  QgsDebugMsg( "bandCount = " + QString::number( mDataProvider->bandCount() ) );
849  QgsDebugMsg( "dataType = " + QString::number( mDataProvider->dataType( 1 ) ) );
850  if (( mDataProvider->bandCount() > 1 ) )
851  {
853  }
854  else if ( mDataProvider->dataType( 1 ) == QGis::ARGB32
856  {
858  }
860  {
862  }
864  {
866  }
867  else
868  {
870  }
871 
872  QgsDebugMsg( "mRasterType = " + QString::number( mRasterType ) );
873  if ( mRasterType == ColorLayer )
874  {
875  QgsDebugMsg( "Setting drawing style to SingleBandColorDataStyle " + QString::number( QgsRaster::SingleBandColorDataStyle ) );
877  }
879  {
881  }
883  {
885  // Load color table
886  QList<QgsColorRampShader::ColorRampItem> colorTable = mDataProvider->colorTable( 1 );
888  if ( r )
889  {
890  // TODO: this should go somewhere else
891  QgsRasterShader* shader = new QgsRasterShader();
892  QgsColorRampShader* colorRampShader = new QgsColorRampShader();
894  colorRampShader->setColorRampItemList( colorTable );
895  shader->setRasterShaderFunction( colorRampShader );
896  r->setShader( shader );
897  }
898  }
899  else if ( mRasterType == Multiband )
900  {
902  }
903  else //GrayOrUndefined
904  {
906  }
907 
908  // Auto set alpha band
909  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
910  {
912  {
913  if ( mPipe.renderer() )
914  {
915  mPipe.renderer()->setAlphaBand( bandNo );
916  }
917  break;
918  }
919  }
920 
921  // brightness filter
923  mPipe.set( brightnessFilter );
924 
925  // hue/saturation filter
927  mPipe.set( hueSaturationFilter );
928 
929  //resampler (must be after renderer)
931  mPipe.set( resampleFilter );
932 
933  // projector (may be anywhere in pipe)
934  QgsRasterProjector * projector = new QgsRasterProjector;
935  mPipe.set( projector );
936 
937  // Set default identify format - use the richest format available
938  int capabilities = mDataProvider->capabilities();
940  if ( capabilities & QgsRasterInterface::IdentifyHtml )
941  {
942  // HTML is usually richest
943  identifyFormat = QgsRaster::IdentifyFormatHtml;
944  }
945  else if ( capabilities & QgsRasterInterface::IdentifyFeature )
946  {
947  identifyFormat = QgsRaster::IdentifyFormatFeature;
948  }
949  else if ( capabilities & QgsRasterInterface::IdentifyText )
950  {
951  identifyFormat = QgsRaster::IdentifyFormatText;
952  }
953  else if ( capabilities & QgsRasterInterface::IdentifyValue )
954  {
955  identifyFormat = QgsRaster::IdentifyFormatValue;
956  }
957  setCustomProperty( "identify/format", QgsRasterDataProvider::identifyFormatName( identifyFormat ) );
958 
959  // Store timestamp
960  // TODO move to provider
962 
963  // Connect provider signals
964  connect(
965  mDataProvider, SIGNAL( progress( int, double, QString ) ),
966  this, SLOT( onProgress( int, double, QString ) )
967  );
968 
969  // Do a passthrough for the status bar text
970  connect(
971  mDataProvider, SIGNAL( statusChanged( QString ) ),
972  this, SIGNAL( statusChanged( QString ) )
973  );
974 
975  //mark the layer as valid
976  mValid = true;
977 
978  QgsDebugMsg( "exiting." );
979 } // QgsRasterLayer::setDataProvider
980 
982 {
983  mValid = false;
985  mDataProvider = 0;
986 }
987 
988 void QgsRasterLayer::setContrastEnhancement( QgsContrastEnhancement::ContrastEnhancementAlgorithm theAlgorithm, QgsRaster::ContrastEnhancementLimits theLimits, QgsRectangle theExtent, int theSampleSize, bool theGenerateLookupTableFlag )
989 {
990  QgsDebugMsg( QString( "theAlgorithm = %1 theLimits = %2 theExtent.isEmpty() = %3" ).arg( theAlgorithm ).arg( theLimits ).arg( theExtent.isEmpty() ) );
991  if ( !mPipe.renderer() || !mDataProvider )
992  {
993  return;
994  }
995 
996  QList<int> myBands;
997  QList<QgsContrastEnhancement*> myEnhancements;
998  QgsSingleBandGrayRenderer* myGrayRenderer = 0;
999  QgsMultiBandColorRenderer* myMultiBandRenderer = 0;
1000  QString rendererType = mPipe.renderer()->type();
1001  if ( rendererType == "singlebandgray" )
1002  {
1003  myGrayRenderer = dynamic_cast<QgsSingleBandGrayRenderer*>( mPipe.renderer() );
1004  if ( !myGrayRenderer ) return;
1005  myBands << myGrayRenderer->grayBand();
1006  }
1007  else if ( rendererType == "multibandcolor" )
1008  {
1009  myMultiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer*>( mPipe.renderer() );
1010  if ( !myMultiBandRenderer ) return;
1011  myBands << myMultiBandRenderer->redBand() << myMultiBandRenderer->greenBand() << myMultiBandRenderer->blueBand();
1012  }
1013 
1014  foreach ( int myBand, myBands )
1015  {
1016  if ( myBand != -1 )
1017  {
1018  QGis::DataType myType = ( QGis::DataType )mDataProvider->dataType( myBand );
1019  QgsContrastEnhancement* myEnhancement = new QgsContrastEnhancement(( QGis::DataType )myType );
1020  myEnhancement->setContrastEnhancementAlgorithm( theAlgorithm, theGenerateLookupTableFlag );
1021 
1022  double myMin = std::numeric_limits<double>::quiet_NaN();
1023  double myMax = std::numeric_limits<double>::quiet_NaN();
1024 
1025  if ( theLimits == QgsRaster::ContrastEnhancementMinMax )
1026  {
1027  QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( myBand, QgsRasterBandStats::Min | QgsRasterBandStats::Max, theExtent, theSampleSize );
1028  myMin = myRasterBandStats.minimumValue;
1029  myMax = myRasterBandStats.maximumValue;
1030  }
1031  else if ( theLimits == QgsRaster::ContrastEnhancementStdDev )
1032  {
1033  double myStdDev = 1; // make optional?
1034  QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( myBand, QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, theExtent, theSampleSize );
1035  myMin = myRasterBandStats.mean - ( myStdDev * myRasterBandStats.stdDev );
1036  myMax = myRasterBandStats.mean + ( myStdDev * myRasterBandStats.stdDev );
1037  }
1038  else if ( theLimits == QgsRaster::ContrastEnhancementCumulativeCut )
1039  {
1040  QSettings mySettings;
1041  double myLower = mySettings.value( "/Raster/cumulativeCutLower", QString::number( CUMULATIVE_CUT_LOWER ) ).toDouble();
1042  double myUpper = mySettings.value( "/Raster/cumulativeCutUpper", QString::number( CUMULATIVE_CUT_UPPER ) ).toDouble();
1043  QgsDebugMsg( QString( "myLower = %1 myUpper = %2" ).arg( myLower ).arg( myUpper ) );
1044  mDataProvider->cumulativeCut( myBand, myLower, myUpper, myMin, myMax, theExtent, theSampleSize );
1045  }
1046 
1047  QgsDebugMsg( QString( "myBand = %1 myMin = %2 myMax = %3" ).arg( myBand ).arg( myMin ).arg( myMax ) );
1048  myEnhancement->setMinimumValue( myMin );
1049  myEnhancement->setMaximumValue( myMax );
1050  myEnhancements.append( myEnhancement );
1051  }
1052  else
1053  {
1054  myEnhancements.append( 0 );
1055  }
1056  }
1057 
1058  if ( rendererType == "singlebandgray" )
1059  {
1060  if ( myEnhancements.value( 0 ) ) myGrayRenderer->setContrastEnhancement( myEnhancements.value( 0 ) );
1061  }
1062  else if ( rendererType == "multibandcolor" )
1063  {
1064  if ( myEnhancements.value( 0 ) ) myMultiBandRenderer->setRedContrastEnhancement( myEnhancements.value( 0 ) );
1065  if ( myEnhancements.value( 1 ) ) myMultiBandRenderer->setGreenContrastEnhancement( myEnhancements.value( 1 ) );
1066  if ( myEnhancements.value( 2 ) ) myMultiBandRenderer->setBlueContrastEnhancement( myEnhancements.value( 2 ) );
1067  }
1068 }
1069 
1071 {
1072  QgsDebugMsg( "Entered" );
1073 
1074  QSettings mySettings;
1075 
1076  QString myKey;
1077  QString myDefault;
1078 
1079  // TODO: we should not test renderer class here, move it somehow to renderers
1080  if ( dynamic_cast<QgsSingleBandGrayRenderer*>( renderer() ) )
1081  {
1082  myKey = "singleBand";
1083  myDefault = "StretchToMinimumMaximum";
1084  }
1085  else if ( dynamic_cast<QgsMultiBandColorRenderer*>( renderer() ) )
1086  {
1087  if ( QgsRasterBlock::typeSize( dataProvider()->srcDataType( 1 ) ) == 1 )
1088  {
1089  myKey = "multiBandSingleByte";
1090  myDefault = "NoEnhancement";
1091  }
1092  else
1093  {
1094  myKey = "multiBandMultiByte";
1095  myDefault = "StretchToMinimumMaximum";
1096  }
1097  }
1098 
1099  if ( myKey.isEmpty() )
1100  {
1101  QgsDebugMsg( "No default contrast enhancement for this drawing style" );
1102  }
1103  QgsDebugMsg( "myKey = " + myKey );
1104 
1105  QString myAlgorithmString = mySettings.value( "/Raster/defaultContrastEnhancementAlgorithm/" + myKey, myDefault ).toString();
1106  QgsDebugMsg( "myAlgorithmString = " + myAlgorithmString );
1107 
1109 
1110  if ( myAlgorithm == QgsContrastEnhancement::NoEnhancement )
1111  {
1112  return;
1113  }
1114 
1115  QString myLimitsString = mySettings.value( "/Raster/defaultContrastEnhancementLimits", "CumulativeCut" ).toString();
1117 
1118  setContrastEnhancement( myAlgorithm, myLimits );
1119 }
1120 
1126 void QgsRasterLayer::setDrawingStyle( QString const & theDrawingStyleQString )
1127 {
1128  QgsDebugMsg( "DrawingStyle = " + theDrawingStyleQString );
1129  QgsRaster::DrawingStyle drawingStyle;
1130  if ( theDrawingStyleQString == "SingleBandGray" )//no need to tr() this its not shown in ui
1131  {
1132  drawingStyle = QgsRaster::SingleBandGray;
1133  }
1134  else if ( theDrawingStyleQString == "SingleBandPseudoColor" )//no need to tr() this its not shown in ui
1135  {
1136  drawingStyle = QgsRaster::SingleBandPseudoColor;
1137  }
1138  else if ( theDrawingStyleQString == "PalettedColor" )//no need to tr() this its not shown in ui
1139  {
1140  drawingStyle = QgsRaster::PalettedColor;
1141  }
1142  else if ( theDrawingStyleQString == "PalettedSingleBandGray" )//no need to tr() this its not shown in ui
1143  {
1144  drawingStyle = QgsRaster::PalettedSingleBandGray;
1145  }
1146  else if ( theDrawingStyleQString == "PalettedSingleBandPseudoColor" )//no need to tr() this its not shown in ui
1147  {
1149  }
1150  else if ( theDrawingStyleQString == "PalettedMultiBandColor" )//no need to tr() this its not shown in ui
1151  {
1152  drawingStyle = QgsRaster::PalettedMultiBandColor;
1153  }
1154  else if ( theDrawingStyleQString == "MultiBandSingleBandGray" )//no need to tr() this its not shown in ui
1155  {
1156  drawingStyle = QgsRaster::MultiBandSingleBandGray;
1157  }
1158  else if ( theDrawingStyleQString == "MultiBandSingleBandPseudoColor" )//no need to tr() this its not shown in ui
1159  {
1161  }
1162  else if ( theDrawingStyleQString == "MultiBandColor" )//no need to tr() this its not shown in ui
1163  {
1164  drawingStyle = QgsRaster::MultiBandColor;
1165  }
1166  else if ( theDrawingStyleQString == "SingleBandColorDataStyle" )//no need to tr() this its not shown in ui
1167  {
1168  QgsDebugMsg( "Setting drawingStyle to SingleBandColorDataStyle " + QString::number( QgsRaster::SingleBandColorDataStyle ) );
1169  drawingStyle = QgsRaster::SingleBandColorDataStyle;
1170  QgsDebugMsg( "Setted drawingStyle to " + QString::number( drawingStyle ) );
1171  }
1172  else
1173  {
1174  drawingStyle = QgsRaster::UndefinedDrawingStyle;
1175  }
1176  setRendererForDrawingStyle( drawingStyle );
1177 }
1178 
1179 void QgsRasterLayer::setLayerOrder( QStringList const & layers )
1180 {
1181  QgsDebugMsg( "entered." );
1182 
1183  if ( mDataProvider )
1184  {
1185  QgsDebugMsg( "About to mDataProvider->setLayerOrder(layers)." );
1186  mDataProvider->setLayerOrder( layers );
1187  }
1188 
1189 }
1190 
1191 void QgsRasterLayer::setSubLayerVisibility( QString name, bool vis )
1192 {
1193 
1194  if ( mDataProvider )
1195  {
1196  QgsDebugMsg( "About to mDataProvider->setSubLayerVisibility(name, vis)." );
1197  mDataProvider->setSubLayerVisibility( name, vis );
1198  }
1199 
1200 }
1201 
1203 {
1204  QgsDebugMsg( "Entered" );
1205  if ( !theRenderer ) { return; }
1206  mPipe.set( theRenderer );
1207 }
1208 
1209 void QgsRasterLayer::showProgress( int theValue )
1210 {
1211  emit progressUpdate( theValue );
1212 }
1213 
1214 
1215 void QgsRasterLayer::showStatusMessage( QString const & theMessage )
1216 {
1217  // QgsDebugMsg(QString("entered with '%1'.").arg(theMessage));
1218 
1219  // Pass-through
1220  // TODO: See if we can connect signal-to-signal. This is a kludge according to the Qt doc.
1221  emit statusChanged( theMessage );
1222 }
1223 
1224 QStringList QgsRasterLayer::subLayers() const
1225 {
1226  return mDataProvider->subLayers();
1227 }
1228 
1229 QPixmap QgsRasterLayer::previewAsPixmap( QSize size, QColor bgColor )
1230 {
1231  QPixmap myQPixmap( size );
1232 
1233  myQPixmap.fill( bgColor ); //defaults to white, set to transparent for rendering on a map
1234 
1235  QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
1236 
1237  double myMapUnitsPerPixel;
1238  double myX = 0.0;
1239  double myY = 0.0;
1240  QgsRectangle myExtent = mDataProvider->extent();
1241  if ( myExtent.width() / myExtent.height() >= myQPixmap.width() / myQPixmap.height() )
1242  {
1243  myMapUnitsPerPixel = myExtent.width() / myQPixmap.width();
1244  myY = ( myQPixmap.height() - myExtent.height() / myMapUnitsPerPixel ) / 2;
1245  }
1246  else
1247  {
1248  myMapUnitsPerPixel = myExtent.height() / myQPixmap.height();
1249  myX = ( myQPixmap.width() - myExtent.width() / myMapUnitsPerPixel ) / 2;
1250  }
1251 
1252  double myPixelWidth = myExtent.width() / myMapUnitsPerPixel;
1253  double myPixelHeight = myExtent.height() / myMapUnitsPerPixel;
1254 
1255  myRasterViewPort->mTopLeftPoint = QgsPoint( myX, myY );
1256  myRasterViewPort->mBottomRightPoint = QgsPoint( myPixelWidth, myPixelHeight );
1257  myRasterViewPort->mWidth = myQPixmap.width();
1258  myRasterViewPort->mHeight = myQPixmap.height();
1259 
1260  myRasterViewPort->mDrawnExtent = myExtent;
1261  myRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
1262  myRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
1263 
1264  QgsMapToPixel *myMapToPixel = new QgsMapToPixel( myMapUnitsPerPixel );
1265 
1266  QPainter * myQPainter = new QPainter( &myQPixmap );
1267  draw( myQPainter, myRasterViewPort, myMapToPixel );
1268  delete myRasterViewPort;
1269  delete myMapToPixel;
1270  myQPainter->end();
1271  delete myQPainter;
1272 
1273  return myQPixmap;
1274 }
1275 
1277 {
1278  emit repaintRequested();
1279 }
1280 
1281 void QgsRasterLayer::updateProgress( int theProgress, int theMax )
1282 {
1283  //simply propogate it on!
1284  emit drawingProgress( theProgress, theMax );
1285 }
1286 
1287 void QgsRasterLayer::onProgress( int theType, double theProgress, QString theMessage )
1288 {
1289  Q_UNUSED( theType );
1290  Q_UNUSED( theMessage );
1291  QgsDebugMsg( QString( "theProgress = %1" ).arg( theProgress ) );
1292  emit progressUpdate(( int )theProgress );
1293 }
1294 
1296 //
1297 // Protected methods
1298 //
1300 /*
1301  * @param QDomNode node that will contain the symbology definition for this layer.
1302  * @param errorMessage reference to string that will be updated with any error messages
1303  * @return true in case of success.
1304  */
1305 bool QgsRasterLayer::readSymbology( const QDomNode& layer_node, QString& errorMessage )
1306 {
1307  Q_UNUSED( errorMessage );
1308  QDomElement rasterRendererElem;
1309 
1310  // pipe element was introduced in the end of 1.9 development when there were
1311  // already many project files in use so we support 1.9 backward compatibility
1312  // even it was never officialy released -> use pipe element if present, otherwise
1313  // use layer node
1314  QDomNode pipeNode = layer_node.firstChildElement( "pipe" );
1315  if ( pipeNode.isNull() ) // old project
1316  {
1317  pipeNode = layer_node;
1318  }
1319 
1320  //rasterlayerproperties element there -> old format (1.8 and early 1.9)
1321  if ( !layer_node.firstChildElement( "rasterproperties" ).isNull() )
1322  {
1323  //copy node because layer_node is const
1324  QDomNode layerNodeCopy = layer_node.cloneNode();
1325  QDomDocument doc = layerNodeCopy.ownerDocument();
1326  QDomElement rasterPropertiesElem = layerNodeCopy.firstChildElement( "rasterproperties" );
1327  QgsProjectFileTransform::convertRasterProperties( doc, layerNodeCopy, rasterPropertiesElem,
1328  this );
1329  rasterRendererElem = layerNodeCopy.firstChildElement( "rasterrenderer" );
1330  QgsDebugMsg( doc.toString() );
1331  }
1332  else
1333  {
1334  rasterRendererElem = pipeNode.firstChildElement( "rasterrenderer" );
1335  }
1336 
1337  if ( !rasterRendererElem.isNull() )
1338  {
1339  QString rendererType = rasterRendererElem.attribute( "type" );
1340  QgsRasterRendererRegistryEntry rendererEntry;
1341  if ( QgsRasterRendererRegistry::instance()->rendererData( rendererType, rendererEntry ) )
1342  {
1343  QgsRasterRenderer *renderer = rendererEntry.rendererCreateFunction( rasterRendererElem, dataProvider() );
1344  mPipe.set( renderer );
1345  }
1346  }
1347 
1348  //brightness
1350  mPipe.set( brightnessFilter );
1351 
1352  //brightness coefficient
1353  QDomElement brightnessElem = pipeNode.firstChildElement( "brightnesscontrast" );
1354  if ( !brightnessElem.isNull() )
1355  {
1356  brightnessFilter->readXML( brightnessElem );
1357  }
1358 
1359  //hue/saturation
1361  mPipe.set( hueSaturationFilter );
1362 
1363  //saturation coefficient
1364  QDomElement hueSaturationElem = pipeNode.firstChildElement( "huesaturation" );
1365  if ( !hueSaturationElem.isNull() )
1366  {
1367  hueSaturationFilter->readXML( hueSaturationElem );
1368  }
1369 
1370  //resampler
1372  mPipe.set( resampleFilter );
1373 
1374  //max oversampling
1375  QDomElement resampleElem = pipeNode.firstChildElement( "rasterresampler" );
1376  if ( !resampleElem.isNull() )
1377  {
1378  resampleFilter->readXML( resampleElem );
1379  }
1380 
1381  // get and set the blend mode if it exists
1382  QDomNode blendModeNode = layer_node.namedItem( "blendMode" );
1383  if ( !blendModeNode.isNull() )
1384  {
1385  QDomElement e = blendModeNode.toElement();
1387  }
1388 
1389  return true;
1390 } //readSymbology
1391 
1398 bool QgsRasterLayer::readXml( const QDomNode& layer_node )
1399 {
1400  QgsDebugMsg( "Entered" );
1402 
1403  //process provider key
1404  QDomNode pkeyNode = layer_node.namedItem( "provider" );
1405 
1406  if ( pkeyNode.isNull() )
1407  {
1408  mProviderKey = "gdal";
1409  }
1410  else
1411  {
1412  QDomElement pkeyElt = pkeyNode.toElement();
1413  mProviderKey = pkeyElt.text();
1414  if ( mProviderKey.isEmpty() )
1415  {
1416  mProviderKey = "gdal";
1417  }
1418  }
1419 
1420  // Open the raster source based on provider and datasource
1421 
1422  // Go down the raster-data-provider paradigm
1423 
1424  // Collect provider-specific information
1425 
1426  QDomNode rpNode = layer_node.namedItem( "rasterproperties" );
1427 
1428  if ( mProviderKey == "wms" )
1429  {
1430  // >>> BACKWARD COMPATIBILITY < 1.9
1431  // The old WMS URI format does not contain all the informations, we add them here.
1432  if ( !mDataSource.contains( "crs=" ) && !mDataSource.contains( "format=" ) )
1433  {
1434  QgsDebugMsg( "Old WMS URI format detected -> adding params" );
1435  QgsDataSourceURI uri;
1436  uri.setEncodedUri( mDataSource );
1437  QDomElement layerElement = rpNode.firstChildElement( "wmsSublayer" );
1438  while ( !layerElement.isNull() )
1439  {
1440  // TODO: sublayer visibility - post-0.8 release timeframe
1441 
1442  // collect name for the sublayer
1443  uri.setParam( "layers", layerElement.namedItem( "name" ).toElement().text() );
1444 
1445  // collect style for the sublayer
1446  uri.setParam( "styles", layerElement.namedItem( "style" ).toElement().text() );
1447 
1448  layerElement = layerElement.nextSiblingElement( "wmsSublayer" );
1449  }
1450 
1451  // Collect format
1452  QDomNode formatNode = rpNode.namedItem( "wmsFormat" );
1453  uri.setParam( "format", rpNode.namedItem( "wmsFormat" ).toElement().text() );
1454 
1455  // WMS CRS URL param should not be mixed with that assigned to the layer.
1456  // In the old WMS URI version there was no CRS and layer crs().authid() was used.
1457  uri.setParam( "crs", crs().authid() );
1458  mDataSource = uri.encodedUri();
1459  }
1460  // <<< BACKWARD COMPATIBILITY < 1.9
1461  }
1462 
1464  if ( !mValid ) return false;
1465 
1466  QString theError;
1467  bool res = readSymbology( layer_node, theError );
1468 
1469  // old wms settings we need to correct
1470  if ( res && mProviderKey == "wms" && ( !renderer() || renderer()->type() != "singlebandcolordata" ) )
1471  {
1473  }
1474 
1475  // Check timestamp
1476  // This was probably introduced to reload completely raster if data changed and
1477  // reset completly symbology to reflect new data type etc. It creates however
1478  // problems, because user defined symbology is complete lost if data file time
1479  // changed (the content may be the same). See also 6900.
1480 #if 0
1481  QDomNode stampNode = layer_node.namedItem( "timestamp" );
1482  if ( !stampNode.isNull() )
1483  {
1484  QDateTime stamp = QDateTime::fromString( stampNode.toElement().text(), Qt::ISODate );
1485  // TODO: very bad, we have to load twice!!! Make QgsDataProvider::timestamp() static?
1486  if ( stamp < mDataProvider->dataTimestamp() )
1487  {
1488  QgsDebugMsg( "data changed, reload provider" );
1490  init();
1492  if ( !mValid ) return false;
1493  }
1494  }
1495 #endif
1496 
1497  // Load user no data value
1498  QDomElement noDataElement = layer_node.firstChildElement( "noData" );
1499 
1500  QDomNodeList noDataBandList = noDataElement.elementsByTagName( "noDataList" );
1501 
1502  for ( int i = 0; i < noDataBandList.size(); ++i )
1503  {
1504  QDomElement bandElement = noDataBandList.at( i ).toElement();
1505  bool ok;
1506  int bandNo = bandElement.attribute( "bandNo" ).toInt( &ok );
1507  QgsDebugMsg( QString( "bandNo = %1" ).arg( bandNo ) );
1508  if ( ok && ( bandNo > 0 ) && ( bandNo <= mDataProvider->bandCount() ) )
1509  {
1510  mDataProvider->setUseSrcNoDataValue( bandNo, bandElement.attribute( "useSrcNoData" ).toInt() );
1511  QgsRasterRangeList myNoDataRangeList;
1512 
1513  QDomNodeList rangeList = bandElement.elementsByTagName( "noDataRange" );
1514 
1515  for ( int j = 0; j < rangeList.size(); ++j )
1516  {
1517  QDomElement rangeElement = rangeList.at( j ).toElement();
1518  QgsRasterRange myNoDataRange( rangeElement.attribute( "min" ).toDouble(),
1519  rangeElement.attribute( "max" ).toDouble() );
1520  QgsDebugMsg( QString( "min = %1 %2" ).arg( rangeElement.attribute( "min" ) ).arg( myNoDataRange.min() ) );
1521  myNoDataRangeList << myNoDataRange;
1522  }
1523  mDataProvider->setUserNoDataValue( bandNo, myNoDataRangeList );
1524  }
1525  }
1526 
1527  return res;
1528 } // QgsRasterLayer::readXml( QDomNode & layer_node )
1529 
1530 /*
1531  * @param QDomNode the node that will have the style element added to it.
1532  * @param QDomDocument the document that will have the QDomNode added.
1533  * @param errorMessage reference to string that will be updated with any error messages
1534  * @return true in case of success.
1535  */
1536 bool QgsRasterLayer::writeSymbology( QDomNode & layer_node, QDomDocument & document, QString& errorMessage ) const
1537 {
1538  Q_UNUSED( errorMessage );
1539  QDomElement layerElem = layer_node.toElement();
1540 
1541  // Store pipe members (except provider) into pipe element, in future, it will be
1542  // possible to add custom filters into the pipe
1543  QDomElement pipeElement = document.createElement( "pipe" );
1544 
1545  for ( int i = 1; i < mPipe.size(); i++ )
1546  {
1547  QgsRasterInterface * interface = mPipe.at( i );
1548  if ( !interface ) continue;
1549  interface->writeXML( document, pipeElement );
1550  }
1551 
1552  layer_node.appendChild( pipeElement );
1553 
1554  // add blend mode node
1555  QDomElement blendModeElement = document.createElement( "blendMode" );
1556  QDomText blendModeText = document.createTextNode( QString::number( QgsMapRenderer::getBlendModeEnum( blendMode() ) ) );
1557  blendModeElement.appendChild( blendModeText );
1558  layer_node.appendChild( blendModeElement );
1559 
1560  return true;
1561 } // bool QgsRasterLayer::writeSymbology
1562 
1563 /*
1564  * virtual
1565  * @note Called by QgsMapLayer::writeXML().
1566  */
1567 bool QgsRasterLayer::writeXml( QDomNode & layer_node,
1568  QDomDocument & document )
1569 {
1570  // first get the layer element so that we can append the type attribute
1571 
1572  QDomElement mapLayerNode = layer_node.toElement();
1573 
1574  if ( mapLayerNode.isNull() || "maplayer" != mapLayerNode.nodeName() )
1575  {
1576  QgsMessageLog::logMessage( tr( "<maplayer> not found." ), tr( "Raster" ) );
1577  return false;
1578  }
1579 
1580  mapLayerNode.setAttribute( "type", "raster" );
1581 
1582  // add provider node
1583 
1584  QDomElement provider = document.createElement( "provider" );
1585  QDomText providerText = document.createTextNode( mProviderKey );
1586  provider.appendChild( providerText );
1587  layer_node.appendChild( provider );
1588 
1589  // User no data
1590  QDomElement noData = document.createElement( "noData" );
1591 
1592  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
1593  {
1594  if ( mDataProvider->userNoDataValues( bandNo ).isEmpty() ) continue;
1595 
1596  QDomElement noDataRangeList = document.createElement( "noDataList" );
1597  noDataRangeList.setAttribute( "bandNo", bandNo );
1598  noDataRangeList.setAttribute( "useSrcNoData", mDataProvider->useSrcNoDataValue( bandNo ) );
1599 
1600  foreach ( QgsRasterRange range, mDataProvider->userNoDataValues( bandNo ) )
1601  {
1602  QDomElement noDataRange = document.createElement( "noDataRange" );
1603 
1604  noDataRange.setAttribute( "min", range.min() );
1605  noDataRange.setAttribute( "max", range.max() );
1606  noDataRangeList.appendChild( noDataRange );
1607  }
1608 
1609  noData.appendChild( noDataRangeList );
1610 
1611  }
1612  if ( noData.hasChildNodes() )
1613  {
1614  layer_node.appendChild( noData );
1615  }
1616 
1617  //write out the symbology
1618  QString errorMsg;
1619  return writeSymbology( layer_node, document, errorMsg );
1620 }
1621 
1623 {
1624  if ( !mDataProvider ) return 0;
1625  return mDataProvider->xSize();
1626 }
1627 
1629 {
1630  if ( !mDataProvider ) return 0;
1631  return mDataProvider->ySize();
1632 }
1633 
1635 //
1636 // Private methods
1637 //
1640 {
1641  QgsDebugMsg( "entered." );
1642  // Check if data changed
1644  {
1645  QgsDebugMsg( "reload data" );
1647  init();
1649  emit dataChanged();
1650  }
1651  return mValid;
1652 }