Curve conversion add-in
CurveConversionDockWin.cs
// Copyright 2012 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

using System;
using System.Windows.Forms;
using ESRI.ArcGIS.Editor;
using ESRI.ArcGIS.EditorExt;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Framework;

namespace CurveConversion
{
    /// <summary>
    /// Designer class of the dockable window add-in. It contains user interfaces that
    /// make up the dockable window.
    /// </summary>
    public partial class CurveConversionDockWin : UserControl
    {
        private IDockableWindow _CurveDockWin;
        public static Double _Tolerance = 0.1;
        public static String _sFieldName = "";
        public static IFields _MFields;
        public static System.Object _UpdateValue = 0;
        private static bool _Cancel = false;
        public bool _UpdateFieldIsString = false;
        private bool _CheckTol = false;
        public IEditor _Editor;
        public IEnvelope _InvalidEnv;
        internal static CurveConversionDockWin _CurveUserControl;

        public CurveConversionDockWin(object hook)
        {
            InitializeComponent();
            this.Hook = hook;
            
            _CurveUserControl = this;
        }

        /// <summary>
        /// Host object of the dockable window
        /// </summary>
        private object Hook
        {
            get;
            set;
        }

        /// <summary>
        /// Implementation class of the dockable window add-in. It is responsible for 
        /// creating and disposing the user interface class of the dockable window.
        /// </summary>
        public class AddinImpl : ESRI.ArcGIS.Desktop.AddIns.DockableWindow
        {
            private CurveConversionDockWin m_windowUI;

            public AddinImpl()
            {
            }

            protected override IntPtr OnCreateChild()
            {
                m_windowUI = new CurveConversionDockWin(this.Hook);
                return m_windowUI.Handle;
            }

            protected override void Dispose(bool disposing)
            {
                if (m_windowUI != null)
                    m_windowUI.Dispose(disposing);

                base.Dispose(disposing);
            }
        }

        /// <summary>
        /// Populates the Fields combo box with the fields other than Shape_length and area.
        /// </summary>
        public static void UpdateFieldList()
        {
            _CurveUserControl.comboBoxField.Items.Clear();
            _CurveUserControl.comboBoxField.Items.Add("<none>");

            if (_MFields != null)
            {
                for (int i = 0; i < _MFields.FieldCount; i++)
                {
                    if (_MFields.get_Field(i).Type == esriFieldType.esriFieldTypeDouble |
                        _MFields.get_Field(i).Type == esriFieldType.esriFieldTypeInteger |
                        _MFields.get_Field(i).Type == esriFieldType.esriFieldTypeSingle |
                        _MFields.get_Field(i).Type == esriFieldType.esriFieldTypeString)
                    {
                        string FieldName = _MFields.get_Field(i).Name.ToUpper();
                        if (FieldName != "SHAPE_LENGTH" & FieldName != "SHAPE_AREA")
                        {
                            _CurveUserControl.comboBoxField.Items.Add(_MFields.get_Field(i).Name);
                        }
                    }
                }
            }

            //Reset the form. 
            _Cancel = true;
            _CurveUserControl.checkBoxTolerance.Checked = false;
            _CurveUserControl.textBoxTolerance.Text = _Tolerance.ToString();
            _CurveUserControl.textBoxTolerance.Enabled = false;
            _CurveUserControl.textBoxvalue.Text = ""; 
            _sFieldName = "";          
            _CurveUserControl.comboBoxField.SelectedIndex = 0;
        }

