ArcObjects SDK開發 021 開發框架搭建-FrameWork包設計

2022-12-23 12:00:45

1、框架引擎部分

引擎模組其實就是之前我們說的App-Command-Tool模組,通過這個模組,把系統的主幹框架搭建起來。

image1.png

其中大部分出現在選單以及工具條上的按鈕都會繼承這個框架定義ICommand和ITool。整個系統也是通過整合一些列Comand和Tool的方式,把整個系統搭建出來,這點也可以通過我們系統的主表單程式碼中看到。

image2.png

2、常用Command和Tool

一些常用的工具,例如地圖放大、縮小、平移等。這些工具要求定義能夠被框架引擎識別,並且符合當前使用的UI風格。定義如下。

image3.png

使用的時候,只需要一行程式碼即可。

this.UI_Tool_Bar.Items.Add(new BarCheckItemCommandUI(new FrameworkUI.MapTools.MapZoomInTool(this._MapApplication)));
this.UI_Tool_Bar.Items.Add(new BarCheckItemCommandUI(new FrameworkUI.MapTools.MapZoomOutTool(this._MapApplication)));
FrameworkUI.MapTools.MapPanTool myMapPanTool = new FrameworkUI.MapTools.MapPanTool(this._MapApplication);
this.UI_Tool_Bar.Items.Add(new BarCheckItemCommandUI(myMapPanTool));
this._MapApplication.MapPanTool = myMapPanTool;
this._MapApplication.CrruteTool = myMapPanTool;
this.UI_Tool_Bar.Items.Add(new BarButtonItemCommandUI(new FrameworkUI.MapTools.MapFullExtentCommand(this._MapApplication)));
this.UI_Tool_Bar.Items.Add(new BarButtonItemCommandUI(new FrameworkUI.MapTools.MapZoomInFixedCommand(this._MapApplication)));
this.UI_Tool_Bar.Items.Add(new BarButtonItemCommandUI(new FrameworkUI.MapTools.MapZoomOutFixedCommand(this._MapApplication)));
this.UI_Tool_Bar.Items.Add(new BarButtonItemCommandUI(new FrameworkUI.MapTools.MapZoomBackCommand(this._MapApplication)));
this.UI_Tool_Bar.Items.Add(new BarButtonItemCommandUI(new FrameworkUI.MapTools.MapZoomForwardCommand(this._MapApplication)));

3、通用模組部分

通用模組設計主要包括一些比較通用且核心的模組定義。目前我開發的GeoChem系統定義了三個模組。進度資訊模組,Geoprocessor擴充套件模組和多語言支援模組。

image4.png

能在Core裡面出現的模組,要滿足通用性以及核心性兩點。我們以最簡單的ProcessInfo為例,來說明這樣的模組的特性。

image5.png

配合這個ProcessInfo的是FrameworkUI裡面定義的ProcessInfoDialog對話方塊,定義如下。

image6.png

設計一,在ProcessInfoDialogHelper使用了一個新執行緒彈出進度對話方塊,在主執行緒進行運算的時候,進度條可以很順暢的響應進度變化不會卡。

設計二,ProcessInfo定義了SubProcessInfo的概念,可以很好的和子函數銜接。例如一個模型計算,有5個步驟,A(10)、B(30)、C(60)、D(90)、E(100)。其中兩個步驟比較複雜,其中B包含3個步驟,B1(20)、B2(80)、B3(100)。D包含了5個步驟,分別是和D1(20)、D2(40)、D3(60)、D4(80)、D5(100)。如何把B和D的計算進度反饋更方便的反饋給計算模型呢?

版本1,不關注B和D的細節了,進度到了A之後,進度條不在動了,只是更新上面的計算資訊,等B計算完,一下跳到B該有的進度。D同理。

版本2,加入B是一個專門為本模型定義的一個函數,那麼可以把ProcessInfo傳入進去,直接設定B1的計算完值為14%,B2計算完之後,設定為26%,B3計算完設定為30%。假如D是一個通用的函數,除了該模組呼叫,其他模組也會呼叫,那麼可以傳ProcessInfo進入,然後再傳起始進度值和結束進度值進度。例如呼叫D,傳入ProcessInfo和60、90。計算完D1,設定進度之為60+(90-60)*20%,依次類推。

版本3,設計了SubProcess的概念。當模型呼叫B的時候,會呼叫主ProcessInfo,建立一個子ProcessInfo,並設定這個子ProcessInfo應該進度到何值。當子ProcessInfo進度值發生變化的時候,反饋給其父ProcessInfo,由父ProcessInfo計算自己應該前進多少進度。二接收子ProcessInfo的函數,還是設定進度從0-100即可。

呼叫程式碼如下。

