先打一个广告:我的独立博客网址是:http://wuyouqiang.sinaapp.com/。
我的新浪微博:http://weibo.com/freshairbrucewoo。
欢迎大家相互交流,共同提高技术。
本系统的分层按照“数据分类”、“要素类别”和“图层”进行组织管理。一个数据分类包含一个或多个要素类别,一个要素类别包含一个或多个图层。图层分层标准可以作为创建图库的数据集和要素类时的参考标准。
数据分类和要素类别的定义是通过将用户提供的相关信息存储到数据库中,主要的信息有相应的名称、ID编号、描述等,存放要素类别的表还保存有数据分类的ID,这样就可以知道要素类别是属于哪一个数据分类了。数据分类和要素类别的管理就是管理存放这些数据的两种表,只需要对这两张表通过SQL语句进行相应的添加、删除和修改就实现了。
图层标准的管理通过将用户定义的标准写入数据库,图层标准主要涉及到图层名称、图层映射的表名、图层中文名称、图层编号、图层描述等信息。图层还必须对应一张数据表来存放图层的数据,而这张表本身就是属于图层标准的一部分,所以在定义一个图层标准的同时还需要定义映射表的数据结构标准,定义这种表的信息会全部存储到另一张表中,这些信息在导入标准和创建图层的时候都可能用到,这些信息包括字段名称、字段类型、字段长度和字段约束等。
“数据分类”、“要素类别”和“图层”之间关系如下图所示。
下面根据代码来讲解具体每一个功能的详细实现。
1.在Load函数中初始化根节点:用树形控件来表示数据分层管理的层次。
/// <summary> /// 初始化根节点 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void FrmLayerManager_Load(object sender, EventArgs e) { Node n = new Node(); n.Text = "数据分层管理"; n.Image = Resources.category; LayerManagerTree.Nodes.Add(n); n.Nodes.Add(new Node()); }2.树形一个节点展开后的处理工作,就是从数据库中读取以后的下层节点,并在节点中显示。
/// <summary> /// 树形控件的节点展开以后的处理,更新所有的子节点 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void LayerManagerTree_AfterExpand(object sender, AdvTreeNodeEventArgs e) { UpdateNode(e.Node); } /// <summary> /// 更新tn节点的子节点信息 /// </summary> /// <param name="tn"></param> private void UpdateNode(Node tn) { string sql; SqlHelper sh = new SqlHelper(); OracleDataReader odr; switch (tn.Level) { case 0: tn.Nodes.Clear(); sql = "select name from category where name='数据标准'"; odr = sh.ReturnDataReader(sql); while (odr.Read()) { Node n = new Node(); n.Text = odr[0].ToString(); n.Image = Resources.datastand; tn.Nodes.Add(n); n.Nodes.Add(new Node()); } break; case 1: tn.Nodes.Clear(); sql = "select name from element where category='" + tn.Text + "'"; odr = sh.ReturnDataReader(sql); while (odr.Read()) { Node n = new Node(); n.Text = odr[0].ToString(); n.Image = Resources.layer; tn.Nodes.Add(n); n.Nodes.Add(new Node()); } break; case 2: tn.Nodes.Clear(); sql = "select table_name from layer l, jcsjk_element e where e.id = l.pid and e.name='" + tn.Text + "' and e.category='数据标准'"; odr = sh.ReturnDataReader(sql); while (odr.Read()) { Node n = new Node(); n.Text = odr[0].ToString(); tn.Nodes.Add(n); } break; default: break; } }3.当在树形控件中选择一个节点时,在右边控件中显示其具体定义。
/// <summary> /// 选择树形控件的某一个节点,在右边的DataGridView控件中显示定义标准 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void LayerManagerTree_AfterNodeSelect(object sender, AdvTreeNodeEventArgs e) { Node n = new Node(); n = e.Node; selectNodeText = n.Text; UpdateTree(n.Level); } /// <summary> /// 显示某一个标准的具体定义 /// </summary> /// <param name="index"></param> private void UpdateTree(int index) { string sql = string.Empty; SqlHelper sh = new SqlHelper(); DataSet ds; switch (index) { case 1: sql = "select d.ID,TABLE_NAME,TABLE_MAPNAME,TYPE,d.DESCRIPTION from " + "layer d,element e,category c where d.pid=e.id" + " and category=c.name and c.name='" + selectNodeText + "'"; break; case 2: sql = "select l.ID,TABLE_NAME,TABLE_MAPNAME,TYPE,l.DESCRIPTION from layer l,element e " + "where l.pid=e.id and e.name = '" + selectNodeText + "' and e.category='数据标准'"; break; case 3: sql = "select l.ID,TABLE_NAME,TABLE_MAPNAME,TYPE,l.DESCRIPTION from layer l,element e where table_name='" + selectNodeText + "' and e.category='数据标准' and l.pid=e.id"; break; default: break; } if (index >= 1 && index <= 3) { ds = sh.ReturnDataSet(sql, "jcslayer"); dataGridViewX1.DataSource = ds.Tables[0]; dataGridViewX1.Columns[0].HeaderText = "图层代码"; dataGridViewX1.Columns[1].HeaderText = "图层名"; dataGridViewX1.Columns[2].HeaderText = "图层映射名"; dataGridViewX1.Columns[3].HeaderText = "类型"; dataGridViewX1.Columns[4].HeaderText = "描述"; } }4.鼠标右击树形控件节点,显示对应的右击菜单
/// <summary> /// 鼠标右击树形控件节点,显示对应的右击菜单 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void LayerManagerTree_NodeMouseDown(object sender, TreeNodeMouseEventArgs e) { if (e.Button == MouseButtons.Right) { Node n = new Node(); n = e.Node; selectNodeText = n.Text; switch (n.Level) { case 0: contextMenuStrip1.Show(MousePosition); break; case 1: contextMenuStrip2.Show(MousePosition); break; case 2: contextMenuStrip3.Show(MousePosition); break; case 3: contextMenuStrip4.Show(MousePosition); break; default: break; } } }5.添加数据分类
/// <summary> /// 添加数据分类 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void AddDataCategory_Click(object sender, EventArgs e) { FrmCategory adl = new FrmCategory(); adl.Text = "添加地图数据分类"; adl.ShowDialog(); Node n = LayerManagerTree.FindNodeByText(selectNodeText); UpdateTree(n.Level);//更新树形控件 UpdateNode(n);//更新节点 }在添加数据分类、修改数据分类、添加要素分类和更新要素分类都是用到一个FrmCategory类,它也是一个和对话框关联的类,下面是这个类具体的实现代码(不包括界面代码):
private string saveId;//保存原ID,以备更新使用 private string saveName;//保存原Name public string categoryName;//保存原分类名称 public FrmCategory() { InitializeComponent(); } //根据Tag值的不同实现不同的功能,因为界面都是一样的 //四个功能:1.添加数据分类;2.更新数据分类;3.添加要素类分类;4.更新要素类分类 private void AddBtn_Click(object sender, EventArgs e) { //1.不为空检查 if (IdTxt.Text.Trim() == "" || NameTxt.Text.Trim() == "") { ErrLabel.Text = "类别编码和类别名称都不能为空!"; return; } //2.记录已存在检查 SqlHelper sh = new SqlHelper(); string sql; //根据标签判断是什么功能 if (this.Tag == "update") { //更新数据分类 sql = "select * from category where name='" + NameTxt.Text + "'" + " and name!='" + saveName + "'" + " or id=" + int.Parse(IdTxt.Text) + " and id!=" + saveId; } else if (this.Tag == "addElement") { //增加要素分类 sql = "select * from element where id=" + int.Parse(IdTxt.Text) + " or name='" + NameTxt.Text + "' and category='" + categoryName + "'"; } else if (this.Tag == "updateElement") { //更新要素分类 sql = "select * from element where id=" + int.Parse(IdTxt.Text) + " and id!=" + saveId + " or name='" + NameTxt.Text + "'" + " and name!='" + saveName + "' and category='" + categoryName + "'"; } else { //添加数据分类 sql = "select * from category where id=" + int.Parse(IdTxt.Text) + " or name='" + NameTxt.Text + "'"; } if (sh.GetRecordCount(sql) > 0) { ErrLabel.Text = "类别编码或类别名称已存在!"; return; } //3.构造插入或更新哈希表 Hashtable ht = new Hashtable(); ht.Add("ID", IdTxt.Text); ht.Add("NAME", NameTxt.Text); ht.Add("DESCRIPTION", DescTxt.Text); //如果是增加要素分类,就需要多添加两个字段的值 if (this.Tag == "addElement") { ht.Add("category", categoryName); ht.Add("datasetname", comboBoxEx1.SelectedItem.ToString()); } if (this.Tag == "update") { //更新了数据分类的名称,要素类分类的分类名称跟着改变 if (sh.Update("category", "ID=" + saveId, ht) <= 0) { ErrLabel.Text = "更新地图数据分层类别失败"; return; } else { ht.Clear(); ht.Add("category", NameTxt.Text); sh.Update("element", "category='" + saveName + "'", ht); } } else if (this.Tag == "addElement") { if (sh.Insert("element", ht) <= 0) { ErrLabel.Text = "添加要素分类失败"; return; } } else if (this.Tag == "updateElement") { ht.Add("datasetname", comboBoxEx1.SelectedItem.ToString()); //更新了要素类分类的id,图层的pid相应改变 if (sh.Update("element", "ID=" + saveId, ht) <= 0) { ErrLabel.Text = "更新地图数据分层类别失败"; return; } else { ht.Clear(); ht.Add("pid", IdTxt.Text); sh.Update("layer", "pid='" + saveId + "'", ht); } } else { if (sh.Insert("category", ht) <= 0) { ErrLabel.Text = "添加地图数据分层类别失败"; return; } } MessageBox.Show("操作成功!"); this.Close(); } private void CancelBtn_Click(object sender, EventArgs e) { this.Close(); } private void AddDataLayer_Load(object sender, EventArgs e) { //如果是更新操作就保存原来的ID和名称 if (this.Tag == "update" || this.Tag == "updateElement") { //IdTxt.Enabled = false; saveId = IdTxt.Text; saveName = NameTxt.Text; } //如果是要素类分类操作就加载具体的数据集,以便指定要素类属于哪一个数据集 if (this.Tag == "addElement" || this.Tag == "updateElement") { labelX4.Visible = true; comboBoxEx1.Visible = true; IFeatureWorkspace pFW = MapOperation.GetFeatrueWorkspace(); IWorkspace pW = pFW as IWorkspace; IEnumDataset pED = pW.get_Datasets(esriDatasetType.esriDTFeatureDataset); IFeatureDataset pFD = pED.Next() as IFeatureDataset; while (pFD != null) { comboBoxEx1.Items.Add(pFD.Name); pFD = pED.Next() as IFeatureDataset; } comboBoxEx1.Items.Add("属性表"); comboBoxEx1.SelectedIndex = 0; } } /// <summary> /// 设置ID /// </summary> /// <param name="id">ID值</param> public void SetId(string id) { IdTxt.Text = id; } /// <summary> /// 设置名称 /// </summary> /// <param name="name">名称值</param> public void SetName(string name) { NameTxt.Text = name; } /// <summary> /// 设置描述 /// </summary> /// <param name="description">描述值</param> public void SetDescription(string description) { DescTxt.Text = description; } /// <summary> /// 设置数据集名称 /// </summary> /// <param name="strDataSet">数据集名称</param> public void SetDataSet(string strDataSet) { comboBoxEx1.SelectedIndex = comboBoxEx1.FindString(strDataSet); }6.添加要素类别,通用使用上面提到的那个类和其操作界面,通过不同的命令来执行不同的操作(用一个变量来表示)。
/// <summary> /// 添加要素类别 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void AddElementCategory_Click(object sender, EventArgs e) { FrmCategory fc = new FrmCategory(); fc.Tag = "addElement"; fc.Text = "添加要素分类"; fc.categoryName = selectNodeText; fc.ShowDialog(); Node n = LayerManagerTree.FindNodeByText(selectNodeText); UpdateTree(n.Level);//更新树形控件 UpdateNode(n);//更新节点 }7.删除数据分类,就是把数据库中定义数据分类的记录删除掉。
/// <summary> /// 删除数据分类 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DelDataCategory_Click(object sender, EventArgs e) { SqlHelper sh = new SqlHelper(); Hashtable ht = new Hashtable(); Node n = LayerManagerTree.FindNodeByText(selectNodeText); foreach (Node t in n.Nodes) { DeleteElement(t.Text); } ht.Add("name", n.Text); if (sh.Del("jcsjk_category", "name='" + selectNodeText + "'", ht) > 0) { n.Remove(); MessageBox.Show("删除地图数据分类成功"); } else { MessageBox.Show("删除地图数据分类失败"); } }8.更新数据分类,同样是更新数据库的一条定义的记录。
/// <summary> /// 更新数据分类 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UpdateDataCategory_Click(object sender, EventArgs e) { FrmCategory fc = new FrmCategory(); fc.Tag = "update"; fc.Text = "更新地图数据分类"; fc.categoryName = selectNodeText; SqlHelper sh = new SqlHelper(); string sql = "select id,name, description from category where name='" + selectNodeText + "'"; OracleDataReader odr = sh.ReturnDataReader(sql); if (odr.Read()) { fc.SetId(odr[0].ToString()); fc.SetName(odr[1].ToString()); fc.SetDescription(odr[2].ToString()); } fc.ShowDialog(); }9.其他操作,其他的操作包括要素类的增删改等,具体实现方式都差不多,只是针对不同的层或内容进行操作。
/// <summary> /// 添加图层 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void AddLayerCategory_Click(object sender, EventArgs e) { FrmLayer fl = new FrmLayer(); fl.Text = "添加图层"; fl.category = selectNodeText; fl.ShowDialog(); Node n = LayerManagerTree.FindNodeByText(selectNodeText); UpdateTree(n.Level); UpdateNode(n); } /// <summary> /// 删除要素类别 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DelElementCategory_Click(object sender, EventArgs e) { if (DeleteElement(selectNodeText)) { Node n = LayerManagerTree.FindNodeByText(selectNodeText); n.Remove(); MessageBox.Show("删除要素类别成功!"); } else { MessageBox.Show("删除要素类别失败!"); } } /// <summary> /// 删除一个要素类别 /// </summary> /// <param name="name">要素名称</param> /// <returns>成功true,失败false</returns> private bool DeleteElement(string name) { bool result = false; Node n = LayerManagerTree.FindNodeByText(name); foreach (Node t in n.Nodes) { DeleteLayer(t.Text); } SqlHelper sh = new SqlHelper(); Hashtable ht = new Hashtable(); ht.Add("name", name); if (sh.Del("element", "name='" + name + "' and category='数据标准'", ht) > 0) { result = true; } else { result = false; } return result; } /// <summary> /// 更新要素类别 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UpdateElementCategory_Click(object sender, EventArgs e) { FrmCategory fc = new FrmCategory(); fc.Tag = "updateElement"; fc.Text = "更新要素类别"; SqlHelper sh = new SqlHelper(); string sql = "select id,name,description,datasetname from element where name='" + selectNodeText + "' and category='数据标准'"; OracleDataReader odr = sh.ReturnDataReader(sql); if (odr.Read()) { fc.SetId(odr[0].ToString()); fc.SetName(odr[1].ToString()); fc.SetDescription(odr[2].ToString()); fc.SetDataSet(odr[3].ToString()); } fc.ShowDialog(); } /// <summary> /// 删除图层定义 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DelLayerDefine_Click(object sender, EventArgs e) { if (DeleteLayer(selectNodeText)) { Node n = LayerManagerTree.FindNodeByText(selectNodeText); n.Remove(); MessageBox.Show("删除图层定义成功!"); } else { MessageBox.Show("删除图层定义失败!"); } } /// <summary> /// 更新图层定义 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UpdateLayerDefine_Click(object sender, EventArgs e) { FrmLayer fl = new FrmLayer(); fl.Text = "更新图层定义"; fl.Tag = "update"; //fl.category = selectNodeText; SqlHelper sh = new SqlHelper(); string sql = "select l.ID,TABLE_NAME,TABLE_MAPNAME,TYPE,l.DESCRIPTION from " + "layer l,element e where table_name='" + selectNodeText + "' and l.pid=e.id and category='数据标准'"; OracleDataReader odr = sh.ReturnDataReader(sql); if (odr.Read()) { fl.SetId(odr[0].ToString()); fl.SetTableName(odr[1].ToString()); fl.SetTabelMapName(odr[2].ToString()); if ("PL" == odr[3].ToString()) { fl.SetType(1); } else if ("PA" == odr[3].ToString()) { fl.SetType(2); } else { fl.SetType(0); } fl.SetDescription(odr[4].ToString()); } fl.ShowDialog(); } /// <summary> /// 删除一个图层 /// </summary> /// <param name="layerName">图层名称</param> /// <returns>成功true,失败false</returns> private bool DeleteLayer(string layerName) { bool result = false; SqlHelper sh = new SqlHelper(); string sql = "select l.id from layer l,element e where e.id=l.pid and table_name='" + layerName + "' and category='数据标准'"; OracleDataReader odr = sh.ReturnDataReader(sql); int layerId; if (odr.Read()) { layerId = int.Parse(odr[0].ToString()); } else { return false; } Hashtable ht = new Hashtable(); ht.Add("id", layerId); if (sh.Del("layer", "id=" + layerId, ht) > 0) { result = true; } else { result = false; } return result; }10.总结
这里主要实现的是对空间数据进行分类管理的一种方案,以后导入导出或查询的空间数据都在这些分类下面进行操作,还有一个例外,就是不属于任何分类的数据,简单称为游离数据或图层。这里的实现都比较简单,主要是一些对数据库的操作,在配合一些逻辑操作就OK了,没有什么技术含量。