一个基于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控件来搭建,下面看图:
菜单栏提供了程序中所有的功能,工具条提供了地图的平移、放大、缩小、属性查询的快捷方式,左侧的树控件提供了图层可见性的控制。
属性查询窗体中提供了选择地物的分类和元素的管理,当在左侧树控件中选择时将属性信息显示在右侧的表格中。
(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集合编辑器,现在可以像集合中添加工具了,如下图:
下面我们使用这些基本功能,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);
}
}
}
由于我们在地图上选择的时候,无论是点选或是框选都有可能同时选择到很多的地物,所以要分别按类别把选择到的地物分类管理,填充到树控件中。
这时我们希望当点击某一个层时,能够显示所有这一层上的元素,当点击某一条记录时只显示这一条记录,因此要对点击的节点进行判断,判断的主要依据就是这个节点是有有父节点!
//当点击子节点时判断节点所在的表,找到相应的记录并在右侧表格中显示数据
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