QGIS API Documentation  2.3.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 = 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
bool hasPyramids()
Returns true if raster has at least one populated histogram.
virtual int bandCount() const =0
Get number of bands.
virtual void readBlock(int bandNo, int xBlock, int yBlock, void *data)
Read block of data.
static unsigned index
IdentifyFormat
Definition: qgsraster.h:54
A rectangle specified with double values.
Definition: qgsrectangle.h:35
bool isEmpty() const
test if rectangle is empty
virtual QgsRasterBlock * block(int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight)
Read block of data using given extent and size.
static bool contains(double value, const QgsRasterRangeList &rangeList)
Test if value is within the list of ranges.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:164
static QgsProviderRegistry * instance(QString pluginPath=QString::null)
means of accessing canonical single instance
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:189
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
void applyNoDataValues(const QgsRasterRangeList &rangeList)
virtual void setUseSrcNoDataValue(int bandNo, bool use)
Set source nodata value usage.
virtual double srcNoDataValue(int bandNo) const
Value representing no data value.
static QString makeTableCell(const QString &value)
Capability
If you add to this, please also add to capabilitiesString()
static Capability identifyFormatToCapability(QgsRaster::IdentifyFormat format)
virtual QgsRasterRangeList userNoDataValues(int bandNo) const
Get list of user no data value ranges.
static QgsRasterDataProvider * create(const QString &providerKey, const QString &uri, const QString &format, int nBands, QGis::DataType type, int width, int height, double *geoTransform, const QgsCoordinateReferenceSystem &crs, QStringList createOptions=QStringList())
Creates a new dataset with mDataSourceURI.
Abstract base class for spatial data provider implementations.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:324
double x() const
Definition: qgspoint.h:110
virtual int ySize() const
static QString identifyFormatName(QgsRaster::IdentifyFormat format)
bool setIsNoData(int row, int column)
Set no data on pixel.
virtual bool useSrcNoDataValue(int bandNo) const
Get source nodata value usage.
virtual QString lastErrorFormat()
Returns the format of the error text for the last error in this provider.
Raster identify results container.
virtual QgsRasterIdentifyResult identify(const QgsPoint &thePoint, QgsRaster::IdentifyFormat theFormat, const QgsRectangle &theExtent=QgsRectangle(), int theWidth=0, int theHeight=0)
Identify raster value(s) found on the point position.
bool setIsNoDataExcept(const QRect &theExceptRect)
Set the whole block to no data except specified rectangle.
Raster data container.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:194
virtual void setUserNoDataValue(int bandNo, QgsRasterRangeList noData)
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:179
virtual QList< QgsRasterPyramid > buildPyramidList(QList< int > overviewList=QList< int >())
Accessor for ths raster layers pyramid list.
double value(int row, int column) const
Read a single value if type of block is numeric.
virtual QgsRectangle extent()=0
Get the extent of the data source.
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:169
QList< QgsRasterHistogram > mHistograms
List of cached histograms, all bands mixed.
virtual QGis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
Base class for processing filters like renderers, reprojector, resampler etc.
A class to represent a point geometry.
Definition: qgspoint.h:63
QgsRasterDataProvider * createFunction_t(const QString &, const QString &, int, QGis::DataType, int, int, double *, const QgsCoordinateReferenceSystem &, QStringList)
unsigned long long qgssize
qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:424
bool userNoDataValuesContains(int bandNo, double value) const
Returns true if user no data contains value.
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
QList< QPair< QString, QString > > * pyramidResamplingMethods_t()
char * bits(int row, int column)
Get pointer to data.
QList< QgsRasterRange > QgsRasterRangeList
virtual int xSize() const
Get raster size.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:174
virtual QString metadata()=0
Get metadata in a format suitable for feeding directly into a subset of the GUI raster properties "Me...
QgsRectangle intersect(const QgsRectangle *rect) const
return the intersection with the given rectangle
static QString makeTableCells(const QStringList &values)
Class for storing a coordinate reference system (CRS)
DataType
Raster data types.
Definition: qgis.h:204
int dataTypeSize(int bandNo)
double y() const
Definition: qgspoint.h:118
void * function(const QString &providerKey, const QString &functionName)
Get pointer to provider function.
static QStringList cStringList2Q_(char **stringList)
static QgsRaster::IdentifyFormat identifyFormatFromName(QString formatName)
void(*)() cast_to_fptr(void *p)
Definition: qgis.h:301
static QList< QPair< QString, QString > > pyramidResamplingMethods(QString providerKey)
Returns a list of pyramid resampling method name and label pairs for given provider.
virtual bool srcHasNoDataValue(int bandNo) const
QList< QgsRasterBandStats > mStatistics
List of cached statistics, all bands mixed.
static QRect subRect(const QgsRectangle &theExtent, int theWidth, int theHeight, const QgsRectangle &theSubExtent)
For theExtent and theWidht, theHeight find rectangle covered by subextent.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:199
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:184
static QString identifyFormatLabel(QgsRaster::IdentifyFormat format)
#define ERR(message)
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:159
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:204
bool isEmpty() const
Returns true if block is empty, i.e.
Base class for raster data providers.
QList< QgsRasterRangeList > mUserNoDataValue
List of lists of user defined additional no data values for each band, indexed from 0...
QList< bool > mUseSrcNoDataValue
Use source nodata value.
#define tr(sourceText)