qt&gdal

转自:http://blog.csdn.net/deirjie/article/details/37872743

使用需要自行配置Qt和GDAL路径。

    近期写了一个高光谱图像光谱曲线匹配的算法,想封装到软件当中方便观察效果,也便于做后期算法改进和实际应用,并且以后的算法可以直接集成上来。于是打算自己写一个基本的框架实现图像浏览的一些基本功能。在网上各种找,利用GDAL进行遥感图像显示的代码很多,但不是有问题就是写的不太清楚,不够简洁,并且大多基于MFC。经过几天的奋战,成功实现了利用Qt框架进行遥感图像显示的基本功能,于是分享出来,对自己是总结,并且希望对别人有所帮助。

开发环境:VS2010 Qt4.8.4GDAL 1.10

       说明:这里并没有QtGDAL,以及C++的一些基本的东西,这里只关注功能的实现做说明。如果对于QtGDAL有问题的,请参见其他资料。并不高深,高手绕道!

   重要说明:Qt发音叫做“Q特”,不是“Q替”,不要再读错了,官网有专门说明的!不要搞半天还说我发音有问题!!!  还有要写成Qt,不是QT,QT是QuickTime,不要混淆了。

         OK,先说说程序的功能:

  • 能够打开图像,并读取图像元数据信息;
  • 能够实现图像的正确显示,包括灰度图和RGB真彩色图的显示;
  • 支持图像的缩放,漫游;

