QGIS API Documentation  2.2.0-Valmiera
 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  QgsDebugMsg( "checking timestamp." );
259 
260  // Check timestamp
261  if ( !update() )
262  {
263  return false;
264  }
265 
266  const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
267 
268  QgsRectangle myProjectedViewExtent;
269  QgsRectangle myProjectedLayerExtent;
270 
271  if ( rendererContext.coordinateTransform() )
272  {
273  QgsDebugMsg( "coordinateTransform set -> project extents." );
274  try
275  {
276  myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
277  }
278  catch ( QgsCsException &cs )
279  {
280  QgsMessageLog::logMessage( tr( "Could not reproject view extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
281  myProjectedViewExtent.setMinimal();
282  }
283 
284  try
285  {
286  myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( extent() );
287  }
288  catch ( QgsCsException &cs )
289  {
290  QgsMessageLog::logMessage( tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
291  myProjectedViewExtent.setMinimal();
292  }
293  }
294  else
295  {
296  QgsDebugMsg( "coordinateTransform not set" );
297  myProjectedViewExtent = rendererContext.extent();
298  myProjectedLayerExtent = extent();
299  }
300 
301  QPainter* theQPainter = rendererContext.painter();
302 
303  if ( !theQPainter )
304  {
305  return false;
306  }
307 
308  // clip raster extent to view extent
309  QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
310  if ( myRasterExtent.isEmpty() )
311  {
312  QgsDebugMsg( "draw request outside view extent." );
313  // nothing to do
314  return true;
315  }
316 
317  QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() );
318  QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() );
319  QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() );
320  QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );
321 
322  //
323  // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
324  // relating to the size (in pixels and coordinate system units) of the raster part that is
325  // in view in the map window. It also stores the origin.
326  //
327  //this is not a class level member because every time the user pans or zooms
328  //the contents of the rasterViewPort will change
329  QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
330 
331  myRasterViewPort->mDrawnExtent = myRasterExtent;
332  if ( rendererContext.coordinateTransform() )
333  {
334  myRasterViewPort->mSrcCRS = crs();
335  myRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
336  myRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform()->sourceDatumTransform();
337  myRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform()->destinationDatumTransform();
338  }
339  else
340  {
341  myRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
342  myRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
343  myRasterViewPort->mSrcDatumTransform = -1;
344  myRasterViewPort->mDestDatumTransform = -1;
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, theRasterViewPort->mSrcDatumTransform, theRasterViewPort->mDestDatumTransform );
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  emit rendererChanged();
1208 }
1209 
1210 void QgsRasterLayer::showProgress( int theValue )
1211 {
1212  emit progressUpdate( theValue );
1213 }
1214 
1215 
1216 void QgsRasterLayer::showStatusMessage( QString const & theMessage )
1217 {
1218  // QgsDebugMsg(QString("entered with '%1'.").arg(theMessage));
1219 
1220  // Pass-through
1221  // TODO: See if we can connect signal-to-signal. This is a kludge according to the Qt doc.
1222  emit statusChanged( theMessage );
1223 }
1224 
1225 QStringList QgsRasterLayer::subLayers() const
1226 {
1227  return mDataProvider->subLayers();
1228 }
1229 
1230 QPixmap QgsRasterLayer::previewAsPixmap( QSize size, QColor bgColor )
1231 {
1232  QPixmap myQPixmap( size );
1233 
1234  myQPixmap.fill( bgColor ); //defaults to white, set to transparent for rendering on a map
1235 
1236  QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
1237 
1238  double myMapUnitsPerPixel;
1239  double myX = 0.0;
1240  double myY = 0.0;
1241  QgsRectangle myExtent = mDataProvider->extent();
1242  if ( myExtent.width() / myExtent.height() >= myQPixmap.width() / myQPixmap.height() )
1243  {
1244  myMapUnitsPerPixel = myExtent.width() / myQPixmap.width();
1245  myY = ( myQPixmap.height() - myExtent.height() / myMapUnitsPerPixel ) / 2;
1246  }
1247  else
1248  {
1249  myMapUnitsPerPixel = myExtent.height() / myQPixmap.height();
1250  myX = ( myQPixmap.width() - myExtent.width() / myMapUnitsPerPixel ) / 2;
1251  }
1252 
1253  double myPixelWidth = myExtent.width() / myMapUnitsPerPixel;
1254  double myPixelHeight = myExtent.height() / myMapUnitsPerPixel;
1255 
1256  myRasterViewPort->mTopLeftPoint = QgsPoint( myX, myY );
1257  myRasterViewPort->mBottomRightPoint = QgsPoint( myPixelWidth, myPixelHeight );
1258  myRasterViewPort->mWidth = myQPixmap.width();
1259  myRasterViewPort->mHeight = myQPixmap.height();
1260 
1261  myRasterViewPort->mDrawnExtent = myExtent;
1262  myRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
1263  myRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
1264  myRasterViewPort->mSrcDatumTransform = -1;
1265  myRasterViewPort->mDestDatumTransform = -1;
1266 
1267  QgsMapToPixel *myMapToPixel = new QgsMapToPixel( myMapUnitsPerPixel );
1268 
1269  QPainter * myQPainter = new QPainter( &myQPixmap );
1270  draw( myQPainter, myRasterViewPort, myMapToPixel );
1271  delete myRasterViewPort;
1272  delete myMapToPixel;
1273  myQPainter->end();
1274  delete myQPainter;
1275 
1276  return myQPixmap;
1277 }
1278 
1280 {
1281  emit repaintRequested();
1282 }
1283 
1284 void QgsRasterLayer::updateProgress( int theProgress, int theMax )
1285 {
1286  //simply propogate it on!
1287  emit drawingProgress( theProgress, theMax );
1288 }
1289 
1290 void QgsRasterLayer::onProgress( int theType, double theProgress, QString theMessage )
1291 {
1292  Q_UNUSED( theType );
1293  Q_UNUSED( theMessage );
1294  QgsDebugMsg( QString( "theProgress = %1" ).arg( theProgress ) );
1295  emit progressUpdate(( int )theProgress );
1296 }
1297 
1299 //
1300 // Protected methods
1301 //
1303 /*
1304  * @param QDomNode node that will contain the symbology definition for this layer.
1305  * @param errorMessage reference to string that will be updated with any error messages
1306  * @return true in case of success.
1307  */
1308 bool QgsRasterLayer::readSymbology( const QDomNode& layer_node, QString& errorMessage )
1309 {
1310  Q_UNUSED( errorMessage );
1311  QDomElement rasterRendererElem;
1312 
1313  // pipe element was introduced in the end of 1.9 development when there were
1314  // already many project files in use so we support 1.9 backward compatibility
1315  // even it was never officialy released -> use pipe element if present, otherwise
1316  // use layer node
1317  QDomNode pipeNode = layer_node.firstChildElement( "pipe" );
1318  if ( pipeNode.isNull() ) // old project
1319  {
1320  pipeNode = layer_node;
1321  }
1322 
1323  //rasterlayerproperties element there -> old format (1.8 and early 1.9)
1324  if ( !layer_node.firstChildElement( "rasterproperties" ).isNull() )
1325  {
1326  //copy node because layer_node is const
1327  QDomNode layerNodeCopy = layer_node.cloneNode();
1328  QDomDocument doc = layerNodeCopy.ownerDocument();
1329  QDomElement rasterPropertiesElem = layerNodeCopy.firstChildElement( "rasterproperties" );
1330  QgsProjectFileTransform::convertRasterProperties( doc, layerNodeCopy, rasterPropertiesElem,
1331  this );
1332  rasterRendererElem = layerNodeCopy.firstChildElement( "rasterrenderer" );
1333  QgsDebugMsg( doc.toString() );
1334  }
1335  else
1336  {
1337  rasterRendererElem = pipeNode.firstChildElement( "rasterrenderer" );
1338  }
1339 
1340  if ( !rasterRendererElem.isNull() )
1341  {
1342  QString rendererType = rasterRendererElem.attribute( "type" );
1343  QgsRasterRendererRegistryEntry rendererEntry;
1344  if ( QgsRasterRendererRegistry::instance()->rendererData( rendererType, rendererEntry ) )
1345  {
1346  QgsRasterRenderer *renderer = rendererEntry.rendererCreateFunction( rasterRendererElem, dataProvider() );
1347  mPipe.set( renderer );
1348  }
1349  }
1350 
1351  //brightness
1353  mPipe.set( brightnessFilter );
1354 
1355  //brightness coefficient
1356  QDomElement brightnessElem = pipeNode.firstChildElement( "brightnesscontrast" );
1357  if ( !brightnessElem.isNull() )
1358  {
1359  brightnessFilter->readXML( brightnessElem );
1360  }
1361 
1362  //hue/saturation
1364  mPipe.set( hueSaturationFilter );
1365 
1366  //saturation coefficient
1367  QDomElement hueSaturationElem = pipeNode.firstChildElement( "huesaturation" );
1368  if ( !hueSaturationElem.isNull() )
1369  {
1370  hueSaturationFilter->readXML( hueSaturationElem );
1371  }
1372 
1373  //resampler
1375  mPipe.set( resampleFilter );
1376 
1377  //max oversampling
1378  QDomElement resampleElem = pipeNode.firstChildElement( "rasterresampler" );
1379  if ( !resampleElem.isNull() )
1380  {
1381  resampleFilter->readXML( resampleElem );
1382  }
1383 
1384  // get and set the blend mode if it exists
1385  QDomNode blendModeNode = layer_node.namedItem( "blendMode" );
1386  if ( !blendModeNode.isNull() )
1387  {
1388  QDomElement e = blendModeNode.toElement();
1390  }
1391 
1392  return true;
1393 } //readSymbology
1394 
1401 bool QgsRasterLayer::readXml( const QDomNode& layer_node )
1402 {
1403  QgsDebugMsg( "Entered" );
1405 
1406  //process provider key
1407  QDomNode pkeyNode = layer_node.namedItem( "provider" );
1408 
1409  if ( pkeyNode.isNull() )
1410  {
1411  mProviderKey = "gdal";
1412  }
1413  else
1414  {
1415  QDomElement pkeyElt = pkeyNode.toElement();
1416  mProviderKey = pkeyElt.text();
1417  if ( mProviderKey.isEmpty() )
1418  {
1419  mProviderKey = "gdal";
1420  }
1421  }
1422 
1423  // Open the raster source based on provider and datasource
1424 
1425  // Go down the raster-data-provider paradigm
1426 
1427  // Collect provider-specific information
1428 
1429  QDomNode rpNode = layer_node.namedItem( "rasterproperties" );
1430 
1431  if ( mProviderKey == "wms" )
1432  {
1433  // >>> BACKWARD COMPATIBILITY < 1.9
1434  // The old WMS URI format does not contain all the informations, we add them here.
1435  if ( !mDataSource.contains( "crs=" ) && !mDataSource.contains( "format=" ) )
1436  {
1437  QgsDebugMsg( "Old WMS URI format detected -> adding params" );
1438  QgsDataSourceURI uri;
1439  uri.setEncodedUri( mDataSource );
1440  QDomElement layerElement = rpNode.firstChildElement( "wmsSublayer" );
1441  while ( !layerElement.isNull() )
1442  {
1443  // TODO: sublayer visibility - post-0.8 release timeframe
1444 
1445  // collect name for the sublayer
1446  uri.setParam( "layers", layerElement.namedItem( "name" ).toElement().text() );
1447 
1448  // collect style for the sublayer
1449  uri.setParam( "styles", layerElement.namedItem( "style" ).toElement().text() );
1450 
1451  layerElement = layerElement.nextSiblingElement( "wmsSublayer" );
1452  }
1453 
1454  // Collect format
1455  QDomNode formatNode = rpNode.namedItem( "wmsFormat" );
1456  uri.setParam( "format", rpNode.namedItem( "wmsFormat" ).toElement().text() );
1457 
1458  // WMS CRS URL param should not be mixed with that assigned to the layer.
1459  // In the old WMS URI version there was no CRS and layer crs().authid() was used.
1460  uri.setParam( "crs", crs().authid() );
1461  mDataSource = uri.encodedUri();
1462  }
1463  // <<< BACKWARD COMPATIBILITY < 1.9
1464  }
1465 
1467  if ( !mValid ) return false;
1468 
1469  QString theError;
1470  bool res = readSymbology( layer_node, theError );
1471 
1472  // old wms settings we need to correct
1473  if ( res && mProviderKey == "wms" && ( !renderer() || renderer()->type() != "singlebandcolordata" ) )
1474  {
1476  }
1477 
1478  // Check timestamp
1479  // This was probably introduced to reload completely raster if data changed and
1480  // reset completly symbology to reflect new data type etc. It creates however
1481  // problems, because user defined symbology is complete lost if data file time
1482  // changed (the content may be the same). See also 6900.
1483 #if 0
1484  QDomNode stampNode = layer_node.namedItem( "timestamp" );
1485  if ( !stampNode.isNull() )
1486  {
1487  QDateTime stamp = QDateTime::fromString( stampNode.toElement().text(), Qt::ISODate );
1488  // TODO: very bad, we have to load twice!!! Make QgsDataProvider::timestamp() static?
1489  if ( stamp < mDataProvider->dataTimestamp() )
1490  {
1491  QgsDebugMsg( "data changed, reload provider" );
1493  init();
1495  if ( !mValid ) return false;
1496  }
1497  }
1498 #endif
1499 
1500  // Load user no data value
1501  QDomElement noDataElement = layer_node.firstChildElement( "noData" );
1502 
1503  QDomNodeList noDataBandList = noDataElement.elementsByTagName( "noDataList" );
1504 
1505  for ( int i = 0; i < noDataBandList.size(); ++i )
1506  {
1507  QDomElement bandElement = noDataBandList.at( i ).toElement();
1508  bool ok;
1509  int bandNo = bandElement.attribute( "bandNo" ).toInt( &ok );
1510  QgsDebugMsg( QString( "bandNo = %1" ).arg( bandNo ) );
1511  if ( ok && ( bandNo > 0 ) && ( bandNo <= mDataProvider->bandCount() ) )
1512  {
1513  mDataProvider->setUseSrcNoDataValue( bandNo, bandElement.attribute( "useSrcNoData" ).toInt() );
1514  QgsRasterRangeList myNoDataRangeList;
1515 
1516  QDomNodeList rangeList = bandElement.elementsByTagName( "noDataRange" );
1517 
1518  for ( int j = 0; j < rangeList.size(); ++j )
1519  {
1520  QDomElement rangeElement = rangeList.at( j ).toElement();
1521  QgsRasterRange myNoDataRange( rangeElement.attribute( "min" ).toDouble(),
1522  rangeElement.attribute( "max" ).toDouble() );
1523  QgsDebugMsg( QString( "min = %1 %2" ).arg( rangeElement.attribute( "min" ) ).arg( myNoDataRange.min() ) );
1524  myNoDataRangeList << myNoDataRange;
1525  }
1526  mDataProvider->setUserNoDataValue( bandNo, myNoDataRangeList );
1527  }
1528  }
1529 
1530  return res;
1531 } // QgsRasterLayer::readXml( QDomNode & layer_node )
1532 
1533 /*
1534  * @param QDomNode the node that will have the style element added to it.
1535  * @param QDomDocument the document that will have the QDomNode added.
1536  * @param errorMessage reference to string that will be updated with any error messages
1537  * @return true in case of success.
1538  */
1539 bool QgsRasterLayer::writeSymbology( QDomNode & layer_node, QDomDocument & document, QString& errorMessage ) const
1540 {
1541  Q_UNUSED( errorMessage );
1542  QDomElement layerElem = layer_node.toElement();
1543 
1544  // Store pipe members (except provider) into pipe element, in future, it will be
1545  // possible to add custom filters into the pipe
1546  QDomElement pipeElement = document.createElement( "pipe" );
1547 
1548  for ( int i = 1; i < mPipe.size(); i++ )
1549  {
1550  QgsRasterInterface * interface = mPipe.at( i );
1551  if ( !interface ) continue;
1552  interface->writeXML( document, pipeElement );
1553  }
1554 
1555  layer_node.appendChild( pipeElement );
1556 
1557  // add blend mode node
1558  QDomElement blendModeElement = document.createElement( "blendMode" );
1559  QDomText blendModeText = document.createTextNode( QString::number( QgsMapRenderer::getBlendModeEnum( blendMode() ) ) );
1560  blendModeElement.appendChild( blendModeText );
1561  layer_node.appendChild( blendModeElement );
1562 
1563  return true;
1564 } // bool QgsRasterLayer::writeSymbology
1565 
1566 /*
1567  * virtual
1568  * @note Called by QgsMapLayer::writeXML().
1569  */
1570 bool QgsRasterLayer::writeXml( QDomNode & layer_node,
1571  QDomDocument & document )
1572 {
1573  // first get the layer element so that we can append the type attribute
1574 
1575  QDomElement mapLayerNode = layer_node.toElement();
1576 
1577  if ( mapLayerNode.isNull() || "maplayer" != mapLayerNode.nodeName() )
1578  {
1579  QgsMessageLog::logMessage( tr( "<maplayer> not found." ), tr( "Raster" ) );
1580  return false;
1581  }
1582 
1583  mapLayerNode.setAttribute( "type", "raster" );
1584 
1585  // add provider node
1586 
1587  QDomElement provider = document.createElement( "provider" );
1588  QDomText providerText = document.createTextNode( mProviderKey );
1589  provider.appendChild( providerText );
1590  layer_node.appendChild( provider );
1591 
1592  // User no data
1593  QDomElement noData = document.createElement( "noData" );
1594 
1595  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
1596  {
1597  if ( mDataProvider->userNoDataValues( bandNo ).isEmpty() ) continue;
1598 
1599  QDomElement noDataRangeList = document.createElement( "noDataList" );
1600  noDataRangeList.setAttribute( "bandNo", bandNo );
1601  noDataRangeList.setAttribute( "useSrcNoData", mDataProvider->useSrcNoDataValue( bandNo ) );
1602 
1603  foreach ( QgsRasterRange range, mDataProvider->userNoDataValues( bandNo ) )
1604  {
1605  QDomElement noDataRange = document.createElement( "noDataRange" );
1606 
1607  noDataRange.setAttribute( "min", range.min() );
1608  noDataRange.setAttribute( "max", range.max() );
1609  noDataRangeList.appendChild( noDataRange );
1610  }
1611 
1612  noData.appendChild( noDataRangeList );
1613 
1614  }
1615  if ( noData.hasChildNodes() )
1616  {
1617  layer_node.appendChild( noData );
1618  }
1619 
1620  //write out the symbology
1621  QString errorMsg;
1622  return writeSymbology( layer_node, document, errorMsg );
1623 }
1624 
1626 {
1627  if ( !mDataProvider ) return 0;
1628  return mDataProvider->xSize();
1629 }
1630 
1632 {
1633  if ( !mDataProvider ) return 0;
1634  return mDataProvider->ySize();
1635 }
1636 
1638 //
1639 // Private methods
1640 //
1643 {
1644  QgsDebugMsg( "entered." );
1645  // Check if data changed
1647  {
1648  QgsDebugMsg( "reload data" );
1650  init();
1652  emit dataChanged();
1653  }
1654  return mValid;
1655 }