1. 程式人生 > >ArcGIS Runtime SDK for .NET 100.0中如何執行.gpk(二)

ArcGIS Runtime SDK for .NET 100.0中如何執行.gpk(二)

接上文,在編寫程式碼之前我們先回憶一下ArcMap中執行該指令碼工具的過程,首先我把一個Dem影像新增進來,然後我在該影像範圍內使用人機互動的方式畫了一條線,那麼我們在Runtime中也可以採用該思路:即首先載入該Dem影像(為了畫線時便於定位),然後map上畫線作為輸入inputLine。

1,首先載入本地Dem影像(這是100.0的新特性哦)並且顯示到該區域。這裡不做詳細介紹,可參考下面程式碼:

//載入DEM並且縮放到該區域
            Map map = new Map(Basemap.CreateTopographic());
            string pathToRaster =
@"D:\work\github\ExecuteGPK\LasVegasNED13_geoid1.tif"; var myRaster = new Raster(pathToRaster); // create a RasterLayer using the Raster var newRasterLayer = new RasterLayer(myRaster); map.OperationalLayers.Add(newRasterLayer); Viewpoint viewPoint =
new Viewpoint(36.131, -115.144, 800000); myMapView.Map = map; await myMapView.SetViewpointAsync(viewPoint, TimeSpan.FromSeconds(2));

2,開啟LocalServer,LocalServer 是單例項物件( singleton object),也就是說整個工程中只有一個,因此我們可以新增全域性變數:

private Esri.ArcGISRuntime.LocalServices.LocalServer _localServer;

接下來開啟LocalServer:

 private async void StartLocalServer()
        {
            // Get the singleton LocalServer object using the static "Instance" property
            _localServer = Esri.ArcGISRuntime.LocalServices.LocalServer.Instance;

            // Handle the StatusChanged event to react when the server is started
            _localServer.StatusChanged += ServerStatusChanged;

            // Start the local server instance
            await _localServer.StartAsync();
        }

        private async void ServerStatusChanged(object sender, Esri.ArcGISRuntime.LocalServices.StatusChangedEventArgs e)
        {
            // Check if the server started successfully
            if (e.Status == Esri.ArcGISRuntime.LocalServices.LocalServerStatus.Started)
            {

            }
        }

ServerStatusChanged事件可以用來監測LocalServer的狀態。

3,執行本地gpk,可按照下面步驟:

a,新建LocalGeoprocessingService物件,傳入該.gpk所在路徑,獲取其full URL,之後的過程其實和執行線上GP服務一樣;

b,建立GeoprocessingTask物件,傳入剛剛獲取到的URL;

c,建立GeoprocessingParameters物件,用於輸入必要引數,注意需要設定GeoprocessingExecutionType為同步或者非同步;

d,使用GeoprocessingTask.CreateJob獲取GeoprocessingJob,該物件可以監聽job的狀態;

e,執行Job,如果執行成功的話會得到GeoprocessingResult,執行失敗可以捕獲異常資訊;

f,最後通過GeoprocessingResult獲取返回值。

4,重點講下引數的構建,構建引數時需要確認引數型別,Runtime中支援的引數型別及說明如下表:

1
2

由上表可以看出如果是輸入向量要素的話需要使用FeatureRecordSetLayer這種型別,對應的Runtime引數型別就是GeoprocessingFeatures,這也是為什麼我們要自定義指令碼工具(而不是直接呼叫系統工具Interpolate Shape)並且輸入引數型別指定為Feature Set的原因。

那麼GeoprocessingFeatures如何構建呢?可以使用FeatureCollectionTable實現,下面是FeatureCollectionTable的幫助:

3

其AddFeatureAsync方法可以新增要素,CreateFeature()方法可以新建要素,返回Feature物件,接下來看下Feature類:

4

與ArcObjects的邏輯一致,可以為該Feature賦予幾何和屬性,那麼怎麼獲取Geometry呢?這裡我們直接螢幕畫線,使用MapView.SketchEditor獲取SketchEditor,然後使用StartAsync(SketchCreationMode, Boolean)裡面指定SketchCreationMode.Polyline就可以直接畫線了,畫完雙擊結束後即可獲取Geometry,賦給Feature.Geometry,最後通過FeatureCollectionTable.AddFeatureAsync將該Feature新增進來即可。

由於我們構建的gpk就需要這一個引數,因此引數就構建完了。不過還有一點需要注意,由於我們需要獲取Z值,所以GeoprocessingParameters.ReturnZ屬性需要設為true,此外,還可以設定輸出要素的空間參考。

5,獲取結果型別為GeoprocessingResult,可以根據輸出的引數名稱“outputLine”獲取GeoprocessingFeatures物件,進而獲取IFeatureSet,由於我們僅輸入了一條線,因此輸出只有一個Feature,獲取到該Feature後可以獲取其Geometry即為Polyline,為了檢測Z值是否返回,我們獲取輸出線的起點Z值和終點Z值,最終以MessageBox方式彈出。