计算机生成了可选文字: 困ImgTest口电口}~!画冲口州openViewoperationWindowHelplmageCloseZoomoutZoomInFitWindowNorm以Size人bout日X日XENVIBand3Band41DescriptionMetaInforDataTypeX引zeySizeBandCountprojectionoriginpixelSizeENVI.hdrLab…Ulnt1653505376pROJCS[.UTM…660221,3.696二8.04627·8.0L二…___

 

计算机生成了可选文字: 困ImgTest}~4县卜口叫openViewoperationWindowHelplmageCloseZoomoutZoomInFitWindowNorm以Size人bout日X1DescriptionMetaInforDataTypeX引zeySizeBandCountprojectionoriginpixelSizeENVIBand3Band4ENVI.hdrLab…Ulnt1653505376pROJCS[.UTM…660221,3.696二8.04627·8.0L二….

    功能非常简单基础,但是这当中很多细节是需要认真参透的。下面开始逐一进行讲解。(PSUI的设计就不讲了。)


        1.程序简单设计

    其实这个程序的设计相对简单,除了显示图像的控件需要自定义,其他的都可以直接在Qt Designer里面拖放就行了。那就来说说图像显示控件吧。先看头文件:

  1. //***********************************************************************  
  2. //Assembly         : ImgTest  
  3. //Author           : Jacory  
  4. //Created          : 07-10-2014  
  5. //  
  6. // LastModified By : Jacory  
  7. // LastModified On : 07-10-2014  
  8. //***********************************************************************  
  9. //<copyright file="MapCanvas.h" company="">  
  10. //     Copyright (c) . All rights reserved.  
  11. //</copyright>  
  12. //<summary>图像显示窗口类,负责图像的读取和显示等功能。</summary>  
  13. //***********************************************************************  
  14. #ifndefMAPCANVAS_H  
  15. #defineMAPCANVAS_H  
  16.    
  17. #include<QtGui/QGraphicsView>  
  18. #include<QStandardItemModel>  
  19. #include<gdal_priv.h>  
  20.    
  21.    
  22. ///<summary>  
  23. /// ClassMapCanvas.  
  24. ///</summary>  
  25. classMapCanvas : public QGraphicsView  
  26. {  
  27.     Q_OBJECT  
  28.      
  29. public:  
  30.     MapCanvas( QWidget *parent = 0 );  
  31.     ~MapCanvas();  
  32.     void ReadImg( const QString imgPath );  
  33.     void CloseCurrentImg();  
  34.     /// <summary>  
  35.     /// 返回图像元数据信息模型.  
  36.     /// </summary>  
  37.     ///<returns>图像元数据信息模型.</returns>  
  38.     QStandardItemModel* ImgMetaModel()  
  39.     {  
  40.         return imgMetaModel;  
  41.     };  
  42.     /// <summary>  
  43.     /// 设置图像元数据信息模型  
  44.     /// </summary>  
  45.     /// <paramname="model">图像元数据信息模型.</param>  
  46.     void SetMetaModel( QStandardItemModel*model )  
  47.     {  
  48.         this->imgMetaModel = model;  
  49.     };  
  50.     /// <summary>  
  51.     /// 返回文件列表数据模型  
  52.     /// </summary>  
  53.     ///<returns>文件列表数据模型.</returns>  
  54.     QStandardItemModel* FileListModel()  
  55.     {  
  56.         return fileListModel;  
  57.     };  
  58.     /// <summary>  
  59.     /// 设置fileListModel图像文件列表数据模型  
  60.     /// </summary>  
  61.     /// <paramname="model">文件列表数据模型.</param>  
  62.     void SetFileListModel( QStandardItemModel*model )  
  63.     {  
  64.         this->fileListModel = model;  
  65.     };  
  66.      
  67.     QSize sizeHint() const;  
  68.      
  69. publicslots:  
  70.     /// <summary>  
  71.     /// 放大图像  
  72.     /// </summary>  
  73.     void ZoomIn()  
  74.     {  
  75.         ScaleImg( 1.2 );  
  76.     };  
  77.     /// <summary>  
  78.     /// 缩小图像  
  79.     /// </summary>  
  80.     void ZoomOut()  
  81.     {  
  82.         ScaleImg( 0.8 );  
  83.     };  
  84.      
  85. protected:  
  86.     void wheelEvent( QWheelEvent *event );  
  87.     void mousePressEvent( QMouseEvent *event );  
  88.     void mouseMoveEvent( QMouseEvent *event );  
  89.     void mouseReleaseEvent( QMouseEvent *event);  
  90.      
  91. private:  
  92.     void ShowBand( GDALRasterBand* band );  
  93.     void ShowImg( QList<GDALRasterBand*>*imgBand );  
  94.     void ShowImgInfor( const QString filename);  
  95.     void ShowFileList( const QString filename);  
  96.     unsigned char* ImgSketch( float* buffer ,GDALRasterBand* currentBand, int size, double noValue );  
  97.     /// <summary>  
  98.     /// 图像缩放  
  99.     /// </summary>  
  100.     /// <paramname="factor">缩放因子</param>  
  101.     void ScaleImg( double factor )  
  102.     {  
  103.         m_scaleFactor *= factor;  
  104.         QMatrix matrix;  
  105.         matrix.scale( m_scaleFactor,m_scaleFactor );  
  106.         this->setMatrix( matrix );  
  107.     };  
  108.      
  109.     /// <summary>  
  110.     /// 图像元数据模型  
  111.     /// </summary>  
  112.     QStandardItemModel *imgMetaModel;  
  113.     /// <summary>  
  114.     /// 图像数据集  
  115.     /// </summary>  
  116.     GDALDataset *poDataset;  
  117.      
  118.     /// <summary>  
  119.     /// 文件列表数据模型  
  120.     /// </summary>  
  121.     QStandardItemModel *fileListModel;  
  122.      
  123.     /// <summary>  
  124.     /// 缩放系数  
  125.     /// </summary>  
  126.     float m_scaleFactor;  
  127.      
  128.     /// <summary>  
  129.     /// 判断是否显示RGB彩色图像  
  130.     /// </summary>  
  131.     bool m_showColor;  
  132.      
  133.     /// <summary>  
  134.     /// 上一个鼠标事件触发时鼠标的位置  
  135.     /// </summary>  
  136.     QPoint lastEventCursorPos;  
  137. };  
  138.    
  139. #endif //MAPCANVAS_H  

        mapView类集成自QGraphicsView类。代码里面对于成员变量和inline函数我已经写了非常详细的注释了,这里再说明一下几个函数的功能。void ReadImg( const QStringimgPath )是读图函数,通过UI来调用,它并不直接读取图像,而是将相应的参数传递给ShowBandShowImgShowImgInforShowFileList来分别实现各自相应的功能。void ShowBand( GDALRasterBand*band )函数是显示单个波段图像,当文件中的波段数不为3个时,没法组成RGB显示,默认就显示单个波段图像,事实上它只是把单个波段加入3次到QList<GDALRasterBand*>,然后把这个波段列表传递给ShowImg进行显示。而void ShowImg( QList<GDALRasterBand*>*imgBand )是显示图像的核心函数,它的内部通过判断m_showColor的真假来决定将要显示的图像的组织方式,并通过unsigned char* ImgSketch(float* buffer , GDALRasterBand* currentBand, int size, double noValue )函数对图像波段进行拉伸,详细说明放在后面,这里只要知道是什么用就行了。void ShowImgInfor( constQString filename )  void ShowFileList( const QString filename )这两个函数,分别是为了读取并显示图像文件的元数据信息和文件结构树,分别位于主界面的两侧。void CloseCurrentImg()这个函数很简单,就是关闭当前图像。void wheelEvent( QWheelEvent*event )void mousePressEvent(QMouseEvent *event )void mouseMoveEvent(QMouseEvent *event )void mouseReleaseEvent(QMouseEvent *event )4个函数都是重写的鼠标事件,具体后面再讨论。

看完了头文件,已经基本知道这个MapView是一个什么样的对象了,事实上所有与图像显示有关的操作都属于这个类的职责,自然功能函数也就应该写在这个类里。下面我们根据需求来逐步实现这些功能函数。

 

      2.打开图像并读取图像元数据信息;

   这一步先不显示图像,只要求能够正确打开图像并读取出图像的基本信息即可。主要的函数就是ShowImgInforShowFileList这两个函数,来看代码:

  1. ///<summary>  
  2. ///显示图像基本信息  
  3. ///</summary>  
  4. ///<param name="filename">文件名</param>  
  5. voidMapCanvas::ShowImgInfor( const QString filename )  
  6. {  
  7.     if ( filename == "" || poDataset== NULL )  
  8.     {  
  9.         return;  
  10.     }  
  11.     int row = 0; // 用来记录数据模型的行号  
  12.      
  13.     // 图像的格式信息  
  14.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Description" ) ) );  
  15.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetDescription() ) );  
  16.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Meta Infor" ) ) );  
  17.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME) ) ) ;  
  18.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Data Type" ) ) );  
  19.     imgMetaModel->setItem( row++, 1, newQStandardItem( GDALGetDataTypeName( ( poDataset->GetRasterBand( 1)->GetRasterDataType() ) ) ) );  
  20.      
  21.     // 图像的大小和波段个数  
  22.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "X Size" ) ) );  
  23.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterXSize() ) ) );  
  24.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Y Size" ) ) );  
  25.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterYSize() ) ) );  
  26.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Band Count" ) ) );  
  27.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterCount() ) ) );  
  28.      
  29.     // 图像的投影信息  
  30.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Projection" ) ) );  
  31.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetProjectionRef() ) );  
  32.      
  33.     // 图像的坐标和分辨率信息  
  34.     double adfGeoTransform[6];  
  35.     QString origin = "";  
  36.     QString pixelSize = "";  
  37.     if( poDataset->GetGeoTransform(adfGeoTransform ) == CE_None )  
  38.     {  
  39.         origin = QString::number(adfGeoTransform[0] ) + ", " + QString::number( adfGeoTransform[3] );  
  40.         pixelSize = QString::number(adfGeoTransform[1] ) + ", " + QString::number( adfGeoTransform[5] );  
  41.     }  
  42.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Origin" ) ) );  
  43.     imgMetaModel->setItem( row++, 1, newQStandardItem( origin ) );  
  44.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Pixel Size" ) ) );  
  45.     imgMetaModel->setItem( row++, 1, newQStandardItem( pixelSize ) );  
  46. }  
  47.    
  48. ///<summary>  
  49. ///显示文件结构树  
  50. ///</summary>  
  51. ///<param name="filename">文件名</param>  
  52. voidMapCanvas::ShowFileList( const QString filename )  
  53. {  
  54.     if ( filename == "" || poDataset== NULL )  
  55.     {  
  56.         return;  
  57.     }  
  58.     QFileInfo fileInfo( filename );  
  59.     QStandardItem *rootItem = newQStandardItem( fileInfo.fileName() );  
  60.     for ( int i = 0; i <poDataset->GetRasterCount(); i++ )  
  61.     {  
  62.         QStandardItem *childItem = newQStandardItem( tr( "Band %1" ).arg( i + 1 ) );  
  63.         rootItem->setChild( i, childItem );  
  64.     }  
  65.     fileListModel->setItem( 0, rootItem );  
  66. }  

