一个基于ArcGIS Mobile 9.3的GIS系统搭建方法

        接触ArcGIS Server 9.3已经有半年的时间了,主要使用ArcGIS Server中提供的Moible SDK开发一个GIS数据采集软件,半年来多多少少也对ArcGIS Mobile有了肤浅的了解,这篇文章主要是介绍一下如何使用Moible SDK开发一个具有基本GIS功能的系统,同时向即将使用ArcGIS Mobie进行开发工作的同学提供一点点的帮助。

        ArcGIS Mobile是ArcGIS Server 9.2中新加入的一个SDK,用来开发与ArcGIS Server服务式GIS平台无缝集成的Mobile应用程序,也可以运行在桌面计算机上。目前SDK最高版本为9.3,不过在ESRI今年发布的ArcGIS 10中会被升级到ArcGIS Mobile 10版本,具体的一些介绍可以到ESRI网站上去搜索。

        据我所知,目前国内使用ArcGIS Mobile做开发的人和公司好像没有多少,毕竟是新东西,技术含量比较高,这一点从ESRI中国社区论坛上就可以看出来,零星的有人问个特简单的问题。 网上目前也有介绍ArcGIS Mobile开发的资料,比较推荐的是“牛魔王的作坊”,这个在百度里面搜索一下就能找到。好了,废话少说现在就开始简单的介绍一下这个小系统是如何实现的!!!

(1)程序UI设计

        比较经典的GIS系统通常要具备地图的显示、地图漫游功能、图层控制功能、属性查询以及量测工具等等。这些功能也是GIS系统最核心的基础功能。该系统界面主要使用了菜单栏、工具条、树控件、Map控件、DataGrid控件来搭建,下面看图:

一个基于ArcGIS Mobile 9.3的GIS系统搭建方法        菜单栏提供了程序中所有的功能,工具条提供了地图的平移、放大、缩小、属性查询的快捷方式,左侧的树控件提供了图层可见性的控制。一个基于ArcGIS Mobile 9.3的GIS系统搭建方法

              属性查询窗体中提供了选择地物的分类和元素的管理,当在左侧树控件中选择时将属性信息显示在右侧的表格中。

(2)功能实现

        在文件菜单中提供了“打开地图”命令,用于选择本地磁盘中的地图文件。由于ArcGIS Mobile是与ArcGIS Server无缝集成的,它所使用的数据源均为Server发布的服务,包括MapService、GeoCoding、GeoProcessing等等,所以本地磁盘的地图文件实际上是保存在磁盘上的地图缓存,它们是以.bin为结尾的。

        地图缓存也可以通过ArcToolbox中提供的Mobile Tools制作。下面看代码:

【1】打开文件

        在打开文件对话框中返回文件的绝对路径,使用FileInfo和DirectoryInfo两个类获取文件的父路径。  

        private void OpenMapMenuItem_Click(object sender, EventArgs e)
        {
            string path = "";

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string filename = openFileDialog1.FileName;
                FileInfo file = new FileInfo(filename);
                DirectoryInfo fileinfo = file.Directory;
                path = fileinfo.ToString();
                OpenMapFiles(path);//打开地图文件
                map1.CurrentMapAction = panMapAction1;
            }
        } 

        OpenMapFiles()方法用来设置地图文件路径、URL,最后加载服务。

        private void OpenMapFiles(string path)
        {
            try
            {
                mobileService1.Close();
                mobileService1.CacheStoragePath = path;//设置缓存路径
                mobileService1.Open(CacheOpenMode.Open);//加载缓存
                PopLayers();
            }
            catch (FileNotFoundException ex)
            {
                MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK);
            }
            catch (OverflowException ex)
            {
                MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK);
            }         
        }

        PopLayers()方法用于向树控件中添加节点,节点的Text属性均被设置为图层名。

        private void PopLayers()
        {
            treeView1.Nodes.Clear();

            foreach (MapLayer layer in map1.MapLayers)
            {
                MobileServiceMapLayer mapLayer = layer as MobileServiceMapLayer;

                if (mapLayer == null)
                    continue;

                FeatureLayer feaLayer = mapLayer.Layer as FeatureLayer;

                if (feaLayer == null)
                    continue;

                string layerName = feaLayer.Name;
                treeView1.Nodes.Add(layerName);
            }

            foreach (TreeNode node in treeView1.Nodes)
            {
                node.Checked = true;
            }
        }

        这里要提一下,ArcGIS Mobile中的图层的概念和AE中的图层有些不一样,在AE中Map控件下面包括的就是实际的图层,而Mobile使用的数据源均为服务器发布的服务,所以Map控件的MapLayers集合中存放的均为服务层,即MobileServiceLayer,它可以包括FeatureLayer、RasterLayer等等,详细的说明可以查看开发文档,不过E文要好啊!