public void Exe(ProcessInfo pProcessInfo)
{
    //獲取柵格CellSize
    pProcessInfo.SetProcess(5, "Get cell size...");
    double myCellSize = GetRasterCellSize();

    //提取柵格資料的範圍
    pProcessInfo.SetProcess(10, "Data Extent...");
    string myRasterDomainFile = this.GetRasterDomain();

    //平滑資料
    pProcessInfo.SetProcess(12, "Smooth Data...");
    string myRasterFilePath = this.SmoothData(this.RasterFilePath);

    //生成等值線
    pProcessInfo.SetProcess(20, "ContourList...");
    string myContourLineFile = this.CreateContourLine(myRasterFilePath);

    //調整等值線值的資料精度
    pProcessInfo.SetProcess(25, "Calculate CONTOUR field...");
    this.AdjustAccuracy(myContourLineFile);

    //平滑等值線
    pProcessInfo.SetProcess(30, "SmoothLine...");
    myContourLineFile = this.SmoothContourLine(myContourLineFile, myCellSize);

    //用柵格資料的範圍裁切等值線
    pProcessInfo.SetProcess(35, "Clip Contour Line...");
    myContourLineFile = this.ClipContourLineByRasterDomain(myContourLineFile, myRasterDomainFile);

    //根據等值線,生成等值面
    pProcessInfo.SetProcess(40, "Create Contour Polygon");
    pProcessInfo.StartSubProcess(70);
    string myContourPolygonFile = this.GetContourPolygonFile(pProcessInfo.SubProcessInfo, myContourLineFile, myRasterDomainFile);
    pProcessInfo.EndSubProcess();

    //得到切割時使用的 Shape檔案
    pProcessInfo.SetProcess(75, "Clip data...");
    this.ClipContourLineAndPolygon(myRasterDomainFile, ref myContourLineFile, ref myContourPolygonFile);

    //刪除面積較為小的面
    pProcessInfo.SetProcess(88, "Eliminate Small Area...");
    this.ContourEliminate(ref myContourLineFile, ref myContourPolygonFile);

    //計算極值標註
    pProcessInfo.SetProcess(90, "Create Limite Value Point...");
    this.UpdateThresholdLabel(myContourPolygonFile);

    pProcessInfo.SetProcess(95, "Save Map Document...");
    this.SaveAsMxdFile(myContourLineFile, myContourPolygonFile);
    pProcessInfo.SetProcess(100, "Complete!");
}

使用子進度物件的函數定義如下。

private string GetContourPolygonFile(ProcessInfo pProcessInfo, string pContourLineFile, string pRasterDomainFile)
{
    var myContourPolygonCal = new ContourPCreator
    {
        ContourLineFilePath = pContourLineFile,
        ExtentFilePath = pRasterDomainFile,
        ResultPolygonFilePath = FilePathHelper.GetTempShapeFilePath()
    };
    myContourPolygonCal.Exe(pProcessInfo);
    return myContourPolygonCal.ResultPolygonFilePath;
}

ContourPCreator類的Exe函數定義如下。

public void Exe(ProcessInfo pProcessInfo)
{
    this._PolygonList.Clear();
    this._PolylineList.Clear();

    //把線轉換成面
    pProcessInfo.SetProcess(0, "Feature To Polygon...");
    var myPolygonFilePath = FilePathHelper.GetTempShapeFilePath();
    var myFeatureToPolygon = new FeatureToPolygon()
    {
        in_features = this.ContourLineFilePath + ";" + this.ExtentFilePath,
        out_feature_class = myPolygonFilePath,
        cluster_tolerance = "0.001 Meters"
    };
    GPEx myGPEx = new GPEx();
    myGPEx.Execute(myFeatureToPolygon);

    //用柵格範圍裁切面
    string myClipOutFilePath = FilePathHelper.GetTempShapeFilePath();
    pProcessInfo.SetProcess(10, "Clip...");
    var myClip = new ESRI.ArcGIS.AnalysisTools.Clip()
    {
        in_features = myPolygonFilePath,
        clip_features = this.ExtentFilePath,
        out_feature_class = myClipOutFilePath
    };
    myGPEx.Execute(myClip);

    //新增欄位
    pProcessInfo.SetProcess(20, "Add Field...");
    var myAddField = new AddField
    {
        in_table = myClipOutFilePath,
        field_name = "Value",
        field_type = "DOUBLE"
    };
    myGPEx.ExecuteByGP(myAddField);

    //拷貝結果資料
    ShapeFileHelper.Copy(myClipOutFilePath, this.ResultPolygonFilePath);

    //讀取面資訊
    pProcessInfo.SetProcess(30, "Init ContourPolygon ...");
    this.LoadContourPolygonList();

    //分析面與面之間的臨近關係
    pProcessInfo.SetProcess(40, "Polygon Neighbors ...");
    this.LoadContourPolygonRels();

    //讀取等值線的值列表
    pProcessInfo.SetProcess(50, "Read Line Value ...");
    this.LoadContourPolylineList();

    //面與線 臨近分析
    pProcessInfo.SetProcess(60, "Load Contour Polygon Line Rels ...");
    this.LoadContourPolygonLineRels();

    //計算等值面值,在此迴圈,主要是為了避免特殊情況,導致死迴圈
    pProcessInfo.SetProcess(70, "Cal Contour Polygon Value...");
    for (int i = 0; i < 100; i++)
    {
        int myUnCalCount = this.CalContourPolygonValue();
        if (myUnCalCount == 0)
        {
            break;
        }
    }

    //把計算的值寫入該檔案中
    pProcessInfo.SetProcess(90, "Write Value To Contour Polygon File...");
    this.WritePolygonValueToFeatureClass();

    //計算完成
    pProcessInfo.SetProcess(100, "Contour Polygon Cal Complete...");
}

因為ProcessInfo是非常核心一個類,使用範圍非常廣泛,如果把這個定義好了,會讓系統非常清晰。這樣的話,每個需要暴露進度的函數,值需要定義一個ProcessInfo引數傳入進來即可,在函數內部進度從0開始,100結束,其他的都不需要關心了。

4、常用函數庫

常用函數庫,我一般會定義到Framework程式集的Helper目錄下。如果是欄位相關的會定義成FieldHelper,空間參考相關的會定義成SpatialReferenceHepler。並且裡面大部分都是靜態函數,方便直接呼叫。

image7.png

在這些裡面用的最多的就是FolderPathHelper、ShapeFileHelper、SpatialReferenceHepler以及FeatureClassHelper等。