其实很简单,就是用了QtModel-View模型,显示的控件分别是QTableViewQTreeView,数据模型分别是这里的imgMetaModelfileListModel,只用将模型适当的初始化并绑定到显示控件上就行了。以上初始化比较简单,这里就不啰嗦了,都是一些基本的GDAL数据模型和Qt的东西。

 

        3.实现图像的显示功能

    这一步就是核心了,我打算通过函数调用的顺序来逐步讲解这里牵涉到的功能函数。首先是UI选择文件后,将文件路径传递给ReadImg函数进行文件读取,那我们先来看看ReadImg函数的实现。

  1. ///<summary>  
  2. /// 读取图像文件  
  3. ///</summary>  
  4. ///<param name="imgPath">图像文件</param>  
  5. voidMapCanvas::ReadImg( const QString imgPath )  
  6. {  
  7.     GDALAllRegister();  
  8.     CPLSetConfigOption("GDAL_FILENAME_IS_UTF8""NO" );  
  9.     poDataset = ( GDALDataset* )GDALOpen(imgPath.toStdString().c_str(), GA_ReadOnly );  
  10.     if ( poDataset == NULL )  
  11.     {  
  12.         QMessageBox::critical( this, tr("Error!" ), tr( "Can not open file %1" ).arg( imgPath ) );  
  13.         return;  
  14.     }  
  15.     ShowFileList( imgPath );  
  16.     ShowImgInfor( imgPath );  
  17.     // 如果图像文件并非三个波段,则默认只显示第一波段灰度图像  
  18.     if ( poDataset->GetRasterCount() != 3 )  
  19.     {  
  20.         m_showColor = false;  
  21.         ShowBand( poDataset->GetRasterBand(1 ) );  
  22.     }  
  23.     // 如果图像正好三个波段,则默认以RGB的顺序显示彩色图  
  24.     else  
  25.     {  
  26.         m_showColor = true;  
  27.         QList<GDALRasterBand*> bandList;  
  28.         bandList.append(poDataset->GetRasterBand( 1 ) );  
  29.         bandList.append(poDataset->GetRasterBand( 2 ) );  
  30.         bandList.append(poDataset->GetRasterBand( 3 ) );  
  31.         ShowImg( &bandList );  
  32.     }  
  33.     GDALClose( poDataset );  
  34. }  

逻辑很清晰,调用ShowFileListShowImgInfor初始化两个model以供相应的显示View控件来显示。再判断如果图像文件波段数不为3,则默认显示第一波段灰度图像,否则显示彩色图。

再来看ShowBand函数:

  1. ///<summary>  
  2. ///显示单波段图像  
  3. ///</summary>  
  4. ///<param name="band">图像波段</param>  
  5. voidMapCanvas::ShowBand( GDALRasterBand* band )  
  6. {  
  7.     if ( band == NULL )  
  8.     {  
  9.         return;  
  10.     }  
  11.      
  12.     QList<GDALRasterBand*> myBand;  
  13.     myBand.append( band );  
  14.     myBand.append( band );  
  15.     myBand.append( band );  
  16.      
  17.     ShowImg( &myBand );  
  18.      
  19. }  

