QGIS API Documentation  2.14.0-Essen
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 ERR(message) QgsError(message, "Raster provider")
32 
33 void QgsRasterDataProvider::setUseSrcNoDataValue( int bandNo, bool use )
34 {
35  if ( mUseSrcNoDataValue.size() < bandNo )
36  {
37  for ( int i = mUseSrcNoDataValue.size(); i < bandNo; i++ )
38  {
39  mUseSrcNoDataValue.append( false );
40  }
41  }
42  mUseSrcNoDataValue[bandNo-1] = use;
43 }
44 
45 QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle const & theExtent, int theWidth, int theHeight )
46 {
47  QgsDebugMsgLevel( QString( "theBandNo = %1 theWidth = %2 theHeight = %3" ).arg( theBandNo ).arg( theWidth ).arg( theHeight ), 4 );
48  QgsDebugMsgLevel( QString( "theExtent = %1" ).arg( theExtent.toString() ), 4 );
49 
51  if ( srcHasNoDataValue( theBandNo ) && useSrcNoDataValue( theBandNo ) )
52  {
53  block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight, srcNoDataValue( theBandNo ) );
54  }
55  else
56  {
57  block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight );
58  }
59 
60  if ( block->isEmpty() )
61  {
62  QgsDebugMsg( "Couldn't create raster block" );
63  return block;
64  }
65 
66  // Read necessary extent only
67  QgsRectangle tmpExtent = extent().intersect( &theExtent );
68 
69  if ( tmpExtent.isEmpty() )
70  {
71  QgsDebugMsg( "Extent outside provider extent" );
72  block->setIsNoData();
73  return block;
74  }
75 
76  double xRes = theExtent.width() / theWidth;
77  double yRes = theExtent.height() / theHeight;
78  double tmpXRes, tmpYRes;
79  double providerXRes = 0;
80  double providerYRes = 0;
81  if ( capabilities() & Size )
82  {
83  providerXRes = extent().width() / xSize();
84  providerYRes = extent().height() / ySize();
85  tmpXRes = qMax( providerXRes, xRes );
86  tmpYRes = qMax( providerYRes, yRes );
87  if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes;
88  if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes;
89  }
90  else
91  {
92  tmpXRes = xRes;
93  tmpYRes = yRes;
94  }
95 
96  if ( tmpExtent != theExtent ||
97  tmpXRes > xRes || tmpYRes > yRes )
98  {
99  // Read smaller extent or lower resolution
100 
101  if ( !extent().contains( theExtent ) )
102  {
103  QRect subRect = QgsRasterBlock::subRect( theExtent, theWidth, theHeight, extent() );
104  block->setIsNoDataExcept( subRect );
105  }
106 
107  // Calculate row/col limits (before tmpExtent is aligned)
108  int fromRow = qRound(( theExtent.yMaximum() - tmpExtent.yMaximum() ) / yRes );
109  int toRow = qRound(( theExtent.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1;
110  int fromCol = qRound(( tmpExtent.xMinimum() - theExtent.xMinimum() ) / xRes );
111  int toCol = qRound(( tmpExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1;
112 
113  QgsDebugMsgLevel( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ), 4 );
114 
115  if ( fromRow < 0 || fromRow >= theHeight || toRow < 0 || toRow >= theHeight ||
116  fromCol < 0 || fromCol >= theWidth || toCol < 0 || toCol >= theWidth )
117  {
118  // Should not happen
119  QgsDebugMsg( "Row or column limits out of range" );
120  return block;
121  }
122 
123  // If lower source resolution is used, the extent must beS aligned to original
124  // resolution to avoid possible shift due to resampling
125  if ( tmpXRes > xRes )
126  {
127  int col = floor(( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes );
128  tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes );
129  col = ceil(( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes );
130  tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes );
131  }
132  if ( tmpYRes > yRes )
133  {
134  int row = floor(( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes );
135  tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes );
136  row = ceil(( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes );
137  tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes );
138  }
139  int tmpWidth = qRound( tmpExtent.width() / tmpXRes );
140  int tmpHeight = qRound( tmpExtent.height() / tmpYRes );
141  tmpXRes = tmpExtent.width() / tmpWidth;
142  tmpYRes = tmpExtent.height() / tmpHeight;
143 
144  QgsDebugMsgLevel( QString( "Reading smaller block tmpWidth = %1 theHeight = %2" ).arg( tmpWidth ).arg( tmpHeight ), 4 );
145  QgsDebugMsgLevel( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ), 4 );
146 
147  QgsRasterBlock *tmpBlock;
148  if ( srcHasNoDataValue( theBandNo ) && useSrcNoDataValue( theBandNo ) )
149  {
150  tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight, srcNoDataValue( theBandNo ) );
151  }
152  else
153  {
154  tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight );
155  }
156 
157  readBlock( theBandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits() );
158 
159  int pixelSize = dataTypeSize( theBandNo );
160 
161  double xMin = theExtent.xMinimum();
162  double yMax = theExtent.yMaximum();
163  double tmpXMin = tmpExtent.xMinimum();
164  double tmpYMax = tmpExtent.yMaximum();
165 
166  for ( int row = fromRow; row <= toRow; row++ )
167  {
168  double y = yMax - ( row + 0.5 ) * yRes;
169  int tmpRow = floor(( tmpYMax - y ) / tmpYRes );
170 
171  for ( int col = fromCol; col <= toCol; col++ )
172  {
173  double x = xMin + ( col + 0.5 ) * xRes;
174  int tmpCol = floor(( x - tmpXMin ) / tmpXRes );
175 
176  if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth )
177  {
178  QgsDebugMsg( "Source row or column limits out of range" );
179  block->setIsNoData(); // so that the problem becomes obvious and fixed
180  delete tmpBlock;
181  return block;
182  }
183 
184  qgssize tmpIndex = static_cast< qgssize >( tmpRow ) * static_cast< qgssize >( tmpWidth ) + tmpCol;
185  qgssize index = row * static_cast< qgssize >( theWidth ) + col;
186 
187  char *tmpBits = tmpBlock->bits( tmpIndex );
188  char *bits = block->bits( index );
189  if ( !tmpBits )
190  {
191  QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) );
192  continue;
193  }
194  if ( !bits )
195  {
196  QgsDebugMsg( "Cannot set output block data." );
197  continue;
198  }
199  memcpy( bits, tmpBits, pixelSize );
200  }
201  }
202 
203  delete tmpBlock;
204  }
205  else
206  {
207  readBlock( theBandNo, theExtent, theWidth, theHeight, block->bits() );
208  }
209 
210  // apply scale and offset
211  block->applyScaleOffset( bandScale( theBandNo ), bandOffset( theBandNo ) );
212  // apply user no data values
213  block->applyNoDataValues( userNoDataValues( theBandNo ) );
214  return block;
215 }
216 
218  : QgsRasterInterface( nullptr )
219  , mDpi( -1 )
220 {
221 }
222 
224  : QgsDataProvider( uri )
225  , QgsRasterInterface( nullptr )
226  , mDpi( -1 )
227 {
228 }
229 
230 //
231 //Random Static convenience function
232 //
234 // convenience function for building metadata() HTML table cells
235 // convenience function for creating a string list from a C style string list
237 {
238  QStringList strings;
239 
240  // presume null terminated string list
241  for ( qgssize i = 0; stringList[i]; ++i )
242  {
243  strings.append( stringList[i] );
244  }
245 
246  return strings;
247 
248 } // cStringList2Q_
249 
251 {
252  return "<p>\n" + value + "</p>\n";
253 } // makeTableCell_
254 
255 // convenience function for building metadata() HTML table cells
257 {
258  QString s( "<tr>" );
259 
260  for ( QStringList::const_iterator i = values.begin();
261  i != values.end();
262  ++i )
263  {
265  }
266 
267  s += "</tr>";
268 
269  return s;
270 } // makeTableCell_
271 
273 {
274  QString s;
275  return s;
276 }
277 
278 // Default implementation for values
279 QgsRasterIdentifyResult QgsRasterDataProvider::identify( const QgsPoint & thePoint, QgsRaster::IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight )
280 {
281  QgsDebugMsgLevel( "Entered", 4 );
282  QMap<int, QVariant> results;
283 
284  if ( theFormat != QgsRaster::IdentifyFormatValue || !( capabilities() & IdentifyValue ) )
285  {
286  QgsDebugMsg( "Format not supported" );
287  return QgsRasterIdentifyResult( ERR( tr( "Format not supported" ) ) );
288  }
289 
290  if ( !extent().contains( thePoint ) )
291  {
292  // Outside the raster
293  for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ )
294  {
295  results.insert( bandNo, QVariant() );
296  }
298  }
299 
300  QgsRectangle myExtent = theExtent;
301  if ( myExtent.isEmpty() ) myExtent = extent();
302 
303  if ( theWidth == 0 )
304  {
305  theWidth = capabilities() & Size ? xSize() : 1000;
306  }
307  if ( theHeight == 0 )
308  {
309  theHeight = capabilities() & Size ? ySize() : 1000;
310  }
311 
312  // Calculate the row / column where the point falls
313  double xres = ( myExtent.width() ) / theWidth;
314  double yres = ( myExtent.height() ) / theHeight;
315 
316  int col = static_cast< int >( floor(( thePoint.x() - myExtent.xMinimum() ) / xres ) );
317  int row = static_cast< int >( floor(( myExtent.yMaximum() - thePoint.y() ) / yres ) );
318 
319  double xMin = myExtent.xMinimum() + col * xres;
320  double xMax = xMin + xres;
321  double yMax = myExtent.yMaximum() - row * yres;
322  double yMin = yMax - yres;
323  QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );
324 
325  for ( int i = 1; i <= bandCount(); i++ )
326  {
327  QgsRasterBlock * myBlock = block( i, pixelExtent, 1, 1 );
328 
329  if ( myBlock )
330  {
331  double value = myBlock->value( 0 );
332 
333  results.insert( i, value );
334  delete myBlock;
335  }
336  else
337  {
338  results.insert( i, QVariant() );
339  }
340  }
342 }
343 
345 {
346  return "text/plain";
347 }
348 
351 {
352  pyramidResamplingMethods_t *pPyramidResamplingMethods = reinterpret_cast< pyramidResamplingMethods_t * >( cast_to_fptr( QgsProviderRegistry::instance()->function( providerKey, "pyramidResamplingMethods" ) ) );
353  if ( pPyramidResamplingMethods )
354  {
355  QList<QPair<QString, QString> > *methods = pPyramidResamplingMethods();
356  if ( !methods )
357  {
358  QgsDebugMsg( "provider pyramidResamplingMethods returned no methods" );
359  }
360  else
361  {
362  return *methods;
363  }
364  }
365  else
366  {
367  QgsDebugMsg( "Could not resolve pyramidResamplingMethods provider library" );
368  }
370 }
371 
373 {
374  QList<QgsRasterPyramid> myPyramidList = buildPyramidList();
375 
376  if ( myPyramidList.isEmpty() )
377  return false;
378 
379  QList<QgsRasterPyramid>::iterator myRasterPyramidIterator;
380  for ( myRasterPyramidIterator = myPyramidList.begin();
381  myRasterPyramidIterator != myPyramidList.end();
382  ++myRasterPyramidIterator )
383  {
384  if ( myRasterPyramidIterator->exists )
385  {
386  return true;
387  }
388  }
389  return false;
390 }
391 
393 {
394  if ( bandNo >= mUserNoDataValue.size() )
395  {
396  for ( int i = mUserNoDataValue.size(); i < bandNo; i++ )
397  {
399  }
400  }
401  QgsDebugMsgLevel( QString( "set %1 band %1 no data ranges" ).arg( noData.size() ), 4 );
402 
403  if ( mUserNoDataValue[bandNo-1] != noData )
404  {
405  // Clear statistics
406  int i = 0;
407  while ( i < mStatistics.size() )
408  {
409  if ( mStatistics.value( i ).bandNumber == bandNo )
410  {
411  mStatistics.removeAt( i );
412  mHistograms.removeAt( i );
413  }
414  else
415  {
416  i++;
417  }
418  }
419  mUserNoDataValue[bandNo-1] = noData;
420  }
421 }
422 
424  const QString&, int,
426  int, int, double*,
428  QStringList );
429 
431  const QString &uri,
432  const QString& format, int nBands,
433  QGis::DataType type,
434  int width, int height, double* geoTransform,
436  const QStringList& createOptions )
437 {
438  createFunction_t *createFn = reinterpret_cast< createFunction_t* >( cast_to_fptr( QgsProviderRegistry::instance()->function( providerKey, "create" ) ) );
439  if ( !createFn )
440  {
441  QgsDebugMsg( "Cannot resolve 'create' function in " + providerKey + " provider" );
442  // TODO: it would be good to return invalid QgsRasterDataProvider
443  // with QgsError set, but QgsRasterDataProvider has pure virtual methods
444  return nullptr;
445  }
446  return createFn( uri, format, nBands, type, width, height, geoTransform, crs, createOptions );
447 }
448 
450 {
451  switch ( format )
452  {
454  return "Value";
456  return "Text";
458  return "Html";
460  return "Feature";
461  default:
462  return "Undefined";
463  }
464 }
465 
467 {
468  switch ( format )
469  {
471  return tr( "Value" );
473  return tr( "Text" );
475  return tr( "Html" );
477  return tr( "Feature" );
478  default:
479  return "Undefined";
480  }
481 }
482 
484 {
485  if ( formatName == "Value" ) return QgsRaster::IdentifyFormatValue;
486  if ( formatName == "Text" ) return QgsRaster::IdentifyFormatText;
487  if ( formatName == "Html" ) return QgsRaster::IdentifyFormatHtml;
488  if ( formatName == "Feature" ) return QgsRaster::IdentifyFormatFeature;
490 }
491 
493 {
494  switch ( format )
495  {
497  return IdentifyValue;
499  return IdentifyText;
501  return IdentifyHtml;
503  return IdentifyFeature;
504  default:
505  return NoCapabilities;
506  }
507 }
508 
509 bool QgsRasterDataProvider::userNoDataValuesContains( int bandNo, double value ) const
510 {
511  QgsRasterRangeList rangeList = mUserNoDataValue.value( bandNo - 1 );
512  return QgsRasterRange::contains( value, rangeList );
513 }
514 
516 {
517  mDpi = other.mDpi;
522  mExtent = other.mExtent;
523 }
524 
525 // 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
static QgsProviderRegistry * instance(const QString &pluginPath=QString::null)
Means of accessing canonical single instance.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
bool isEmpty() const
test if rectangle is empty.
void copyBaseSettings(const QgsRasterDataProvider &other)
Copy member variables from other raster data provider.
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, const QStringList &createOptions=QStringList())
Creates a new dataset with mDataSourceURI.
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:172
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:197
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
virtual QgsCoordinateReferenceSystem crs()=0
Get the QgsCoordinateReferenceSystem for this layer.
void applyNoDataValues(const QgsRasterRangeList &rangeList)
virtual double bandOffset(int bandNo) const
Read band offset for raster value.
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)
void removeAt(int i)
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.
Abstract base class for spatial data provider implementations.
QString tr(const char *sourceText, const char *disambiguation, int n)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
double x() const
Get the x value of the point.
Definition: qgspoint.h:128
virtual int ySize() const
static QString identifyFormatName(QgsRaster::IdentifyFormat format)
int size() const
T value(int i) const
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.
void append(const T &value)
Raster data container.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:202
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:187
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
virtual QList< QgsRasterPyramid > buildPyramidList(QList< int > overviewList=QList< int >())
Accessor for ths raster layers pyramid list.
static QgsRaster::IdentifyFormat identifyFormatFromName(const QString &formatName)
double value(int row, int column) const
Read a single value if type of block is numeric.
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:177
bool isEmpty() const
QList< QgsRasterHistogram > mHistograms
List of cached histograms, all bands mixed.
QList< bool > mSrcHasNoDataValue
Source no data value exists.
bool setIsNoDataExcept(QRect theExceptRect)
Set the whole block to no data except specified rectangle.
virtual QgsRasterBlock * block(int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight) override
Read block of data using given extent and size.
Base class for processing filters like renderers, reprojector, resampler etc.
A class to represent a point.
Definition: qgspoint.h:65
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:392
iterator end()
bool userNoDataValuesContains(int bandNo, double value) const
Returns true if user no data contains value.
QList< double > mSrcNoDataValue
Source no data value is available and is set to be used or internal no data is available.
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
virtual double bandScale(int bandNo) const
Read band scale for raster value.
static QList< QPair< QString, QString > > pyramidResamplingMethods(const QString &providerKey)
Returns a list of pyramid resampling method name and label pairs for given provider.
QList< QPair< QString, QString > > * pyramidResamplingMethods_t()
virtual QgsRectangle extent() override=0
Get the extent of the data source.
virtual QGis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
char * bits(int row, int column)
Get pointer to data.
QList< QgsRasterRange > QgsRasterRangeList
virtual int xSize() const
Get raster size.
void applyScaleOffset(double scale, double offset)
Apply band scale and offset to raster block values @note added in 2.3.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:182
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:129
int dataTypeSize(int bandNo)
double y() const
Get the y value of the point.
Definition: qgspoint.h:136
static QStringList cStringList2Q_(char **stringList)
iterator insert(const Key &key, const T &value)
void(*)() cast_to_fptr(void *p)
Definition: qgis.h:258
virtual bool srcHasNoDataValue(int bandNo) const
Return true if source band has no data value.
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:207
virtual void setUserNoDataValue(int bandNo, const QgsRasterRangeList &noData)
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:192
static QString identifyFormatLabel(QgsRaster::IdentifyFormat format)
iterator begin()
#define ERR(message)
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:167
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:212
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.