3838#include < QFileInfo>
3939#include < QFile>
4040#include < QHash>
41+ #include < QTime>
4142
4243#include " gdalwarper.h"
4344#include " ogr_spatialref.h"
@@ -630,58 +631,26 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
630631 double srcYRes = mGeoTransform [5 ]; // may be negative?
631632 QgsDebugMsg ( QString ( " xRes = %1 yRes = %2 srcXRes = %3 srcYRes = %4" ).arg ( xRes ).arg ( yRes ).arg ( srcXRes ).arg ( srcYRes ) );
632633
633- double center, centerRaster;
634-
635634 // target size in pizels
636635 int width = right - left + 1 ;
637636 int height = bottom - top + 1 ;
638637
639- int srcWidth, srcHeight; // source size in pixels
640638
641639 int srcLeft = 0 ; // source raster x offset
642640 int srcTop = 0 ; // source raster x offset
643641 int srcBottom = ySize () - 1 ;
644642 int srcRight = xSize () - 1 ;
645643
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 ...
654651
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:
685654 // If the edge of the source is greater than the edge of destination:
686655 // src: | | | | | | | | |
687656 // dst: | | | |
@@ -695,173 +664,97 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
695664 // case, but the first column may be rendered as without values event if its center
696665 // is covered by src column. That could result in wrongly rendered (missing) edges
697666 // 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- //
702667
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 () )
704673 {
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 ) );
713675 }
714- if ( yRes > qAbs ( srcYRes ) )
676+ if ( mExtent . xMaximum () > myRasterExtent. xMaximum ( ) )
715677 {
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 ) );
724679 }
725680
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 () )
741683 {
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 ) );
766685 }
767-
768- int yAddPixels = 0 ;
769- int topAddPixels = 0 ;
770- if ( yRes < qAbs ( srcYRes ) )
686+ if ( mExtent .yMinimum () < myRasterExtent.yMinimum () )
771687 {
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 ) );
789689 }
790690
791- srcWidth = srcRight - srcLeft + 1 ;
792- srcHeight = srcBottom - srcTop + 1 ;
793-
794691 QgsDebugMsg ( QString ( " srcTop = %1 srcBottom = %2 srcLeft = %3 srcRight = %4" ).arg ( srcTop ).arg ( srcBottom ).arg ( srcLeft ).arg ( srcRight ) );
795692
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 ;
798695
799696 QgsDebugMsg ( QString ( " width = %1 height = %2 srcWidth = %3 srcHeight = %4" ).arg ( width ).arg ( height ).arg ( srcWidth ).arg ( srcHeight ) );
800697
698+ int tmpWidth = srcWidth;
699+ int tmpHeight = srcHeight;
801700
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;
808703
704+ // Allocate temporary block
705+ char *tmpBlock = ( char * )malloc ( dataSize * tmpWidth * tmpHeight );
809706
810707 GDALRasterBandH gdalBand = GDALGetRasterBand ( mGdalDataset , theBandNo );
811708 GDALDataType type = ( GDALDataType )mGdalDataType [theBandNo-1 ];
812709 CPLErrorReset ();
710+ CPLErr err = GDALRasterIO ( gdalBand, GF_Read,
711+ srcLeft, srcTop, srcWidth, srcHeight,
712+ ( void * )tmpBlock,
713+ tmpWidth, tmpHeight, type,
714+ 0 , 0 );
813715
814- if ( xAddPixels == 0 && yAddPixels == 0 )
716+ if ( err != CPLE_None )
815717 {
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 ;
834722 }
835- else
836- {
837- // Allocate temporary block
838- void *tmpBlock = malloc ( dataSize * totalWidth * totalHeight );
839723
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 ();
846726
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 ) );
854733
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++ )
856742 {
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 );
861752 }
862-
863- free ( tmpBlock );
864753 }
754+
755+ free ( tmpBlock );
756+ QgsDebugMsg ( QString ( " resample time (ms): %1" ).arg ( time.elapsed () ) );
757+
865758 return ;
866759}
867760
0 commit comments