QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsrasterdataprovider.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterdataprovider.cpp - DataProvider Interface for raster layers
3  --------------------------------------
4  Date : Mar 11, 2005
5  Copyright : (C) 2005 by Brendan Morley
6  email : morb at ozemail dot com dot au
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 
18 #include "qgsproviderregistry.h"
19 #include "qgsrasterdataprovider.h"
21 #include "qgsrasterprojector.h"
22 #include "qgslogger.h"
23 
24 #include <QTime>
25 #include <QMap>
26 #include <QByteArray>
27 #include <QVariant>
28 
29 #include <qmath.h>
30 
31 #define ERRMSG(message) QGS_ERROR_MESSAGE(message, "Raster provider")
32 #define ERR(message) QgsError(message, "Raster provider")
33 
34 void QgsRasterDataProvider::setUseSrcNoDataValue( int bandNo, bool use )
35 {
36  if ( mUseSrcNoDataValue.size() < bandNo )
37  {
38  for ( int i = mUseSrcNoDataValue.size(); i < bandNo; i++ )
39  {
40  mUseSrcNoDataValue.append( false );
41  }
42  }
43  mUseSrcNoDataValue[bandNo-1] = use;
44 }
45 
46 QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle const & theExtent, int theWidth, int theHeight )
47 {
48  QgsDebugMsg( QString( "theBandNo = %1 theWidth = %2 theHeight = %3" ).arg( theBandNo ).arg( theWidth ).arg( theHeight ) );
49  QgsDebugMsg( QString( "theExtent = %1" ).arg( theExtent.toString() ) );
50 
52  if ( srcHasNoDataValue( theBandNo ) && useSrcNoDataValue( theBandNo ) )
53  {
54  block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight, srcNoDataValue( theBandNo ) );
55  }
56  else
57  {
58  block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight );
59  }
60 
61  if ( block->isEmpty() )
62  {
63  QgsDebugMsg( "Couldn't create raster block" );
64  return block;
65  }
66 
67  // Read necessary extent only
68  QgsRectangle tmpExtent = extent().intersect( &theExtent );
69 
70  if ( tmpExtent.isEmpty() )
71  {
72  QgsDebugMsg( "Extent outside provider extent" );
73  block->setIsNoData();
74  return block;
75  }
76 
77  double xRes = theExtent.width() / theWidth;
78  double yRes = theExtent.height() / theHeight;
79  double tmpXRes, tmpYRes;
80  double providerXRes = 0;
81  double providerYRes = 0;
82  if ( capabilities() & Size )
83  {
84  providerXRes = extent().width() / xSize();
85  providerYRes = extent().height() / ySize();
86  tmpXRes = qMax( providerXRes, xRes );
87  tmpYRes = qMax( providerYRes, yRes );
88  if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes;
89  if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes;
90  }
91  else
92  {
93  tmpXRes = xRes;
94  tmpYRes = yRes;
95  }
96 
97  if ( tmpExtent != theExtent ||
98  tmpXRes > xRes || tmpYRes > yRes )
99  {
100  // Read smaller extent or lower resolution
101 
102  if ( !extent().contains( theExtent ) )
103  {
104  QRect subRect = QgsRasterBlock::subRect( theExtent, theWidth, theHeight, extent() );
105  block->setIsNoDataExcept( subRect );
106  }
107 
108  // Calculate row/col limits (before tmpExtent is aligned)
109  int fromRow = qRound(( theExtent.yMaximum() - tmpExtent.yMaximum() ) / yRes );
110  int toRow = qRound(( theExtent.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1;
111  int fromCol = qRound(( tmpExtent.xMinimum() - theExtent.xMinimum() ) / xRes ) ;
112  int toCol = qRound(( tmpExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1;
113 
114  QgsDebugMsg( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ) );
115 
116  if ( fromRow < 0 || fromRow >= theHeight || toRow < 0 || toRow >= theHeight ||
117  fromCol < 0 || fromCol >= theWidth || toCol < 0 || toCol >= theWidth )
118  {
119  // Should not happen
120  QgsDebugMsg( "Row or column limits out of range" );
121  return block;
122  }
123 
124  // If lower source resolution is used, the extent must beS aligned to original
125  // resolution to avoid possible shift due to resampling
126  if ( tmpXRes > xRes )
127  {
128  int col = floor(( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes );
129  tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes );
130  col = ceil(( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes );
131  tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes );
132  }
133  if ( tmpYRes > yRes )
134  {
135  int row = floor(( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes );
136  tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes );
137  row = ceil(( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes );
138  tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes );
139  }
140  int tmpWidth = qRound( tmpExtent.width() / tmpXRes );
141  int tmpHeight = qRound( tmpExtent.height() / tmpYRes );
142  tmpXRes = tmpExtent.width() / tmpWidth;
143  tmpYRes = tmpExtent.height() / tmpHeight;
144 
145  QgsDebugMsg( QString( "Reading smaller block tmpWidth = %1 theHeight = %2" ).arg( tmpWidth ).arg( tmpHeight ) );
146  QgsDebugMsg( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ) );
147 
148  QgsRasterBlock *tmpBlock;
149  if ( srcHasNoDataValue( theBandNo ) && useSrcNoDataValue( theBandNo ) )
150  {
151  tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight, srcNoDataValue( theBandNo ) );
152  }
153  else
154  {
155  tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight );
156  }
157 
158  readBlock( theBandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits() );
159 
160  int pixelSize = dataTypeSize( theBandNo );
161 
162  double xMin = theExtent.xMinimum();
163  double yMax = theExtent.yMaximum();
164  double tmpXMin = tmpExtent.xMinimum();
165  double tmpYMax = tmpExtent.yMaximum();
166 
167  for ( int row = fromRow; row <= toRow; row++ )
168  {
169  double y = yMax - ( row + 0.5 ) * yRes;
170  int tmpRow = floor(( tmpYMax - y ) / tmpYRes );
171 
172  for ( int col = fromCol; col <= toCol; col++ )
173  {
174  double x = xMin + ( col + 0.5 ) * xRes;
175  int tmpCol = floor(( x - tmpXMin ) / tmpXRes );
176 
177  if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth )
178  {
179  QgsDebugMsg( "Source row or column limits out of range" );
180  block->setIsNoData(); // so that the problem becomes obvious and fixed
181  delete tmpBlock;
182  return block;
183  }
184 
185  qgssize tmpIndex = tmpRow * tmpWidth + tmpCol;
186  qgssize index = row * theWidth + col;
187 
188  char *tmpBits = tmpBlock->bits( tmpIndex );
189  char *bits = block->bits( index );
190  if ( !tmpBits )
191  {
192  QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) );
193  continue;
194  }
195  if ( !bits )
196  {
197  QgsDebugMsg( "Cannot set output block data." );
198  continue;
199  }
200  memcpy( bits, tmpBits, pixelSize );
201  }
202  }
203 
204  delete tmpBlock;
205  }
206  else
207  {
208  readBlock( theBandNo, theExtent, theWidth, theHeight, block->bits() );
209  }
210 
211  // apply user no data values
212  block->applyNoDataValues( userNoDataValues( theBandNo ) );
213  return block;
214 }
215 
217  : QgsRasterInterface( 0 )
218  , mDpi( -1 )
219 {
220 }
221 
223  : QgsDataProvider( uri )
224  , QgsRasterInterface( 0 )
225  , mDpi( -1 )
226 {
227 }
228 
229 //
230 //Random Static convenience function
231 //
233 // convenience function for building metadata() HTML table cells
234 // convenience function for creating a string list from a C style string list
235 QStringList QgsRasterDataProvider::cStringList2Q_( char ** stringList )
236 {
237  QStringList strings;
238 
239  // presume null terminated string list
240  for ( qgssize i = 0; stringList[i]; ++i )
241  {
242  strings.append( stringList[i] );
243  }
244 
245  return strings;
246 
247 } // cStringList2Q_
248 
249 QString QgsRasterDataProvider::makeTableCell( QString const & value )
250 {
251  return "<p>\n" + value + "</p>\n";
252 } // makeTableCell_
253 
254 // convenience function for building metadata() HTML table cells
255 QString QgsRasterDataProvider::makeTableCells( QStringList const & values )
256 {
257  QString s( "<tr>" );
258 
259  for ( QStringList::const_iterator i = values.begin();
260  i != values.end();
261  ++i )
262  {
264  }
265 
266  s += "</tr>";
267 
268  return s;
269 } // makeTableCell_
270 
272 {
273  QString s;
274  return s;
275 }
276 
277 // Default implementation for values
278 QgsRasterIdentifyResult QgsRasterDataProvider::identify( const QgsPoint & thePoint, QgsRaster::IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight )
279 {
280  QgsDebugMsg( "Entered" );
281  QMap<int, QVariant> results;
282 
283  if ( theFormat != QgsRaster::IdentifyFormatValue || !( capabilities() & IdentifyValue ) )
284  {
285  QgsDebugMsg( "Format not supported" );
286  return QgsRasterIdentifyResult( ERR( tr( "Format not supported" ) ) );
287  }
288 
289  if ( !extent().contains( thePoint ) )
290  {
291  // Outside the raster
292  for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ )
293  {
294  results.insert( bandNo, QVariant() );
295  }
297  }
298 
299  QgsRectangle myExtent = theExtent;
300  if ( myExtent.isEmpty() ) myExtent = extent();
301 
302  if ( theWidth == 0 )
303  {
304  theWidth = capabilities() & Size ? xSize() : 1000;
305  }
306  if ( theHeight == 0 )
307  {
308  theHeight = capabilities() & Size ? ySize() : 1000;
309  }
310 
311  // Calculate the row / column where the point falls
312  double xres = ( myExtent.width() ) / theWidth;
313  double yres = ( myExtent.height() ) / theHeight;
314 
315  int col = ( int ) floor(( thePoint.x() - myExtent.xMinimum() ) / xres );
316  int row = ( int ) floor(( myExtent.yMaximum() - thePoint.y() ) / yres );
317 
318  double xMin = myExtent.xMinimum() + col * xres;
319  double xMax = xMin + xres;
320  double yMax = myExtent.yMaximum() - row * yres;
321  double yMin = yMax - yres;
322  QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );
323 
324  for ( int i = 1; i <= bandCount(); i++ )
325  {
326  QgsRasterBlock * myBlock = block( i, pixelExtent, 1, 1 );
327 
328  if ( myBlock )
329  {
330  double value = myBlock->value( 0 );
331 
332  results.insert( i, value );
333  delete myBlock;
334  }
335  else
336  {
337  results.insert( i, QVariant() );
338  }
339  }
341 }
342 
344 {
345  return "text/plain";
346 }
347 
348 typedef QList<QPair<QString, QString> > *pyramidResamplingMethods_t();
349 QList<QPair<QString, QString> > QgsRasterDataProvider::pyramidResamplingMethods( QString providerKey )
350 {
351  pyramidResamplingMethods_t *pPyramidResamplingMethods = ( pyramidResamplingMethods_t * ) cast_to_fptr( QgsProviderRegistry::instance()->function( providerKey, "pyramidResamplingMethods" ) );
352  if ( pPyramidResamplingMethods )
353  {
354  QList<QPair<QString, QString> > *methods = pPyramidResamplingMethods();
355  if ( !methods )
356  {
357  QgsDebugMsg( "provider pyramidResamplingMethods returned no methods" );
358  }
359  else
360  {
361  return *methods;
362  }
363  }
364  else
365  {
366  QgsDebugMsg( "Could not resolve pyramidResamplingMethods provider library" );
367  }
368  return QList<QPair<QString, QString> >();
369 }
370 
372 {
373  QList<QgsRasterPyramid> myPyramidList = buildPyramidList();
374 
375  if ( myPyramidList.isEmpty() )
376  return false;
377 
378  QList<QgsRasterPyramid>::iterator myRasterPyramidIterator;
379  for ( myRasterPyramidIterator = myPyramidList.begin();
380  myRasterPyramidIterator != myPyramidList.end();
381  ++myRasterPyramidIterator )
382  {
383  if ( myRasterPyramidIterator->exists )
384  {
385  return true;
386  }
387  }
388  return false;
389 }
390 
392 {
393  if ( bandNo >= mUserNoDataValue.size() )
394  {
395  for ( int i = mUserNoDataValue.size(); i < bandNo; i++ )
396  {
398  }
399  }
400  QgsDebugMsg( QString( "set %1 band %1 no data ranges" ).arg( noData.size() ) );
401 
402  if ( mUserNoDataValue[bandNo-1] != noData )
403  {
404  // Clear statistics
405  int i = 0;
406  while ( i < mStatistics.size() )
407  {
408  if ( mStatistics.value( i ).bandNumber == bandNo )
409  {
410  mStatistics.removeAt( i );
411  mHistograms.removeAt( i );
412  }
413  else
414  {
415  i++;
416  }
417  }
418  mUserNoDataValue[bandNo-1] = noData;
419  }
420 }
421 
422 typedef QgsRasterDataProvider * createFunction_t( const QString&,
423  const QString&, int,
425  int, int, double*,
427  QStringList );
428 
430  const QString &uri,
431  const QString& format, int nBands,
432  QGis::DataType type,
433  int width, int height, double* geoTransform,
434  const QgsCoordinateReferenceSystem& crs,
435  QStringList createOptions )
436 {
437  createFunction_t *createFn = ( createFunction_t* ) cast_to_fptr( QgsProviderRegistry::instance()->function( providerKey, "create" ) );
438  if ( !createFn )
439  {
440  QgsDebugMsg( "Cannot resolve 'create' function in " + providerKey + " provider" );
441  // TODO: it would be good to return invalid QgsRasterDataProvider
442  // with QgsError set, but QgsRasterDataProvider has pure virtual methods
443  return 0;
444  }
445  return createFn( uri, format, nBands, type, width, height, geoTransform, crs, createOptions );
446 }
447 
449 {
450  switch ( format )
451  {
453  return "Value";
455  return "Text";
457  return "Html";
459  return "Feature";
460  default:
461  return "Undefined";
462  }
463 }
464 
466 {
467  switch ( format )
468  {
470  return tr( "Value" );
472  return tr( "Text" );
474  return tr( "Html" );
476  return tr( "Feature" );
477  default:
478  return "Undefined";
479  }
480 }
481 
483 {
484  if ( formatName == "Value" ) return QgsRaster::IdentifyFormatValue;
485  if ( formatName == "Text" ) return QgsRaster::IdentifyFormatText;
486  if ( formatName == "Html" ) return QgsRaster::IdentifyFormatHtml;
487  if ( formatName == "Feature" ) return QgsRaster::IdentifyFormatFeature;
489 }
490 
492 {
493  switch ( format )
494  {
496  return IdentifyValue;
498  return IdentifyText;
500  return IdentifyHtml;
502  return IdentifyFeature;
503  default:
504  return NoCapabilities;
505  }
506 }
507 
508 bool QgsRasterDataProvider::userNoDataValuesContains( int bandNo, double value ) const
509 {
510  QgsRasterRangeList rangeList = mUserNoDataValue.value( bandNo - 1 );
511  return QgsRasterRange::contains( value, rangeList );
512 }
513 
514 // ENDS