下面就把這一部分的程式碼彙總一下:

 StartLocalServer();
            LocalGeoprocessingService localServiceGP = new LocalGeoprocessingService(@"D:\work\github\ExecuteGPK\InterpolateShape.gpk");
            localServiceGP.ServiceType = GeoprocessingServiceType.SynchronousExecute;
            // Handle the status changed event to check when it's loaded
            localServiceGP.StatusChanged += async (svc, args) =>
            {
                // If service started successfully, create a gp task
                if (args.Status == LocalServerStatus.Started)
                {
                    // Get the URL for the specific geoprocessing tool
                    var gpSvcUrl = (svc as LocalGeoprocessingService).Url.AbsoluteUri + "/InterpolateShape";
                    // Create the geoprocessing task
                    GeoprocessingTask gpRouteTask = new GeoprocessingTask(new Uri(gpSvcUrl));
                    GeoprocessingParameters para = new GeoprocessingParameters(GeoprocessingExecutionType.SynchronousExecute);
                    // Create the schema for a lines table (one text field to contain a name attribute)
                    var inputFeatures = new FeatureCollectionTable(new List<Field>(), GeometryType.Polyline, myMapView.SpatialReference);
                    Feature inputFeature = inputFeatures.CreateFeature();
                    var geometry = await myMapView.SketchEditor.StartAsync(SketchCreationMode.Polyline, false);
                    inputFeature.Geometry = geometry;
                    await inputFeatures.AddFeatureAsync(inputFeature);          

                    para.Inputs.Add("inputLine", new GeoprocessingFeatures(inputFeatures));
                    para.ReturnZ = true;
                    para.OutputSpatialReference = myMapView.SpatialReference;

                    GeoprocessingJob routeJob = gpRouteTask.CreateJob(para);

                    try
                    {
                        // Execute analysis and wait for the results
                        GeoprocessingResult geoprocessingResult = await routeJob.GetResultAsync();
                        GeoprocessingFeatures resultFeatures = geoprocessingResult.Outputs["outputLine"] as GeoprocessingFeatures;
                        IFeatureSet interpolateShapeResult = resultFeatures.Features;
                        Esri.ArcGISRuntime.Geometry.Polyline elevationLine = interpolateShapeResult.First().Geometry as Esri.ArcGISRuntime.Geometry.Polyline;
                        MapPoint startPoint = elevationLine.Parts[0].Points[0];
                        int count = elevationLine.Parts[0].PointCount;
                        MapPoint stopPoint = elevationLine.Parts[0].Points[count - 1];
                        double chazhi = stopPoint.Z - startPoint.Z;
                        MessageBox.Show("終點的Z值為: " + stopPoint.Z.ToString() + ",起點的Z值為: " + startPoint.Z.ToString());
                    }
                    catch (Exception ex)
                    {
                        if (routeJob.Status == JobStatus.Failed && routeJob.Error != null)
                            MessageBox.Show("Executing geoprocessing failed. " + routeJob.Error.Message, "Geoprocessing error");
                        else
                            MessageBox.Show("An error occurred. " + ex.ToString(), "Sample error");
                    }
                    // Create parameters, run the task, process results, etc.
                    // ...
                }
            };
            // Start the local geoprocessing service
            await localServiceGP.StartAsync();

介面為:

5

結果為:

6

三、遇到的問題與技巧

遇到的問題

釋出過GP服務的使用者都知道,GP服務的執行方式有兩種:即同步和非同步:

7

同樣執行gpk也是如此,上面程式碼中有一點不知道您注意到沒有,那就是LocalGeoprocessingService.ServiceType屬性,這裡指定的是GeoprocessingServiceType.SynchronousExecute也就是同步執行。悲劇的是我開始沒加這句程式碼(其實也不怪我,Runtime的官網示例中也沒有這句程式碼而且也沒有地方有相關說明),後面引數處使用GeoprocessingParameters para = new GeoprocessingParameters(GeoprocessingExecutionType.SynchronousExecute);會報錯:

8

那就改成非同步執行吧,使用GeoprocessingExecutionType.AsynchronousSubmit,這次沒有報錯,但輸出線要素的Z值始終是0!崩潰了,好在後來看到LocalGeoprocessingService.ServiceType屬性,設定為同步後,引數處也設為同步就執行成功了。後來想嘗試一下非同步執行,於是將ServiceType設為非同步,引數也設為非同步,但始終得到的Z值為0,不知道為什麼,如果大家非同步執行這個工具成功了的話歡迎給我留言!

技巧

localServer會把gpk釋出成本地的GP Service,如下圖:

9

這時我們可以在瀏覽器中輸入該URL,跟線上的GP Service類似,這裡我將服務型別設為了非同步,然後點選提交:、

10

輸入inputLine的Json格式,執行:

11

生成的結果:

12

如果這裡是可以成功的,那麼我們在程式中引數構建正確了也應該是可以成功的,但如果這裡都無法執行成功,那就別指望在Runtime中執行成功了,所以建議如果大家執行gpk失敗了,可以在這裡測試一下。

執行成功後,在那個臨時目錄下(注意,如果LocalServer關閉了,這個臨時目錄就銷燬了)找到該outputLine是有Z值的。也就是說非同步是正常的,但是為什麼我的Runtime程式中獲取的Z值就是0呢?我也是醉了。

好吧,就囉嗦到這吧,希望您看完後能對Desktop中如何製作gpk以及ArcGIS Runtime for .NET 100.0中如何執行gpk有個初步認識,測試工程和測試資料已上傳至百度雲盤了,連結:http://pan.baidu.com/s/1jIIE5Ps 密碼:lixo,有興趣的同學可以下載。