ArcGis add-in插件开发:自动匹配最近道路交叉点

本文记录了一次使用C#开发ArcGIS插件的过程,该插件功能是找出地图中点要素与最近线要素的交点,并将点移动到交点位置。在实现过程中,详细介绍了如何加载图层到界面,遍历图层寻找交点,并处理线与线相交的各种情况,包括首尾相切和线自身相交的特殊情况。最后展示了插件在ArcGIS中的运行效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在学习arcengine插件开发,经过不断尝试查找资料看说明文档终于让我成功做成了一个小插件。写个文档做个记录,方便后面查看。本文有参考利用C#制作ArcMap插件 - 知乎 (zhihu.com)

功能很简单,找出距离点要素最近的线要素的交点,并且把点移动到最近的交点上去。

 插件的界面设计:

 首先要在Button代码里设置响应函数,以便启动插件时能打开界面

protected override void OnClick()
        {
            //
            //  TODO: Sample code showing how to access button host
            //
            Form1 f1 = new Form1();
            f1.ShowDialog();
            ArcMap.Application.CurrentTool = null;

        }
        protected override void OnUpdate()
        {
            Enabled = ArcMap.Application != null;
        }

接下来要想办法把地图里的图层加载到界面的下拉框里去,这里需要3个函数

 
        //定义getlayername函数,获取已经加载的图层名称

        public List<string> getLayerName(IMapDocument pMxdDocument)
        {
            List<string> layername = new List<string>();
            IMap pMap = pMxdDocument.ActiveView.FocusMap;
            for (int i = 0; i < pMap.LayerCount; i++)
            {
                ILayer pLayer = pMap.get_Layer(i);
                layername.Add(pLayer.Name.ToString());

            }
            return layername;

        }

        //定义getlayeratname函数,通过图层名称获取图层
        public ILayer getLayerAtname(string name)
        {
            ILayer pLayer = null;
            IMap pMap = pMxd.ActiveView.FocusMap;
            for (int i = 0; i < pMap.LayerCount; i++)
            {
                ILayer layer = pMap.get_Layer(i);
                if (layer.Name.ToString() == name)
                {
                    pLayer = layer;

                }
            }
            return pLayer;

        }


 //定义一个getlayer函数,导入图层加载到地图窗口
        public ILayer GetLayer()
        {
            OpenFileDialog openfiledialog = new OpenFileDialog();
            openfiledialog.Filter = "shp(*.shp|*.shp";
            openfiledialog.InitialDirectory = @"D:\";

            openfiledialog.Multiselect = false;

            ILayer pLayer = null;
            if (openfiledialog.ShowDialog() == DialogResult.OK)
            {
                string pPath = openfiledialog.FileName;
                string pFolder = System.IO.Path.GetFileName(pPath);
                string pFileName = System.IO.Path.GetFileName(pPath);

                IWorkspaceFactory pWorkspaceFactory = new ShapefileWorkspaceFactory();
                IWorkspace pWorkspace = pWorkspaceFactory.OpenFromFile(pFolder, 0);
                IFeatureWorkspace pFeatureWs = pWorkspace as IFeatureWorkspace;

                //根据文件名打开要素
                IFeatureClass pFC = pFeatureWs.OpenFeatureClass(pFileName);
                IFeatureLayer  pFlayer = new FeatureLayerClass();
                pFlayer.FeatureClass = pFC;
                pFlayer.Name = pFC.AliasName;
                pLayer = pFlayer as ILayer;

                //加载数据至地图窗口
                pMxd.ActiveView.FocusMap.AddLayer(pLayer);
                pMxd.ActiveView.Refresh();
            }
            return pLayer;
        }

对于事先没有打开的shp文件,就需要第三个函数,在点图层和线图层的按钮响应函数中引用

        //导入点图层
        private void Openfile1_Click(object sender, EventArgs e)
        {
            pPointLayer = GetLayer();
            if (pPointLayer != null)
            {
                comboBox1.Items.Add(pPointLayer.Name.ToString());
                comboBox1.SelectedIndex = 0;

            }
        }

        //导入线图层
        private void Openfile2_Click(object sender, EventArgs e)
        {
            pLineLayer = GetLayer();
            if(pLineLayer != null)
            {
                comboBox2.Items.Add(pLineLayer.Name.ToString());
                comboBox2.SelectedIndex = 0;

            }

        }

双击界面中的“确定”按钮,OK_click作为功能实现的主体,大部分代码都集中在这里。调用getlayername函数获取图层名称后,再调用getlayeratname获取图层。

为了获取每条线之间的交点,我定义了两个ifeaturecursor,用于嵌套循环,这里要注意的是featurecursor在光标移动到最后之后并不会重置,所以需要在第一个循环内部定义第二个featurecursor以达到重置的效果。