可以看到,它只是把单个波段用列表的方式存储了三次,传递给ShowImg来进行显示。那直接看ShowImg吧。

  1. ///<summary>  
  2. /// 显示图像  
  3. ///</summary>  
  4. ///<param name="imgBand">图像波段</param>  
  5. voidMapCanvas::ShowImg( QList<GDALRasterBand*> *imgBand )  
  6. {  
  7.     if ( imgBand->size() != 3 )  
  8.     {  
  9.         return;  
  10.     }  
  11.      
  12.     int imgWidth = imgBand->at( 0)->GetXSize();  
  13.     int imgHeight = imgBand->at( 0)->GetYSize();  
  14.      
  15.     m_scaleFactor = this->height() * 1.0 /imgHeight;  
  16.      
  17.     int iScaleWidth = ( int )( imgWidth *m_scaleFactor - 1 );  
  18.     int iScaleHeight = ( int )( imgHeight *m_scaleFactor - 1 );  
  19.      
  20.     GDALDataType dataType = imgBand->at( 0)->GetRasterDataType();  
  21.      
  22.     // 首先分别读取RGB三个波段  
  23.     float* rBand = new float[iScaleWidth *iScaleHeight];  
  24.     float* gBand = new float[iScaleWidth *iScaleHeight];  
  25.     float* bBand = new float[iScaleWidth *iScaleHeight];  
  26.      
  27.     unsigned char *rBandUC, *gBandUC, *bBandUC;  
  28.      
  29.     // 根据是否显示彩色图像,判断RGB三个波段的组成方式,并分别读取  
  30.     if ( m_showColor == true )  
  31.     {  
  32.         imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
  33.         imgBand->at( 1 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, gBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
  34.         imgBand->at( 2 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, bBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
  35.          
  36.         // 分别拉伸每个波段并将Float转换为unsigned char  
  37.         rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );  
  38.         gBandUC = ImgSketch( gBand,imgBand->at( 1 ), iScaleWidth * iScaleHeight, imgBand->at( 1)->GetNoDataValue() );  
  39.         bBandUC = ImgSketch( bBand,imgBand->at( 2 ), iScaleWidth * iScaleHeight, imgBand->at( 2)->GetNoDataValue() );  
  40.     }  
  41.     else  
  42.     {  
  43.         imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
  44.          
  45.         rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );  
  46.         gBandUC = rBandUC;  
  47.         bBandUC = rBandUC;  
  48.     }  
  49.      
  50.     // 将三个波段组合起来  
  51.     int bytePerLine = ( iScaleWidth * 24 + 31 )/ 8;  
  52.     unsigned char* allBandUC = new unsignedchar[bytePerLine * iScaleHeight * 3];  
  53.     forint h = 0; h < iScaleHeight; h++ )  
  54.     {  
  55.         forint w = 0; w < iScaleWidth; w++)  
  56.         {  
  57.             allBandUC[h * bytePerLine + w * 3 +0] = rBandUC[h * iScaleWidth + w];  
  58.             allBandUC[h * bytePerLine + w * 3 +1] = gBandUC[h * iScaleWidth + w];  
  59.             allBandUC[h * bytePerLine + w * 3 +2] = bBandUC[h * iScaleWidth + w];  
  60.         }  
  61.     }  
  62.      
  63.     // 构造图像并显示  
  64.     QGraphicsPixmapItem *imgItem = newQGraphicsPixmapItem(  QPixmap::fromImage(QImage( allBandUC, iScaleWidth, iScaleHeight, bytePerLine,QImage::Format_RGB888  ) ) );  
  65.     QGraphicsScene *myScene = newQGraphicsScene();  
  66.     myScene->addItem( imgItem );  
  67.     this->setScene( myScene );  
  68. }  

这个函数的核心在于构造QGraphicsPixmapItem,也就是这里的imgItem这个对象,之前的代码都是为了正确构造这个对象而做的工作。这里的imgWidthimgHeight分别是图像文件的宽和高,而iScaleWidthiScaleHeight是缩放后的宽和高,缩放的条件正是显示窗口的大小。读取图像时,采用分别读取RGB三个波段,最后再组合起来的方式,读取波段都是采用RasterIO函数,采用float的数组来进行读取避免一些格式问题带来的精度截断,读取之后,通过ImgSketch函数进行图像拉伸,把图像值拉伸到0~255灰度级。

  1. ///<summary>  
  2. /// 图像线性拉伸  
  3. ///</summary>  
  4. ///<param name="buffer">图像缓存</param>  
  5. ///<param name="currentBand">当前波段</param>  
  6. ///<param name="size">The size.</param>  
  7. ///<param name="noValue">图像中的异常值</param>  
  8. ///<returns>经过拉伸的8位图像缓存</returns>  
  9. unsignedchar* MapCanvas::ImgSketch( float* buffer , GDALRasterBand* currentBand, intbandSize, double noValue )  
  10. {  
  11.     unsigned char* resBuffer = new unsignedchar[bandSize];  
  12.     double max, min;  
  13.     double minmax[2];  
  14.    
  15.     currentBand->ComputeRasterMinMax( 1,minmax );  
  16.     min = minmax[0];  
  17.     max = minmax[1];  
  18.     if( min <= noValue && noValue<= max )  
  19.     {  
  20.         min = 0;  
  21.     }  
  22.     for ( int i = 0; i < bandSize; i++ )  
  23.     {  
  24.         if ( buffer[i] > max )  
  25.         {  
  26.             resBuffer[i] = 255;  
  27.         }  
  28.         else if ( buffer[i] <= max&& buffer[i] >= min )  
  29.         {  
  30.             resBuffer[i] =static_cast<uchar>( 255 - 255 * ( max - buffer[i] ) / ( max - min ) );  
  31.         }  
  32.         else  
  33.         {  
  34.             resBuffer[i] = 0;  
  35.         }  
  36.     }  
  37.      
  38.     return resBuffer;  
  39. }  

