开始之前的一段废话:

放假回家了也就没有写博客了,家里没有网,偶尔也只能用手机上一上,看一看是否有新的评论和回复,是否有和我研究相关的博文和动态。今天是三月一号,学校的校园网帐号解封了,于是便把寒假部分的工作内容总结一下,在此也和大家一起分享我的心得,希望对各位读者能有所帮助。

本文的例子是我帮一位兄弟做的,算起还是我的学长,只不过不同学校,具体的要求如下:

于是假期的时候便做了一个该功能的例子,然后还加了一点小功能。后面的文章中将会详细介绍,下面就进入正题吧!

一、本文实现的功能

1.1 要素查询功能

根据要素属性,查询出相应的要素

1.2 栅格数据的像元统计

例如输入一个几何图形(点,线,面)可以计算出该图像范围内栅格数据的像元信息,如:像元最大值,最小值,以及平均值等,具体如下图所示:                  

       

 1.3 绘制地形剖面图

 该功能和官网的这个例子差不多:

(不知道为什么,我这里查看不了这里例子)

效果如下图所示:

二、使用到的数据和知识

2.1地图数据

        本例的地形高程数据来自于国际科学数据服务平台,下载网址:

        本例的各区县要素数据来自于国家基础地理信息中心,下载网址:

上面的地图是我自己在ArcMap中制作的一张地图,外加了一点地形阴影效果,这是在ArcMap中的图层列表

 

各县面要素图层

       

 最后通过以上数据得到了本文使用的底图。在查询部分中,实际上查询的就是各区县的面要素,可通过输入曲线的民称或编号来查询出相对应的县,然后再通过调研像元统计的GP服务工具,即可实现像元统计,得到对应区县范围内像元的最大值,最小值等信息,实际上这里的值也就是对应区县的海拔(准确度由下载的高程数据所决定),最后的地图:

2.2 使用到如下的知识:

2.2.1 要素的属性查询

2.2.2 GP服务的调用

2.2.3 通过开源的Silverlight图表控件:OxyPlot

以上前两部分内容都在之前的博文中讲过,所以不是本文的重点。关于OxyPlot图表控件也很简单,这里不会过多的解释说明。官网有很好的帮助文档。

三、实现过程

3.1 查询(要素属性查询和空间查询)

可参考之前的博文:

3.2 根据查询得到的面要素,计算出面要素对应区域内的像元信息。

3.2.1  发布像元统计的GP服务

这里主要用到的就是GP服务。下面是本文的GP服务模型:

 

其中ZoneFiled就是统计字段,InputFeature为输入的要素,ZonalRaster为统计的删格数据

 

提醒:在10.1中可以将栅格数据作为输入参数。在10.0中不可以,如果您是10.0版本的,那么这里你只能在建模的时候指定栅格数据,然后去掉设置为模型参数。在10.1中如果你有可数的几个不同栅格数据,那么你可以将其添加到ArcMap中,在发布GP服务的时候,会提示你从列表选择还是自定义栅格数据,这个时候你可以选择从列表选择。然后你可以将ArcMap中的删格数据图层添加到选择列表中,最后在Web端调用的时候,你只需要输入栅格数据列表中对于栅格数据的名称即可,例如本文的列表如下:

 

这里可以看出只有一个栅格数据选项:hunandata。

3.2.2   Web端调用GP服务

调用GP服务的过程之前的博文也有讲到,可参考这里:

下面是本文的调用代码:

