Convert annotation to polygon features - Python Script...

01-08-2012 01:09 PM
New Contributor

Is there a Python Script for Converting Annotation FeatureClass to Polygon FeatureClass?

There is VBA Script ( that seems to be working fine (more or less).
It might have some problems with Multipart Annotations, but I haven't tried that (


Igor Karuza
0 Kudos
2 Replies
New Contributor II
Hi Igor,

Hopefully someone will come up with a better answer than this but you can use ArcObjects in python so I converted the VBA script you mentioned to python. You will have to install the comtype modules for python and then load the ArcMap Modules. Luckily Mark Cederholm shows you how to do this see ArcMap and Python

In C:\Python27\ArcGIS10.1\Lib\site-packages\comtypes
Delete automation.pyc, automation.pyo, safearray.pyc, safearray.pyo
Add the following entry to the _ctype_to_vartype dictionary (line 794):

At the Python prompt:
>>> from comtypes.client import GetModule
>>> GetModule("c:/program files/arcgis/desktop10.1/com/esriArcMapUI.olb")
TIP: If loading one or modules fails, delete all files in the comtypes/gen folder before trying again.

Then you're set for using ArcObjects in Python. Create a new script with the following in it
Open a new ArcMap session. Add an annotation layer and polygon layer and you're good to go.
# Updated for python ArcGIS 10.1 using comtypes
# Import Modules
import arcpy
from arcpy import env
import os
import sys
import comtypes.client
import comtypes.gen.esriSystem as esriSystem
import comtypes.gen.esriGeometry as esriGeometry
import comtypes.gen.esriGeoDatabase as esriGeoDatabase
import comtypes.gen.esriGeoprocessing as esriGeoprocessing
import comtypes.gen.esriArcMapUI as esriArcMapUI
import comtypes.gen.esriCarto as esriCarto
import comtypes.gen.esriDisplay as esriDisplay
import comtypes.gen.esriFramework as esriFramework
import comtypes.gen.esriDataSourcesFile as esriDataSourcesGDB
def NewObj(MyClass, MyInterface):
    from comtypes.client import CreateObject
        ptr = CreateObject(MyClass, interface=MyInterface)
        return ptr
        return None
def CType(obj, interface):
        newobj = obj.QueryInterface(interface)
        return newobj
        return None
def GetApp():
    """Get a hook into the current session of ArcMap"""
    pAppROT = NewObj(esriFramework.AppROT, esriFramework.IAppROT)
    if pAppROT is not None:
        iCount = pAppROT.Count
        if iCount == 0:
            print 'No ArcGIS application currently running.  Terminating ...'
            return None
        for i in range(iCount):
            pApp = pAppROT.Item(i)  #returns IApplication on AppRef
            print pApp.Name
            if pApp.Name == 'ArcMap':
                print "ArcMap found"
                pDoc = pApp.Document
                print pDoc.Title
                if pDoc.Title == "untitled.mxd" or pDoc.Title == "Untitled":
                    return pApp
    print 'No ArcMap session is running at this time.'
    print "No AppROT found"
    print "Failed"
    return None
def AnnotoPoly():
    #SET these variables for your individual case
    FDOLayerNum = 0 #Set annotation layer here (zero-based: 0 is first layer in TOC)
    FLayerNum = 1 #Set empty feature layer here (zero-based: 1 is second layer in TOC)
    OutputDPI = 1200 #Highest DPI of your final output device(s)
    ScreenResolution = 96 #Resolution of your monitor
    FinalOutputScale = 24000 #Final scale that your map will be printed with  
    pApp = GetApp()
    pDoc = pApp.Document
    pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)
    pMap = pMxDoc.FocusMap
    pActiveView = CType(pMap, esriCarto.IActiveView)
    pScreenDisplay = pActiveView.ScreenDisplay
    pDisplayTransform = pScreenDisplay.DisplayTransformation
    pFLayer = pMap.Layer(FLayerNum)
    pFL = CType(pFLayer, esriCarto.IFeatureLayer)
    pFClass = CType(pFL.FeatureClass, esriGeoDatabase.IFeatureClass)
    pClass = pMap.Layer(FDOLayerNum)
    pAnnoClass = CType(pClass,esriCarto.IFeatureLayer)
    pAnnoClass = pAnnoClass.FeatureClass
    pAnnoClass = CType(pAnnoClass.Extension,esriCarto.IAnnoClass)
    ReferenceScale = pMap.ReferenceScale
    MapScale = pMap.MapScale
    OptimumScale = float(ScreenResolution) / OutputDPI * FinalOutputScale / 2
    AnnoScaleFactor = float(pAnnoClass.ReferenceScale / OptimumScale)
    pFDOGraphicsLayer = pMap.Layer(FDOLayerNum)
    pFDOGraphicsRead = CType(pFDOGraphicsLayer, esriCarto.IFDOGraphicsLayerRead)
    pMap.ReferenceScale = 0
    pMap.MapScale = OptimumScale
    # Generate Annotation Graphics
    pFDOGraphicsRead.StartGeneratingGraphics(None, pScreenDisplay, True, True, False)
    pAnnoElement = pFDOGraphicsRead.NextGraphic
    while pAnnoElement:
        pTextElement = CType(pAnnoElement, esriCarto.ITextElement)
        pTextSymbol = pTextElement.Symbol
        #Temporarily change text symbol's size
        TempTextSize = pTextSymbol.Size
        pTextSymbol.Size = float(TempTextSize) * AnnoScaleFactor
        pTextQuery = CType(pTextSymbol, esriDisplay.IQueryGeometry)
        pTextPointGeo = CType(pAnnoElement.Geometry, esriGeometry.IGeometry)
        pScreenDisplay.StartDrawing(pScreenDisplay.WindowDC, pScreenDisplay.ActiveCache)
        pPolygon = CType(pTextQuery.GetGeometry(pScreenDisplay.WindowDC, pDisplayTransform, pTextPointGeo), esriGeometry.IPolygon)
        #Ensure geometry is suitable for a feature (sorts inner/outter rings)
        pTopoOperator2 = CType(pPolygon, esriGeometry.ITopologicalOperator2)
        pTopoOperator2.IsKnowSimple_2 = False
        #Restore textsymbol size
        pTextSymbol.Size = TempTextSize
        pFeature = CType(pFClass.CreateFeature(), esriGeoDatabase.IFeature)
        pFeature.Shape = pPolygon
        pAnnoElement = pFDOGraphicsRead.NextGraphic
    #Restore dataframe's previous extent
    pMap.ReferenceScale = ReferenceScale
    pMap.MapScale = MapScale
if __name__ == '__main__':
New Contributor III

Thanks for converting this script. Any idea why the new polygons are being created in a different location than the original annotation layer?