private void OK_Click(object sender, EventArgs e)
{
    //调用getlayeratname根据图层名称获取图层
    pPointLayer = getLayerAtname(comboBox1.SelectedItem.ToString());
    pLineLayer = getLayerAtname(comboBox2.SelectedItem.ToString());
         
    if (pPointLayer == null || pLineLayer == null)
    {
        MessageBox.Show("请选择完整图层");
    }
    else
    {
        //获取线图层

        IFeatureLayer pFeatureLayer = pLineLayer as IFeatureLayer;
        IFeatureClass pFeatureclass = pFeatureLayer.FeatureClass;

        IFeatureLayer ppfeatureLayer = pPointLayer as IFeatureLayer;
        IFeatureClass ppFeatureclass = ppfeatureLayer.FeatureClass;


        //判断数据类型是否符合要求

        bool type1 = ppFeatureclass.ShapeType == esriGeometryType.esriGeometryMultipoint || ppFeatureclass.ShapeType == esriGeometryType.esriGeometryPoint;
        bool type2 = pFeatureclass.ShapeType == esriGeometryType.esriGeometryPolyline || pFeatureclass.ShapeType == esriGeometryType.esriGeometryLine;

        if (type1 && type2)
        {
                    
            IFeatureCursor pfFeaturecursor = pFeatureclass.Search(null, false);
                    
                  

            //获取第一个线要素
            IFeature firstFeature = pfFeaturecursor.NextFeature();
                    
            //存放线交点
            IPointCollection xPointCollection = new MultipointClass();
                    

            while (firstFeature != null)
            {
                IGeometry firstLine = firstFeature.Shape;
                firstLine.SnapToSpatialReference();
                ITopologicalOperator pTopo = firstLine as ITopologicalOperator;
                pTopo.Simplify();

                IFeatureCursor pFeaturecursor = pFeatureclass.Search(null, false);

                //获取第二条线要素
                IFeature lineFeature = pFeaturecursor.NextFeature();
                lineFeature = pFeaturecursor.NextFeature();
                        
                        
                       
                while (lineFeature != null   )
                {
                    if (firstFeature.OID < lineFeature.OID)//防止重复计算交点
                    {
                        IRelationalOperator pRelation = firstLine as IRelationalOperator;

                        IGeometry lineGeo = lineFeature.Shape;
                        lineGeo.SnapToSpatialReference();
                        if (pRelation.Touches(lineGeo) || pRelation.Crosses(lineGeo))//交叉或者相接

                        {
                            //计算两条线的交点

                            IGeometry xpt = pTopo.Intersect(lineGeo, esriGeometryDimension.esriGeometry0Dimension);
                            求两个几何对象的重叠部分
                            两个几何对象的重叠部分,可以有很多种几何类型组合,例如面与面重叠是面,线与线重叠是线或者点,点与点重叠是点,点与面重叠是点,线与面重叠是线等等
                            参数2是返回结果是多少维的意思,根据经验如果返回结果是点就是0维(esriGeometry0Dimension),线就是1维,面就是2维
                            xpt.SpatialReference = lineGeo.SpatialReference;
                            
                     
                            IPointCollection pPointco = xpt as IPointCollection;
                            
                            xPointCollection.AddPointCollection(pPointco);
                            
                        }

                    }
                            
                    lineFeature = pFeaturecursor.NextFeature();

                }

                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(pFeaturecursor);

                firstFeature = pfFeaturecursor.NextFeature();
            }

                    
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(pfFeaturecursor);

            //获取点要素
            IFeatureCursor pPfeaturecursor = ppFeatureclass.Search(null, false);
            IFeature pPFeature = pPfeaturecursor.NextFeature();
                    

            while (pPFeature != null)
            {
                IPoint pt = pPFeature.Shape as IPoint;

        
                IProximityOperator pProximityor = xPointCollection as IProximityOperator;
                        

                //寻找线上最近点
                IPoint newpt = pProximityor.ReturnNearestPoint(pt, esriSegmentExtension.esriNoExtension);
                        
                pPFeature.Shape = newpt;
                pPFeature.Store();
                      
                pPFeature = pPfeaturecursor.NextFeature();
            }
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(pPfeaturecursor);

            pMxd.ActiveView.Refresh();
            MessageBox.Show("完成!");


        }
        else
            MessageBox.Show("请选择正确的数据!");

            
    }

}

在最后判断线与线相交的时候,Touches和Crosses两种情况都要考虑到。因为当首尾相切的时候,程序并不会判定为cross,而是touch。

还有一种情况比较难处理,那就是有的线自身与自身相交,在这种情况下如何提取交点是比较麻烦的。

在程序编写完成后就可以把add-in插件加载到arcgis里尝试运行了,这里得到的最终效果图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值