tags:

views:

115

answers:

1

I have a roads layer. I need to ad a point to another, point layer where ever roads intersect. How can I add such points to the existing points layer.

In hand I have the line layer, the line feature / To and From Points, and the points layer. I just need a pointer as to how to create points in the points layer.

I am using C#

Thank you.

+1  A: 

The code below illustrates ways of doing this through the use of extension methods. The ArcObjects API is often criticized for having too may interfaces, resulting in less readable code. I believe extension methods can address many of these concerns. The down side is that people reading the code cannot readily distinguish between extension methods and methods that are part of the API.

I'm not sure of the context of your problem, but if you're creating points for street intersections you might consider using a different Point hashing function. This would allow road intersections where roads cross at more than one point to be represented by a single point. This would be analogous to the cluster tolerance used in map topologies.

I haven't really gotten my head around a nice inheritance method for calling base constructors for custom Exceptions. Any suggestions there would be appreciated.

using System;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.Framework;
using System.Windows.Forms;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Carto;
using System.Collections.Generic;
using ESRI.ArcGIS.Editor;

namespace IGG.SurveyTools
{
    /// <summary>
    /// Summary description for TestCommand.
    /// </summary>
    [ProgId("IGG.SurveyTools.TestCommand")]
    public sealed class TestCommand : BaseCommand
    {
        private IEditor m_Editor;
        public TestCommand()
        {
            //
            // TODO: Define values for the public properties
            //
            base.m_category = ""; //localizable text
            base.m_caption = "Add End points";  //localizable text
            base.m_message = "";  //localizable text 
            base.m_toolTip = "";  //localizable text 
            base.m_name = "";   //unique id, non-localizable (e.g. "MyCategory_MyCommand")
        }

        #region Overriden Class Methods

        /// <summary>
        /// Occurs when this command is created
        /// </summary>
        /// <param name="hook">Instance of the application</param>
        public override void OnCreate(object hook)
        {
            IApplication app = hook as IApplication;
            if (app == null)
                return;
            m_Editor = app.FindExtensionByName("ESRI Object Editor") as IEditor;
        }
        public override bool Enabled
        {
            get
            {
                return (m_Editor != null && m_Editor.EditState == esriEditState.esriStateEditing);
            }
        }
        public override void OnClick()
        {
            try
            {
                string fmt = "{0},{1}";

                IMxDocument mxDoc = (IMxDocument)m_Editor.Parent.Document;

                IFeatureLayer polylineLayer = mxDoc.FocusMap.FindFLayer("My Polylines");
                IFeatureLayer pointLayer = mxDoc.FocusMap.FindFLayer("My Points");
                if(((IDataset)pointLayer.FeatureClass).Workspace != m_Editor.EditWorkspace)
                {
                    MessageBox.Show(new Win32Win(m_Editor), "Points layer is not being edited");
                    return;
                }
                Dictionary<string, IPoint> endPoints =  polylineLayer.GetEndPoints(fmt);
                if (endPoints.Count == 0)
                {
                    MessageBox.Show("no end points found");
                    return;
                }
                Dictionary<string,IPoint> existingPoints = pointLayer.GetPoints(fmt);
                Dictionary<string,IPoint> newPoints = endPoints.Subtract(existingPoints);
                if(newPoints.Count == 0)
                {
                    MessageBox.Show(new Win32Win(m_Editor.Parent),"all endpoints are present in pointslayer");
                    return;
                }

                m_Editor.StartOperation();
                try
                {
                    pointLayer.FeatureClass.PutPoints(newPoints.Values);
                    m_Editor.StopOperation(String.Format("Added {0} new endpoints", newPoints.Count));
                    ((IActiveView)m_Editor.Map).Refresh();
                }
                catch(Exception ex)
                {
                    m_Editor.AbortOperation();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(new Win32Win(m_Editor), ex.Message + Environment.NewLine + ex.StackTrace);
            }
        }        
        #endregion

    }

    public class Win32Win : IWin32Window
    {
        private IntPtr m_handle;
        public Win32Win(IApplication app) { m_handle = new IntPtr(app.hWnd); }
        public IntPtr Handle { get { return m_handle; } }
        public Win32Win(int hwnd) { m_handle = new IntPtr(hwnd); }
        public Win32Win(IEditor editor) { m_handle = new IntPtr(editor.Parent.hWnd); }
    }

    public class LayerNotFoundException : Exception
    {
        public LayerNotFoundException(string lyrName) 
            : base("Layer not found: " + lyrName) 
        { 
        }
    }

    public class FeatureLayerNotFoundException : LayerNotFoundException
    {
        public FeatureLayerNotFoundException(string lyrName)
            : base(lyrName)
        {
        }
    }