这里的转换核心为

resBuffer[i] = static_cast<uchar>( 255 - 255 * ( max -buffer[i] ) / ( max - min ) );

转换公式也可以变为((buffer[i]-min)/(max-min))*255,是一样的,当然还有其他的拉伸方法,可以自行使用。

拉伸之后组合波段,需要用到int bytePerLine = (iScaleWidth * 24 + 31 ) / 8;这句,因为要显示的图像为8位的,需要把图像值变换为unsigned char型,这个工作在imgSketch里面已经做了,但是由于要将3个波段组织正确,还需要字节对齐,也就是这里的bytePerLine,这个是byte型表示的这个图像的宽度,当然是包含了RGB三个波段。然后就是将每个波段的值一一对齐放入allBandUC这个数组中,最后根据这个组合后的数组构QImageQPixmap以及QGraphicsPixmapItem,最后把这个Item加入到scene里面用MapView显示就行了。需要注意的是,这个过程看似简单,但里面有很多细节的地方需要认真对待,仔细思考,不然很容易得不到正确的结果。

 

4.实现图像的漫游功能

这一部分我在网上看了很多代码,大部分人都是从底层原理开始又来写一遍,但其实对于漫游的操作其实Qt在后台已经支持的非常好了,我们的MapView继承自QGraphicsView,这个父类已经把很多操作封装到位了,我们只要合理组织就好了,没有必要再去写一遍。那就很简单了,来分别看看实现代码吧。

  1. ///<summary>  
  2. ///鼠标滚轮事件,实现图像缩放  
  3. ///</summary>  
  4. ///<param name="event">滚轮事件</param>  
  5. voidMapCanvas::wheelEvent( QWheelEvent *event )  
  6. {  
  7.     // 滚轮向上滑动,放大图像  
  8.     if ( event->delta() > 0 )  
  9.     {  
  10.         ZoomIn();  
  11.     }  
  12.     // 滚轮向下滑动,缩小图像  
  13.     if ( event->delta() < 0 )  
  14.     {  
  15.         ZoomOut();  
  16.     }  
  17. }  
  18.    
  19. ///<summary>  
  20. ///鼠标按键按下事件  
  21. ///</summary>  
  22. ///<param name="event">鼠标事件.</param>  
  23. voidMapCanvas::mousePressEvent( QMouseEvent *event )  
  24. {  
  25.     // 滚轮键按下,平移图像  
  26.     if ( event->button() == Qt::MidButton )  
  27.     {  
  28.         this->setDragMode(QGraphicsView::ScrollHandDrag );  
  29.         this->setInteractive( false );  
  30.         lastEventCursorPos = event->pos();  
  31.     }  
  32. }  
  33.    
  34. ///<summary>  
  35. /// 鼠标移动事件  
  36. ///</summary>  
  37. ///<param name="event">鼠标事件</param>  
  38. voidMapCanvas::mouseMoveEvent( QMouseEvent *event )  
  39. {  
  40.     if ( this->dragMode() ==QGraphicsView::ScrollHandDrag )  
  41.     {  
  42.         QPoint delta = ( event->pos() -lastEventCursorPos ) / 10;  
  43.        this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );  
  44.        this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );  
  45.         this->viewport()->setCursor(Qt::ClosedHandCursor );  
  46.     }  
  47.      
  48. }  
  49.    
  50. ///<summary>  
  51. ///鼠标按键释放事件  
  52. ///</summary>  
  53. ///<param name="event">鼠标事件</param>  
  54. voidMapCanvas::mouseReleaseEvent( QMouseEvent *event )  
  55. {  
  56.     if ( event->button() == Qt::MidButton )  
  57.     {  
  58.         this->setDragMode(QGraphicsView::NoDrag );  
  59.     }  
  60. }  

重写鼠标事件,这里需要说明的是,漫游的操作Qt采用鼠标左键按下并移动来实现视图平移,但是我这里强制定义为了鼠标中键按下,所以还比较麻烦相对来说,如果你只想用默认的漫游操作,直接把this->setDragMode(QGraphicsView::NoDrag );这句加入到你的构造函数中就行了,非常简单。事实上我这个平移函数有问题,我后面再解释。这里我们先关注放大缩小功能,这部分代码我是通过inline的方式添加到头文件中的。

  1. ///<summary>  
  2.     /// 放大图像  
  3.     /// </summary>  
  4.     void ZoomIn()  
  5.     {  
  6.         ScaleImg( 1.2 );  
  7.     };  
  8.     /// <summary>  
  9.     /// 缩小图像  
  10.     /// </summary>  
  11.     void ZoomOut()  
  12.     {  
  13.         ScaleImg( 0.8 );  
  14.     };  
  15.    
  16. ///<summary>  
  17.     /// 图像缩放  
  18.     /// </summary>  
  19.     /// <paramname="factor">缩放因子</param>  
  20.     void ScaleImg( double factor )  
  21.     {  
  22.         m_scaleFactor *= factor;  
  23.         QMatrix matrix;  
  24.         matrix.scale( m_scaleFactor,m_scaleFactor );  
  25.         this->setMatrix( matrix );  
  26.     };  

