38
38
#include < QFileInfo>
39
39
#include < QFile>
40
40
#include < QHash>
41
+ #include < QTime>
41
42
42
43
#include " gdalwarper.h"
43
44
#include " ogr_spatialref.h"
@@ -630,58 +631,26 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
630
631
double srcYRes = mGeoTransform [5 ]; // may be negative?
631
632
QgsDebugMsg ( QString ( " xRes = %1 yRes = %2 srcXRes = %3 srcYRes = %4" ).arg ( xRes ).arg ( yRes ).arg ( srcXRes ).arg ( srcYRes ) );
632
633
633
- double center, centerRaster;
634
-
635
634
// target size in pizels
636
635
int width = right - left + 1 ;
637
636
int height = bottom - top + 1 ;
638
637
639
- int srcWidth, srcHeight; // source size in pixels
640
638
641
639
int srcLeft = 0 ; // source raster x offset
642
640
int srcTop = 0 ; // source raster x offset
643
641
int srcBottom = ySize () - 1 ;
644
642
int srcRight = xSize () - 1 ;
645
643
646
- // We have to extend destination grid for GDALRasterIO to keep resolution:
647
- double topSpace = 0 ;
648
- double bottomSpace = 0 ;
649
- double leftSpace = 0 ;
650
- double rightSpace = 0 ;
651
-
652
- // It is easier to think separately about 3 cases for each axe: xRes = srcXRes, xRes > srcXRes, xRes < srcXRes, yRes = srcYRes, yRes > srcYRes, yRes < srcYRes
653
- // Some expressions may be duplicated but it is better for keeping clear ideas
644
+ QTime time ;
645
+ time .start ();
646
+ // Note: original approach for xRes < srcXRes || yRes < qAbs( srcYRes ) was to avoid
647
+ // second resampling and read with GDALRasterIO to another temporary data block
648
+ // extended to fit src grid. The problem was that with src resolution much bigger
649
+ // than dst res, the target could become very large
650
+ // in theory it was going to infinity when zooming in ...
654
651
655
- // *** CASE 1: xRes = srcXRes, yRes = srcYRes
656
- // This is not that rare because of 'Zoom to best resolution' tool
657
- // We just need to align the first cell
658
- if ( xRes == srcXRes )
659
- {
660
- if ( mExtent .xMinimum () < myRasterExtent.xMinimum () ) // raster runs outside to left, calc offset
661
- {
662
- srcLeft = qRound (( myRasterExtent.xMinimum () - mExtent .xMinimum () ) / srcXRes );
663
- }
664
- if ( mExtent .xMaximum () > myRasterExtent.xMaximum () )
665
- {
666
- srcRight = qRound (( myRasterExtent.xMaximum () - mExtent .xMinimum () ) / srcXRes ) - 1 ;
667
- }
668
- }
669
-
670
- if ( yRes == qAbs ( srcYRes ) )
671
- {
672
- // GDAL states that mGeoTransform[3] is top, may it also be bottom and mGeoTransform[5] positive?
673
- if ( mExtent .yMaximum () > myRasterExtent.yMaximum () ) // raster runs outside up, calc offset
674
- {
675
- // QgsDebugMsg( QString( "mExtent.yMaximum() - myRasterExtent.yMaximum() = %1 ( mExtent.yMaximum() - myRasterExtent.yMaximum() ) / srcYRes = %2 srcTop = %3" ).arg( mExtent.yMaximum() - myRasterExtent.yMaximum() ).arg( ( mExtent.yMaximum() - myRasterExtent.yMaximum() ) / srcYRes ).arg( srcTop ) );
676
- srcTop = qRound ( -1 . * ( mExtent .yMaximum () - myRasterExtent.yMaximum () ) / srcYRes );
677
- }
678
- if ( mExtent .yMinimum () < myRasterExtent.yMinimum () )
679
- {
680
- srcBottom = qRound ( -1 . * ( mExtent .yMaximum () - myRasterExtent.yMinimum () ) / srcYRes ) - 1 ;
681
- }
682
- }
683
-
684
- // *** CASE 2: xRes > srcXRes, yRes > srcYRes
652
+ // Note: original approach for xRes > srcXRes, yRes > srcYRes was to read directly with GDALRasterIO
653
+ // but we would face this problem:
685
654
// If the edge of the source is greater than the edge of destination:
686
655
// src: | | | | | | | | |
687
656
// dst: | | | |
@@ -695,173 +664,97 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
695
664
// case, but the first column may be rendered as without values event if its center
696
665
// is covered by src column. That could result in wrongly rendered (missing) edges
697
666
// which could be easily noticed by user
698
- //
699
- // Conclusion: Both are incorrect, but the b) is probably worse because it can be easily
700
- // noticed. Missing data are worse than slightly shifted nearest src cells.
701
- //
702
667
703
- if ( xRes > srcXRes )
668
+ // Because of problems mentioned above we read to another temporary block and do i
669
+ // another resampling here which appeares to be quite fast
670
+
671
+ // Get necessary src extent aligned to src resolution
672
+ if ( mExtent .xMinimum () < myRasterExtent.xMinimum () )
704
673
{
705
- if ( mExtent .xMinimum () < myRasterExtent.xMinimum () )
706
- {
707
- srcLeft = qRound (( myRasterExtent.xMinimum () - mExtent .xMinimum () ) / srcXRes );
708
- }
709
- if ( mExtent .xMaximum () > myRasterExtent.xMaximum () )
710
- {
711
- srcRight = qRound (( myRasterExtent.xMaximum () - mExtent .xMinimum () ) / srcXRes ) - 1 ;
712
- }
674
+ srcLeft = static_cast <int >( floor (( myRasterExtent.xMinimum () - mExtent .xMinimum () ) / srcXRes ) );
713
675
}
714
- if ( yRes > qAbs ( srcYRes ) )
676
+ if ( mExtent . xMaximum () > myRasterExtent. xMaximum ( ) )
715
677
{
716
- if ( mExtent .yMaximum () > myRasterExtent.yMaximum () )
717
- {
718
- srcTop = qRound ( -1 . * ( mExtent .yMaximum () - myRasterExtent.yMaximum () ) / srcYRes );
719
- }
720
- if ( mExtent .yMinimum () < myRasterExtent.yMinimum () )
721
- {
722
- srcBottom = qRound ( -1 . * ( mExtent .yMaximum () - myRasterExtent.yMinimum () ) / srcYRes ) - 1 ;
723
- }
678
+ srcRight = static_cast <int >( floor (( myRasterExtent.xMaximum () - mExtent .xMinimum () ) / srcXRes ) );
724
679
}
725
680
726
- // *** CASE 3: xRes < srcXRes, yRes < srcYRes
727
- // IMHO, we cannot align target grid to raster grid using target grid edges
728
- // and finding the nearest raster grid because it could lead to cell center
729
- // getting outside the right cell when doing resampling, example
730
- // src: | | | |
731
- // dst: | | | |
732
- // Raster width is 30m and it has 3 columns and we want to read xrange 5.1-30
733
- // to 3 columns, the nearest edge for beginning in raster grid is 10.0
734
- // reading cols 1-2, we get raster[1] value in target[0], but the center of
735
- // target[0] is 5.1 + ((30-5.1)/3)/2 = 9.25 so it falls to raster[0]. Is it right?
736
- // => We are looking for such alignment with which the center of first/last cell
737
- // falls to the right raster cell
738
- int xAddPixels = 0 ;
739
- int leftAddPixels = 0 ;
740
- if ( xRes < srcXRes )
681
+ // GDAL states that mGeoTransform[3] is top, may it also be bottom and mGeoTransform[5] positive?
682
+ if ( mExtent .yMaximum () > myRasterExtent.yMaximum () )
741
683
{
742
- center = myRasterExtent.xMinimum () + xRes / 2 ;
743
- centerRaster = ( center - mExtent .xMinimum () ) / srcXRes;
744
- srcLeft = static_cast <int >( floor ( centerRaster ) );
745
-
746
- // Space which has to be added to destination to fit better source
747
- // Warning - TODO: if xRes is much smaller than srcXRes, adding space and pixels to dst may
748
- // result in very large dst grids!!! E.g.
749
- // src: | | |
750
- // dst: | | |
751
- // Solution could be vector rendering of rectangles.
752
-
753
- leftSpace = myRasterExtent.xMinimum () - ( mExtent .xMinimum () + srcLeft * srcXRes );
754
- leftSpace = leftSpace > 0 ? leftSpace : 0 ; // makes only sense for positive
755
- center = myRasterExtent.xMaximum () - xRes / 2 ;
756
- centerRaster = ( center - mExtent .xMinimum () ) / srcXRes;
757
- srcRight = static_cast <int >( floor ( centerRaster ) );
758
-
759
- rightSpace = ( mExtent .xMinimum () + ( srcRight + 1 ) * srcXRes ) - myRasterExtent.xMaximum ();
760
- rightSpace = rightSpace > 0 ? rightSpace : 0 ;
761
- QgsDebugMsg ( QString ( " center = %1 centerRaster = %2 srcRight = %3 rightSpace = %4" ).arg ( center ).arg ( centerRaster ).arg ( srcRight ).arg ( rightSpace ) );
762
-
763
- double xAdd = leftSpace + rightSpace;
764
- xAddPixels = qRound ( xAdd / xRes );
765
- leftAddPixels = qRound ( leftSpace / xRes );
684
+ srcTop = static_cast <int >( floor ( -1 . * ( mExtent .yMaximum () - myRasterExtent.yMaximum () ) / srcYRes ) );
766
685
}
767
-
768
- int yAddPixels = 0 ;
769
- int topAddPixels = 0 ;
770
- if ( yRes < qAbs ( srcYRes ) )
686
+ if ( mExtent .yMinimum () < myRasterExtent.yMinimum () )
771
687
{
772
- center = myRasterExtent.yMaximum () - yRes / 2 ;
773
- centerRaster = -1 . * ( mExtent .yMaximum () - center ) / srcYRes;
774
- srcTop = static_cast <int >( floor ( centerRaster ) );
775
- topSpace = ( mExtent .yMaximum () + srcTop * srcYRes ) - myRasterExtent.yMaximum ();
776
- topSpace = topSpace > 0 ? topSpace : 0 ;
777
- center = myRasterExtent.yMinimum () + yRes / 2 ;
778
- centerRaster = -1 . * ( mExtent .yMaximum () - center ) / srcYRes;
779
-
780
- srcBottom = static_cast <int >( floor ( centerRaster ) );
781
- QgsDebugMsg ( QString ( " myRasterExtent.yMinimum() = %1 myRasterExtent.yMaximum() = %2" ).arg ( myRasterExtent.yMinimum () ).arg ( myRasterExtent.yMaximum () ) );
782
- bottomSpace = myRasterExtent.yMinimum () - ( mExtent .yMaximum () + ( srcBottom + 1 ) * srcYRes );
783
- bottomSpace = bottomSpace > 0 ? bottomSpace : 0 ;
784
- QgsDebugMsg ( QString ( " center = %1 centerRaster = %2 srcBottom = %3 bottomSpace = %4" ).arg ( center ).arg ( centerRaster ).arg ( srcBottom ).arg ( bottomSpace ) );
785
-
786
- double yAdd = topSpace + bottomSpace;
787
- yAddPixels = qRound ( yAdd / yRes );
788
- topAddPixels = qRound ( topSpace / yRes );
688
+ srcBottom = static_cast <int >( floor ( -1 . * ( mExtent .yMaximum () - myRasterExtent.yMinimum () ) / srcYRes ) );
789
689
}
790
690
791
- srcWidth = srcRight - srcLeft + 1 ;
792
- srcHeight = srcBottom - srcTop + 1 ;
793
-
794
691
QgsDebugMsg ( QString ( " srcTop = %1 srcBottom = %2 srcLeft = %3 srcRight = %4" ).arg ( srcTop ).arg ( srcBottom ).arg ( srcLeft ).arg ( srcRight ) );
795
692
796
- QgsDebugMsg ( QString ( " topSpace = %1 bottomSpace = %2 leftSpace = %3 rightSpace = %4 " ). arg ( topSpace ). arg ( bottomSpace ). arg ( leftSpace ). arg ( rightSpace ) ) ;
797
-
693
+ int srcWidth = srcRight - srcLeft + 1 ;
694
+ int srcHeight = srcBottom - srcTop + 1 ;
798
695
799
696
QgsDebugMsg ( QString ( " width = %1 height = %2 srcWidth = %3 srcHeight = %4" ).arg ( width ).arg ( height ).arg ( srcWidth ).arg ( srcHeight ) );
800
697
698
+ int tmpWidth = srcWidth;
699
+ int tmpHeight = srcHeight;
801
700
802
- QgsDebugMsg ( QString ( " xAddPixels = %1 yAddPixels = %2 leftAddPixels = %3 topAddPixels = %4" ).arg ( xAddPixels ).arg ( yAddPixels ).arg ( leftAddPixels ).arg ( topAddPixels ) );
803
-
804
- int totalWidth = width + xAddPixels;
805
- int totalHeight = height + yAddPixels;
806
-
807
- QgsDebugMsg ( QString ( " totalWidth = %1 totalHeight = %2" ).arg ( totalWidth ).arg ( totalHeight ) );
701
+ if ( xRes > srcXRes ) tmpWidth = width;
702
+ if ( yRes > srcYRes ) tmpHeight = height;
808
703
704
+ // Allocate temporary block
705
+ char *tmpBlock = ( char * )malloc ( dataSize * tmpWidth * tmpHeight );
809
706
810
707
GDALRasterBandH gdalBand = GDALGetRasterBand ( mGdalDataset , theBandNo );
811
708
GDALDataType type = ( GDALDataType )mGdalDataType [theBandNo-1 ];
812
709
CPLErrorReset ();
710
+ CPLErr err = GDALRasterIO ( gdalBand, GF_Read,
711
+ srcLeft, srcTop, srcWidth, srcHeight,
712
+ ( void * )tmpBlock,
713
+ tmpWidth, tmpHeight, type,
714
+ 0 , 0 );
813
715
814
- if ( xAddPixels == 0 && yAddPixels == 0 )
716
+ if ( err != CPLE_None )
815
717
{
816
- // Calc beginnig of data if raster does not start at top
817
- block = ( char * ) theBlock;
818
- if ( top != 0 )
819
- {
820
- block += dataSize * thePixelWidth * top;
821
- }
822
-
823
- // Cal nLineSpace if raster does not cover whole extent
824
- int nLineSpace = dataSize * thePixelWidth;
825
- if ( left != 0 )
826
- {
827
- block += dataSize * left;
828
- }
829
- CPLErr err = GDALRasterIO ( gdalBand, GF_Read,
830
- srcLeft, srcTop, srcWidth, srcHeight,
831
- ( void * )block,
832
- width, height, type,
833
- 0 , nLineSpace );
718
+ QgsLogger::warning ( " RasterIO error: " + QString::fromUtf8 ( CPLGetLastErrorMsg () ) );
719
+ QgsDebugMsg ( " RasterIO error: " + QString::fromUtf8 ( CPLGetLastErrorMsg () ) );
720
+ free ( tmpBlock );
721
+ return ;
834
722
}
835
- else
836
- {
837
- // Allocate temporary block
838
- void *tmpBlock = malloc ( dataSize * totalWidth * totalHeight );
839
723
840
- CPLErrorReset ();
841
- CPLErr err = GDALRasterIO ( gdalBand, GF_Read,
842
- srcLeft, srcTop, srcWidth, srcHeight,
843
- ( void * )tmpBlock,
844
- totalWidth, totalHeight, type,
845
- 0 , 0 );
724
+ QgsDebugMsg ( QString ( " GDALRasterIO time (ms): %1" ).arg ( time .elapsed () ) );
725
+ time .start ();
846
726
847
- if ( err != CPLE_None )
848
- {
849
- QgsLogger::warning ( " RasterIO error: " + QString::fromUtf8 ( CPLGetLastErrorMsg () ) );
850
- QgsDebugMsg ( " RasterIO error: " + QString::fromUtf8 ( CPLGetLastErrorMsg () ) );
851
- free ( tmpBlock );
852
- return ;
853
- }
727
+ double tmpXMin = mExtent .xMinimum () + srcLeft * srcXRes;
728
+ double tmpYMax = mExtent .yMaximum () + srcTop * srcYRes;
729
+ double tmpXRes = srcWidth * srcXRes / tmpWidth;
730
+ double tmpYRes = srcHeight * srcYRes / tmpHeight; // negative
731
+
732
+ QgsDebugMsg ( QString ( " tmpXMin = %1 tmpYMax = %2 tmpWidth = %3 tmpHeight = %4" ).arg ( tmpXMin ).arg ( tmpYMax ).arg ( tmpWidth ).arg ( tmpHeight ) );
854
733
855
- for ( int i = 0 ; i < height; i++ )
734
+ for ( int row = 0 ; row < height; row++ )
735
+ {
736
+ double y = myRasterExtent.yMaximum () - ( row + 0.5 ) * yRes;
737
+ int tmpRow = static_cast <int >( floor ( -1 . * ( tmpYMax - y ) / tmpYRes ) );
738
+
739
+ char *srcRowBlock = tmpBlock + dataSize * tmpRow * tmpWidth;
740
+ char *dstRowBlock = ( char * )theBlock + dataSize * ( top + row ) * thePixelWidth;
741
+ for ( int col = 0 ; col < width; col++ )
856
742
{
857
- int r = i + topAddPixels;
858
- char *src = ( char * )tmpBlock + dataSize * r * totalWidth + dataSize * leftAddPixels;
859
- char *dst = ( char * )theBlock + dataSize * ( top + i ) * thePixelWidth + dataSize * ( left );
860
- memcpy ( dst, src, dataSize*width );
743
+ // cell center
744
+ double x = myRasterExtent.xMinimum () + ( col + 0.5 ) * xRes;
745
+ // floor() is quite slow! Use just cast to int.
746
+ int tmpCol = static_cast <int >(( x - tmpXMin ) / tmpXRes ) ;
747
+ // QgsDebugMsg( QString( "row = %1 col = %2 tmpRow = %3 tmpCol = %4" ).arg(row).arg(col).arg(tmpRow).arg(tmpCol) );
748
+
749
+ char *src = srcRowBlock + dataSize * tmpCol;
750
+ char *dst = dstRowBlock + dataSize * ( left + col );
751
+ memcpy ( dst, src, dataSize );
861
752
}
862
-
863
- free ( tmpBlock );
864
753
}
754
+
755
+ free ( tmpBlock );
756
+ QgsDebugMsg ( QString ( " resample time (ms): %1" ).arg ( time .elapsed () ) );
757
+
865
758
return ;
866
759
}
867
760
0 commit comments