private void AccessGPService(FeatureSet featureset)          {              List
gppara = new List
(); //输入GP服务的参数 gppara.Add(new GPString("ZoneFiled", "OBJECTID")); gppara.Add(new GPFeatureRecordSetLayer("InputFeature", featureset)); gppara.Add(new GPString("ZonalRaster", "hunandata")); _geoprocessor.SubmitJobAsync(gppara); } private void _geoprocessor_JobCompleted(object sender, JobInfoEventArgs e) { HttpWebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp); Geoprocessor gp = sender as Geoprocessor; //请求GP服务的结果 gp.GetResultDataAsync(e.JobInfo.JobId, "ResultTable"); } private void _geoprocessor_GetResultDataCompleted(object sender, GPParameterEventArgs e) { MyDataGrid.ItemsSource = null; GPRecordSet gpr = e.Parameter as GPRecordSet; if (gpr.FeatureSet != null) { for (int i = 0; i < gpr.FeatureSet.Features.Count; i++) { for (int j = 2; j < gpr.FeatureSet.Features[i].Attributes.Count; j++) { //这里本文有个Bug,就是当FeatureDataGrid绑定到GraphicsLayer后, //如果修改GrapicsLayer的属性(增加或者删除)会报错,以下代码不能通过,所以这里选择了先修改再绑定 List
keyList = gpr.FeatureSet.Features[i].Attributes.Keys.ToList
(); List
valueList = gpr.FeatureSet.Features[i].Attributes.Values.ToList(); graphicsLayer.Graphics[i].Attributes.Add(keyList[j], valueList[j]); } } //在此绑定DataGrid,不然会出现异常 MyDataGrid.GraphicsLayer = graphicsLayer; MyDataGrid.Visibility = Visibility.Visible; //绘制地形图 if (createPlot == true) { CreatePlot(); TerrainBorder.Visibility = Visibility.Visible; } featureSet.Features.Clear(); } //等待过程 BusyRectangle.Visibility = Visibility.Collapsed; busyIndicator.IsBusy = false; } private void _geoprocessor_Failed(object sender, TaskFailedEventArgs e) { MessageBox.Show(e.Error.ToString()); }

以上是调用GP服务的代码,这里有个Bug。

最后的统计结果如下所示:

 

3.2.3 Web客户端绘制地形图

 这里借助的是开源的图表控件:OxyPlot。感觉绘制图表很强大,而且开源啊。

绘制地形剖面图的功能大致上的过程如下:

首先在地图上绘制一条线,然后等分这条线,例如一百份,然后得到这一百个点的像元统计值(最后的值就是海拔值,这里数据的准确度和提供的高程数据经度有关,高程数据经度越高,结果也就越准确),得到一百个点的高程数据之后,就通过OxyPlot绘图了。

具体代码:

a.OxyPlot绘图

声明一个PlotModel,PlotModel代表绘制图表的类型。

PlotModel plotModel = null;

在构造函数中初始化:

var linearAxis1 = new LinearAxis(AxisPosition.Bottom) { IsZoomEnabled=false,Title="距离"};              plotModel.Axes.Add(linearAxis1);              var linearAxis2 = new LinearAxis(AxisPosition.Left) { IsZoomEnabled=false,Title="海拔"};              plotModel.Axes.Add(linearAxis2);              areaSeries1 = new AreaSeries              {                  Fill=OxyColors.DarkOrange,                  DataFieldX="距离",                  DataFieldY="最高海拔",                  DataFieldX2 = "距离",                  DataFieldY2 = "最低海拔",                  Color=OxyColors.Red,                  StrokeThickness=0,                  MarkerFill=OxyColors.Transparent,              };

b.然后绘制一条直线并构造点:

