@@ -348,13 +348,13 @@ bool QgsGdalProvider::crsFromWkt( const char *wkt )
348
348
{
349
349
void *hCRS = OSRNewSpatialReference ( NULL );
350
350
351
- if ( OSRImportFromWkt ( hCRS, (char **) &wkt ) == OGRERR_NONE )
351
+ if ( OSRImportFromWkt ( hCRS, ( char ** ) &wkt ) == OGRERR_NONE )
352
352
{
353
353
if ( OSRAutoIdentifyEPSG ( hCRS ) == OGRERR_NONE )
354
354
{
355
355
QString authid = QString ( " %1:%2" )
356
- .arg ( OSRGetAuthorityName ( hCRS, NULL ) )
357
- .arg ( OSRGetAuthorityCode ( hCRS, NULL ) );
356
+ .arg ( OSRGetAuthorityName ( hCRS, NULL ) )
357
+ .arg ( OSRGetAuthorityCode ( hCRS, NULL ) );
358
358
QgsDebugMsg ( " authid recognized as " + authid );
359
359
mCrs .createFromOgcWmsCrs ( authid );
360
360
}
@@ -570,14 +570,25 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
570
570
QgsDebugMsg ( QString ( " transform : %1" ).arg ( mGeoTransform [i] ) );
571
571
}
572
572
573
- // TODO: fill block with no data values
573
+ int dataSize = dataTypeSize ( theBandNo ) / 8 ;
574
+
575
+ // fill with null values
576
+ QByteArray ba = noValueBytes ( theBandNo );
577
+ char *nodata = ba.data ();
578
+ char *block = ( char * ) theBlock;
579
+ for ( int i = 0 ; i < thePixelWidth * thePixelHeight; i++ )
580
+ {
581
+ memcpy ( block, nodata, dataSize );
582
+ block += dataSize;
583
+ }
574
584
575
585
QgsRectangle myRasterExtent = theExtent.intersect ( &mExtent );
576
586
if ( myRasterExtent.isEmpty () )
577
587
{
578
588
QgsDebugMsg ( " draw request outside view extent." );
579
589
return ;
580
590
}
591
+ QgsDebugMsg ( " mExtent: " + mExtent .toString () );
581
592
QgsDebugMsg ( " myRasterExtent: " + myRasterExtent.toString () );
582
593
583
594
double xRes = theExtent.width () / thePixelWidth;
@@ -614,167 +625,243 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
614
625
615
626
// Calculate rows/cols limits in raster grid space
616
627
617
- // IMHO, we cannot align target grid to raster grid using target grid edges
618
- // and finding the nearest raster grid because it could lead to cell center
619
- // getting outside the right cell when doing resampling, example
620
- // Raster width is 30m and it has 3 columns and we want to read xrange 5.1-30
621
- // to 3 columns, the nearest edge for beginning in raster grid is 10.0
622
- // reading cols 1-2, we get raster[1] value in target[0], but the center of
623
- // target[0] is 5.1 + ((30-5.1)/3)/2 = 9.25 so it falls to raster[0]. Is it right?
624
- // => We are looking for such alignment with which the center of first/last cell
625
- // alls to the right raster cell
628
+ // Set readable names
629
+ double srcXRes = mGeoTransform [1 ];
630
+ double srcYRes = mGeoTransform [5 ]; // may be negative?
631
+ QgsDebugMsg ( QString ( " xRes = %1 yRes = %2 srcXRes = %3 srcYRes = %4" ).arg ( xRes ).arg ( yRes ).arg ( srcXRes ).arg ( srcYRes ) );
626
632
627
633
double center, centerRaster;
628
- int rasterTop, rasterBottom, rasterLeft, rasterRight;
634
+
635
+ // target size in pizels
636
+ int width = right - left + 1 ;
637
+ int height = bottom - top + 1 ;
638
+
639
+ int srcWidth, srcHeight; // source size in pixels
640
+
641
+ int srcLeft = 0 ; // source raster x offset
642
+ int srcTop = 0 ; // source raster x offset
643
+ int srcBottom = ySize () - 1 ;
644
+ int srcRight = xSize () - 1 ;
629
645
630
646
// We have to extend destination grid for GDALRasterIO to keep resolution:
631
- double topSpace, bottomSpace, leftSpace, rightSpace;
632
-
633
- // top
634
- center = myRasterExtent.yMaximum () - yRes / 2 ;
635
- // center in raster space
636
- // GDAL states that mGeoTransform[3] is top, but probably it can be also bottom
637
- // if mGeoTransform[5] is negative ??? No, mGeoTransform[5] is negative normaly
638
- // - vice versa?
639
- // if ( mGeoTransform[5] > 0 )
640
- if ( mGeoTransform [5 ] < 0 )
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
654
+
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 )
641
659
{
642
- centerRaster = -1 . * ( mGeoTransform [3 ] - center ) / mGeoTransform [5 ];
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
+ }
643
668
}
644
- else
669
+
670
+ if ( yRes == qAbs ( srcYRes ) )
645
671
{
646
- centerRaster = ( center - mGeoTransform [3 ] ) / mGeoTransform [5 ];
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
+ }
647
682
}
648
- rasterTop = static_cast <int >( floor ( centerRaster ) );
649
- topSpace = ( mGeoTransform [3 ] + rasterTop * mGeoTransform [5 ] ) - myRasterExtent.yMaximum ();
650
683
651
- // bottom
652
- center = myRasterExtent.yMinimum () + yRes / 2 ;
653
- // if ( mGeoTransform[5] > 0 )
654
- if ( mGeoTransform [5 ] < 0 )
684
+ // *** CASE 2: xRes > srcXRes, yRes > srcYRes
685
+ // If the edge of the source is greater than the edge of destination:
686
+ // src: | | | | | | | | |
687
+ // dst: | | | |
688
+ // We have 2 options for resampling:
689
+ // a) 'Stretch' the src and align the start edge of src to the start edge of dst.
690
+ // That means however, that to the target cells may be assigned values of source
691
+ // which are not nearest to the center of dst cells. Usualy probably not a problem
692
+ // but we are not precise. The shift is in maximum ... TODO
693
+ // b) We could cut the first destination column and left only the second one which is
694
+ // completely covered by src. No (significant) stretching is applied in that
695
+ // case, but the first column may be rendered as without values event if its center
696
+ // is covered by src column. That could result in wrongly rendered (missing) edges
697
+ // 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
+
703
+ if ( xRes > srcXRes )
655
704
{
656
- centerRaster = -1 . * ( mGeoTransform [3 ] - center ) / mGeoTransform [5 ];
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
+ }
657
713
}
658
- else
714
+ if ( yRes > qAbs ( srcYRes ) )
659
715
{
660
- centerRaster = ( center - mGeoTransform [3 ] ) / mGeoTransform [5 ];
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
+ }
661
724
}
662
- rasterBottom = static_cast <int >( floor ( centerRaster ) );
663
- bottomSpace = myRasterExtent.yMinimum () - ( mGeoTransform [3 ] + ( rasterBottom + 1 ) * mGeoTransform [5 ] );
664
725
665
- // left
666
- center = myRasterExtent.xMinimum () + xRes / 2 ;
667
- centerRaster = ( center - mGeoTransform [0 ] ) / mGeoTransform [1 ];
668
- rasterLeft = static_cast <int >( floor ( centerRaster ) );
669
- leftSpace = myRasterExtent.xMinimum () - ( mGeoTransform [0 ] + rasterLeft * mGeoTransform [1 ] );
670
-
671
- // right
672
- center = myRasterExtent.xMaximum () - xRes / 2 ;
673
- centerRaster = ( center - mGeoTransform [0 ] ) / mGeoTransform [1 ];
674
- rasterRight = static_cast <int >( floor ( centerRaster ) );
675
- rightSpace = ( mGeoTransform [0 ] + ( rasterRight + 1 ) * mGeoTransform [1 ] ) - myRasterExtent.xMaximum ();
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 )
741
+ {
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 );
766
+ }
676
767
677
- QgsDebugMsg ( QString ( " rasterTop = %1 rasterBottom = %2 rasterLeft = %3 rasterRight = %4" ).arg ( rasterTop ).arg ( rasterBottom ).arg ( rasterLeft ).arg ( rasterRight ) );
768
+ int yAddPixels = 0 ;
769
+ int topAddPixels = 0 ;
770
+ if ( yRes < qAbs ( srcYRes ) )
771
+ {
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 );
789
+ }
678
790
679
- QgsDebugMsg ( QString ( " topSpace = %1 bottomSpace = %2 leftSpace = %3 rightSpace = %4" ).arg ( topSpace ).arg ( bottomSpace ).arg ( leftSpace ).arg ( rightSpace ) );
791
+ srcWidth = srcRight - srcLeft + 1 ;
792
+ srcHeight = srcBottom - srcTop + 1 ;
680
793
681
- int width = right - left + 1 ;
682
- int height = bottom - top + 1 ;
794
+ QgsDebugMsg ( QString ( " srcTop = %1 srcBottom = %2 srcLeft = %3 srcRight = %4" ).arg ( srcTop ).arg ( srcBottom ).arg ( srcLeft ).arg ( srcRight ) );
683
795
684
- int rasterWidth = rasterRight - rasterLeft + 1 ;
685
- int rasterHeight = rasterBottom - rasterTop + 1 ;
796
+ QgsDebugMsg ( QString ( " topSpace = %1 bottomSpace = %2 leftSpace = %3 rightSpace = %4" ).arg ( topSpace ).arg ( bottomSpace ).arg ( leftSpace ).arg ( rightSpace ) );
686
797
687
- QgsDebugMsg ( QString ( " width = %1 height = %2 rasterWidth = %3 rasterHeight = %4" ).arg ( width ).arg ( height ).arg ( rasterWidth ).arg ( rasterHeight ) );
688
798
689
- // TODO: what is better floor/ceil, can be negative?
690
- // should be similar
691
- // double xAdd = rasterWidth*rasterXRes - width*xRes;
692
- double xAdd = leftSpace + rightSpace;
693
- int xAddPixels = qRound ( xAdd / xRes );
694
- int leftAddPixels = qRound ( leftSpace / xRes );
799
+ QgsDebugMsg ( QString ( " width = %1 height = %2 srcWidth = %3 srcHeight = %4" ).arg ( width ).arg ( height ).arg ( srcWidth ).arg ( srcHeight ) );
695
800
696
- // double leftAdd = rasterWidth*rasterXRes - width*xRes;
697
- double yAdd = topSpace + bottomSpace;
698
- int yAddPixels = qRound ( yAdd / yRes );
699
- int topAddPixels = qRound ( topSpace / yRes );
700
801
701
802
QgsDebugMsg ( QString ( " xAddPixels = %1 yAddPixels = %2 leftAddPixels = %3 topAddPixels = %4" ).arg ( xAddPixels ).arg ( yAddPixels ).arg ( leftAddPixels ).arg ( topAddPixels ) );
702
- // Currently only positive allowed, verify if negative has sense and check following use
703
- xAddPixels = xAddPixels > 0 ? xAddPixels : 0 ;
704
- yAddPixels = yAddPixels > 0 ? yAddPixels : 0 ;
705
- leftAddPixels = leftAddPixels > 0 ? leftAddPixels : 0 ;
706
- topAddPixels = topAddPixels > 0 ? topAddPixels : 0 ;
707
803
708
804
int totalWidth = width + xAddPixels;
709
805
int totalHeight = height + yAddPixels;
710
806
711
807
QgsDebugMsg ( QString ( " totalWidth = %1 totalHeight = %2" ).arg ( totalWidth ).arg ( totalHeight ) );
712
808
713
- int size = dataTypeSize ( theBandNo ) / 8 ;
714
-
715
- // fill with null values
716
- QByteArray ba = noValueBytes ( theBandNo );
717
- char *nodata = ba.data ();
718
- char *block = ( char * ) theBlock;
719
- for ( int i = 0 ; i < thePixelWidth * thePixelHeight; i++ )
720
- {
721
- memcpy ( block, nodata, size );
722
- block += size;
723
- }
724
809
725
810
GDALRasterBandH gdalBand = GDALGetRasterBand ( mGdalDataset , theBandNo );
726
811
GDALDataType type = ( GDALDataType )mGdalDataType [theBandNo-1 ];
727
812
CPLErrorReset ();
728
813
729
- // This can be probably used if xAddPixels and yAddPixels are 0 to avoid memcpy
730
- #if 0
731
- // Calc beginnig of data if raster does not start at top
732
- block = ( char * ) theBlock;
733
- if ( top != 0 )
814
+ if ( xAddPixels == 0 && yAddPixels == 0 )
734
815
{
735
- block += size * thePixelWidth * top;
736
- }
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
+ }
737
822
738
- // Cal nLineSpace if raster does not cover whole extent
739
- int nLineSpace = size * thePixelWidth;
740
- if ( left != 0 )
741
- {
742
- block += size * left;
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 );
743
834
}
744
- CPLErr err = GDALRasterIO( gdalBand, GF_Read,
745
- rasterLeft, rasterTop, rasterWidth, rasterHeight,
746
- ( void * )block,
747
- width, height, type,
748
- 0, nLineSpace );
749
- #endif
835
+ else
836
+ {
837
+ // Allocate temporary block
838
+ void *tmpBlock = malloc ( dataSize * totalWidth * totalHeight );
750
839
751
- // Allocate temporary block
752
- void *tmpBlock = malloc ( size * totalWidth * totalHeight );
840
+ CPLErrorReset ();
841
+ CPLErr err = GDALRasterIO ( gdalBand, GF_Read,
842
+ srcLeft, srcTop, srcWidth, srcHeight,
843
+ ( void * )tmpBlock,
844
+ totalWidth, totalHeight, type,
845
+ 0 , 0 );
753
846
754
- CPLErrorReset ();
755
- CPLErr err = GDALRasterIO ( gdalBand, GF_Read,
756
- rasterLeft, rasterTop, rasterWidth, rasterHeight,
757
- ( void * )tmpBlock,
758
- totalWidth, totalHeight, type,
759
- 0 , 0 );
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
+ }
760
854
761
- if ( err != CPLE_None )
762
- {
763
- QgsLogger::warning ( " RasterIO error: " + QString::fromUtf8 ( CPLGetLastErrorMsg () ) ) ;
764
- QgsDebugMsg ( " RasterIO error: " + QString::fromUtf8 ( CPLGetLastErrorMsg () ) ) ;
765
- free ( tmpBlock );
766
- return ;
767
- }
855
+ for ( int i = 0 ; i < height; i++ )
856
+ {
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 ) ;
861
+ }
768
862
769
- for ( int i = 0 ; i < height; i++ )
770
- {
771
- int r = i + topAddPixels;
772
- char *src = ( char * )tmpBlock + size * r * totalWidth + size * leftAddPixels;
773
- char *dst = ( char * )theBlock + size * ( top + i ) * thePixelWidth + size * ( left );
774
- memcpy ( dst, src, size*width );
863
+ free ( tmpBlock );
775
864
}
776
-
777
- free ( tmpBlock );
778
865
return ;
779
866
}
780
867
0 commit comments