How can I draw a freehand polygon?

2747
2
Jump to solution
05-10-2011 01:21 PM
KenBuja
MVP Honored Contributor
I'm creating an Add-in that lets a user select the type of feature to draw on the map. I've gotten the code for adding points, polygons, and rectangles, but I'd like to add in the capability of drawing polygons in freehand mode. Is there way of doing that? Here's my existing code for the drawing tool

Public Class DrawTool
    Inherits ESRI.ArcGIS.Desktop.AddIns.Tool

    Public Sub New()

    End Sub

    Protected Overrides Sub OnUpdate()

    End Sub

    Protected Overrides Sub OnMouseDown(ByVal arg As ESRI.ArcGIS.Desktop.AddIns.Tool.MouseEventArgs)
        MyBase.OnMouseDown(arg)

        Dim activeView As ESRI.ArcGIS.Carto.IActiveView = My.ArcMap.Document.ActiveView
        Dim rgbColor As ESRI.ArcGIS.Display.IRgbColor = New ESRI.ArcGIS.Display.RgbColorClass()
        Dim pGeometry As ESRI.ArcGIS.Geometry.IGeometry5 = GetFeatureFromMouse(activeView)

        rgbColor.Red = 255

        'Add the user's drawn graphics as persistent on the map.
        AddGraphicToMap(activeView.FocusMap, pGeometry, rgbColor, rgbColor)
        'Best practice: Redraw only the portion of the active view that contains graphics.
        activeView.PartialRefresh(ESRI.ArcGIS.Carto.esriViewDrawPhase.esriViewGraphics, Nothing, Nothing)

    End Sub

    Public Sub AddGraphicToMap(ByVal map As ESRI.ArcGIS.Carto.IMap, ByVal geometry As ESRI.ArcGIS.Geometry.IGeometry, ByVal rgbColor As ESRI.ArcGIS.Display.IRgbColor, ByVal outlineRgbColor As ESRI.ArcGIS.Display.IRgbColor)

        Dim graphicsContainer As ESRI.ArcGIS.Carto.IGraphicsContainer = CType(map, ESRI.ArcGIS.Carto.IGraphicsContainer) ' Explicit Cast
        Dim element As ESRI.ArcGIS.Carto.IElement = Nothing

        graphicsContainer.DeleteAllElements()

        If (geometry.GeometryType) = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPoint Then

            ' Marker symbols
            Dim simpleMarkerSymbol As ESRI.ArcGIS.Display.ISimpleMarkerSymbol = New ESRI.ArcGIS.Display.SimpleMarkerSymbolClass()
            simpleMarkerSymbol.Color = rgbColor
            simpleMarkerSymbol.Outline = True
            simpleMarkerSymbol.OutlineColor = outlineRgbColor
            simpleMarkerSymbol.Size = 8
            simpleMarkerSymbol.Style = ESRI.ArcGIS.Display.esriSimpleMarkerStyle.esriSMSCross

            Dim markerElement As ESRI.ArcGIS.Carto.IMarkerElement = New ESRI.ArcGIS.Carto.MarkerElementClass()
            markerElement.Symbol = simpleMarkerSymbol
            element = CType(markerElement, ESRI.ArcGIS.Carto.IElement) ' Explicit Cast

        ElseIf (geometry.GeometryType) = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline Then

            '  Line elements
            Dim simpleLineSymbol As ESRI.ArcGIS.Display.ISimpleLineSymbol = New ESRI.ArcGIS.Display.SimpleLineSymbolClass()
            simpleLineSymbol.Color = rgbColor
            simpleLineSymbol.Style = ESRI.ArcGIS.Display.esriSimpleLineStyle.esriSLSSolid
            simpleLineSymbol.Width = 5

            Dim lineElement As ESRI.ArcGIS.Carto.ILineElement = New ESRI.ArcGIS.Carto.LineElementClass()
            lineElement.Symbol = simpleLineSymbol
            element = CType(lineElement, ESRI.ArcGIS.Carto.IElement) ' Explicit Cast

        ElseIf (geometry.GeometryType) = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon Then

            ' Polygon elements
            Dim simpleFillSymbol As ESRI.ArcGIS.Display.ISimpleFillSymbol = New ESRI.ArcGIS.Display.SimpleFillSymbolClass()
            simpleFillSymbol.Color = rgbColor
            simpleFillSymbol.Style = ESRI.ArcGIS.Display.esriSimpleFillStyle.esriSFSForwardDiagonal
            Dim fillShapeElement As ESRI.ArcGIS.Carto.IFillShapeElement = New ESRI.ArcGIS.Carto.PolygonElementClass()
            fillShapeElement.Symbol = simpleFillSymbol
            element = CType(fillShapeElement, ESRI.ArcGIS.Carto.IElement) ' Explicit Cast

        End If

        If Not (element Is Nothing) Then

            element.Geometry = geometry
            graphicsContainer.AddElement(element, 0)

        End If

    End Sub

    Public Function GetFeatureFromMouse(activeView As ESRI.ArcGIS.Carto.IActiveView) As ESRI.ArcGIS.Geometry.IGeometry5

        Dim screenDisplay As ESRI.ArcGIS.Display.IScreenDisplay = activeView.ScreenDisplay
        Dim pPointSymbol As New ESRI.ArcGIS.Display.MarkerFillSymbol
        Dim pRGBColor As New ESRI.ArcGIS.Display.RgbColor
        Dim pPolygonSymbol As New ESRI.ArcGIS.Display.SimpleFillSymbol
        Dim pOutline As New ESRI.ArcGIS.Display.SimpleLineSymbol
        Dim pRubberBand As ESRI.ArcGIS.Display.IRubberBand2
        Dim pGeometry As ESRI.ArcGIS.Geometry.IGeometry5

        Try
            Select Case Globals.DrawFeatureType
                Case "Point"
                    pRubberBand = New ESRI.ArcGIS.Display.RubberPoint
                 Case "Polygon"
                    pRubberBand = New ESRI.ArcGIS.Display.RubberPolygon
                Case "Rectangle"
                    pRubberBand = New ESRI.ArcGIS.Display.RubberEnvelope
                Case "Freehand"
                    System.Windows.Forms.MessageBox.Show("Freehand not coded yet")
                    Return Nothing
                Case Else
                    System.Windows.Forms.MessageBox.Show("Not coded yet")
                    Return Nothing
            End Select

            pGeometry = pRubberBand.TrackNew(screenDisplay, Nothing)
            Return pGeometry

        Catch ex As Exception
            System.Windows.Forms.MessageBox.Show(ex.ToString, "Get Feature")
            Return Nothing
        End Try

    End Function