    public static class MyExtensions
    {
        public static void PutPoints(this IFeatureClass fc, IEnumerable<IPoint> pnts)
        {
            IFeatureCursor fCur = fc.Insert(false);
            IFeatureBuffer buff = fc.CreateFeatureBuffer();
            foreach (IPoint pnt in pnts)
            {
                buff.Shape = pnt;
                fCur.InsertFeature(buff);
            }
            fCur.Flush();
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(fCur);
        }

        /// <summary>
        /// returns first layer in map with case-insensitive name
        /// </summary>
        /// <param name="map"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static ILayer FindLayer(this IMap map, string name)
        {
            if (map.LayerCount == 0)
                throw new LayerNotFoundException(name);
            IEnumLayer enumLayer = map.get_Layers(null, true);
            ILayer layer;
            while ((layer = enumLayer.Next()) != null)
            {
                if (layer.Name.Trim().ToUpper() == name.Trim().ToUpper())
                    return layer;
            }
            throw new LayerNotFoundException(name);
        }

        public static IFeatureLayer FindFLayer(this IMap map, string name)
        {
            IFeatureLayer fLayer = map.FindLayer(name) as IFeatureLayer;
            if (fLayer == null)
                throw new FeatureLayerNotFoundException(name);
            return fLayer;
        }

        public static Dictionary<string, IPoint> GetPoints(this IFeatureLayer fLayer, string fmt)
        {
            if (fLayer.FeatureClass == null
                || fLayer.FeatureClass.ShapeType != esriGeometryType.esriGeometryPoint)
                throw new Exception("bad point layer: " + fLayer.Name);

            Dictionary<string, IPoint> outDict = new Dictionary<string, IPoint>();
            IFeatureCursor fCur = fLayer.FeatureClass.Search(null, false);
            IFeature feat;
            while ((feat = fCur.NextFeature()) != null)
            {
                outDict.AddPoint((IPoint)feat.ShapeCopy,fmt);
            }
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(fCur);
            return outDict;
        }

        public static Dictionary<string, IPoint> GetEndPoints(this IFeatureLayer fLayer, string fmt)
        {
            if (fLayer.FeatureClass == null 
                || fLayer.FeatureClass.ShapeType != esriGeometryType.esriGeometryPolyline)
                throw new Exception("bad polyline layer: " + fLayer.Name);

            Dictionary<string, IPoint> outDict = new Dictionary<string, IPoint>();
            IFeatureCursor fCur = fLayer.FeatureClass.Search(null, false);
            IFeature feat;
            while ((feat = fCur.NextFeature()) != null)
            {
                IPolyline polyline = (IPolyline)feat.ShapeCopy;
                if (polyline == null || polyline.IsEmpty)
                    continue;
                outDict.AddPoint(polyline.FromPoint,fmt);
                outDict.AddPoint(polyline.ToPoint,fmt);
            }
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(fCur);
            return outDict;
        }
        public static string Hash(this IPoint pnt, string fmt)
        {
            // use different formatting options to do quick and dirty clustering
            return String.Format(fmt, pnt.X, pnt.Y);
        }

        public static void AddPoint(this Dictionary<string,IPoint> dict ,IPoint pnt, string fmt)
        {
            string hash = pnt.Hash(fmt);
            if (!dict.ContainsKey(hash))
                dict.Add(hash, pnt);
        }
        public static Dictionary<string, IPoint> Subtract(this Dictionary<string, IPoint> inDict, Dictionary<string, IPoint> otherDict)
        {
            Dictionary<string, IPoint> outDict = new Dictionary<string, IPoint>();
            foreach (KeyValuePair<string, IPoint> kvp in inDict)
            {
                if (!otherDict.ContainsKey(kvp.Key))
                    outDict.Add(kvp.Key, kvp.Value);
            }
            return outDict;
        }
    }
}
Kirk Kuykendall
Awesome! I will give this code a spin as soon as I get into work in the morning.This is my first Arc project, so I'm still finding my feat, but I have already started abstracting loads of stuff into extension methods that make my code a whole lot more comprehensible.I'm not sure I understand what you mean with the base constructors for custom Exceptions thing. What you have done looks perfectly fine to me. The only thing I might change is to inherit from ApplicationException instead, to pedantically indicate it is an exception from my app stack upward, and not from below. But it's trivial.
Jacques Bosch
@kirkktx: For your GetPoints method, could you not use a recycling cursor because you are making copies of the points anyway?
Jacques Bosch
Yes, that could be recycling, and would make it run faster. Also to make the queries faster, you could pass an IQueryFilter to the Search method, and be sure to set IQueryFilter.SubFields to contain only the ObjectID field. By default the cursor fetches all fields.
Kirk Kuykendall

related questions