QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsrasterinterface.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterface.cpp - Internal raster processing modules interface
3  --------------------------------------
4  Date : Jun 21, 2012
5  Copyright : (C) 2012 by Radim Blazek
6  email : radim dot blazek at gmail dot 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 
18 #include <limits>
19 #include <typeinfo>
20 
21 #include <QByteArray>
22 #include <QTime>
23 
24 #include <qmath.h>
25 
26 #include "qgslogger.h"
27 #include "qgsrasterbandstats.h"
28 #include "qgsrasterhistogram.h"
29 #include "qgsrasterinterface.h"
30 #include "qgsrectangle.h"
31 
33  : mInput( input )
34  , mOn( true )
35 {
36 }
37 
39 {
40 }
41 
43  int theBandNo,
44  int theStats,
45  const QgsRectangle & theExtent,
46  int theSampleSize )
47 {
48  QgsDebugMsg( QString( "theBandNo = %1 theSampleSize = %2" ).arg( theBandNo ).arg( theSampleSize ) );
49 
50  theStatistics.bandNumber = theBandNo;
51  theStatistics.statsGathered = theStats;
52 
53  QgsRectangle myExtent;
54  if ( theExtent.isEmpty() )
55  {
56  myExtent = extent();
57  }
58  else
59  {
60  myExtent = extent().intersect( &theExtent );
61  }
62  theStatistics.extent = myExtent;
63 
64  if ( theSampleSize > 0 )
65  {
66  // Calc resolution from theSampleSize
67  double xRes, yRes;
68  xRes = yRes = sqrt(( myExtent.width() * myExtent.height() ) / theSampleSize );
69 
70  // But limit by physical resolution
71  if ( capabilities() & Size )
72  {
73  double srcXRes = extent().width() / xSize();
74  double srcYRes = extent().height() / ySize();
75  if ( xRes < srcXRes ) xRes = srcXRes;
76  if ( yRes < srcYRes ) yRes = srcYRes;
77  }
78  QgsDebugMsg( QString( "xRes = %1 yRes = %2" ).arg( xRes ).arg( yRes ) );
79 
80  theStatistics.width = static_cast <int>( myExtent.width() / xRes );
81  theStatistics.height = static_cast <int>( myExtent.height() / yRes );
82  }
83  else
84  {
85  if ( capabilities() & Size )
86  {
87  theStatistics.width = xSize();
88  theStatistics.height = ySize();
89  }
90  else
91  {
92  theStatistics.width = 1000;
93  theStatistics.height = 1000;
94  }
95  }
96  QgsDebugMsg( QString( "theStatistics.width = %1 theStatistics.height = %2" ).arg( theStatistics.width ).arg( theStatistics.height ) );
97 }
98 
100  int theStats,
101  const QgsRectangle & theExtent,
102  int theSampleSize )
103 {
104  QgsDebugMsg( QString( "theBandNo = %1 theStats = %2 theSampleSize = %3" ).arg( theBandNo ).arg( theStats ).arg( theSampleSize ) );
105  if ( mStatistics.size() == 0 ) return false;
106 
107  QgsRasterBandStats myRasterBandStats;
108  initStatistics( myRasterBandStats, theBandNo, theStats, theExtent, theSampleSize );
109 
110  foreach ( QgsRasterBandStats stats, mStatistics )
111  {
112  if ( stats.contains( myRasterBandStats ) )
113  {
114  QgsDebugMsg( "Has cached statistics." );
115  return true;
116  }
117  }
118  return false;
119 }
120 
122  int theStats,
123  const QgsRectangle & theExtent,
124  int theSampleSize )
125 {
126  QgsDebugMsg( QString( "theBandNo = %1 theStats = %2 theSampleSize = %3" ).arg( theBandNo ).arg( theStats ).arg( theSampleSize ) );
127 
128  // TODO: null values set on raster layer!!!
129 
130  QgsRasterBandStats myRasterBandStats;
131  initStatistics( myRasterBandStats, theBandNo, theStats, theExtent, theSampleSize );
132 
133  foreach ( QgsRasterBandStats stats, mStatistics )
134  {
135  if ( stats.contains( myRasterBandStats ) )
136  {
137  QgsDebugMsg( "Using cached statistics." );
138  return stats;
139  }
140  }
141 
142  QgsRectangle myExtent = myRasterBandStats.extent;
143  int myWidth = myRasterBandStats.width;
144  int myHeight = myRasterBandStats.height;
145 
146  //int myDataType = dataType( theBandNo );
147 
148  int myXBlockSize = xBlockSize();
149  int myYBlockSize = yBlockSize();
150  if ( myXBlockSize == 0 ) // should not happen, but happens
151  {
152  myXBlockSize = 500;
153  }
154  if ( myYBlockSize == 0 ) // should not happen, but happens
155  {
156  myYBlockSize = 500;
157  }
158 
159  int myNXBlocks = ( myWidth + myXBlockSize - 1 ) / myXBlockSize;
160  int myNYBlocks = ( myHeight + myYBlockSize - 1 ) / myYBlockSize;
161 
162  double myXRes = myExtent.width() / myWidth;
163  double myYRes = myExtent.height() / myHeight;
164  // TODO: progress signals
165 
166  // used by single pass stdev
167  double myMean = 0;
168  double mySumOfSquares = 0;
169 
170  bool myFirstIterationFlag = true;
171  for ( int myYBlock = 0; myYBlock < myNYBlocks; myYBlock++ )
172  {
173  for ( int myXBlock = 0; myXBlock < myNXBlocks; myXBlock++ )
174  {
175  QgsDebugMsg( QString( "myYBlock = %1 myXBlock = %2" ).arg( myYBlock ).arg( myXBlock ) );
176  int myBlockWidth = qMin( myXBlockSize, myWidth - myXBlock * myXBlockSize );
177  int myBlockHeight = qMin( myYBlockSize, myHeight - myYBlock * myYBlockSize );
178 
179  double xmin = myExtent.xMinimum() + myXBlock * myXBlockSize * myXRes;
180  double xmax = xmin + myBlockWidth * myXRes;
181  double ymin = myExtent.yMaximum() - myYBlock * myYBlockSize * myYRes;
182  double ymax = ymin - myBlockHeight * myYRes;
183 
184  QgsRectangle myPartExtent( xmin, ymin, xmax, ymax );
185 
186  QgsRasterBlock* blk = block( theBandNo, myPartExtent, myBlockWidth, myBlockHeight );
187 
188  // Collect the histogram counts.
189  for ( qgssize i = 0; i < (( qgssize ) myBlockHeight ) * myBlockWidth; i++ )
190  {
191  if ( blk->isNoData( i ) ) continue; // NULL
192 
193  double myValue = blk->value( i );
194 
195  myRasterBandStats.sum += myValue;
196  myRasterBandStats.elementCount++;
197 
198  if ( myFirstIterationFlag )
199  {
200  myFirstIterationFlag = false;
201  myRasterBandStats.minimumValue = myValue;
202  myRasterBandStats.maximumValue = myValue;
203  }
204  else
205  {
206  if ( myValue < myRasterBandStats.minimumValue )
207  {
208  myRasterBandStats.minimumValue = myValue;
209  }
210  if ( myValue > myRasterBandStats.maximumValue )
211  {
212  myRasterBandStats.maximumValue = myValue;
213  }
214  }
215 
216  // Single pass stdev
217  double myDelta = myValue - myMean;
218  myMean += myDelta / myRasterBandStats.elementCount;
219  mySumOfSquares += myDelta * ( myValue - myMean );
220  }
221  delete blk;
222  }
223  }
224 
225  myRasterBandStats.range = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue;
226  myRasterBandStats.mean = myRasterBandStats.sum / myRasterBandStats.elementCount;
227 
228  myRasterBandStats.sumOfSquares = mySumOfSquares; // OK with single pass?
229 
230  // stdDev may differ from GDAL stats, because GDAL is using naive single pass
231  // algorithm which is more error prone (because of rounding errors)
232  // Divide result by sample size - 1 and get square root to get stdev
233  myRasterBandStats.stdDev = sqrt( mySumOfSquares / ( myRasterBandStats.elementCount - 1 ) );
234 
235  QgsDebugMsg( "************ STATS **************" );
236  QgsDebugMsg( QString( "MIN %1" ).arg( myRasterBandStats.minimumValue ) );
237  QgsDebugMsg( QString( "MAX %1" ).arg( myRasterBandStats.maximumValue ) );
238  QgsDebugMsg( QString( "RANGE %1" ).arg( myRasterBandStats.range ) );
239  QgsDebugMsg( QString( "MEAN %1" ).arg( myRasterBandStats.mean ) );
240  QgsDebugMsg( QString( "STDDEV %1" ).arg( myRasterBandStats.stdDev ) );
241 
242  myRasterBandStats.statsGathered = QgsRasterBandStats::All;
243  mStatistics.append( myRasterBandStats );
244 
245  return myRasterBandStats;
246 }
247 
249  int theBandNo,
250  int theBinCount,
251  double theMinimum, double theMaximum,
252  const QgsRectangle & theExtent,
253  int theSampleSize,
254  bool theIncludeOutOfRange )
255 {
256  theHistogram.bandNumber = theBandNo;
257  theHistogram.minimum = theMinimum;
258  theHistogram.maximum = theMaximum;
259  theHistogram.includeOutOfRange = theIncludeOutOfRange;
260 
261  int mySrcDataType = srcDataType( theBandNo );
262 
263  if ( qIsNaN( theHistogram.minimum ) )
264  {
265  // TODO: this was OK when stats/histogram were calced in provider,
266  // but what TODO in other interfaces? Check for mInput for now.
267  if ( !mInput && mySrcDataType == QGis::Byte )
268  {
269  theHistogram.minimum = 0; // see histogram() for shift for rounding
270  }
271  else
272  {
273  // We need statistics -> avoid histogramDefaults in hasHistogram if possible
274  // TODO: use approximated statistics if aproximated histogram is requested
275  // (theSampleSize > 0)
276  QgsRasterBandStats stats = bandStatistics( theBandNo, QgsRasterBandStats::Min, theExtent, theSampleSize );
277  theHistogram.minimum = stats.minimumValue;
278  }
279  }
280  if ( qIsNaN( theHistogram.maximum ) )
281  {
282  if ( !mInput && mySrcDataType == QGis::Byte )
283  {
284  theHistogram.maximum = 255;
285  }
286  else
287  {
288  QgsRasterBandStats stats = bandStatistics( theBandNo, QgsRasterBandStats::Max, theExtent, theSampleSize );
289  theHistogram.maximum = stats.maximumValue;
290  }
291  }
292 
293  QgsRectangle myExtent;
294  if ( theExtent.isEmpty() )
295  {
296  myExtent = extent();
297  }
298  else
299  {
300  myExtent = extent().intersect( &theExtent );
301  }
302  theHistogram.extent = myExtent;
303 
304  if ( theSampleSize > 0 )
305  {
306  // Calc resolution from theSampleSize
307  double xRes, yRes;
308  xRes = yRes = sqrt(( myExtent.width() * myExtent.height() ) / theSampleSize );
309 
310  // But limit by physical resolution
311  if ( capabilities() & Size )
312  {
313  double srcXRes = extent().width() / xSize();
314  double srcYRes = extent().height() / ySize();
315  if ( xRes < srcXRes ) xRes = srcXRes;
316  if ( yRes < srcYRes ) yRes = srcYRes;
317  }
318  QgsDebugMsg( QString( "xRes = %1 yRes = %2" ).arg( xRes ).arg( yRes ) );
319 
320  theHistogram.width = static_cast <int>( myExtent.width() / xRes );
321  theHistogram.height = static_cast <int>( myExtent.height() / yRes );
322  }
323  else
324  {
325  if ( capabilities() & Size )
326  {
327  theHistogram.width = xSize();
328  theHistogram.height = ySize();
329  }
330  else
331  {
332  theHistogram.width = 1000;
333  theHistogram.height = 1000;
334  }
335  }
336  QgsDebugMsg( QString( "theHistogram.width = %1 theHistogram.height = %2" ).arg( theHistogram.width ).arg( theHistogram.height ) );
337 
338  int myBinCount = theBinCount;
339  if ( myBinCount == 0 )
340  {
341  // TODO: this was OK when stats/histogram were calced in provider,
342  // but what TODO in other interfaces? Check for mInput for now.
343  if ( !mInput && mySrcDataType == QGis::Byte )
344  {
345  myBinCount = 256; // Cannot store more values in byte
346  }
347  else
348  {
349  // There is no best default value, to display something reasonable in histogram chart, binCount should be small, OTOH, to get precise data for cumulative cut, the number should be big. Because it is easier to define fixed lower value for the chart, we calc optimum binCount for higher resolution (to avoid calculating that where histogram() is used. In any any case, it does not make sense to use more than width*height;
350  myBinCount = theHistogram.width * theHistogram.height;
351  if ( myBinCount > 1000 ) myBinCount = 1000;
352 
353  // for Int16/Int32 make sure bin count <= actual range, because there is no sense in having
354  // bins at fractional values
355  if ( !mInput && (
356  mySrcDataType == QGis::Int16 || mySrcDataType == QGis::Int32 ||
357  mySrcDataType == QGis::UInt16 || mySrcDataType == QGis::UInt32 ) )
358  {
359  if ( myBinCount > theHistogram.maximum - theHistogram.minimum + 1 )
360  myBinCount = int( ceil( theHistogram.maximum - theHistogram.minimum + 1 ) );
361  }
362  }
363  }
364  theHistogram.binCount = myBinCount;
365  QgsDebugMsg( QString( "theHistogram.binCount = %1" ).arg( theHistogram.binCount ) );
366 }
367 
368 
370  int theBinCount,
371  double theMinimum, double theMaximum,
372  const QgsRectangle & theExtent,
373  int theSampleSize,
374  bool theIncludeOutOfRange )
375 {
376  QgsDebugMsg( QString( "theBandNo = %1 theBinCount = %2 theMinimum = %3 theMaximum = %4 theSampleSize = %5" ).arg( theBandNo ).arg( theBinCount ).arg( theMinimum ).arg( theMaximum ).arg( theSampleSize ) );
377  // histogramDefaults() needs statistics if theMinimum or theMaximum is NaN ->
378  // do other checks which don't need statistics before histogramDefaults()
379  if ( mHistograms.size() == 0 ) return false;
380 
381  QgsRasterHistogram myHistogram;
382  initHistogram( myHistogram, theBandNo, theBinCount, theMinimum, theMaximum, theExtent, theSampleSize, theIncludeOutOfRange );
383 
385  {
386  if ( histogram == myHistogram )
387  {
388  QgsDebugMsg( "Has cached histogram." );
389  return true;
390  }
391  }
392  return false;
393 }
394 
396  int theBinCount,
397  double theMinimum, double theMaximum,
398  const QgsRectangle & theExtent,
399  int theSampleSize,
400  bool theIncludeOutOfRange )
401 {
402  QgsDebugMsg( QString( "theBandNo = %1 theBinCount = %2 theMinimum = %3 theMaximum = %4 theSampleSize = %5" ).arg( theBandNo ).arg( theBinCount ).arg( theMinimum ).arg( theMaximum ).arg( theSampleSize ) );
403 
404  QgsRasterHistogram myHistogram;
405  initHistogram( myHistogram, theBandNo, theBinCount, theMinimum, theMaximum, theExtent, theSampleSize, theIncludeOutOfRange );
406 
407  // Find cached
409  {
410  if ( histogram == myHistogram )
411  {
412  QgsDebugMsg( "Using cached histogram." );
413  return histogram;
414  }
415  }
416 
417  int myBinCount = myHistogram.binCount;
418  int myWidth = myHistogram.width;
419  int myHeight = myHistogram.height;
420  QgsRectangle myExtent = myHistogram.extent;
421  myHistogram.histogramVector.resize( myBinCount );
422 
423  int myXBlockSize = xBlockSize();
424  int myYBlockSize = yBlockSize();
425  if ( myXBlockSize == 0 ) // should not happen, but happens
426  {
427  myXBlockSize = 500;
428  }
429  if ( myYBlockSize == 0 ) // should not happen, but happens
430  {
431  myYBlockSize = 500;
432  }
433 
434  int myNXBlocks = ( myWidth + myXBlockSize - 1 ) / myXBlockSize;
435  int myNYBlocks = ( myHeight + myYBlockSize - 1 ) / myYBlockSize;
436 
437  double myXRes = myExtent.width() / myWidth;
438  double myYRes = myExtent.height() / myHeight;
439 
440  double myMinimum = myHistogram.minimum;
441  double myMaximum = myHistogram.maximum;
442 
443  // To avoid rounding errors
444  // TODO: check this
445  double myerval = ( myMaximum - myMinimum ) / myHistogram.binCount;
446  myMinimum -= 0.1 * myerval;
447  myMaximum += 0.1 * myerval;
448 
449  QgsDebugMsg( QString( "binCount = %1 myMinimum = %2 myMaximum = %3" ).arg( myHistogram.binCount ).arg( myMinimum ).arg( myMaximum ) );
450 
451  double myBinSize = ( myMaximum - myMinimum ) / myBinCount;
452 
453  // TODO: progress signals
454  for ( int myYBlock = 0; myYBlock < myNYBlocks; myYBlock++ )
455  {
456  for ( int myXBlock = 0; myXBlock < myNXBlocks; myXBlock++ )
457  {
458  int myBlockWidth = qMin( myXBlockSize, myWidth - myXBlock * myXBlockSize );
459  int myBlockHeight = qMin( myYBlockSize, myHeight - myYBlock * myYBlockSize );
460 
461  double xmin = myExtent.xMinimum() + myXBlock * myXBlockSize * myXRes;
462  double xmax = xmin + myBlockWidth * myXRes;
463  double ymin = myExtent.yMaximum() - myYBlock * myYBlockSize * myYRes;
464  double ymax = ymin - myBlockHeight * myYRes;
465 
466  QgsRectangle myPartExtent( xmin, ymin, xmax, ymax );
467 
468  QgsRasterBlock* blk = block( theBandNo, myPartExtent, myBlockWidth, myBlockHeight );
469 
470  // Collect the histogram counts.
471  for ( qgssize i = 0; i < (( qgssize ) myBlockHeight ) * myBlockWidth; i++ )
472  {
473  if ( blk->isNoData( i ) )
474  {
475  continue; // NULL
476  }
477  double myValue = blk->value( i );
478 
479  int myBinIndex = static_cast <int>( qFloor(( myValue - myMinimum ) / myBinSize ) );
480 
481  if (( myBinIndex < 0 || myBinIndex > ( myBinCount - 1 ) ) && !theIncludeOutOfRange )
482  {
483  continue;
484  }
485  if ( myBinIndex < 0 ) myBinIndex = 0;
486  if ( myBinIndex > ( myBinCount - 1 ) ) myBinIndex = myBinCount - 1;
487 
488  myHistogram.histogramVector[myBinIndex] += 1;
489  myHistogram.nonNullCount++;
490  }
491  delete blk;
492  }
493  }
494 
495  myHistogram.valid = true;
496  mHistograms.append( myHistogram );
497 
498 #ifdef QGISDEBUG
499  QString hist;
500  for ( int i = 0; i < qMin( myHistogram.histogramVector.size(), 500 ); i++ )
501  {
502  hist += QString::number( myHistogram.histogramVector.value( i ) ) + " ";
503  }
504  QgsDebugMsg( "Histogram (max first 500 bins): " + hist );
505 #endif
506 
507  return myHistogram;
508 }
509 
511  double theLowerCount, double theUpperCount,
512  double &theLowerValue, double &theUpperValue,
513  const QgsRectangle & theExtent,
514  int theSampleSize )
515 {
516  QgsDebugMsg( QString( "theBandNo = %1 theLowerCount = %2 theUpperCount = %3 theSampleSize = %4" ).arg( theBandNo ).arg( theLowerCount ).arg( theUpperCount ).arg( theSampleSize ) );
517 
518  int mySrcDataType = srcDataType( theBandNo );
519 
520  // Init to NaN is better than histogram min/max to catch errors
521  theLowerValue = std::numeric_limits<double>::quiet_NaN();
522  theUpperValue = std::numeric_limits<double>::quiet_NaN();
523 
524  //get band stats to specify real histogram min/max (fix #9793 Byte bands)
525  QgsRasterBandStats stats = bandStatistics( theBandNo, QgsRasterBandStats::Min, theExtent, theSampleSize );
526  if ( stats.maximumValue < stats.minimumValue )
527  return;
528 
529  // for byte bands make sure bin count == actual range
530  int myBinCount = ( mySrcDataType == QGis::Byte ) ? int( ceil( stats.maximumValue - stats.minimumValue + 1 ) ) : 0;
531  QgsRasterHistogram myHistogram = histogram( theBandNo, myBinCount, stats.minimumValue, stats.maximumValue, theExtent, theSampleSize );
532  //QgsRasterHistogram myHistogram = histogram( theBandNo, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), theExtent, theSampleSize );
533 
534  double myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / myHistogram.binCount;
535  int myCount = 0;
536  int myMinCount = ( int ) qRound( theLowerCount * myHistogram.nonNullCount );
537  int myMaxCount = ( int ) qRound( theUpperCount * myHistogram.nonNullCount );
538  bool myLowerFound = false;
539  QgsDebugMsg( QString( "binCount = %1 minimum = %2 maximum = %3 myBinXStep = %4" ).arg( myHistogram.binCount ).arg( myHistogram.minimum ).arg( myHistogram.maximum ).arg( myBinXStep ) );
540  QgsDebugMsg( QString( "myMinCount = %1 myMaxCount = %2" ).arg( myMinCount ).arg( myMaxCount ) );
541 
542  for ( int myBin = 0; myBin < myHistogram.histogramVector.size(); myBin++ )
543  {
544  int myBinValue = myHistogram.histogramVector.value( myBin );
545  myCount += myBinValue;
546  if ( !myLowerFound && myCount > myMinCount )
547  {
548  theLowerValue = myHistogram.minimum + myBin * myBinXStep;
549  myLowerFound = true;
550  QgsDebugMsg( QString( "found lowerValue %1 at bin %2" ).arg( theLowerValue ).arg( myBin ) );
551  }
552  if ( myCount >= myMaxCount )
553  {
554  theUpperValue = myHistogram.minimum + myBin * myBinXStep;
555  QgsDebugMsg( QString( "found upperValue %1 at bin %2" ).arg( theUpperValue ).arg( myBin ) );
556  break;
557  }
558  }
559 
560  // fix integer data - round down/up
561  if ( mySrcDataType == QGis::Byte ||
562  mySrcDataType == QGis::Int16 || mySrcDataType == QGis::Int32 ||
563  mySrcDataType == QGis::UInt16 || mySrcDataType == QGis::UInt32 )
564  {
565  if ( theLowerValue != std::numeric_limits<double>::quiet_NaN() )
566  theLowerValue = floor( theLowerValue );
567  if ( theUpperValue != std::numeric_limits<double>::quiet_NaN() )
568  theUpperValue = ceil( theUpperValue );
569  }
570 }
571 
573 {
574  QStringList abilitiesList;
575 
576  int abilities = capabilities();
577 
578  // Not all all capabilities are here (Size, IdentifyValue, IdentifyText,
579  // IdentifyHtml, IdentifyFeature) because those are quite technical and probably
580  // would be confusing for users
581 
582  if ( abilities & QgsRasterInterface::Identify )
583  {
584  abilitiesList += tr( "Identify" );
585  }
586 
587  if ( abilities & QgsRasterInterface::Create )
588  {
589  abilitiesList += tr( "Create Datasources" );
590  }
591 
592  if ( abilities & QgsRasterInterface::Remove )
593  {
594  abilitiesList += tr( "Remove Datasources" );
595  }
596 
597  if ( abilities & QgsRasterInterface::BuildPyramids )
598  {
599  abilitiesList += tr( "Build Pyramids" );
600  }
601 
602  QgsDebugMsg( "Capability: " + abilitiesList.join( ", " ) );
603 
604  return abilitiesList.join( ", " );
605 }