【2】地图浏览功能

        地图的浏览功能其实都是Map控件提供的一些基本功能,我们注意一下Map控件的属性中有一项叫做“MapActions”,点击右侧的“...”按钮,可以看到MapActions集合编辑器,现在可以像集合中添加工具了,如下图:一个基于ArcGIS Mobile 9.3的GIS系统搭建方法

        下面我们使用这些基本功能,Map控件有一个CurrentMapAction属性,它可以设置Map控件的行为,我们可以在代码中进行操作。

        //平移
        private void PanMenuItem_Click(object sender, EventArgs e)
        {
            map1.CurrentMapAction = panMapAction1;
        }

        //放大
        private void ZoomInMenuItem_Click(object sender, EventArgs e)
        {
            map1.CurrentMapAction = zoomInMapAction1;
        }

        //缩小
        private void ZoomOutMenuItem_Click(object sender, EventArgs e)
        {
            map1.CurrentMapAction = zoomOutMapAction1;
        }

        //元素识别
        private void IdentifyMenuItem_Click(object sender, EventArgs e)
        {
            map1.CurrentMapAction = selectionMapAction1;
            selectionMapAction1.SelectionType = SelectionType.Point;
        }

        平移、放大、缩小功能只要将相应的值赋给CurrentMapAction就可以了,对于属性查询功能小比较来说就要麻烦点,首先要把地图的行为设置为选择,并且设置它的选择类型,可以点选、框选等等。回到上面的MapActions集合编辑器中,当向集合中添加selectionMapAction1项时,可以为它注册StatusChanged事件处理程序,当我们在地图上选择Features时,就会触发这个事件,这时我们可以从selectionMapAction1中获得所选择到的Features,剩下的就是现实数据的问题了!

        //处理选择状态并弹出属性表
        private GridForm grid;
        private void selectionMapAction1_StatusChanged(object sender, MapActionStatusChangedEventArgs e)
        {
            if (e.StatusId != 1005)//判断选择的状态是否完成,很重要!
                return;

            if (selectionMapAction1.SelectedFeatures.Count < 1)
                return;

            List<FeatureDataTable> feaTables = selectionMapAction1.SelectedFeatures as List<FeatureDataTable>;
            grid = new GridForm(feaTables,map1);
            grid.Show();
        }

        当捕获到选择事件后,可以通过selectionMapAction1.SelectedFeatures返回选择到的元素,它们可以是不同图层的多个表!由于要在不同的窗体*享数据,我在属性窗体中定义了一个非默认构造函数,用来传递集合和Map控件。

        public GridForm(List<FeatureDataTable> tables,ESRI.ArcGIS.Mobile.Map map)
        {
            InitializeComponent();
            tempTables = tables;
            tempMap=map;
            PopSelectedFeas();
        }

        //向树控件中填充选择到的元素类型和记录
        private void PopSelectedFeas()
        {
            SelectionResultTreeView.Nodes.Clear();

            //注意节点的层次性
            foreach (FeatureDataTable tempTable in tempTables)
            {
                string tableName = tempTable.TableName;
                TreeNode tempNode=SelectionResultTreeView.Nodes.Add(tableName);

                foreach (FeatureDataRow tempRow in tempTable.Rows)
                {
                    string rowID = tempRow.Fid.ToString();
                    tempNode.Nodes.Add(rowID);
                }
            }
        }

        由于我们在地图上选择的时候,无论是点选或是框选都有可能同时选择到很多的地物,所以要分别按类别把选择到的地物分类管理,填充到树控件中。

一个基于ArcGIS Mobile 9.3的GIS系统搭建方法

         这时我们希望当点击某一个层时,能够显示所有这一层上的元素,当点击某一条记录时只显示这一条记录,因此要对点击的节点进行判断,判断的主要依据就是这个节点是有有父节点!

        //当点击子节点时判断节点所在的表,找到相应的记录并在右侧表格中显示数据
        private void SelectionResultTreeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            TreeNode selectedNode = e.Node;
            string nodeName = selectedNode.Text;

            if (selectedNode.Parent == null)
            {
                string layerName = nodeName;

                //用来保存临时从表集合中寻找到的表
                FeatureDataTable tempTinL = null;
                foreach (FeatureDataTable feaTable in tempTables)
                {
                    if (feaTable.TableName == layerName)
                        tempTinL = feaTable;
                }              

                if(tempTinL!=null)
                    dataGridView1.DataSource = tempTinL;
            }
            else
            {
                TreeNode parentNode = selectedNode.Parent;
                string layerName = parentNode.Text;

                //用来保存临时从表集合中寻找到的表
                FeatureDataTable tempTinL = null;

                foreach (FeatureDataTable feaTable in tempTables)
                {
                    if (feaTable.TableName == layerName)
                        tempTinL = feaTable;
                }              

                int id = Convert.ToInt32(nodeName);
                int fid=tempTinL.FidColumnIndex;
                FeatureDataRow temp = null;

                //寻找ID值与所选择记录ID相等的记录
                foreach (FeatureDataRow tempRow in tempTinL.Rows)
                {
                    if (Convert.ToInt32(tempRow[fid]) == id)
                        temp = tempRow as FeatureDataRow;
                }

                if (temp != null)
                {
                    //复制表的结构而不是复制所有数据
                    DataTable table = tempTinL.Clone();
                    table.Rows.Add(temp.ItemArray);
                    dataGridView1.DataSource = table;
                }
            }
        }