private void CreatPoint(ESRI.ArcGIS.Client.Geometry.Polyline polyline,int count)          {              //默认沿直线构造一百个点              double xStep = (polyline.Paths[0][1].X - polyline.Paths[0][0].X) / count;              double yStep = (polyline.Paths[0][1].Y - polyline.Paths[0][0].Y) / count;              interval = Math.Sqrt(xStep * xStep + yStep * yStep);              //清空FeatureSet便于之后的GP服务调用              if (featureSet.Features.Count > 0)              {                  featureSet.Features.Clear();              }              for (int i = 0; i < count; i++)              {                  MapPoint mp = new MapPoint(polyline.Paths[0][0].X + i*xStep, polyline.Paths[0][0].Y + i*yStep);                  mp.SpatialReference = map1.SpatialReference;                  Graphic gPoint = new Graphic()                  {                      Symbol = new SimpleMarkerSymbol()                      {                          Color=new SolidColorBrush(Colors.Blue),                          Size=8,                          Style=SimpleMarkerSymbol.SimpleMarkerStyle.Circle                      },                      Geometry=mp,                  };                  featureSet.Features.Add(gPoint);                  graphicsLayer.Graphics.Add(gPoint);              }          }

c.调用GP服务统计像元值(代码如上)

d.根据统计值绘制图表,这里Graphic的MAX属性表示的即为最高海拔,对于点要素MAX和MIN值是一样的。

private void CreatePlot()          {              areaSeries1.Points.Clear();              areaSeries1.Points2.Clear();              plotModel.Series.Clear();                for (int i = 0; i < graphicsLayer.Graphics.Count; i++)              {                  areaSeries1.Points.Add(new DataPoint(interval * i, Convert.ToDouble(graphicsLayer.Graphics[i].Attributes["MAX"])));                  areaSeries1.Points2.Add(new DataPoint(interval * i, 0));              }              plotModel.Series.Add(areaSeries1);              areaSeries1.Title = "海拔";              Myplot.Model = plotModel;              createPlot = false;          }

最后的MainPage.cs代码:

View Code
using System;  using System.Collections.Generic;  using System.Linq;  using System.Net;  using System.Windows;  using System.Windows.Controls;  using System.Windows.Documents;  using System.Windows.Input;  using System.Windows.Media;  using System.Windows.Media.Animation;  using System.Windows.Shapes;  using ESRI.ArcGIS.Client.Symbols;  using ESRI.ArcGIS.Client.Geometry;  using ESRI.ArcGIS.Client.Tasks;  using ESRI.ArcGIS.Client;  using System.Text.RegularExpressions;  using OxyPlot.Silverlight;  using OxyPlot;  namespace QueryAndStatisticDemo  {      public partial class MainPage : UserControl      {          //用于查询的Task          QueryTask queryTask = new QueryTask();          //声明一个Draw,用于绘制查询的区域,进行空间查询          Draw myDraw;          //绘制的Geomotry          ESRI.ArcGIS.Client.Geometry.Geometry drawGeometry;           //查询SQL字符串          string QueryString;          //选择查询结果中的Graphic          Graphic selectedGraphic;          //地理处理服务          Geoprocessor _geoprocessor = null;          //输入要素集          FeatureSet featureSet = new FeatureSet();          GraphicsLayer graphicsLayer;          PlotModel plotModel = null;          //Interval          AreaSeries areaSeries1;          double interval;          bool createPlot = false;            public MainPage()          {              InitializeComponent();              plotModel = new PlotModel("地形剖面图");              var linearAxis1 = new LinearAxis(AxisPosition.Bottom) { IsZoomEnabled=false,Title="距离"};              plotModel.Axes.Add(linearAxis1);              var linearAxis2 = new LinearAxis(AxisPosition.Left) { IsZoomEnabled=false,Title="海拔"};              plotModel.Axes.Add(linearAxis2);              areaSeries1 = new AreaSeries              {                  Fill=OxyColors.DarkOrange,                  DataFieldX="距离",                  DataFieldY="最高海拔",                  DataFieldX2 = "距离",                  DataFieldY2 = "最低海拔",                  Color=OxyColors.Red,                  StrokeThickness=0,                  MarkerFill=OxyColors.Transparent,              };                            graphicsLayer = map1.Layers["QueryResultLayer"] as GraphicsLayer;                queryTask.Url = "http://qzj-pc:6080/arcgis/rest/services/Provinces_Map/HunanMap/MapServer/3";              queryTask.AutoNormalize = true;              //很重要,否则出现查询成功和失败交替出现的现象              queryTask.DisableClientCaching = true;              queryTask.ExecuteCompleted += new EventHandler
(queryTask_ExecuteCompleted); queryTask.Failed += new EventHandler
(queryTask_Failed); _geoprocessor = new Geoprocessor("http://qzj-pc:6080/arcgis/rest/services/RasterStatisticServer/GPServer/DemStatisticsRaster"); _geoprocessor.JobCompleted += new EventHandler
(_geoprocessor_JobCompleted); _geoprocessor.Failed += new EventHandler
(_geoprocessor_Failed); _geoprocessor.GetResultDataCompleted += new EventHandler
(_geoprocessor_GetResultDataCompleted); myDraw = new Draw(map1); myDraw.DrawMode = DrawMode.Polygon; myDraw.IsEnabled = false; myDraw.FillSymbol = new SimpleFillSymbol() { BorderBrush = new SolidColorBrush(Colors.Black), BorderThickness = 3, Fill = new SolidColorBrush(Colors.Red), }; myDraw.LineSymbol = new LineSymbol() { Color=new SolidColorBrush(Colors.Red), Width=3 }; myDraw.DrawComplete += new EventHandler
(myDraw_DrawComplete); QueryTypeCombox.ItemsSource = AttributeString.GraphicAttributesEnNameString; } private void myDraw_DrawComplete(object sender, DrawEventArgs e) { graphicsLayer.Graphics.Clear(); drawGeometry = e.Geometry; myDraw.IsEnabled = false; if (drawGeometry is ESRI.ArcGIS.Client.Geometry.Polyline) { CreatPoint((ESRI.ArcGIS.Client.Geometry.Polyline)drawGeometry,100); BusyRectangle.Visibility = Visibility.Visible; busyIndicator.IsBusy = true; AccessGPService(featureSet); } else { StartQueryBySpatial(e.Geometry); } } private void ExpressionButton_Click(object sender, RoutedEventArgs e) { string pattern = @"^[0-9]*$"; Button button = sender as Button; Match m = Regex.Match(button.Content.ToString(), pattern); if (m.Success) ExpressionTextBox.Text = string.Format("{0}{1}", ExpressionTextBox.Text, button.Content); else ExpressionTextBox.Text = string.Format("{0} {1} ", ExpressionTextBox.Text, button.Content); } private void ExpressionQueryButton_Click(object sender, RoutedEventArgs e) { CreatQueryString(); StartQueryBySQL(QueryString); } private void SpatialQueryButton_Click(object sender, RoutedEventArgs e) { ExpressionTextBox.Text = ""; myDraw.DrawMode = DrawMode.Polygon; myDraw.IsEnabled = true; } private void FiledsCombox_SelectionChanged(object sender, SelectionChangedEventArgs e) { ComboBox comb = sender as ComboBox; if (comb.SelectedIndex != -1) { int index = comb.SelectedIndex; ExpressionTextBox.Text = string.Format("{0}{1}", ExpressionTextBox.Text, comb.SelectedItem.ToString()); } comb.SelectedIndex = -1; } private void ExpressionTextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) { // 按了回车键就向服务端发送数据 if (e.Key == Key.Enter) { CreatQueryString(); StartQueryBySQL(QueryString); } } private void QoutButton_Click(object sender, RoutedEventArgs e) { Button button = sender as Button; ExpressionTextBox.Text = string.Format("{0}{1}", ExpressionTextBox.Text, button.Content); } private void NoButton_Click(object sender, RoutedEventArgs e) { Button button = sender as Button; ExpressionTextBox.Text = string.Format("{0} {1}", ExpressionTextBox.Text, button.Content); } private void ClearGraphicsButton_Click(object sender, RoutedEventArgs e) { graphicsLayer.Graphics.Clear(); } private void GraphicsLayer_MouseLeftButtonDown(object sender, GraphicMouseButtonEventArgs e) { if (selectedGraphic != null) { selectedGraphic.UnSelect(); selectedGraphic.Symbol = new SimpleFillSymbol() { BorderBrush = new SolidColorBrush(Colors.Red), BorderThickness = 2, Fill = new SolidColorBrush(Colors.Green) }; } e.Graphic.Select(); e.Graphic.Symbol = new SimpleFillSymbol() { Fill=new SolidColorBrush(Colors.Red), BorderBrush = new SolidColorBrush(Colors.Yellow), BorderThickness=3 }; if (e.Graphic.Selected&&MyDataGrid.GraphicsLayer!=null) MyDataGrid.ScrollIntoView(e.Graphic, null); selectedGraphic = e.Graphic; } #region 查询失败几完成事件 private void queryTask_ExecuteCompleted(object sender, QueryEventArgs e) { graphicsLayer.ClearGraphics(); if (e.FeatureSet.Features.Count > 0) { foreach (Graphic resultFeature in e.FeatureSet.Features) { if (drawGeometry != null) { //查询结果落在绘制的多边形之内 if (resultFeature.Geometry.Extent.XMax < drawGeometry.Extent.XMax && resultFeature.Geometry.Extent.XMin > drawGeometry.Extent.XMin && resultFeature.Geometry.Extent.YMax < drawGeometry.Extent.YMax && resultFeature.Geometry.Extent.YMin > drawGeometry.Extent.YMin) { resultFeature.Symbol = new SimpleFillSymbol() { BorderBrush = new SolidColorBrush(Colors.Red), BorderThickness = 2, Fill = new SolidColorBrush(Colors.Green) }; featureSet.Features.Add(resultFeature); graphicsLayer.Graphics.Add(resultFeature); } } else { resultFeature.Symbol = new SimpleFillSymbol() { BorderBrush = new SolidColorBrush(Colors.Red), BorderThickness = 2, Fill = new SolidColorBrush(Colors.Green) }; featureSet.Features.Add(resultFeature); graphicsLayer.Graphics.Add(resultFeature); } } if (graphicsLayer.Graphics.Count < 1) MessageBox.Show("没有查询到目标要素!"); } else { MessageBox.Show("没有查询到目标要素!"); } drawGeometry = null; } private void queryTask_Failed(object sender, TaskFailedEventArgs e) { MessageBox.Show("Query failed: " + e.Error); } #endregion #region 查询自定义方法 ///
/// 条件查询 /// ///
查询SQL语句 private void StartQueryBySQL(string sqlString) { queryTask.CancelAsync(); if (ExpressionTextBox.Text == "") return; Query query = new Query(); query.ReturnGeometry = true; query.OutFields.AddRange(new string[] { "NAME99", "ADCODE99" }); query.Where = sqlString; query.OutSpatialReference = map1.SpatialReference; queryTask.ExecuteAsync(query); } ///
/// 空间查询 /// ///
几何图形 private void StartQueryBySpatial(ESRI.ArcGIS.Client.Geometry.Geometry geometry) { queryTask.CancelAsync(); Query query = new Query(); query.ReturnGeometry = true; query.OutFields.AddRange(new string[] { "NAME99", "ADCODE99" }); query.Geometry = geometry; query.SpatialRelationship = SpatialRelationship.esriSpatialRelContains; query.OutSpatialReference = map1.SpatialReference; queryTask.ExecuteAsync(query); } ///
/// 构造查询Where条件的SQL语句 /// private void CreatQueryString() { if (QueryTypeCombox.SelectedIndex == 0) QueryString =string.Format("NAME99 = '{0}'",ExpressionTextBox.Text); else if (QueryTypeCombox.SelectedIndex == 1) QueryString = string.Format("ADCODE99 = '{0}'",ExpressionTextBox.Text); else return; } #endregion # region 调用GP服务进行栅格像元统计 private void StatisticButton_Click(object sender, RoutedEventArgs e) { if (featureSet.Features.Count <= 0) return; BusyRectangle.Visibility = Visibility.Visible; busyIndicator.IsBusy = true; AccessGPService(featureSet); } private void AccessGPService(FeatureSet featureset) { List
gppara = new List
(); //输入GP服务的参数 gppara.Add(new GPString("ZoneFiled", "OBJECTID")); gppara.Add(new GPFeatureRecordSetLayer("InputFeature", featureset)); gppara.Add(new GPString("ZonalRaster", "hunandata")); _geoprocessor.SubmitJobAsync(gppara); } private void _geoprocessor_JobCompleted(object sender, JobInfoEventArgs e) { HttpWebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp); Geoprocessor gp = sender as Geoprocessor; //请求GP服务的结果 gp.GetResultDataAsync(e.JobInfo.JobId, "ResultTable"); } private void _geoprocessor_GetResultDataCompleted(object sender, GPParameterEventArgs e) { MyDataGrid.ItemsSource = null; GPRecordSet gpr = e.Parameter as GPRecordSet; if (gpr.FeatureSet != null) { for (int i = 0; i < gpr.FeatureSet.Features.Count; i++) { for (int j = 2; j < gpr.FeatureSet.Features[i].Attributes.Count; j++) { //这里本文有个Bug,就是当FeatureDataGrid绑定到GraphicsLayer后, //如果修改GrapicsLayer的属性(增加或者删除)会报错,以下代码不能通过,所以这里选择了先修改再绑定 List
keyList = gpr.FeatureSet.Features[i].Attributes.Keys.ToList
(); List
valueList = gpr.FeatureSet.Features[i].Attributes.Values.ToList(); graphicsLayer.Graphics[i].Attributes.Add(keyList[j], valueList[j]); } } //在此绑定DataGrid,不然会出现异常 MyDataGrid.GraphicsLayer = graphicsLayer; MyDataGrid.Visibility = Visibility.Visible; //绘制地形图 if (createPlot == true) { CreatePlot(); TerrainBorder.Visibility = Visibility.Visible; } featureSet.Features.Clear(); } //等待过程 BusyRectangle.Visibility = Visibility.Collapsed; busyIndicator.IsBusy = false; } private void _geoprocessor_Failed(object sender, TaskFailedEventArgs e) { MessageBox.Show(e.Error.ToString()); } #endregion private void terrainButton_Click(object sender, RoutedEventArgs e) { myDraw.DrawMode = DrawMode.Polyline; TerrainBorder.Visibility = Visibility.Collapsed; myDraw.IsEnabled = true; createPlot = true; } private void CreatPoint(ESRI.ArcGIS.Client.Geometry.Polyline polyline,int count) { //默认沿直线构造一百个点 double xStep = (polyline.Paths[0][1].X - polyline.Paths[0][0].X) / count; double yStep = (polyline.Paths[0][1].Y - polyline.Paths[0][0].Y) / count; interval = Math.Sqrt(xStep * xStep + yStep * yStep); //清空FeatureSet便于之后的GP服务调用 if (featureSet.Features.Count > 0) { featureSet.Features.Clear(); } for (int i = 0; i < count; i++) { MapPoint mp = new MapPoint(polyline.Paths[0][0].X + i*xStep, polyline.Paths[0][0].Y + i*yStep); mp.SpatialReference = map1.SpatialReference; Graphic gPoint = new Graphic() { Symbol = new SimpleMarkerSymbol() { Color=new SolidColorBrush(Colors.Blue), Size=8, Style=SimpleMarkerSymbol.SimpleMarkerStyle.Circle }, Geometry=mp, }; featureSet.Features.Add(gPoint); graphicsLayer.Graphics.Add(gPoint); } } private void CreatePlot() { areaSeries1.Points.Clear(); areaSeries1.Points2.Clear(); plotModel.Series.Clear(); for (int i = 0; i < graphicsLayer.Graphics.Count; i++) { areaSeries1.Points.Add(new DataPoint(interval * i, Convert.ToDouble(graphicsLayer.Graphics[i].Attributes["MAX"]))); areaSeries1.Points2.Add(new DataPoint(interval * i, 0)); } plotModel.Series.Add(areaSeries1); areaSeries1.Title = "海拔"; Myplot.Model = plotModel; createPlot = false; } private void Button_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e) { // TODO: Add event handler implementation here. BusyButton.Content = "取消"; } private void Button_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e) { // TODO: Add event handler implementation here. BusyButton.Content = "正在计算..."; } private void BusyButton_Click(object sender, System.Windows.RoutedEventArgs e) { // TODO: Add event handler implementation here. BusyRectangle.Visibility = Visibility.Collapsed; busyIndicator.IsBusy = false; } } }

最后的MainPage.xaml代码:

View Code

最终的效果:

按县区名称查询双峰县,结果如下图所示:

正在统计双峰县的像元信息

统计结果如下图所示:

 

绘制一条直线

通过直线得到构造点,并进行像元统计

 

最后的统计结果,及地下剖面图,根据直线经过的地方来看,地形图是正确的。

 

在图上可以查看剖面图的数据:

空间查询,并进行统计

 

总结:以上的过程大致上说了一下Web端区域像元统计的过程,其中涉及到了要素查询,GP服务,Web端栅格数据作为输入数据的处理问题,OxyPlot图表控件的使用,FeatureDataGrid的数据绑定等,有些内容进行了详细的说明,有些由于时间限制就没有进行细致的说明,在此还望谅解。有什么问题可以留言反映,我一定都会逐一回复。当然程序中还有很多Bug,也没有处理,程序的结构也不是很好,代码质量也不高,希望高手勿喷,欢迎您提出宝贵的意见和分享您的解决方法。

还有需要提醒一点的是本文使用的服务都是我自己发布的服务,所以您需要自己发布程序中用到的服务。

 【】