I am working on a Python Add-In using ArcGIS 10.2.2 where the user can draw lines and the Add-In will add dimension annotation showing length for each segment of the line that is drawn. I can get this working just fine and the text is angled properly, but the OnLine() method does not use snapping in the map. The add-in does however, properly set the snapping (I verified the snapping tolerance in the map and tested with different tolerances) in the map for everything BUT the onLine() method for the tool.

In the ArcObjects help docs, it says this is usually invoked on a OnClick method from the ICommand Interface. I do not know how I could use this in a Python Add-In. Here is my attempt to use comtypes and some custom modules to invoke snapping for my Add-In. Unfortunately, it is not working. Maybe I should attempt this in .NET?

import arcobjects #custom module
from arcobjects import elements #custom module
import arcpy
import pythonaddins
import math
import comtypes.gen.esriControls as esriControls
import comtypes.gen.esriArcMapUI as esriArcMapUI
from arcobjects import CType, NewObj, CLSID #custom

def midpoint(point_a, point_b):
    x1 = point_a.X
    y1 = point_a.Y
    x2 = point_b.X
    y2 = point_b.Y

    # Find midpoint
    x = (x1 + x2) / 2.0
    y = (y1 + y2) / 2.0
    return (x, y)

def get_angle(xy1, xy2):
    """Calculate azimuth angle from two points. (Zero is north.)"""
    # ArcPy point objects
    x1, y1, x2, y2 = xy1.X, xy1.Y, xy2.X, xy2.Y
    dx, dy = (x2 - x1, y2 - y1)
    return math.degrees(math.atan2(dy, dx))

class DrawLine(object):
    """Implementation for AddMeasurementAnno_addin.tool (Tool)"""
    def __init__(self):
        self.enabled = True
        self.shape = 'Line'
        self.cursor = 3

        # arcobjects get current app (IApplication interface Pointer)
        self.pApp = arcobjects.GetCurrentApp()
        self.pDoc = self.pApp.Document
        self.pMxDoc = CType(self.pDoc, esriArcMapUI.IMxDocument)
        m_SnappingEnv = NewObj(esriControls.Snapping,
        m_SnappingEnv.Enabled = True
        m_SnappingEnv.IgnoreIMSLayers = True
        m_SnappingEnv.ShowSnapTips = True
        m_SnappingEnv.ShowSnapTipBackground = True
        m_SnappingEnv.Tolerance = 10
        m_SnappingEnv.SnappingType = esriControls.esriSnappingTypeVertex + \
                                     esriControls.esriSnappingTypeEndpoint + \
        print m_SnappingEnv.SnappingType
        m_Snapper = m_SnappingEnv.PointSnapper
        m_SnappingFeedback = NewObj(esriControls.SnappingFeedback,
        m_SnappingFeedback.Initialize(self.pMxDoc, m_SnappingEnv, True)

    def onLine(self, line_geometry):
        # Report back mouse position at line endpoint
        m_CurrentMouseCoords = self.pMxDoc.CurrentLocation
        print m_CurrentMouseCoords.X, m_CurrentMouseCoords.Y

        # loop through geom and grab vertices
        for part in line_geometry:
            for i in range(part.count):
                if i != (part.count - 1):
                    pt1, pt2 = part.getObject(i), part.getObject(i+1)
                    line = arcpy.Polyline(arcpy.Array([pt1, pt2]))
                    length = str(round(line.length,1)) + "'"
                    x, y = midpoint(pt1, pt2)
                    elements.add_text(self.pApp, text=length, name='length', size=9, bold=True,
                                      x=x, y=y, angle=get_angle(pt1, pt2), view='data')

Here is what it does in ArcMap, but the distances are not correct because the line was not snapped. Without snapping this Add-In is pretty useless:

enter image description here

And the snap options window:

enter image description here

Has anyone had success in doing this?

  • 3
    Yes, you may need to look into .Net development. Python add-ins weren't really designed for this level of customization, they were meant for extremely simple functionality. Oct 13 '14 at 17:09

To compliment the other answers it looks like the Python Addin Tool is drawing a graphic and not a SKETCH which is what you would use in .NET. To my knowledge when drawing graphics on a map a graphic does not honour the snapping environment (I may be wrong on that?).

Looking at your screen shot they appear to be simple rectangles. An alternative approach would be to select the edge and extract out that line segment. I'm assuming that your polygons are composed of 4 vertices.

  • Unfortunately I have no control over how the line is drawn since that method is coming from Esri's python Add-In framework, but it is actually drawing a Polyline object (the polygons were already there, I was just measuring the sides of them with the add-in). The annotation the add-in is creating is based on the length of the line in between vertices (added each time the user clicks). I think I would need to extend the Esri "Tool" class within the add-in framework to enable snapping while drawing the line.
    – crmackey
    Nov 25 '15 at 13:48

As commented by @JasonScheirer:

Yes, you may need to look into .Net development. Python add-ins weren't really designed for this level of customization, they were meant for extremely simple functionality.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.