基于ArcEngine進行數據共邊檢查
問題描述:
用戶提供的數據經常會存在共邊問題,將數據提報給上級后審核不通過,導致需要數據反復修改,因此需要增加數據共邊檢查,自行檢查無誤后再進行上報,因為這些問題人工檢查比較困難,因此考慮使用工具進行檢查。
問題分析:
從用戶提供數據分析,主要存在下面幾種問題:
1、存在點偏移,應該是同一個點的坐標有細微偏差,可能會導致兩個圖形之間存在輕微的壓蓋、或者碎屑多邊形等;
2、共邊的點數不一致;
思路整理:
考慮使用ArcEngine工具編寫相關的分析功能,主要是調用ToolBox工具箱中的工具以及GP服務進行分析,整理思路如下:
1、進行圖形相交處理分析,輸出結果為面,如果存在相交,則不合格,返回相關記錄;
2、進行圖形之間距離計算,如果距離大于0并小于閾值,則認為其應該是共點或者共邊,返回相關記錄;
3、進行數據擦除分析,使用數據的公共外接矩形擦除分析數據(我這里為了方便直接使用行政區的外接矩形),擦除后進行圖形打散(因為擦除后返回一個圖形,可能是多部件),檢查是否存在碎屑多邊形(面積小于閾值),返回相關記錄;
4、進行分析數據相交處理,設置返回結果為線,獲取到圖形的公共邊;循環每條公共邊,獲取和公共邊線相交的圖形(不包含點相交),然后判斷公共邊上面的點是否都圖形之上,如果不符合則返回相關記錄;
環境:
ArcEngine10.1 VS2012 DevExpress15.2
主要代碼:
1、主程序窗口:

using DevExpress.XtraEditors; using ESRI.ArcGIS.AnalysisTools; using ESRI.ArcGIS.DataManagementTools; using ESRI.ArcGIS.DataSourcesGDB; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.Geoprocessing; using ESRI.ArcGIS.Geoprocessor; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace SDECatalog { public partial class frmMain : Form { public frmMain() { InitializeComponent(); } /// <summary> /// 加載數據源 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_AddShape_ButtonClick(object sender, DevExpress.XtraEditors.Controls.ButtonPressedEventArgs e) { //打開文件對話框 OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Title = "加載shapefile數據";//設置title openFileDialog.Filter = "(*.shp)|*.shp";//設置過濾模式 if (openFileDialog.ShowDialog() == DialogResult.OK) { //設置路徑 btn_AddShape.Text = openFileDialog.FileName;//全部路徑 } } private void btn_check_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(btn_AddShape.Text) || string.IsNullOrEmpty(btn_AddShape.Text.Trim())) { XtraMessageBox.Show("請先選擇shape文件路徑!"); return; } if (!btn_AddShape.Text.EndsWith("shp") && !btn_AddShape.Text.EndsWith("SHP")) { XtraMessageBox.Show("請選擇shape文件!"); return; } //創建工作空間 string strDataPath = Application.StartupPath + @"\data\" + DateTime.Now.ToString("yyyyMMddHHmmss"); if (!Directory.Exists(strDataPath)) { Directory.CreateDirectory(strDataPath); } string strMDBTemp = Application.StartupPath + @"\DataMDB\DataCheck.mdb"; File.Copy(strMDBTemp, strDataPath + @"/DataCheck.mdb"); string strResult = GISOper.GP_Copy(btn_AddShape.Text, strDataPath + @"\DataCheck.mdb", "PDDK"); if (strResult != "SUCCESS") { MessageBox.Show("復制shape到mdb失敗,請檢查shape文件是否存在!" + strResult); return; } //存儲結果的表格 DataTable dt = CreateTable(); strResult = GISOper.GP_Intersect(strDataPath + @"/DataCheck.mdb/PDDK", strDataPath + @"/DataCheck.mdb/GGLINE", "LINE"); if (strResult != "SUCCESS") { MessageBox.Show("提取公共邊失敗!" + strResult); return; } //進行相交檢查 strResult = GISOper.GP_Intersect(strDataPath + @"/DataCheck.mdb/PDDK", strDataPath + @"/DataCheck.mdb/PDIntersect", null); if (strResult != "SUCCESS") { MessageBox.Show("進行圖形相交失敗!" + strResult); return; } //開始用gp工具的擦除來做,擦除之后在進行拆分,但是擦除一直報ERROR 000824: The tool is not licensed.錯誤,因此改用了拓撲來做 //如果用擦除工具,效果會更好,但是注意一定要設置容差(0.0001),用拓撲對于差距很小的無法識別出來 strResult = GISOper.GP_Erase(strDataPath + @"\DataCheck.mdb\XZQ", strDataPath + @"\DataCheck.mdb\PDDK", strDataPath + @"\DataCheck.mdb\CCJG"); if (strResult != "SUCCESS") { MessageBox.Show("進行圖形擦除失敗!" + strResult); return; } IWorkspaceFactory pWF = new AccessWorkspaceFactoryClass(); IWorkspace pWS = pWF.OpenFromFile(strDataPath + @"\DataCheck.mdb", 0); IFeatureWorkspace pFeatWorkspace = pWS as IFeatureWorkspace; IFeatureClass feaClsPDIntersect = pFeatWorkspace.OpenFeatureClass("PDIntersect"); IFeatureClass feaClsXZQ = pFeatWorkspace.OpenFeatureClass("XZQ"); IFeatureClass feaClsPDDK = pFeatWorkspace.OpenFeatureClass("PDDK"); //進行相交檢查 IFeatureCursor feaCurTemp = feaClsPDIntersect.Search(null, false); IFeature feaPDIntersect = feaCurTemp.NextFeature(); while (feaPDIntersect != null) { IArea are = feaPDIntersect.ShapeCopy as IArea; if (are.Area > 0) { DataRow dr = dt.NewRow(); dr[0] = feaPDIntersect.OID; dr[1] = "無"; dr[2] = ""; dr[3] = "存在圖形相交,請檢查圖形"; dt.Rows.Add(dr); } feaPDIntersect = feaCurTemp.NextFeature(); } //進行圖形之間的距離檢查 IFeatureCursor feaCurTTemp = feaClsPDDK.Search(null, false); IFeature feaPDDK = feaCurTTemp.NextFeature(); while (feaPDDK != null) { IGeometry geo = feaPDDK.ShapeCopy; ITopologicalOperator pTopo = geo as ITopologicalOperator; IGeometry pGeoBuffer = pTopo.Buffer(5); ISpatialFilter filter = new SpatialFilterClass(); filter.Geometry = pGeoBuffer; filter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects; IFeatureCursor curT = feaClsPDDK.Search(filter, false); if (curT == null) { feaPDDK = feaCurTTemp.NextFeature(); continue; } IFeature feaTT = curT.NextFeature(); while (feaTT != null) { double dis = GISOper.GetTwoGeometryDistance(feaPDDK.ShapeCopy, feaTT.ShapeCopy); if (dis != 0) { DataRow dr = dt.NewRow(); dr[0] = feaPDDK.OID; dr[1] = feaTT.OID; dr[2] = dis; dr[3] = "存在圖形距離過近,請檢查圖形"; dt.Rows.Add(dr); } feaTT = curT.NextFeature(); } feaPDDK = feaCurTTemp.NextFeature(); } IFeature fea = feaClsXZQ.Search(null, false).NextFeature(); if (fea == null) { MessageBox.Show("獲取裁剪的空間圖形失敗!"); return; } //如果擦除工具有問題,可以用圖形循環擦除的方法 //EraseClass eraseCls = new EraseClass(fea, feaClsPDDK); //bool bSuccess = eraseCls.EraseOperate(); //if (!bSuccess) //{ // MessageBox.Show("進行圖形擦除失敗!"); // return; //} //strResult = GISOper.GP_MultipartToSinglepart(strDataPath + @"\DataCheck.mdb\XZQ", strDataPath + @"\DataCheck.mdb\XZQ_SPLIT"); strResult = GISOper.GP_MultipartToSinglepart(strDataPath + @"\DataCheck.mdb\CCJG", strDataPath + @"\DataCheck.mdb\XZQ_SPLIT"); if (strResult != "SUCCESS") { MessageBox.Show("擦除后結果進行多部件拆分失敗!" + strResult); return; } IFeatureClass feaClsXZQ_SPLIT = pFeatWorkspace.OpenFeatureClass("XZQ_SPLIT"); IFeatureCursor feaCursor = feaClsXZQ_SPLIT.Search(null, false); if (feaCursor != null) { IFeature feaTT = feaCursor.NextFeature(); while (feaTT != null) { IArea area = (IArea)feaTT.ShapeCopy; double d = area.Area; if (d < 50) { DataRow dr = dt.NewRow(); dr[0] = d.ToString(); dr[1] = "無"; dr[2] = ""; dr[3] = "存在碎屑多邊形(面積小于50),請檢查圖形"; dt.Rows.Add(dr); } feaTT = feaCursor.NextFeature(); } } IFeatureClass feaclsLINE = pFeatWorkspace.OpenFeatureClass("GGLINE"); IFeatureCursor feaCur = feaclsLINE.Search(null, false); IFeature feaLINE = feaCur.NextFeature(); while (feaLINE != null) { ISpatialFilter filterTemp = new SpatialFilterClass(); filterTemp.Geometry = feaLINE.Shape as IGeometry; //這里用相交,會出現線兩頭的面也會被搜索出來,而不僅僅是線所在的面,但是別的沒法返回想要的結果 filterTemp.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects; IFeatureCursor feacurTemp = feaClsPDDK.Search(filterTemp, false); IFeature feaTemp = feacurTemp.NextFeature(); int i = 0; while (feaTemp != null) { //相交可能只有一個點相交,對于這種情況應該排除掉 ITopologicalOperator topoOperator = feaTemp.ShapeCopy as ITopologicalOperator; IGeometry geo = topoOperator.Intersect(feaLINE.ShapeCopy, esriGeometryDimension.esriGeometry1Dimension); if (!geo.IsEmpty) { IPointCollection Pc = geo as IPointCollection; if (Pc.PointCount <= 1) { feaTemp = feacurTemp.NextFeature(); continue; } } else { feaTemp = feacurTemp.NextFeature(); continue; } IPointCollection pcPDDK = feaTemp.Shape as IPointCollection; IPointCollection pcLINE = feaLINE.Shape as IPointCollection; bool bcontain = bPcContainsPc(pcLINE, pcPDDK); if (bcontain) { DataRow dr = dt.NewRow(); dr[0] = feaLINE.OID; dr[1] = feaTemp.OID; //dr[2] = feaTemp.OID; dr[3] = "公共邊所有的點都包含在地塊要素上"; dt.Rows.Add(dr); } else { DataRow dr = dt.NewRow(); dr[0] = feaLINE.OID; dr[1] = feaTemp.OID; //dr[2] = feaTemp.get_Value(feaclsPDDK.FindField("KCDJDKID")).ToString(); dr[3] = "!!!注意:存在公共邊的點不在在地塊要素上!!!"; dt.Rows.Add(dr); } //不管用,好像一直返回線圖層的點信息 //ITopologicalOperator topoOperator = feaLINE.Shape as ITopologicalOperator; //IGeometry geo = topoOperator.Intersect(feaTemp.Shape as IGeometry, esriGeometryDimension.esriGeometryNoDimension); //if (!geo.IsEmpty) //{ //IPointCollection Pc = geo as IPointCollection; //} feaTemp = feacurTemp.NextFeature(); } feaLINE = feaCur.NextFeature(); } grdCtrlResult.DataSource = dt; grdCtrlResult.RefreshDataSource(); } /// <summary> /// 點集pc1是否完全在pc2中 /// </summary> /// <param name="pc1"></param> /// <param name="pc2"></param> /// <returns></returns> public bool bPcContainsPc(IPointCollection pc1, IPointCollection pc2) { int mixCount = 0; for (int i = 0; i < pc1.PointCount; i++) { IPoint pp = pc1.get_Point(i); for (int a = 0; a < pc2.PointCount; a++) { //double dd = Math.Abs(pp.X - pc2.get_Point(a).X); //double ss = Math.Abs(pp.Y - pc2.get_Point(a).Y); //注意這里的容差,必須設置,現在的圖形為保留三維小數 if (Math.Abs(pp.X - pc2.get_Point(a).X) < 0.0001 && Math.Abs(pp.Y - pc2.get_Point(a).Y) < 0.001) { mixCount++; break; } } } if (mixCount == pc1.PointCount) { return true; } else { return false; } } /// <summary> /// 創建結果datatable /// </summary> /// <returns></returns> public DataTable CreateTable() { DataTable dt = new DataTable(); try { DataColumn dcDsName = new DataColumn("線OID"); DataColumn dcFeaClsName = new DataColumn("地塊OID"); DataColumn dcDKID = new DataColumn("地塊ID"); DataColumn dcFeaClsAliasName = new DataColumn("說明"); dt.Columns.Add(dcDsName); dt.Columns.Add(dcFeaClsName); dt.Columns.Add(dcDKID); dt.Columns.Add(dcFeaClsAliasName); return dt; } catch (Exception) { return null; } } private void btn_exportExcel_Click(object sender, EventArgs e) { SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.Title = "導出Excel"; saveFileDialog.FileName = DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Millisecond.ToString(); saveFileDialog.Filter = "Excel文件(*.xls)|*.xls"; DialogResult dialogResult = saveFileDialog.ShowDialog(this); if (dialogResult == DialogResult.OK) { DevExpress.XtraPrinting.XlsExportOptions options = new DevExpress.XtraPrinting.XlsExportOptions(); grdCtrlResult.ExportToXls(saveFileDialog.FileName); XtraMessageBox.Show("保存成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } }
2、主要GP分析函數:
using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.DataManagementTools; using ESRI.ArcGIS.DataSourcesFile; using ESRI.ArcGIS.DataSourcesGDB; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.Geoprocessing; using ESRI.ArcGIS.Geoprocessor; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace SDECatalog { public class GISOper { /// <summary> /// 復制SHAPE到mdb /// </summary> /// <param name="inPath"></param> /// <param name="strOutPath"></param> /// <returns></returns> public static string GP_Copy(string inPath, string strOutPath, string strOutName) { Geoprocessor GP = new Geoprocessor(); GP.OverwriteOutput = true; //覆蓋原有文件并重寫 try { ESRI.ArcGIS.ConversionTools.FeatureClassToFeatureClass feaClsToFeaCls = new ESRI.ArcGIS.ConversionTools.FeatureClassToFeatureClass(); feaClsToFeaCls.in_features = inPath; feaClsToFeaCls.out_path = strOutPath; feaClsToFeaCls.out_name = strOutName; Geoprocessor gp = new Geoprocessor { OverwriteOutput = true }; IGeoProcessorResult2 result = gp.Execute(feaClsToFeaCls as IGPProcess, null) as IGeoProcessorResult2; return "SUCCESS"; } catch (Exception ex) { string str = ""; for (int i = 0; i < GP.MessageCount; i++) { str += GP.GetMessage(i); str += "\n"; } return "執行復制操作失敗!" + str; } } /// <summary> /// 執行GP相交 /// 這里注意,如果是多個圖層做相交處理,in_features參數如下:XXX圖層全路徑 + ";" + XXX圖層全路徑; /// </summary> public static string GP_Intersect(string strINPath, string strOutPutPath, string strOutType) { Geoprocessor GP = new Geoprocessor(); GP.OverwriteOutput = true; //覆蓋原有文件并重寫 try { //初始化Merge ESRI.ArcGIS.AnalysisTools.Intersect intersect = new ESRI.ArcGIS.AnalysisTools.Intersect(); intersect.in_features = strINPath; intersect.join_attributes = "ALL"; intersect.out_feature_class = strOutPutPath; if (!string.IsNullOrEmpty(strOutType)) { intersect.output_type = strOutType; } IGeoProcessorResult tGeoResult = GP.Execute(intersect, null) as IGeoProcessorResult; return "SUCCESS"; } catch (Exception ex) { string str = ""; for (int i = 0; i < GP.MessageCount; i++) { str += GP.GetMessage(i); str += "\n"; } return "執行相交操作失敗!" + str; } } /// <summary> /// 執行GP擦除 /// 因為一直出現ERROR 000824: The tool is not licensed.錯誤,因此改用了拓撲來實現 /// </summary> public static string GP_Erase(string strINPath, string strINPath2, string strOutPutPath) { Geoprocessor GP = new Geoprocessor(); GP.OverwriteOutput = true; //覆蓋原有文件并重寫 try { //初始化Merge ESRI.ArcGIS.AnalysisTools.Erase erase = new ESRI.ArcGIS.AnalysisTools.Erase(); erase.in_features = strINPath; erase.erase_features = strINPath2; erase.cluster_tolerance = 0.0001; erase.out_feature_class = strOutPutPath; IGeoProcessorResult tGeoResult = GP.Execute(erase, null) as IGeoProcessorResult; //IFeatureClass resultFeaCls = GP.Open(tGeoResult.ReturnValue) as IFeatureClass; return "SUCCESS"; } catch (Exception ex) { string str = ""; for (int i = 0; i < GP.MessageCount; i++) { str += GP.GetMessage(i); str += "\n"; } return "執行擦除操作失敗!" + str; } } /// <summary> /// 拆分多部件 /// </summary> public static string GP_MultipartToSinglepart(string strINPath, string strOutPutPath) { Geoprocessor GP = new Geoprocessor(); GP.OverwriteOutput = true; //覆蓋原有文件并重寫 try { ESRI.ArcGIS.DataManagementTools.MultipartToSinglepart cf = new MultipartToSinglepart(); cf.in_features = strINPath; cf.out_feature_class = strOutPutPath; IGeoProcessorResult tGeoResult = GP.Execute(cf, null) as IGeoProcessorResult; //IFeatureClass resultFeaCls = GP.Open(tGeoResult.ReturnValue) as IFeatureClass; return "SUCCESS"; } catch (Exception ex) { string str = ""; for (int i = 0; i < GP.MessageCount; i++) { str += GP.GetMessage(i); str += "\n"; } return "執行拆分操作失敗!" + str; } } /// <summary> /// 獲取兩個幾何圖形的距離 /// </summary> /// <param name="pGeometryA">幾何圖形A</param> /// <param name="pGeometryB">幾何圖形B</param> /// <returns>兩個幾何圖形的距離</returns> public static double GetTwoGeometryDistance(IGeometry pGeometryA, IGeometry pGeometryB) { IProximityOperator pProOperator = pGeometryA as IProximityOperator; if (pGeometryA != null || pGeometryB != null) { double distance = pProOperator.ReturnDistance(pGeometryB); return distance; } else { return 0; } } } }
posted on 2025-09-10 11:39 jingkunliu 閱讀(6) 評論(0) 收藏 舉報
浙公網安備 33010602011771號