End Class
0 Kudos
1 Solution

Accepted Solutions
SeanJones
Esri Regular Contributor
Ken,
In the editor we use the INewBezierCurveFeedback interface to provide feedback for the freehand tools. This works a little differently from the simple rubber band classes though.

The code example below uses the feedback to populate a sketch for an edit task. In your case you would just need to take the returned polyline geometry and convert it to polygon.

    private INewBezierCurveFeedback m_LineFeedback = null;

    protected sealed override void OnMouseDown(MouseEventArgs arg)
    {
      IPoint point = m_editor.Display.DisplayTransformation.ToMapPoint(arg.X, arg.Y);

      if (m_LineFeedback == null)
      {
        //first mouse click - start feedback
        m_LineFeedback = new NewBezierCurveFeedbackClass();
        m_LineFeedback.Display = m_editor.Display;
        m_LineFeedback.Start(point);
      }
      else
      {
        //second mouse click - end feedback and create feature
        IPolyline polyline = m_LineFeedback.Stop();
        m_LineFeedback = null;
        IEditSketch editSketch = m_editor as IEditSketch;
        editSketch.Geometry = polyline;
        editSketch.FinishSketch();
      }
      base.OnMouseDown(arg);
    }

    protected sealed override void OnMouseMove(MouseEventArgs arg)
    {
      if (m_LineFeedback != null)
      {
        IPoint point = m_editor.Display.DisplayTransformation.ToMapPoint(arg.X, arg.Y);
        m_LineFeedback.AddPoint(point);
      }
      base.OnMouseMove(arg);
    }

