|
23 | 23 | #include <QMap>
|
24 | 24 | #include <QByteArray>
|
25 | 25 |
|
| 26 | +#include <cmath> |
| 27 | + |
26 | 28 | void QgsRasterDataProvider::readBlock( int bandNo, QgsRectangle
|
27 | 29 | const & viewExtent, int width,
|
28 | 30 | int height,
|
@@ -270,7 +272,6 @@ QByteArray QgsRasterDataProvider::noValueBytes( int theBandNo )
|
270 | 272 | return ba;
|
271 | 273 | }
|
272 | 274 |
|
273 |
| - |
274 | 275 | QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo )
|
275 | 276 | {
|
276 | 277 | double myNoDataValue = noDataValue();
|
@@ -426,6 +427,169 @@ QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo )
|
426 | 427 | return myRasterBandStats;
|
427 | 428 | }
|
428 | 429 |
|
| 430 | +QgsRasterHistogram QgsRasterDataProvider::histogram( int theBandNo, |
| 431 | + double theMinimum, double theMaximum, |
| 432 | + int theBinCount, |
| 433 | + const QgsRectangle & theExtent, |
| 434 | + int theSampleSize, |
| 435 | + bool theIncludeOutOfRange ) |
| 436 | +{ |
| 437 | + QgsRasterHistogram myHistogram; |
| 438 | + myHistogram.bandNumber = theBandNo; |
| 439 | + myHistogram.minimum = theMinimum; |
| 440 | + myHistogram.maximum = theMaximum; |
| 441 | + myHistogram.includeOutOfRange = theIncludeOutOfRange; |
| 442 | + //myHistogram.sampleSize = theSampleSize |
| 443 | + |
| 444 | + // First calc defaults |
| 445 | + QgsRectangle myExtent = theExtent.isEmpty() ? extent() : theExtent; |
| 446 | + myHistogram.extent = myExtent; |
| 447 | + |
| 448 | + int myWidth, myHeight; |
| 449 | + if ( theSampleSize > 0 ) |
| 450 | + { |
| 451 | + // Calc resolution from theSampleSize |
| 452 | + double xRes, yRes; |
| 453 | + xRes = yRes = sqrt(( myExtent.width() * myExtent.height() ) / theSampleSize ); |
| 454 | + |
| 455 | + // But limit by physical resolution |
| 456 | + if ( capabilities() & Size ) |
| 457 | + { |
| 458 | + double srcXRes = extent().width() / xSize(); |
| 459 | + double srcYRes = extent().height() / ySize(); |
| 460 | + if ( xRes < srcXRes ) xRes = srcXRes; |
| 461 | + if ( yRes < srcYRes ) yRes = srcYRes; |
| 462 | + } |
| 463 | + |
| 464 | + myWidth = static_cast <int>( myExtent.width() / xRes ); |
| 465 | + myHeight = static_cast <int>( myExtent.height() / yRes ); |
| 466 | + } |
| 467 | + else |
| 468 | + { |
| 469 | + if ( capabilities() & Size ) |
| 470 | + { |
| 471 | + myWidth = xSize(); |
| 472 | + myHeight = ySize(); |
| 473 | + } |
| 474 | + else |
| 475 | + { |
| 476 | + myWidth = 1000; |
| 477 | + myHeight = 1000; |
| 478 | + } |
| 479 | + } |
| 480 | + myHistogram.width = myWidth; |
| 481 | + myHistogram.height = myHeight; |
| 482 | + QgsDebugMsg( QString( "myWidth = %1 myHeight = %2" ).arg( myWidth ).arg( myHeight ) ); |
| 483 | + |
| 484 | + double myNoDataValue = noDataValue(); |
| 485 | + int myDataType = dataType( theBandNo ); |
| 486 | + |
| 487 | + int myBinCount = theBinCount; |
| 488 | + if ( myBinCount == 0 ) |
| 489 | + { |
| 490 | + if ( myDataType == QgsRasterDataProvider::Byte ) |
| 491 | + { |
| 492 | + myBinCount = 256; // Cannot store more values in byte |
| 493 | + } |
| 494 | + else |
| 495 | + { |
| 496 | + // 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; |
| 497 | + myBinCount = myWidth * myHeight; |
| 498 | + if ( myBinCount > 1000 ) myBinCount = 1000; |
| 499 | + } |
| 500 | + } |
| 501 | + myHistogram.binCount = theBinCount; |
| 502 | + QgsDebugMsg( QString( "myBinCount = %1" ).arg( myBinCount ) ); |
| 503 | + |
| 504 | + // Check if we have cached |
| 505 | + foreach( QgsRasterHistogram histogram, mHistograms ) |
| 506 | + { |
| 507 | + if ( histogram.bandNumber == theBandNo && |
| 508 | + histogram.minimum == theMinimum && |
| 509 | + histogram.maximum == theMaximum && |
| 510 | + histogram.binCount == myBinCount && |
| 511 | + histogram.extent == myExtent && |
| 512 | + histogram.width == myWidth && |
| 513 | + histogram.height == myHeight && |
| 514 | + histogram.includeOutOfRange == theIncludeOutOfRange ) |
| 515 | + { |
| 516 | + return histogram; |
| 517 | + } |
| 518 | + } |
| 519 | + |
| 520 | + myHistogram.histogramVector.resize( myBinCount ); |
| 521 | + |
| 522 | + int myNXBlocks, myNYBlocks, myXBlockSize, myYBlockSize; |
| 523 | + myXBlockSize = xBlockSize(); |
| 524 | + myYBlockSize = yBlockSize(); |
| 525 | + |
| 526 | + if ( myXBlockSize == 0 || myYBlockSize == 0 ) // should not happen |
| 527 | + { |
| 528 | + return myHistogram; |
| 529 | + } |
| 530 | + |
| 531 | + myNXBlocks = ( myWidth + myXBlockSize - 1 ) / myXBlockSize; |
| 532 | + myNYBlocks = ( myHeight + myYBlockSize - 1 ) / myYBlockSize; |
| 533 | + |
| 534 | + void *myData = CPLMalloc( myXBlockSize * myYBlockSize * ( dataTypeSize( theBandNo ) / 8 ) ); |
| 535 | + |
| 536 | + int myBandXSize = xSize(); |
| 537 | + int myBandYSize = ySize(); |
| 538 | + double myXRes = myExtent.width() / myWidth; |
| 539 | + double myYRes = myExtent.height() / myHeight; |
| 540 | + double binSize = ( theMaximum - theMinimum ) / myBinCount; |
| 541 | + for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ ) |
| 542 | + { |
| 543 | + for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ ) |
| 544 | + { |
| 545 | + int myPartWidth = qMin( myXBlockSize, myWidth - iXBlock * myXBlockSize ); |
| 546 | + int myPartHeight = qMin( myYBlockSize, myHeight - iYBlock * myYBlockSize ); |
| 547 | + |
| 548 | + double xmin = myExtent.xMinimum() + iXBlock * myXBlockSize * myXRes; |
| 549 | + double xmax = xmin + myPartWidth * myXRes; |
| 550 | + double ymin = myExtent.yMaximum() - iYBlock * myYBlockSize * myYRes; |
| 551 | + double ymax = ymin - myPartHeight * myYRes; |
| 552 | + |
| 553 | + QgsRectangle myPartExtent( xmin, ymin, xmax, ymax ); |
| 554 | + |
| 555 | + readBlock( theBandNo, myPartExtent, myPartWidth, myPartHeight, myData ); |
| 556 | + |
| 557 | + // Collect the histogram counts. |
| 558 | + for ( int iY = 0; iY < myPartHeight; iY++ ) |
| 559 | + { |
| 560 | + for ( int iX = 0; iX < myPartWidth; iX++ ) |
| 561 | + { |
| 562 | + double myValue = readValue( myData, myDataType, iX + ( iY * myPartWidth ) ); |
| 563 | + //QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (iX).arg(iY).arg( myValue ) ); |
| 564 | + |
| 565 | + if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) ) |
| 566 | + { |
| 567 | + continue; // NULL |
| 568 | + } |
| 569 | + |
| 570 | + int myBinIndex = static_cast <int>( floor(( myValue - theMinimum ) / binSize ) ) ; |
| 571 | + if (( myBinIndex < 0 || myBinIndex > ( myBinCount - 1 ) ) && !theIncludeOutOfRange ) |
| 572 | + { |
| 573 | + continue; |
| 574 | + } |
| 575 | + if ( myBinIndex < 0 ) myBinIndex = 0; |
| 576 | + if ( myBinIndex > ( myBinCount - 1 ) ) myBinIndex = myBinCount - 1; |
| 577 | + |
| 578 | + myHistogram.histogramVector[myBinIndex] += 1; |
| 579 | + myHistogram.nonNullCount++; |
| 580 | + } |
| 581 | + } |
| 582 | + } //end of column wise loop |
| 583 | + } //end of row wise loop |
| 584 | + |
| 585 | + CPLFree( myData ); |
| 586 | + |
| 587 | + myHistogram.valid = true; |
| 588 | + mHistograms.append( myHistogram ); |
| 589 | + |
| 590 | + return myHistogram; |
| 591 | +} |
| 592 | + |
429 | 593 | double QgsRasterDataProvider::readValue( void *data, int type, int index )
|
430 | 594 | {
|
431 | 595 | if ( !data )
|
|
0 commit comments