Curve conversion add-in
CurveConversionDockWin.vb
' 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.
' 

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

''' <summary>
''' Designer class of the dockable window add-in. It contains user interfaces that
''' make up the dockable window.
''' </summary>
Public Class CurveConversionDockWin

    Private _CurveDockwin As ESRI.ArcGIS.Framework.IDockableWindow
    Public Shared _MFields As IFields
    Dim _UpdateValue As System.Object
    Shared _Cancel As Boolean = False
    Dim _UpdateFieldIsString As Boolean = False
    Private _CheckTol As Boolean = False
    Dim _Editor As IEditor
    Dim _InvalidEnv As IEnvelope
    Shared _Tolerance As [Double] = 0.1
    Shared _sFieldName As [String] = ""
    Shared _CurveUserControl As CurveConversionDockWin

    Public Sub New(ByVal hook As Object)

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.Hook = hook
        _CurveUserControl = Me
    End Sub

    Private m_hook As Object
    ''' <summary>
    ''' Host object of the dockable window
    ''' </summary> 
    Public Property Hook() As Object
        Get
            Return m_hook
        End Get
        Set(ByVal value As Object)
            m_hook = value
        End Set
    End Property

    Private Sub buttonOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles buttonOK.Click
        'You may also want to check that a shapefile is not being used. 
        'Get a refence to the editor.
        Dim uidEditor As New UID
        uidEditor.Value = "esriEditor.Editor"
        _Editor = TryCast(My.ArcMap.Application.FindExtensionByCLSID(uidEditor), ESRI.ArcGIS.Editor.IEditor)

        If CheckValues() = False Then
            Return
        End If
        _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 Is Nothing Then
            Return
        End If
        _CurveDockwin.Show(False)
    End Sub

    Private Sub buttonCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles buttonCancel.Click
        'hide the dockable window from the user. 
        _CurveDockwin = CurveConversionCmd.GetCurveConversionWindow
        If _CurveDockwin Is Nothing Then
            Return
        End If
        _CurveDockwin.Show(False)
    End Sub

    Private Function CheckValues() As Boolean
        CheckValues = False

        'Check for numeric tolerance value if option is checked
        If checkBoxTolerance.Checked = True And Not IsNumeric(textBoxTolerance.Text) Then
            MessageBox.Show("Tolerance value must be numeric!!")
            Exit Function
        End If

        'Check for numeric value if field to update is not a string field
        If comboBoxField.SelectedIndex > 0 Then
            If Not IsNumeric(textBoxvalue.Text) And Not _UpdateFieldIsString Then
                MessageBox.Show("Update value must be numeric when the field is numeric!!!")
                Exit Function
            End If
        End If

        CheckValues = True
    End Function

    ''' <summary>
    ''' Populates the Fields combo box with the fields other than Shape_length and area.
    ''' </summary>
    ''' <remarks></remarks>
    Shared Sub UpdateFieldList()
        Dim lLoop As Long
        _CurveUserControl.comboBoxField.Items.Clear()
        _CurveUserControl.comboBoxField.Items.Add("<none>")
        If Not _MFields Is Nothing Then
            For lLoop = 0 To _MFields.FieldCount - 1
                If _MFields.Field(lLoop).Type = esriFieldType.esriFieldTypeDouble Or _
                 _MFields.Field(lLoop).Type = esriFieldType.esriFieldTypeSingle Or _
                 _MFields.Field(lLoop).Type = esriFieldType.esriFieldTypeInteger Or _
                 _MFields.Field(lLoop).Type = esriFieldType.esriFieldTypeSmallInteger Or _
                 _MFields.Field(lLoop).Type = esriFieldType.esriFieldTypeString Then
                    If UCase(_MFields.Field(lLoop).Name) <> "SHAPE_LENGTH" And _
                     UCase(_MFields.Field(lLoop).Name) <> "SHAPE_AREA" Then
                        _CurveUserControl.comboBoxField.Items.Add(_MFields.Field(lLoop).Name)
                    End If
                End If
            Next lLoop
        End If

        _Cancel = True
        _CurveUserControl.checkBoxTolerance.Checked = False
        _CurveUserControl.textBoxTolerance.Text = CStr(_Tolerance)
        _CurveUserControl.textBoxTolerance.Enabled = False
        _CurveUserControl.textBoxvalue.Text = ""
        _sFieldName = ""
        _CurveUserControl.comboBoxField.SelectedIndex = 0

    End Sub

    ''' <summary>
    ''' Implementation class of the dockable window add-in. It is responsible for
    ''' creating and disposing the user interface class for the dockable window.
    ''' </summary>
    Public Class AddinImpl
        Inherits ESRI.ArcGIS.Desktop.AddIns.DockableWindow

        Private m_windowUI As CurveConversionDockWin

        Protected Overrides Function OnCreateChild() As System.IntPtr
            m_windowUI = New CurveConversionDockWin(Me.Hook)
            Return m_windowUI.Handle
        End Function

        Protected Overrides Sub Dispose(ByVal Param As Boolean)
            If m_windowUI IsNot Nothing Then
                m_windowUI.Dispose(Param)
            End If

            MyBase.Dispose(Param)
        End Sub

    End Class

    Private Sub CurveUpdate(ByVal _CheckTol As Boolean)
        Try
            'Set up the progress bar.
            Dim _Status As IStatusBar = My.ArcMap.Application.StatusBar
            Dim _StepProg As IStepProgressor = _Status.ProgressBar
            With _StepProg
                .Position = 1
                .MaxRange = _Editor.SelectionCount
                .Message = "Update progress:"
                .StepValue = 1
                .Show()
            End With
            Dim lCount As Integer = 0
            _InvalidEnv = Nothing
            '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.

            Dim _WorkspaceEdit As IWorkspaceEdit2 = DirectCast(_Editor.EditWorkspace, IWorkspaceEdit2)
            If Not _WorkspaceEdit.IsInEditOperation Then
                _Editor.StartOperation()
            End If

            Dim _Curve As ICurve
            Dim _Curve2 As ICurve
            Dim _OrigSegs As ISegmentCollection
            Dim _Polyline As ISegmentCollection
            Dim _MidPoint As IPoint
            Dim _NewLine As IConstructCircularArc
            Dim _bSegCheck As [Boolean]
            Dim _OutPoint As IPoint = New PointClass()
            Dim _Seg As ISegment
            Dim _Dist As [Double] = 0, _Dist1 As [Double] = 0, _Dist2 As [Double] = 0
            Dim _Side As [Boolean] = False, _Updated As [Boolean] = False
            Dim _EdgeEnum As IEnumTopologyEdge
            Dim _Path As ISegmentCollection = New PathClass()
            Dim _TopoEdge As ITopologyEdge
            Dim _SomeLine As IPolyline
            Dim _EnumFeat As IEnumFeature = _Editor.EditSelection
            Dim feat As IFeature = _EnumFeat.[Next]()
            'Check for a topology
            Dim _TopoGraph As ITopologyGraph = TopologyCheck(feat.[Class])

            Do
                If feat.Shape.GeometryType = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline And (Not IsStraight(feat)) Then
                    'Create the new segment based on a 3pt curve through the start point, mid point and end point of the feat geometry.
                    _Curve = TryCast(feat.Shape, ICurve)
                    _OrigSegs = TryCast(_Curve, ISegmentCollection)
                    _MidPoint = New PointClass()
                    _Curve.QueryPoint(ESRI.ArcGIS.Geometry.esriSegmentExtension.esriNoExtension, 0.5, True, _MidPoint)
                    _NewLine = New CircularArcClass()
                    _NewLine.ConstructThreePoints(_Curve.FromPoint, _MidPoint, _Curve.ToPoint, False)
                    _Curve2 = TryCast(_NewLine, ICurve)

                    'Check that the segments are within the tolerance. 
                    _bSegCheck = False
                    If _CheckTol Then
                        For lLoop As Integer = 1 To _OrigSegs.SegmentCount - 1
                            _Seg = _OrigSegs.Segment(lLoop)
                            _Curve2.QueryPointAndDistance(ESRI.ArcGIS.Geometry.esriSegmentExtension.esriNoExtension, _Seg.FromPoint, False, _MidPoint, _Dist, _Dist1, _
                            _Side)
                            _Curve2.QueryPointAndDistance(ESRI.ArcGIS.Geometry.esriSegmentExtension.esriNoExtension, _Seg.ToPoint, False, _MidPoint, _Dist, _Dist2, _
                            _Side)

                            If _Dist1 > _Tolerance Or _Dist2 > _Tolerance Then
                                _bSegCheck = True
                                Exit For
                            End If
                        Next
                    End If

                    If Not _bSegCheck Then
                        If _TopoGraph IsNot Nothing Then
                            _EdgeEnum = GetParents(feat, _TopoGraph)
                            If _EdgeEnum Is Nothing Then
                                'not sure what i need to add, but to get here something went wrong. 
                            Else
                                Dim Missing As Object = Type.Missing
                                _Path.AddSegment(TryCast(_NewLine, ISegment), Missing, Missing)


                                Select Case _EdgeEnum.Count
                                    Case 1
                                        _EdgeEnum.Reset()
                                        _TopoEdge = _EdgeEnum.[Next]()
                                        _Updated = UpdateEdge(_TopoGraph, _TopoEdge, _Path)
                                        If _Updated Then
                                            UpdateField(feat, True)
                                            lCount += 1
                                        End If
                                        Exit Select
                                    Case 2
                                        _EdgeEnum.Reset()
                                        _TopoEdge = _EdgeEnum.[Next]()

                                        Do
                                            _SomeLine = TryCast(_TopoEdge.Geometry, IPolyline)
                                            Dim X1 As Double = _Path.Segment(0).FromPoint.X
                                            Dim Y1 As Double = _Path.Segment(0).FromPoint.Y
                                            Dim X2 As Double = _Path.Segment(0).ToPoint.X
                                            Dim Y2 As Double = _Path.Segment(0).ToPoint.Y

                                            If X1 = _SomeLine.FromPoint.X And Y1 = _SomeLine.FromPoint.Y And X2 = _SomeLine.ToPoint.X And Y2 = _SomeLine.FromPoint.Y Then
                                                _Updated = UpdateEdge(_TopoGraph, _TopoEdge, _Path)
                                                If _Updated Then
                                                    UpdateField(feat, True)
                                                    lCount += 1
                                                End If
                                                Exit Do
                                            End If
                                            _TopoEdge = _EdgeEnum.[Next]()
                                        Loop While _TopoEdge IsNot Nothing
                                        Exit Select
                                    Case Else

                                        Exit Select
                                End Select
                                _EdgeEnum = Nothing

                            End If
                        Else
                            'the current feature is not part of a topo, so just update it.
                            _Polyline = New PolylineClass()
                            Dim Missing As Object = Type.Missing
                            _Polyline.AddSegment(TryCast(_NewLine, ISegment), Missing, 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. 
                            'Dim zAware as IZAware = _Polyline as IZAware
                            'zAware.ZAware = true
                            feat.Shape = TryCast(_Polyline, IGeometry)
                            UpdateField(feat, False)
                            feat.Store()
                            lCount += 1
                            If _InvalidEnv Is Nothing Then
                                _InvalidEnv = feat.Shape.Envelope
                            Else
                                _InvalidEnv.Union(feat.Shape.Envelope)

                            End If
                        End If
                    End If
                End If
                feat = _EnumFeat.[Next]()
                My.ArcMap.Application.StatusBar.ProgressBar.[Step]()
            Loop While feat IsNot Nothing

            If lCount = 0 Then
                MessageBox.Show("No features were updated.")
                _Editor.AbortOperation()
            Else
                MessageBox.Show(lCount & " feature(s) updated")
                _Editor.StopOperation("Update Curves")
            End If

            If _InvalidEnv IsNot Nothing Then
                Dim _Doc As IMxDocument = TryCast(My.ArcMap.Application.Document, IMxDocument)
                _Doc.ActiveView.PartialRefresh(ESRI.ArcGIS.Carto.esriViewDrawPhase.esriViewAll, Nothing, _InvalidEnv)
            End If
            My.ArcMap.Application.StatusBar.ProgressBar.Hide()

        Catch ex As Exception
            MessageBox.Show("Error in updating the Curve, make sure you are not using a shapefile." + ex.Message)
            My.ArcMap.Application.StatusBar.ProgressBar.Hide()
            Return
        End Try
    End Sub

    Private Sub UpdateField(ByVal feat As IFeature, ByVal shouldStore As Boolean)
        Try
            Dim lFieldIndex As Integer = 0
           
            If Not _sFieldName Is "<none>" Then
                lFieldIndex = feat.Fields.FindField(_sFieldName)
                If lFieldIndex > -1 Then
                    _UpdateValue = textBoxvalue.Text

                    feat.Value(lFieldIndex) = _UpdateValue
                    Select Case feat.Fields.Field(lFieldIndex).Type
                        Case esriFieldType.esriFieldTypeString
                            feat.Value(lFieldIndex) = _UpdateValue.ToString()

                        Case esriFieldType.esriFieldTypeInteger Or esriFieldType.esriFieldTypeString
                            feat.Value(lFieldIndex) = Convert.ToInt32(_UpdateValue)

                        Case esriFieldType.esriFieldTypeDouble Or esriFieldType.esriFieldTypeSingle
                            feat.Value(lFieldIndex) = Convert.ToDouble(_UpdateValue)

                        Case Else
                            Return
                    End Select
                    If shouldStore Then
                        feat.Store()
                    End If
                End If
            End If

        Catch ex As Exception
            Return
        End Try
    End Sub

    Private Function UpdateEdge(ByVal topoGraph As ITopologyGraph, ByVal _TopoEdge As ITopologyEdge, ByVal _Path As ISegmentCollection) As Boolean
        Try
            Dim _NewSegs As ISegmentCollection = TryCast(_TopoEdge.Geometry, ISegmentCollection)
            Dim _Invalid As IEnvelope = New EnvelopeClass()
            Dim inCount As Integer = _NewSegs.SegmentCount
            topoGraph.SetEdgeGeometry(_TopoEdge, TryCast(_Path, IPath))
            topoGraph.Post(_Invalid)
            If _InvalidEnv Is Nothing Then
                _InvalidEnv = _Invalid
            Else
                _InvalidEnv.Union(_Invalid)
            End If

            _NewSegs = TryCast(_TopoEdge.Geometry, ISegmentCollection)
            If _NewSegs.SegmentCount = 1 And inCount > 1 Then
                Return True
            Else
                Return False
            End If
        Catch e As Exception
            MessageBox.Show("Error in updating the topo edge " & e.Message)
            Return False
        End Try
    End Function

    Private Function GetParents(ByVal feat As IFeature, ByVal _TopoGraph As ITopologyGraph) As IEnumTopologyEdge
        Dim fFC As IFeatureClass = TryCast(feat.[Class], IFeatureClass)
        Dim _EnumTopoEdge As IEnumTopologyEdge = _TopoGraph.GetParentEdges(fFC, feat.OID)
        If _EnumTopoEdge Is Nothing Then
            _TopoGraph.SetEmpty()
            _TopoGraph.Build(feat.Shape.Envelope, False)
            _EnumTopoEdge = _TopoGraph.GetParentEdges(fFC, feat.OID)
        Else
            If _EnumTopoEdge.Count > 1 Then
                _TopoGraph.DeletePseudoNodesFromSelection()
            End If
        End If
        Return _EnumTopoEdge
    End Function

    ''' <summary>
    ''' Check to see if the line is straight. So we do NOT convert straight lines to polycurves.
    ''' </summary>
    ''' <param name="feat"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function IsStraight(ByVal feat As IFeature) As Boolean
        Try
            If feat.Shape.GeometryType <> ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline Then
                Return True
            End If
            Dim _SR As ISpatialReference = feat.Shape.SpatialReference
            Dim _PolyCurve As IPolycurve = TryCast(feat.ShapeCopy, IPolycurve)
            Dim _SegColl As ISegmentCollection = TryCast(_PolyCurve, ISegmentCollection)
            Dim _x As Double
            Dim _y As Double
            Dim _units As Double
            _SR.GetFalseOriginAndUnits(_x, _y, _units)
            _PolyCurve.Generalize(2 / _units)

            If _SegColl.SegmentCount = 1 Then
                Return True
            Else
                Return False
            End If
        Catch e As Exception
            MessageBox.Show("Error in checking straight " & e.Message)
            Return False
        End Try
    End Function

    ''' <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>
    ''' <remarks></remarks>
    Private Function TopologyCheck(ByVal oBJClass As IObjectClass) As ITopologyGraph
        Try

            Dim topoClass As ITopologyClass = TryCast(oBJClass, ITopologyClass)
            Dim topoGraph As ITopologyGraph

            If topoClass.IsInTopology Then
                If topoClass.Topology.Cache IsNot Nothing Then
                    topoGraph = TryCast(topoClass.Topology.Cache, ITopologyGraph)
                Else
                    topoGraph = Nothing
                End If
            Else
                'Check to see if the class of the selected feature is part of a map topology.
                Dim mUID As UID = New UIDClass()
                mUID.Value = "esriEditor.TopologyExtension"
                Dim topoExt As ITopologyExtension = TryCast(My.ArcMap.Application.FindExtensionByCLSID(mUID), ITopologyExtension)
                Dim mapTopo As IMapTopology = topoExt.MapTopology
                Dim featClass As IFeatureClass = TryCast(oBJClass, IFeatureClass)
                Dim lID As Integer = mapTopo.FindClass(featClass)
                If lID > -1 Then
                    topoGraph = mapTopo.Cache
                Else
                    topoGraph = Nothing
                End If
            End If
            Return topoGraph
        Catch e As Exception
            MessageBox.Show("Error checking topology: " & e.Message)
            Return Nothing
        End Try
    End Function

    Private Sub checkBoxTolerance_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles checkBoxTolerance.CheckedChanged
        If checkBoxTolerance.Checked = False Then
            _CheckTol = False
            textBoxTolerance.Enabled = False
        Else
            _CheckTol = True
            textBoxTolerance.Enabled = True
        End If
    End Sub

    Private Sub comboBoxField_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles comboBoxField.SelectedIndexChanged
        Try
            If comboBoxField.SelectedIndex > 0 Then

                Dim lIndex As Integer = _MFields.FindField(comboBoxField.Text)
                If lIndex > -1 Then
                    If _MFields.Field(lIndex).Type = esriFieldType.esriFieldTypeString Then
                        _UpdateFieldIsString = True
                        _sFieldName = _MFields.Field(lIndex).Name
                    Else
                        _UpdateFieldIsString = False
                        _sFieldName = _MFields.Field(lIndex).Name
                    End If
                Else
                    'resets the field to none.
                    comboBoxField.SelectedIndex = 0
                End If
            Else
                _UpdateFieldIsString = False
            End If
        Catch ex As Exception
            Return
        End Try
    End Sub
End Class