图像缩放也非常简单,让你的QGraphicsView去处理细节,不要自己又去从底层计算然后写一大堆代码,还不一定就比Qt默认的方式效率高。

做到这里基本就搞定了,我刚刚说的平移函数那里,实际上我在mouseMoveEvent里采用计算当前坐标点和事件发生的坐标点差值的方式,然后去设置水平和竖直滑动条的值来实现平移

  1. QPointdelta = ( event->pos() - lastEventCursorPos ) / 10;  
  2.        this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );  
  3.        this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );  

我这样写后,我的平移实际上并不是非常流畅,而是有点像浏览网页时按住鼠标中键往下浏览的效果,但实际上很多GIS和遥感软件的平移方式都不是这样的。这里应该是我对Qt还有些不熟悉,也希望大家知道的话告诉我一下,我认为Qt应该是支持的。

好了,差不多了,其他的功能我也还在完善,先写到这里吧,希望对大家有所帮助。最后还是把MapView的实现文件整体粘上来。VS2010工程文件我放到CSDN的资源里面大家可以下载。

  1. #include"MapCanvas.h"  
  2. #include<QtGui/QMessageBox>  
  3. #include<QtCore/QFileInfo>  
  4. #include<QtGui/QImage>  
  5. #include<QtGui/QPixmap>  
  6. #include<QtGui/QGraphicsPixmapItem>  
  7. #include<QtGui/QMatrix>  
  8. #include<QtGui/QWheelEvent>  
  9. #include<QtGui/QScrollBar>  
  10.    
  11. MapCanvas::MapCanvas(QWidget *parent /*= 0 */ )  
  12.     : QGraphicsView( parent )  
  13. {  
  14.     poDataset = NULL;  
  15.     m_scaleFactor = 1.0;  
  16.     m_showColor = true;  
  17.     imgMetaModel = new QStandardItemModel;  
  18.     imgMetaModel->setColumnCount( 2 );  
  19.     fileListModel = new QStandardItemModel;  
  20.     QSizePolicy policy( QSizePolicy::Preferred,QSizePolicy::Preferred );  
  21.     this->setSizePolicy( policy );  
  22. }  
  23.    
  24. ///<summary>  
  25. ///Finalizes an instance of the <see cref="MapCanvas" /> class.  
  26. ///</summary>  
  27. MapCanvas::~MapCanvas()  
  28. {  
  29.    
  30. }  
  31.    
  32. ///<summary>  
  33. /// 读取图像文件  
  34. ///</summary>  
  35. ///<param name="imgPath">图像文件</param>  
  36. voidMapCanvas::ReadImg( const QString imgPath )  
  37. {  
  38.     GDALAllRegister();  
  39.     CPLSetConfigOption("GDAL_FILENAME_IS_UTF8""NO" );  
  40.     poDataset = ( GDALDataset* )GDALOpen(imgPath.toStdString().c_str(), GA_ReadOnly );  
  41.     if ( poDataset == NULL )  
  42.     {  
  43.         QMessageBox::critical( this, tr("Error!" ), tr( "Can not open file %1" ).arg( imgPath ) );  
  44.         return;  
  45.     }  
  46.     ShowFileList( imgPath );  
  47.     ShowImgInfor( imgPath );  
  48.     // 如果图像文件并非三个波段,则默认只显示第一波段灰度图像  
  49.     if ( poDataset->GetRasterCount() != 3 )  
  50.     {  
  51.         m_showColor = false;  
  52.         ShowBand( poDataset->GetRasterBand(1 ) );  
  53.     }  
  54.     // 如果图像正好三个波段,则默认以RGB的顺序显示彩色图  
  55.     else  
  56.     {  
  57.         m_showColor = true;  
  58.         QList<GDALRasterBand*> bandList;  
  59.         bandList.append(poDataset->GetRasterBand( 1 ) );  
  60.         bandList.append(poDataset->GetRasterBand( 2 ) );  
  61.         bandList.append(poDataset->GetRasterBand( 3 ) );  
  62.         ShowImg( &bandList );  
  63.     }  
  64.     GDALClose( poDataset );  
  65. }  
  66.    
  67. ///<summary>  
  68. ///关闭当前图像文件  
  69. ///</summary>  
  70. voidMapCanvas::CloseCurrentImg()  
  71. {  
  72.     poDataset = NULL;  
  73.     imgMetaModel->clear();  
  74.     fileListModel->clear();  
  75. }  
  76.    
  77. ///<summary>  
  78. ///显示单波段图像  
  79. ///</summary>  
  80. ///<param name="band">图像波段</param>  
  81. voidMapCanvas::ShowBand( GDALRasterBand* band )  
  82. {  
  83.     if ( band == NULL )  
  84.     {  
  85.         return;  
  86.     }  
  87.      
  88.     QList<GDALRasterBand*> myBand;  
  89.     myBand.append( band );  
  90.     myBand.append( band );  
  91.     myBand.append( band );  
  92.      
  93.     ShowImg( &myBand );  
  94.      
  95. }  
  96.    
  97. ///<summary>  
  98. /// 显示图像  
  99. ///</summary>  
  100. ///<param name="imgBand">图像波段</param>  
  101. voidMapCanvas::ShowImg( QList<GDALRasterBand*> *imgBand )  
  102. {  
  103.     if ( imgBand->size() != 3 )  
  104.     {  
  105.         return;  
  106.     }  
  107.      
  108.     int imgWidth = imgBand->at( 0)->GetXSize();  
  109.     int imgHeight = imgBand->at( 0)->GetYSize();  
  110.      
  111.     m_scaleFactor = this->height() * 1.0 /imgHeight;  
  112.      
  113.     int iScaleWidth = ( int )( imgWidth *m_scaleFactor - 1 );  
  114.     int iScaleHeight = ( int )( imgHeight *m_scaleFactor - 1 );  
  115.      
  116.     GDALDataType dataType = imgBand->at( 0)->GetRasterDataType();  
  117.      
  118.     // 首先分别读取RGB三个波段  
  119.     float* rBand = new float[iScaleWidth *iScaleHeight];  
  120.     float* gBand = new float[iScaleWidth *iScaleHeight];  
  121.     float* bBand = new float[iScaleWidth *iScaleHeight];  
  122.      
  123.     unsigned char *rBandUC, *gBandUC, *bBandUC;  
  124.      
  125.     // 根据是否显示彩色图像,判断RGB三个波段的组成方式,并分别读取  
  126.     if ( m_showColor == true )  
  127.     {  
  128.         imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
  129.         imgBand->at( 1 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, gBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
  130.         imgBand->at( 2 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, bBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
  131.          
  132.         // 分别拉伸每个波段并将Float转换为unsigned char  
  133.         rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );  
  134.         gBandUC = ImgSketch( gBand,imgBand->at( 1 ), iScaleWidth * iScaleHeight, imgBand->at( 1)->GetNoDataValue() );  
  135.         bBandUC = ImgSketch( bBand,imgBand->at( 2 ), iScaleWidth * iScaleHeight, imgBand->at( 2)->GetNoDataValue() );  
  136.     }  
  137.     else  
  138.     {  
  139.         imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
  140.          
  141.         rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );  
  142.         gBandUC = rBandUC;  
  143.         bBandUC = rBandUC;  
  144.     }  
  145.      
  146.     // 将三个波段组合起来  
  147.     int bytePerLine = ( iScaleWidth * 24 + 31 )/ 8;  
  148.     unsigned char* allBandUC = new unsignedchar[bytePerLine * iScaleHeight * 3];  
  149.     forint h = 0; h < iScaleHeight; h++ )  
  150.     {  
  151.         forint w = 0; w < iScaleWidth; w++)  
  152.         {  
  153.             allBandUC[h * bytePerLine + w * 3 +0] = rBandUC[h * iScaleWidth + w];  
  154.             allBandUC[h * bytePerLine + w * 3 +1] = gBandUC[h * iScaleWidth + w];  
  155.             allBandUC[h * bytePerLine + w * 3 +2] = bBandUC[h * iScaleWidth + w];  
  156.         }  
  157.     }  
  158.      
  159.     // 构造图像并显示  
  160.     QGraphicsPixmapItem *imgItem = newQGraphicsPixmapItem(  QPixmap::fromImage(QImage( allBandUC, iScaleWidth, iScaleHeight, bytePerLine,QImage::Format_RGB888  ) ) );  
  161.     QGraphicsScene *myScene = newQGraphicsScene();  
  162.     myScene->addItem( imgItem );  
  163.     this->setScene( myScene );  
  164. }  
  165.    
  166. ///<summary>  
  167. ///显示图像基本信息  
  168. ///</summary>  
  169. ///<param name="filename">文件名</param>  
  170. voidMapCanvas::ShowImgInfor( const QString filename )  
  171. {  
  172.     if ( filename == "" || poDataset== NULL )  
  173.     {  
  174.         return;  
  175.     }  
  176.     int row = 0; // 用来记录数据模型的行号  
  177.      
  178.     // 图像的格式信息  
  179.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Description" ) ) );  
  180.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetDescription() ) );  
  181.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Meta Infor" ) ) );  
  182.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME) ) ) ;  
  183.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Data Type" ) ) );  
  184.     imgMetaModel->setItem( row++, 1, newQStandardItem( GDALGetDataTypeName( ( poDataset->GetRasterBand( 1)->GetRasterDataType() ) ) ) );  
  185.      
  186.     // 图像的大小和波段个数  
  187.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "X Size" ) ) );  
  188.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterXSize() ) ) );  
  189.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Y Size" ) ) );  
  190.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterYSize() ) ) );  
  191.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Band Count" ) ) );  
  192.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterCount() ) ) );  
  193.      
  194.     // 图像的投影信息  
  195.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Projection" ) ) );  
  196.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetProjectionRef() ) );  
  197.      
  198.     // 图像的坐标和分辨率信息  
  199.     double adfGeoTransform[6];  
  200.     QString origin = "";  
  201.     QString pixelSize = "";  
  202.     if( poDataset->GetGeoTransform(adfGeoTransform ) == CE_None )  
  203.     {  
  204.         origin = QString::number(adfGeoTransform[0] ) + ", " + QString::number( adfGeoTransform[3] );  
  205.         pixelSize = QString::number(adfGeoTransform[1] ) + ", " + QString::number( adfGeoTransform[5] );  
  206.     }  
  207.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Origin" ) ) );  
  208.     imgMetaModel->setItem( row++, 1, newQStandardItem( origin ) );  
  209.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Pixel Size" ) ) );  
  210.     imgMetaModel->setItem( row++, 1, newQStandardItem( pixelSize ) );  
  211. }  
  212.    
  213. ///<summary>  
  214. ///显示文件结构树  
  215. ///</summary>  
  216. ///<param name="filename">文件名</param>  
  217. voidMapCanvas::ShowFileList( const QString filename )  
  218. {  
  219.     if ( filename == "" || poDataset== NULL )  
  220.     {  
  221.         return;  
  222.     }  
  223.     QFileInfo fileInfo( filename );  
  224.     QStandardItem *rootItem = newQStandardItem( fileInfo.fileName() );  
  225.     for ( int i = 0; i <poDataset->GetRasterCount(); i++ )  
  226.     {  
  227.         QStandardItem *childItem = newQStandardItem( tr( "Band %1" ).arg( i + 1 ) );  
  228.         rootItem->setChild( i, childItem );  
  229.     }  
  230.     fileListModel->setItem( 0, rootItem );  
  231. }  
  232.    
  233. ///<summary>  
  234. /// 图像线性拉伸  
  235. ///</summary>  
  236. ///<param name="buffer">图像缓存</param>  
  237. ///<param name="currentBand">当前波段</param>  
  238. ///<param name="size">The size.</param>  
  239. ///<param name="noValue">图像中的异常值</param>  
  240. ///<returns>经过拉伸的8位图像缓存</returns>  
  241. unsignedchar* MapCanvas::ImgSketch( float* buffer , GDALRasterBand* currentBand, intbandSize, double noValue )  
  242. {  
  243.     unsigned char* resBuffer = new unsignedchar[bandSize];  
  244.     double max, min;  
  245.     double minmax[2];  
  246.      
  247.      
  248.     currentBand->ComputeRasterMinMax( 1,minmax );  
  249.     min = minmax[0];  
  250.     max = minmax[1];  
  251.     if( min <= noValue && noValue<= max )  
  252.     {  
  253.         min = 0;  
  254.     }  
  255.     for ( int i = 0; i < bandSize; i++ )  
  256.     {  
  257.         if ( buffer[i] > max )  
  258.         {  
  259.             resBuffer[i] = 255;  
  260.         }  
  261.         else if ( buffer[i] <= max&& buffer[i] >= min )  
  262.         {  
  263.             resBuffer[i] =static_cast<uchar>( 255 - 255 * ( max - buffer[i] ) / ( max - min ) );  
  264.         }  
  265.         else  
  266.         {  
  267.             resBuffer[i] = 0;  
  268.         }  
  269.     }  
  270.      
  271.     return resBuffer;  
  272. }  
  273.    
  274. ///<summary>  
  275. /// 控件大小  
  276. ///</summary>  
  277. ///<returns>QSize.</returns>  
  278. QSizeMapCanvas::sizeHint() const  
  279. {  
  280.     return QSize( 1024, 768 );  
  281. }  
  282.    
  283. ///<summary>  
  284. ///鼠标滚轮事件,实现图像缩放  
  285. ///</summary>  
  286. ///<param name="event">滚轮事件</param>  
  287. voidMapCanvas::wheelEvent( QWheelEvent *event )  
  288. {  
  289.     // 滚轮向上滑动,放大图像  
  290.     if ( event->delta() > 0 )  
  291.     {  
  292.         ZoomIn();  
  293.     }  
  294.     // 滚轮向下滑动,缩小图像  
  295.     if ( event->delta() < 0 )  
  296.     {  
  297.         ZoomOut();  
  298.     }  
  299. }  
  300.    
  301. ///<summary>  
  302. ///鼠标按键按下事件  
  303. ///</summary>  
  304. ///<param name="event">鼠标事件.</param>  
  305. voidMapCanvas::mousePressEvent( QMouseEvent *event )  
  306. {  
  307.     // 滚轮键按下,平移图像  
  308.     if ( event->button() == Qt::MidButton )  
  309.     {  
  310.         this->setDragMode(QGraphicsView::ScrollHandDrag );  
  311.         this->setInteractive( false );  
  312.         lastEventCursorPos = event->pos();  
  313.     }  
  314. }  
  315.    
  316. ///<summary>  
  317. /// 鼠标移动事件  
  318. ///</summary>  
  319. ///<param name="event">鼠标事件</param>  
  320. voidMapCanvas::mouseMoveEvent( QMouseEvent *event )  
  321. {  
  322.     if ( this->dragMode() ==QGraphicsView::ScrollHandDrag )  
  323.     {  
  324.         QPoint delta = ( event->pos() -lastEventCursorPos ) / 10;  
  325.        this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );  
  326.        this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );  
  327.         this->viewport()->setCursor(Qt::ClosedHandCursor );  
  328.     }  
  329.      
  330. }  
  331.    
  332. ///<summary>  
  333. ///鼠标按键释放事件  
  334. ///</summary>  
  335. ///<param name="event">鼠标事件</param>  
  336. voidMapCanvas::mouseReleaseEvent( QMouseEvent *event )  
  337. {  
  338.     if ( event->button() == Qt::MidButton )  
  339.     {  
  340.         this->setDragMode(QGraphicsView::NoDrag );  
  341.     }  
  342. }  
原文地址:https://www.cnblogs.com/zfluo/p/5131828.html