GDAL库中提供了非常多的算法,同一时候也提供了进度条的參数。对于C++调用来说,应该没什么问题,可是对C#调用来说,在进度条这块须要写一个代理来进行传递。首先写一个简单的測试代码。
首先定义一个托付函数原型,须要与GDAL库中的C#进度条接口保持一致,一个简单的原型例如以下。
/// <summary> /// 进度信息回调函数 /// </summary> /// <param name="dfComplete">完毕比例,0~1之间的数</param> /// <param name="pszMessage">进度条信息</param> /// <param name="pProgressArg">进度条用户数据</param> /// <returns></returns> public delegate int ProgressFunc(double dfComplete, char[] pszMessage, IntPtr pProgressArg);接下来在自己的測试代码中编写一个进度条函数,为了方便,先编写一个控制台的,直接从GDAL库中提供的控制台进度条改动而成,进度条函数代码例如以下。详细能够參考GDAL库中的控制台进度条实现代码,差点儿全然一样,除了将printf函数改成了Console.Write,别的没有啥差别。
class GDALAlgCsTest { //进度信息回调函数 static int nLastTick = -1; public int TermProgress(double dfComplete, char[] strMessage, IntPtr Data) { int nThisTick = (int) (dfComplete * 40.0); nThisTick = Math.Min(40, Math.Max(0, nThisTick)); // Have we started a new progress run? if( nThisTick < nLastTick && nLastTick >= 39 ) nLastTick = -1; if( nThisTick <= nLastTick ) return 1; while( nThisTick > nLastTick ) { nLastTick++; if( nLastTick % 4 == 0 ) Console.Write("{0}", (nLastTick / 4) * 10); else Console.Write("."); } if( nThisTick == 40 ) Console.Write( " - done. "); else Console.Write(""); return 1; } }接下来就是在測试代码中进行调用了。以下是我将GDAL库的算法进行了封装,只是进度条的接口与GDAL库中的算法进度条接口一模一样,我封装了一个波段合并的算法,就是将好几个单波段文件合并为一个多波段文件,主要是用来将Landsat卫星下载的分波段存储的数据进行合并形成一个多波段的数据。函数的接口声明例如以下:
/// <summary> /// 图像波段合并 /// </summary> /// <param name="astrSrcFileList">输入文件列表,全部的输入路径中间使用*号进行切割</param> /// <param name="strDstFile">输出文件路径</param> /// <param name="iDataType">栅格数据的数据类型(參考GDALDataType)</param> /// <param name="bUnion">范围不一致的图像处理方式,true为求并,false为求交</param> /// <param name="strFormat">输出文件格式,详细參考GDAL支持数据类型</param> /// <param name="pFun">进度条回调函数</param> /// <param name="pUserData">进度条指针</param> /// <returns>返回值,表示计算过程中出现的各种错误信息</returns> [DllImport("GDALAlg", EntryPoint = "ImageLayerStack")] public static extern int ImageLayerStack(char[] astrSrcFileList, char[] strDstFile, int iDataType, bool bUnion, string strFormat, ProgressFunc pFun, IntPtr pUserData);最后两个參数就是进度条參数,倒数第二个为进度条回调函数(C#里面的托付函数),倒数第一个參数为进度条所需的參数信息。详细请參考我之前的进度条相关的博客。上面的函数实现此处不再进行说明,也不是本文的重点。以下就看看怎么调用这个函数并将进度条传入。
using System; using GdalAlg; using System.Collections; using System.IO; using System.Text; namespace GDALAlgCS { class GDALAlgCsTest { //进度信息回调函数 static int nLastTick = -1; public int TermProgress1(double dfComplete, char[] strMessage, IntPtr Data) { int nThisTick = (int) (dfComplete * 40.0); nThisTick = Math.Min(40, Math.Max(0, nThisTick)); // Have we started a new progress run? if( nThisTick < nLastTick && nLastTick >= 39 ) nLastTick = -1; if( nThisTick <= nLastTick ) return 1; while( nThisTick > nLastTick ) { nLastTick++; if( nLastTick % 4 == 0 ) Console.Write("{0}", (nLastTick / 4) * 10); else Console.Write("."); } if( nThisTick == 40 ) Console.Write( " - done. "); else Console.Write(""); return 1; } static void Main(string[] args) { //声明进度信息回调函数 ProgressFunc pd = new ProgressFunc(new GDALAlgCsTest().TermProgress1); IntPtr p = new IntPtr(0); int ire = 0; string strLandsat1 = @"F:DataLandSatLT51230322011159IKR00LT51230322011159IKR00_B1.TIF"; string strLandsat2 = @"F:DataLandSatLT51230322011159IKR00LT51230322011159IKR00_B2.TIF"; string strLandsat3 = @"F:DataLandSatLT51230322011159IKR00LT51230322011159IKR00_B3.TIF"; string strLandsat4 = @"F:DataLandSatLT51230322011159IKR00LT51230322011159IKR00_B4.TIF"; string strLandsat5 = @"F:DataLandSatLT51230322011159IKR00LT51230322011159IKR00_B5.TIF"; string strLandsat6 = @"F:DataLandSatLT51230322011159IKR00LT51230322011159IKR00_B6.TIF"; string strLandsat7 = @"F:DataLandSatLT51230322011159IKR00LT51230322011159IKR00_B7.TIF"; string strInfiles = strLandsat1 + "*" + strLandsat2 + "*" + strLandsat3 + "*" + strLandsat4 + "*" + strLandsat5 + "*" + strLandsat6 + "*" + strLandsat7; string strChineseOut = @"F:DataLandSatLT51230322011159IKR00.tif"; ire = GdalAlgInterface.ImageLayerStack(strInfiles.ToCharArray(), strChineseOut.ToCharArray(), 0, false, "GTiff", pd, p); Console.Write(ire.ToString()); } } }上面程序执行中进度效果例如以下图所看到的。上面的是控制台的,那么在图形界面中怎样编写呢。接下来就写一个图形界面的进度条。声明代理函数都一样,仅仅只是就是自己须要依据各自界面的进度条控件编写相应的进度函数。也就是上面相似的TermProgress1函数。首先做一个简单的界面,例如以下图所看到的。首先看这个界面的进度条实现函数。在该Form类中定义一个进度条类,详细代码例如以下:
public class Progress { public int ProgressBarInfo(double dfComplete, char[] strMessage, IntPtr pData) { GDALForm form = (GDALForm)Control.FromHandle(pData); int iValue = (int)(100 * dfComplete + 0.5); form.progressBar.Value = iValue; string strMsg = new string(strMessage); form.labelMessage.Text = strMsg; return 1; } }首先对函数ProgressBarInfo的參数进行说明,dfComplete是进度信息,0~1之间的小数,strMessage是进度信息,主要是一些说明文字,最后一个pData是用户自己定义的结构信息,这里的pData就是整个Form的handle。这样就能够从这个Form的handle中转换为界面类的一个对象,并从中获取进度条控件,然后将进度信息设置给进度条控件,将Message设置给界面的一个Label。
以下再看看函数调用。
private void buttonOK_Click(object sender, EventArgs e) { try { int iRev = 0; string strInput = textBoxInput.Text; string strOutput = textBoxOutput.Text; string strField = "OBJECTID"; ProgressFunc pd = new ProgressFunc(new Progress().ProgressBarInfo); IntPtr pre = this.Handle; iRev = GdalAlgInterface.ShpRasterize(strInput.ToCharArray(), strOutput.ToCharArray(), 10, 1, 0, strField.ToCharArray(), "GTiff", pd, pre); MessageBox.Show("处理返回代码:" + iRev.ToString(), "提示"); } catch (System.Exception ex) { MessageBox.Show(ex.ToString(), "提示"); } }在点击【计算】按钮之后,先获取输入和输出的文件路径,然后声明一个托付,用户定义的结构信息给当前form的handle。然后将托付函数和handle传入算法函数就可以。程序执行的截图例如以下所看到的。该算法为矢量栅格化的一个封装函数。上面的两个截图和界面设计截图有点不一致,主要是将矢量栅格化的几个參数能够由界面进行设置。上面的代码中这几个參数都是在代码中设置死的。其它的都一样,对于进度条这块没有不论什么变动。