Skip to content

Commit a2020cb

Browse files
author
rblazek
committed
throw away all the tricks and do second resampling, it is quite fast
git-svn-id: http://svn.osgeo.org/qgis/trunk@15530 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent d8a2848 commit a2020cb

File tree

1 file changed

+70
-177
lines changed

1 file changed

+70
-177
lines changed

src/providers/gdal/qgsgdalprovider.cpp

+70-177
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
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

Comments
 (0)