View solution in original post

0 Kudos
2 Replies
SeanJones
Esri Regular Contributor
Ken,
In the editor we use the INewBezierCurveFeedback interface to provide feedback for the freehand tools. This works a little differently from the simple rubber band classes though.

The code example below uses the feedback to populate a sketch for an edit task. In your case you would just need to take the returned polyline geometry and convert it to polygon.

    private INewBezierCurveFeedback m_LineFeedback = null;

    protected sealed override void OnMouseDown(MouseEventArgs arg)
    {
      IPoint point = m_editor.Display.DisplayTransformation.ToMapPoint(arg.X, arg.Y);

      if (m_LineFeedback == null)
      {
        //first mouse click - start feedback
        m_LineFeedback = new NewBezierCurveFeedbackClass();
        m_LineFeedback.Display = m_editor.Display;
        m_LineFeedback.Start(point);
      }
      else
      {
        //second mouse click - end feedback and create feature
        IPolyline polyline = m_LineFeedback.Stop();
        m_LineFeedback = null;
        IEditSketch editSketch = m_editor as IEditSketch;
        editSketch.Geometry = polyline;
        editSketch.FinishSketch();
      }
      base.OnMouseDown(arg);
    }

    protected sealed override void OnMouseMove(MouseEventArgs arg)
    {
      if (m_LineFeedback != null)
      {
        IPoint point = m_editor.Display.DisplayTransformation.ToMapPoint(arg.X, arg.Y);
        m_LineFeedback.AddPoint(point);
      }
      base.OnMouseMove(arg);
    }

View solution in original post

0 Kudos
KenBuja
MVP Honored Contributor
Thanks, Sean. This is what my code now looks like