【3】工具

        在这个菜单中提供了距离和面积量测功能,在实现的过程中为了简单起见,使用了Map控件的AddVertexSketchTool工具,这个工具会根据SketchGraphicLayer的Geometry类型绘制不同的地物,不同的地物会提供如Length和Area等属性,这样很容易提取他们的距离和面积。同时还要注意这些结果使用的单位和制图时所使用的单位是一致的!我们无法更改。

        //距离测量
        private void LineMeaMenuItem_Click(object sender, EventArgs e)
        {
            ClearSketchGraphicLayer();
            panelMeasure.Visible = true;

            if (map1.CurrentMapAction != addVertexSketchTool1)
            {
                map1.CurrentMapAction = addVertexSketchTool1;
                sketchGraphicLayer1.Geometry = new Polyline();//绘制的地物是线类型
            }
            else
            {
                sketchGraphicLayer1.Geometry = new Polyline();
            }

            tempGeometry = sketchGraphicLayer1.Geometry;
        }

        //面积测量
        private void AreaMeaMenuItem_Click(object sender, EventArgs e)
        {
            ClearSketchGraphicLayer();
            panelMeasure.Visible = true;

            if (map1.CurrentMapAction != addVertexSketchTool1)
            {
                map1.CurrentMapAction = addVertexSketchTool1;
                sketchGraphicLayer1.Geometry = new Polygon();//绘制的地物是面状的
            }
            else
            {
                sketchGraphicLayer1.Geometry = new Polygon();
            }

            tempGeometry = sketchGraphicLayer1.Geometry;
        }

        //清除绘制的临时Geometry
        private void ClearSketchGraphicLayer()
        {
            if(sketchGraphicLayer1.Geometry!=null)
                sketchGraphicLayer1.Geometry.Clear();//SketchGraphicLayer也是MapLayers集合中的一个层,用来保存绘制的元素,也可以被保存到实际的图层中
        }

        测量的结果显示在一个Panel上。

        //显示测量结果面板
        private void CalculateBtn_Click(object sender, EventArgs e)
        {
            if (tempGeometry.GeometryType == GeometryType.Polyline)
            {
                Polyline tempLine = tempGeometry as Polyline;
                MeaResultLab.Text = tempLine.GetLength().ToString();//用来获取线的长度
            }
            else
            {
                Polygon tempArea = tempGeometry as Polygon;
                MeaResultLab.Text = tempArea.GetArea().ToString();//用来获取面积
            }
        }

【4】图层管理器

        图层管理器是用来控制图层的可见性、可选择性、可编辑性的工具,在该系统中只是简单的实现了图层的可见性,图层的可见性可以通过MapLayers[].Visible属性控制。

        //动态的显示或关闭图层
        private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
        {
            TreeNode selectedNode = e.Node;
            bool nodeState=selectedNode.Checked?true:false;
            string layerName=selectedNode.Text;
            selectedLayer = mobileService1.Layers[layerName] as FeatureLayer;

            if (nodeState)
            {
                map1.MapLayers[layerName].Visible = nodeState;
            }
            else
            {
                map1.MapLayers[layerName].Visible = nodeState;
            }
        }

(3)总结

        本文只是简单的把ArcGIS Mobile的功能做一个介绍,要想发挥Mobile的强大功能还需要结合ArcGIS Server来使用,例如我们可以为Windows Mobile系统的设备开发Mobile程序,在野外采集数据并实时的将数据回传到服务器,并且ArcGIS Server与SDE也是无缝集成的,所以后台的数据库可以是多版本的,这样数据冲突的问题也能得到很好的控制。

        可参考的资源:

        http://resources.esri.com/arcgismobile/ ArcGIS Mobile资源中心

        http://wu-yongfeng.blogspot.com/2008/08/arcgis-mobile-sdk-93.html 牛魔王的作坊,使用代理访问

转载于:https://www.cnblogs.com/1986ssy/archive/2010/03/03/1677005.html