        private void buttonOK_Click(object sender, EventArgs e)
        {
            //You may also want to check that a shapefile is not being used. 



            //Get the editor for the dockable window. 
            UID eUID = new UIDClass();
            eUID.Value = "esriEditor.Editor";
            _Editor = ArcMap.Application.FindExtensionByCLSID(eUID) as IEditor;

            if (CheckValues() == false)
                return;
            _Tolerance = Convert.ToDouble(textBoxTolerance.Text);
            System.Windows.Forms.Cursor.Current = Cursors.WaitCursor;
            //Run the curve update. 
            CurveUpdate(_CheckTol);
            _Cancel = false;
            System.Windows.Forms.Cursor.Current = Cursors.Default;
            
            //cast the control to a dockable window to hide it from the user. 
            _CurveDockWin = CurveConversionCmd.GetCurveConversionWindow;
            if (_CurveDockWin == null)
                return;
            _CurveDockWin.Show(false);

        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            //hide the dockable window from the user. 
            _CurveDockWin = CurveConversionCmd.GetCurveConversionWindow;
            if (_CurveDockWin == null)
                return;
            _CurveDockWin.Show(false);
        }

        private void checkBoxTolerance_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxTolerance.Checked == false)
            {
                _CheckTol = false;
                textBoxTolerance.Enabled = false;
                
            }
            else
            {
                _CheckTol = true;
                textBoxTolerance.Enabled = true;
            }
        }

        private void comboBoxField_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBoxField.SelectedIndex > 0)
            {
                int lIndex = _MFields.FindField(comboBoxField.Text);
                if (lIndex > -1)
                {
                    if (_MFields.get_Field(lIndex).Type == esriFieldType.esriFieldTypeString)
                    {
                        _UpdateFieldIsString = true;
                        _sFieldName = _MFields.get_Field(lIndex).Name;
                    }
                    else
                    {
                        _UpdateFieldIsString = false;
                        _sFieldName = _MFields.get_Field(lIndex).Name;
                    }
                }
                else
                {
                    //resets the field to none.
                    comboBoxField.SelectedIndex = 0;
                }
            }
            else
            {
                _UpdateFieldIsString = false;
            }
        }

        private bool CheckValues()
        {
            //Check for numeric tolerance value if option is checked
            int i;
            bool isnumeric = Int32.TryParse(textBoxTolerance.Text, out i);
            double a;
            isnumeric = double.TryParse(textBoxTolerance.Text, out a);
            if ((checkBoxTolerance.Checked == true) & (isnumeric == false))
            {
                MessageBox.Show("Tolerance value must be numeric!!");
                return false;
            }

            //Check for numeric value if field to update is not a string field
            if (comboBoxField.SelectedIndex > 0)
            {
                isnumeric = int.TryParse(textBoxvalue.Text, out i);
                if ((isnumeric = false) & (_UpdateFieldIsString = false))
                {
                    MessageBox.Show("Update value must be numeric when the field is numeric!!!");
                    return false;
                }
            }
            return true;
        }

        private void CurveUpdate(bool checkTol)
        {
            try
            {
                //***Not sure that the progress bar is needed***//
                //Set up the progress bar. 
                IStatusBar _Status = ArcMap.Application.StatusBar;
                IStepProgressor _StepProg = _Status.ProgressBar;
                _StepProg.Position = 1;
                _StepProg.MaxRange = _Editor.SelectionCount;
                _StepProg.Message = "Update progress:";
                _StepProg.StepValue = 1;
                _StepProg.Show();

                int lCount = 0;
                _InvalidEnv = null;
                //You will get an error if your feature is stored in a shapefile.
                //Check to make sure there is no existing edit operation, then start one.
                IWorkspaceEdit2 _WorkspaceEdit = (IWorkspaceEdit2)_Editor.EditWorkspace;
                if (!_WorkspaceEdit.IsInEditOperation)
                {
                    _Editor.StartOperation();
                }
                
                ICurve _Curve;
                ICurve _Curve2;
                ISegmentCollection _OrigSegs;
                ISegmentCollection _Polyline;
                IConstructCircularArc _NewLine;
                Boolean _bSegCheck;
                IPoint _MidPoint;
                ISegment _Seg;
                Double _Dist = 0, _Dist1 = 0, _Dist2 = 0;
                Boolean _Side = false, _Updated = false;
                IEnumTopologyEdge _EdgeEnum;
                ISegmentCollection _Path = new PathClass();
                ITopologyEdge _TopoEdge;
                IPolyline _SomeLine;
                IEnumFeature _EnumFeat = _Editor.EditSelection;
                IFeature feat = _EnumFeat.Next();
                
                //Check for a topology
                ITopologyGraph _TopoGraph = TopologyCheck(feat.Class);

                do
                {
                    if (feat.Shape.GeometryType == esriGeometryType.esriGeometryPolyline & (!IsStraight(feat)))
                    {
                        //Create the new segment based on a 3pt curve through the start point, mid point and end point of the feat geometry.
                        _Curve = feat.Shape as ICurve;
                        _OrigSegs = _Curve as ISegmentCollection;
                        _MidPoint = new PointClass();
                        _Curve.QueryPoint(esriSegmentExtension.esriNoExtension, 0.5, true, _MidPoint);
                        _NewLine = new CircularArcClass();
                        _NewLine.ConstructThreePoints(_Curve.FromPoint, _MidPoint, _Curve.ToPoint, false);
                        _Curve2 = _NewLine as ICurve;

                        //Check that the segments are within the tolerance. 
                        _bSegCheck = false;
                        if (checkTol)
                        {
                            for (int lLoop = 1; lLoop < _OrigSegs.SegmentCount; lLoop++)
                            {
                                _Seg = _OrigSegs.get_Segment(lLoop);
                                _Curve2.QueryPointAndDistance(esriSegmentExtension.esriNoExtension, _Seg.FromPoint, false, _MidPoint, ref _Dist, ref  _Dist1, ref _Side);
                                _Curve2.QueryPointAndDistance(esriSegmentExtension.esriNoExtension, _Seg.ToPoint, false, _MidPoint, ref _Dist, ref  _Dist2, ref _Side);

                                if (_Dist1 > _Tolerance | _Dist2 > _Tolerance)
                                {
                                    _bSegCheck = true;
                                    break;
                                }
                            }
                        }

                        if (!_bSegCheck)
                        {
                            //Check for a topology. 
                            if (_TopoGraph != null)
                            {
                                _EdgeEnum = GetParents(feat, _TopoGraph);
                                if (_EdgeEnum == null)
                                {
                                    //not sure what i need to add, but to get here something went wrong. 
                                }
                                else
                                {
                                    object Missing = Type.Missing;
                                    _Path.AddSegment(_NewLine as ISegment, ref Missing, ref Missing);


                                    switch (_EdgeEnum.Count)
                                    {
                                        case 1:
                                            _EdgeEnum.Reset();
                                            _TopoEdge = _EdgeEnum.Next();
                                            _Updated = UpdateEdge(_TopoGraph, _TopoEdge, _Path);
                                            if (_Updated)
                                            {
                                                UpdateField(feat, true);
                                                lCount++;
                                            }
                                            break;
                                        case 2:
                                            _EdgeEnum.Reset();
                                            _TopoEdge = _EdgeEnum.Next();

                                            do
                                            {
                                                _SomeLine = _TopoEdge.Geometry as IPolyline;
                                                double X1 = _Path.get_Segment(0).FromPoint.X;
                                                double Y1 = _Path.get_Segment(0).FromPoint.Y;
                                                double X2 = _Path.get_Segment(0).ToPoint.X;
                                                double Y2 = _Path.get_Segment(0).ToPoint.Y;

                                                if (X1 == _SomeLine.FromPoint.X & Y1 == _SomeLine.FromPoint.Y &
                                                    X2 == _SomeLine.ToPoint.X & Y2 == _SomeLine.FromPoint.Y)
                                                {
                                                    _Updated = UpdateEdge(_TopoGraph, _TopoEdge, _Path);
                                                    if (_Updated)
                                                    {
                                                        UpdateField(feat, true);
                                                        lCount++;
                                                    }
                                                    break;
                                                }
                                                _TopoEdge = _EdgeEnum.Next();
                                            }
                                            while (_TopoEdge != null);
                                            break;
                                        default:

                                            break;
                                    }
                                    _EdgeEnum = null;
                                }

                            }
                            else
                            {
                                //the current feature is not part of a topo, so just update it.
                                _Polyline = new PolylineClass();
                                object Missing = Type.Missing;
                                _Polyline.AddSegment(_NewLine as ISegment, ref Missing, ref Missing);
                                //This code sets the polyline ZAware so it will handle Z aware features. 
                                //However you need to check that the feature is Z aware first, so you can add this code here. 
                                //IZAware zAware = _Polyline as IZAware;
                                //zAware.ZAware = true;

                                feat.Shape = _Polyline as IGeometry;
                                UpdateField(feat, false);
                                feat.Store();
                                lCount++;
                                if (_InvalidEnv == null)
                                {
                                    _InvalidEnv = feat.Shape.Envelope;
                                }
                                else
                                {
                                    _InvalidEnv.Union(feat.Shape.Envelope);
                                }
                            }
                        }
                    }

                    feat = _EnumFeat.Next();
                    ArcMap.Application.StatusBar.ProgressBar.Step();
                } while (feat != null);

                if (lCount == 0)
                {
                    MessageBox.Show("No features were updated.");
                    _Editor.AbortOperation();
                }
                else
                {
                    MessageBox.Show(lCount + " feature(s) updated");
                    _Editor.StopOperation("Update Curves");
                }

                if (_InvalidEnv != null)
                {
                    IMxDocument _Doc = ArcMap.Application.Document as IMxDocument;
                    _Doc.ActiveView.PartialRefresh(ESRI.ArcGIS.Carto.esriViewDrawPhase.esriViewAll, null, _InvalidEnv);
                }
                ArcMap.Application.StatusBar.ProgressBar.Hide();
            }
            catch(Exception e)
            {
                MessageBox.Show("Error in updating the Curve, make sure you are not using a shapefile." + e.Message);
                ArcMap.Application.StatusBar.ProgressBar.Hide();
                return;
            } 
        }

        /// <summary>
        /// Checks the field type and converts the update value to the correct type and updates the feature.
        /// </summary>
        /// <param name="feat"></param>
        /// <param name="shouldStore"></param>
        private void UpdateField(IFeature feat, bool shouldStore)
        {
            try
            {
                int lFieldIndex = 0;
                
                if (_sFieldName != "<none>")
                {
                    lFieldIndex = feat.Fields.FindField(_sFieldName);
                    if (lFieldIndex > -1)
                    {
                        _UpdateValue = textBoxvalue.Text;
                        switch (feat.Fields.get_Field(lFieldIndex).Type)
                        {
                            case esriFieldType.esriFieldTypeString:
                                feat.set_Value(lFieldIndex, _UpdateValue.ToString());
                                break;
                            case esriFieldType.esriFieldTypeInteger | esriFieldType.esriFieldTypeSmallInteger:
                                feat.set_Value(lFieldIndex, Convert.ToInt32(_UpdateValue));
                                break;
                            case esriFieldType.esriFieldTypeDouble | esriFieldType.esriFieldTypeSingle:
                                feat.set_Value(lFieldIndex, Convert.ToDouble(_UpdateValue));
                                break;
                            default:
                                break;
                        }
                        if (shouldStore)
                            feat.Store();
                    }
                }
            }
            catch 
            {
                return;
            }
        }

        private bool UpdateEdge(ITopologyGraph topoGraph, ITopologyEdge _TopoEdge, ISegmentCollection _Path)
        {
            try
            {
                ISegmentCollection _NewSegs = _TopoEdge.Geometry as ISegmentCollection;
                IEnvelope _Invalid = new EnvelopeClass();
                int inCount = _NewSegs.SegmentCount;
                topoGraph.SetEdgeGeometry(_TopoEdge, _Path as IPath);
                topoGraph.Post(out _Invalid);
                if (_InvalidEnv == null)
                { _InvalidEnv = _Invalid; }
                else { _InvalidEnv.Union(_Invalid); }

                _NewSegs = _TopoEdge.Geometry as ISegmentCollection;
                if (_NewSegs.SegmentCount == 1 & inCount > 1)
                {
                    return true;
                }
                else
                    return false;
            }
            catch (Exception e)
            {
                MessageBox.Show("Error in updating the topo edge " + e.Message);
                return false;
            }
        }

        /// <summary>
        /// Gets an enum of the parent edges for the feature. 
        /// </summary>
        /// <param name="feat"></param>
        /// <param name="topoGraph"></param>
        /// <returns></returns>
        private IEnumTopologyEdge GetParents(IFeature feat, ITopologyGraph topoGraph)
        {
            IFeatureClass fFC = feat.Class as IFeatureClass;
            IEnumTopologyEdge _EnumTopoEdge = topoGraph.GetParentEdges(fFC, feat.OID);
            if (_EnumTopoEdge == null)
            {
                topoGraph.SetEmpty();
                topoGraph.Build(feat.Shape.Envelope, false);
                _EnumTopoEdge = topoGraph.GetParentEdges(fFC, feat.OID);
            }
            else
            {
                if (_EnumTopoEdge.Count > 1)
                {
                    topoGraph.DeletePseudoNodesFromSelection();
                }
            }
            return _EnumTopoEdge;
        }

        /// <summary>
        /// Check to see if the line is straight. So we do NOT convert straight lines to polycurves.
        /// </summary>
        /// <param name="feat"></param>
        private bool IsStraight(IFeature feat)
        {
            try
            {
                if (feat.Shape.GeometryType != esriGeometryType.esriGeometryPolyline)
                    return true;
                ISpatialReference _SR = feat.Shape.SpatialReference;
                IPolycurve _PolyCurve = feat.ShapeCopy as IPolycurve;
                ISegmentCollection _SegColl = _PolyCurve as ISegmentCollection;
                double _x;
                double _y;
                double _units;
                _SR.GetFalseOriginAndUnits(out _x, out _y, out _units);
                _PolyCurve.Generalize(2 / _units);

                if (_SegColl.SegmentCount == 1)
                { return true; }
                else
                { return false; }
            }
            catch (Exception e)
            {
                MessageBox.Show("Error in checking straight " + e.Message);
                return false;
            }
        }

        /// <summary>
        /// Checks to see if the feature class is part of a gdb or map topology. 
        /// If it is it will build the topo cache and return the topology graph.
        /// </summary>
        /// <param name="oBJClass"></param>
        /// <returns></returns>
        private ITopologyGraph TopologyCheck(IObjectClass oBJClass)
        {
            try
            {
                ITopologyClass topoClass = oBJClass as ITopologyClass;
                ITopologyGraph topoGraph;
                

                if (topoClass.IsInTopology) 
                {
                    //Check to see if the class of the selected feature is part of the a topology.
                    if (topoClass.Topology.Cache != null)
                    {
                       return topoGraph = topoClass.Topology.Cache as ITopologyGraph;
                    }
                    else
                    {
                        return topoGraph = null;
                    }
                }
                else
                {
                    //Check to see if the class of the selected feature is part of a map topology.
                    UID mUID = new UIDClass();
                    mUID.Value = "esriEditor.TopologyExtension";
                    ITopologyExtension topoExt = ArcMap.Application.FindExtensionByCLSID(mUID) as ITopologyExtension;
                    IMapTopology mapTopo = topoExt.MapTopology; 
                    IFeatureClass featClass = oBJClass as IFeatureClass;
                    int lID = mapTopo.FindClass(featClass);
                    if (lID > -1)
                    {
                       return topoGraph = mapTopo.Cache;
                    }
                    else
                    {
                       return topoGraph = null;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Error checking topology" + e);
                return null;
            }
        }

    }
}