Public Class DrawTool
    Inherits ESRI.ArcGIS.Desktop.AddIns.Tool

    Private m_LineFeedback As ESRI.ArcGIS.Display.INewBezierCurveFeedback = Nothing

    Public Sub New()

    End Sub

    Protected Overrides Sub OnUpdate()

    End Sub

    Protected Overrides Sub OnMouseDown(ByVal arg As ESRI.ArcGIS.Desktop.AddIns.Tool.MouseEventArgs)
        MyBase.OnMouseDown(arg)

        Try
            Dim activeView As ESRI.ArcGIS.Carto.IActiveView = My.ArcMap.Document.ActiveView
            Dim rgbColor As ESRI.ArcGIS.Display.IRgbColor = New ESRI.ArcGIS.Display.RgbColorClass()
            Dim pGeometry As ESRI.ArcGIS.Geometry.IGeometry5 = GetFeatureFromMouse(activeView, arg.X, arg.Y)

            If pGeometry Is Nothing Then Exit Sub

            rgbColor.Red = 255

            'Add the user's drawn graphics as persistent on the map.
            AddGraphicToMap(activeView.FocusMap, pGeometry, rgbColor, rgbColor)
            'Best practice: Redraw only the portion of the active view that contains graphics.
            activeView.PartialRefresh(ESRI.ArcGIS.Carto.esriViewDrawPhase.esriViewGraphics, Nothing, Nothing)

        Catch ex As Exception
            System.Windows.Forms.MessageBox.Show(ex.ToString)
        End Try
    End Sub

    Protected Overrides Sub OnMouseMove(arg As ESRI.ArcGIS.Desktop.AddIns.Tool.MouseEventArgs)
        MyBase.OnMouseMove(arg)

        Try
            If m_LineFeedback IsNot Nothing Then
                Dim pPoint As ESRI.ArcGIS.Geometry.IPoint = My.ArcMap.Document.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(arg.X, arg.Y)

                m_LineFeedback.AddPoint(pPoint)

            End If

        Catch ex As Exception
            System.Windows.Forms.MessageBox.Show(ex.ToString)
        End Try

    End Sub

    Public Sub AddGraphicToMap(ByVal map As ESRI.ArcGIS.Carto.IMap, ByVal geometry As ESRI.ArcGIS.Geometry.IGeometry, ByVal rgbColor As ESRI.ArcGIS.Display.IRgbColor, ByVal outlineRgbColor As ESRI.ArcGIS.Display.IRgbColor)

        Dim graphicsContainer As ESRI.ArcGIS.Carto.IGraphicsContainer = CType(map, ESRI.ArcGIS.Carto.IGraphicsContainer) ' Explicit Cast
        Dim element As ESRI.ArcGIS.Carto.IElement = Nothing

        graphicsContainer.DeleteAllElements()

        If (geometry.GeometryType) = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPoint Then

            ' Marker symbols
            Dim simpleMarkerSymbol As ESRI.ArcGIS.Display.ISimpleMarkerSymbol = New ESRI.ArcGIS.Display.SimpleMarkerSymbolClass()
            simpleMarkerSymbol.Color = rgbColor
            simpleMarkerSymbol.Outline = True
            simpleMarkerSymbol.OutlineColor = outlineRgbColor
            simpleMarkerSymbol.Size = 8
            simpleMarkerSymbol.Style = ESRI.ArcGIS.Display.esriSimpleMarkerStyle.esriSMSCross

            Dim markerElement As ESRI.ArcGIS.Carto.IMarkerElement = New ESRI.ArcGIS.Carto.MarkerElementClass()
            markerElement.Symbol = simpleMarkerSymbol
            element = CType(markerElement, ESRI.ArcGIS.Carto.IElement) ' Explicit Cast

        ElseIf (geometry.GeometryType) = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline Then

            '  Line elements
            Dim simpleLineSymbol As ESRI.ArcGIS.Display.ISimpleLineSymbol = New ESRI.ArcGIS.Display.SimpleLineSymbolClass()
            simpleLineSymbol.Color = rgbColor
            simpleLineSymbol.Style = ESRI.ArcGIS.Display.esriSimpleLineStyle.esriSLSSolid
            simpleLineSymbol.Width = 5

            Dim lineElement As ESRI.ArcGIS.Carto.ILineElement = New ESRI.ArcGIS.Carto.LineElementClass()
            lineElement.Symbol = simpleLineSymbol
            element = CType(lineElement, ESRI.ArcGIS.Carto.IElement) ' Explicit Cast

        ElseIf geometry.GeometryType = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon Then

            ' Polygon elements
            Dim simpleFillSymbol As ESRI.ArcGIS.Display.ISimpleFillSymbol = New ESRI.ArcGIS.Display.SimpleFillSymbolClass()
            simpleFillSymbol.Color = rgbColor
            simpleFillSymbol.Style = ESRI.ArcGIS.Display.esriSimpleFillStyle.esriSFSForwardDiagonal
            Dim fillShapeElement As ESRI.ArcGIS.Carto.IFillShapeElement = New ESRI.ArcGIS.Carto.PolygonElementClass()
            fillShapeElement.Symbol = simpleFillSymbol
            element = CType(fillShapeElement, ESRI.ArcGIS.Carto.IElement) ' Explicit Cast

        ElseIf geometry.GeometryType = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryEnvelope Then

            Dim simpleFillSymbol As ESRI.ArcGIS.Display.ISimpleFillSymbol = New ESRI.ArcGIS.Display.SimpleFillSymbolClass()
            simpleFillSymbol.Color = rgbColor
            simpleFillSymbol.Style = ESRI.ArcGIS.Display.esriSimpleFillStyle.esriSFSForwardDiagonal
            Dim fillShapeElement As ESRI.ArcGIS.Carto.IFillShapeElement = New ESRI.ArcGIS.Carto.RectangleElementClass
            fillShapeElement.Symbol = simpleFillSymbol
            element = CType(fillShapeElement, ESRI.ArcGIS.Carto.IElement) ' Explicit Cast

        End If

        If Not (element Is Nothing) Then
            Try
                element.Geometry = geometry
                graphicsContainer.AddElement(element, 0)

            Catch ex As Exception
                System.Windows.Forms.MessageBox.Show(ex.ToString)
            End Try

        End If

    End Sub

    Public Function GetFeatureFromMouse(activeView As ESRI.ArcGIS.Carto.IActiveView, XVal As Integer, YVal As Integer) As ESRI.ArcGIS.Geometry.IGeometry5

        Dim screenDisplay As ESRI.ArcGIS.Display.IScreenDisplay = activeView.ScreenDisplay
        Dim pRubberBand As ESRI.ArcGIS.Display.IRubberBand2
        Dim pPoint As ESRI.ArcGIS.Geometry.IPoint
        Dim pPolygon As ESRI.ArcGIS.Geometry.IPolygon4
        Dim pGeometry As ESRI.ArcGIS.Geometry.IGeometry5
        Dim pPolyLine As ESRI.ArcGIS.Geometry.IPolyline6

        Try
             Select Case Globals.DrawFeatureType
                Case "Point"
                    pRubberBand = New ESRI.ArcGIS.Display.RubberPoint
                    pPoint = pRubberBand.TrackNew(screenDisplay, Nothing)
                    Return pPoint
                Case "Polygon"
                    pRubberBand = New ESRI.ArcGIS.Display.RubberPolygon
                    pPolygon = pRubberBand.TrackNew(screenDisplay, Nothing)
                    Return pPolygon
                Case "Rectangle"
                    pRubberBand = New ESRI.ArcGIS.Display.RubberEnvelope
                    pGeometry = pRubberBand.TrackNew(screenDisplay, Nothing)
                    Return pGeometry
                Case "Freehand"
                    pPoint = activeView.ScreenDisplay.DisplayTransformation.ToMapPoint(XVal, YVal)
                    If m_LineFeedback Is Nothing Then
                        m_LineFeedback = New ESRI.ArcGIS.Display.NewBezierCurveFeedback
                        m_LineFeedback.Display = screenDisplay
                        m_LineFeedback.Start(pPoint)
                        Return Nothing
                    Else
                        pPolyLine = m_LineFeedback.Stop
                        m_LineFeedback = Nothing
                        pPolygon = CreatePolygonfromPolyline(pPolyLine)
                        Return pPolygon
                    End If
                Case Else
                    System.Windows.Forms.MessageBox.Show("Not coded yet")
                    Return Nothing
            End Select

        Catch ex As Exception
            System.Windows.Forms.MessageBox.Show(ex.ToString, "Get Feature")
            Return Nothing
        End Try

    End Function

    Private Function CreatePolygonfromPolyline(pPolyline As ESRI.ArcGIS.Geometry.IPolyline6) As ESRI.ArcGIS.Geometry.IPolygon4

        Try
            Dim pOutCollection As ESRI.ArcGIS.Geometry.IPointCollection4 = New ESRI.ArcGIS.Geometry.Polygon
            Dim pPolygon As ESRI.ArcGIS.Geometry.IPolygon4

            pOutCollection.AddPointCollection(pPolyline)

            pPolygon = pOutCollection
            pPolygon.Close()
            Return pPolygon

        Catch ex As Exception
            System.Windows.Forms.MessageBox.Show(ex.ToString, "Conversion error")
            Return Nothing
        End Try

    End Function

End Class
0 Kudos