QGIS API Documentation  2.9.0-Master
 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 = ( qgssize )tmpRow * ( qgssize )tmpWidth + tmpCol;
186  qgssize index = row * ( qgssize )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 scale and offset
212  block->applyScaleOffset( bandScale( theBandNo ), bandOffset( theBandNo ) );
213  // apply user no data values
214  block->applyNoDataValues( userNoDataValues( theBandNo ) );
215  return block;
216 }
217 
219  : QgsRasterInterface( 0 )
220  , mDpi( -1 )
221 {
222 }
223 
225  : QgsDataProvider( uri )
226  , QgsRasterInterface( 0 )
227  , mDpi( -1 )
228 {
229 }
230 
231 //
232 //Random Static convenience function
233 //
235 // convenience function for building metadata() HTML table cells
236 // convenience function for creating a string list from a C style string list
237 QStringList QgsRasterDataProvider::cStringList2Q_( char ** stringList )
238 {
239  QStringList strings;
240 
241  // presume null terminated string list
242  for ( qgssize i = 0; stringList[i]; ++i )
243  {
244  strings.append( stringList[i] );
245  }
246 
247  return strings;
248 
249 } // cStringList2Q_
250 
251 QString QgsRasterDataProvider::makeTableCell( QString const & value )
252 {
253  return "<p>\n" + value + "</p>\n";
254 } // makeTableCell_
255 
256 // convenience function for building metadata() HTML table cells
257 QString QgsRasterDataProvider::makeTableCells( QStringList const & values )
258 {
259  QString s( "<tr>" );
260 
261  for ( QStringList::const_iterator i = values.begin();
262  i != values.end();
263  ++i )
264  {
266  }
267 
268  s += "</tr>";
269 
270  return s;
271 } // makeTableCell_
272 
274 {
275  QString s;
276  return s;
277 }
278 
279 // Default implementation for values
280 QgsRasterIdentifyResult QgsRasterDataProvider::identify( const QgsPoint & thePoint, QgsRaster::IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight )
281 {
282  QgsDebugMsg( "Entered" );
283  QMap<int, QVariant> results;
284 
285  if ( theFormat != QgsRaster::IdentifyFormatValue || !( capabilities() & IdentifyValue ) )
286  {
287  QgsDebugMsg( "Format not supported" );
288  return QgsRasterIdentifyResult( ERR( tr( "Format not supported" ) ) );
289  }
290 
291  if ( !extent().contains( thePoint ) )
292  {
293  // Outside the raster
294  for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ )
295  {
296  results.insert( bandNo, QVariant() );
297  }
299  }
300 
301  QgsRectangle myExtent = theExtent;
302  if ( myExtent.isEmpty() ) myExtent = extent();
303 
304  if ( theWidth == 0 )
305  {
306  theWidth = capabilities() & Size ? xSize() : 1000;
307  }
308  if ( theHeight == 0 )
309  {
310  theHeight = capabilities() & Size ? ySize() : 1000;
311  }
312 
313  // Calculate the row / column where the point falls
314  double xres = ( myExtent.width() ) / theWidth;
315  double yres = ( myExtent.height() ) / theHeight;
316 
317  int col = ( int ) floor(( thePoint.x() - myExtent.xMinimum() ) / xres );
318  int row = ( int ) floor(( myExtent.yMaximum() - thePoint.y() ) / yres );
319 
320  double xMin = myExtent.xMinimum() + col * xres;
321  double xMax = xMin + xres;
322  double yMax = myExtent.yMaximum() - row * yres;
323  double yMin = yMax - yres;
324  QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );
325 
326  for ( int i = 1; i <= bandCount(); i++ )
327  {
328  QgsRasterBlock * myBlock = block( i, pixelExtent, 1, 1 );
329 
330  if ( myBlock )
331  {
332  double value = myBlock->value( 0 );
333 
334  results.insert( i, value );
335  delete myBlock;
336  }
337  else
338  {
339  results.insert( i, QVariant() );
340  }
341  }
343 }
344 
346 {
347  return "text/plain";
348 }
349 
350 typedef QList<QPair<QString, QString> > *pyramidResamplingMethods_t();
351 QList<QPair<QString, QString> > QgsRasterDataProvider::pyramidResamplingMethods( QString providerKey )
352 {
353  pyramidResamplingMethods_t *pPyramidResamplingMethods = ( pyramidResamplingMethods_t * ) cast_to_fptr( QgsProviderRegistry::instance()->function( providerKey, "pyramidResamplingMethods" ) );
354  if ( pPyramidResamplingMethods )
355  {
356  QList<QPair<QString, QString> > *methods = pPyramidResamplingMethods();
357  if ( !methods )
358  {
359  QgsDebugMsg( "provider pyramidResamplingMethods returned no methods" );
360  }
361  else
362  {
363  return *methods;
364  }
365  }
366  else
367  {
368  QgsDebugMsg( "Could not resolve pyramidResamplingMethods provider library" );
369  }
370  return QList<QPair<QString, QString> >();
371 }
372 
374 {
375  QList<QgsRasterPyramid> myPyramidList = buildPyramidList();
376 
377  if ( myPyramidList.isEmpty() )
378  return false;
379 
380  QList<QgsRasterPyramid>::iterator myRasterPyramidIterator;
381  for ( myRasterPyramidIterator = myPyramidList.begin();
382  myRasterPyramidIterator != myPyramidList.end();
383  ++myRasterPyramidIterator )
384  {
385  if ( myRasterPyramidIterator->exists )
386  {
387  return true;
388  }
389  }
390  return false;
391 }
392 
394 {
395  if ( bandNo >= mUserNoDataValue.size() )
396  {
397  for ( int i = mUserNoDataValue.size(); i < bandNo; i++ )
398  {
400  }
401  }
402  QgsDebugMsg( QString( "set %1 band %1 no data ranges" ).arg( noData.size() ) );
403 
404  if ( mUserNoDataValue[bandNo-1] != noData )
405  {
406  // Clear statistics
407  int i = 0;
408  while ( i < mStatistics.size() )
409  {
410  if ( mStatistics.value( i ).bandNumber == bandNo )
411  {
412  mStatistics.removeAt( i );
413  mHistograms.removeAt( i );
414  }
415  else
416  {
417  i++;
418  }
419  }
420  mUserNoDataValue[bandNo-1] = noData;
421  }
422 }
423 
424 typedef QgsRasterDataProvider * createFunction_t( const QString&,
425  const QString&, int,
427  int, int, double*,
429  QStringList );
430 
432  const QString &uri,
433  const QString& format, int nBands,
434  QGis::DataType type,
435  int width, int height, double* geoTransform,
436  const QgsCoordinateReferenceSystem& crs,
437  QStringList createOptions )
438 {
439  createFunction_t *createFn = ( createFunction_t* ) cast_to_fptr( QgsProviderRegistry::instance()->function( providerKey, "create" ) );
440  if ( !createFn )
441  {
442  QgsDebugMsg( "Cannot resolve 'create' function in " + providerKey + " provider" );
443  // TODO: it would be good to return invalid QgsRasterDataProvider
444  // with QgsError set, but QgsRasterDataProvider has pure virtual methods
445  return 0;
446  }
447  return createFn( uri, format, nBands, type, width, height, geoTransform, crs, createOptions );
448 }
449 
451 {
452  switch ( format )
453  {
455  return "Value";
457  return "Text";
459  return "Html";
461  return "Feature";
462  default:
463  return "Undefined";
464  }
465 }
466 
468 {
469  switch ( format )
470  {
472  return tr( "Value" );
474  return tr( "Text" );
476  return tr( "Html" );
478  return tr( "Feature" );
479  default:
480  return "Undefined";
481  }
482 }
483 
485 {
486  if ( formatName == "Value" ) return QgsRaster::IdentifyFormatValue;
487  if ( formatName == "Text" ) return QgsRaster::IdentifyFormatText;
488  if ( formatName == "Html" ) return QgsRaster::IdentifyFormatHtml;
489  if ( formatName == "Feature" ) return QgsRaster::IdentifyFormatFeature;
491 }
492 
494 {
495  switch ( format )
496  {
498  return IdentifyValue;
500  return IdentifyText;
502  return IdentifyHtml;
504  return IdentifyFeature;
505  default:
506  return NoCapabilities;
507  }
508 }
509 
510 bool QgsRasterDataProvider::userNoDataValuesContains( int bandNo, double value ) const
511 {
512  QgsRasterRangeList rangeList = mUserNoDataValue.value( bandNo - 1 );
513  return QgsRasterRange::contains( value, rangeList );
514 }
515 
517 {
518  mDpi = other.mDpi;
523  mExtent = other.mExtent;
524 }
525 
526 // ENDS