基于ArcEngine进行数据共边检查

问题描述:

用户提供的数据经常会存在共边问题,将数据提报给上级后审核不通过,导致需要数据反复修改,因此需要增加数据共边检查,自行检查无误后再进行上报,因为这些问题人工检查比较困难,因此考虑使用工具进行检查。

问题分析:

从用户提供数据分析,主要存在下面几种问题:

1、存在点偏移,应该是同一个点的坐标有细微偏差,可能会导致两个图形之间存在轻微的压盖、或者碎屑多边形等;

2、共边的点数不一致;

思路整理:

考虑使用ArcEngine工具编写相关的分析功能,主要是调用ToolBox工具箱中的工具以及GP服务进行分析,整理思路如下:

1、进行图形相交处理分析,输出结果为面,如果存在相交,则不合格,返回相关记录;

2、进行图形之间距离计算,如果距离大于0并小于阈值,则认为其应该是共点或者共边,返回相关记录;

3、进行数据擦除分析,使用数据的公共外接矩形擦除分析数据(我这里为了方便直接使用行政区的外接矩形),擦除后进行图形打散(因为擦除后返回一个图形,可能是多部件),检查是否存在碎屑多边形(面积小于阈值),返回相关记录;

4、进行分析数据相交处理,设置返回结果为线,获取到图形的公共边;循环每条公共边,获取和公共边线相交的图形(不包含点相交),然后判断公共边上面的点是否都图形之上,如果不符合则返回相关记录;

环境:

ArcEngine10.1   VS2012   DevExpress15.2

主要代码:

1、主程序窗口:

image

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)    收藏  举报

导航