Vitals: ArcGIS-ArcView 10.0, Python 2.6.5, PythonWin, comtypes
I'll start with my main questions:
- How, using ArcObjects, do I go about creating non-feature-linked geodatabase annotation features that have a simple line callout format, and
- How do I set the anchor point so that the leader line remains functional, i.e. remains anchored on the point feature it references even when the annotation is moved?
I have a standalone Python script that accesses ArcObjects through comtypes wrappings of ArcGIS OLBs -- works great. Overall, this ArcMap automation script cursors through a point feature class, creating complex vector graphics (in map units in the data view) from attribute info in the points and loads each of those graphics (an IGroupElement) into a non-feature-linked annotation feature as the anno's graphic element. I want to have functional simple line callout leader lines, but the latter graphics-containing anno features won't support a simple line callout. So my idea is to create, for each of the graphic anno features, an underlying, transparent dummy text anno feature that will carry the leader line. I've tried a bunch of things, but I can't get a leader line to show up. I have defined separate annotation classes within the target annotation feature class for storage of graphic versus dummy text leader line anno features. The leader line anno class was defined with transparent text and a simple line callout symbology. Now, below are code excerpts that include the function I call to create each annotation feature, be it graphic or leader line dummy:
#make front leader text element
ldrText = '|'
pFLdrTextElement = BuildTextElement(ldrText, 'Leader', 'null', annoRefScale, False, (fX, fY)) #returns ITextElement
pFLdrTxtElem = CType(pFLdrTextElement, esriCarto.IElement)
pFLdrTxtElem.Geometry = pTweakedTxtPoint
...
ldrannoclass = 1
CreateAnnoFeature(annoFC, pFLdrTxtElem, fOID, ldrannoclass)
def BuildTextElement(s, signType, txtcolor, annoRefScale, has_outline, featPos=None):
...
#separate out leader annotation text
if signType == 'Leader':
import comtypes.gen.esriGeometry as esriGeometry
pTextColor = GetRGBColor(txtcolor)
pSimpleLineCallout = NewObj(esriDisplay.SimpleLineCallout, esriDisplay.ISimpleLineCallout)
anchorPoint = NewObj(esriGeometry.Point, esriGeometry.IPoint)
anchorPoint.PutCoords(featPos[0], featPos[1])
pSimpleLineCallout.AnchorPoint = anchorPoint
pSimpleLineCallout.AutoSnap = True
pLeaderLineSymbol = NewObj(esriDisplay.CartographicLineSymbol, esriDisplay.ILineSymbol)
pLineColor = NewObj(esriDisplay.RgbColor, esriDisplay.IRgbColor)
pLineColor.Red = 0
pLineColor.Green = 0
pLineColor.Blue = 0
pLeaderColor = CType(pLineColor, esriDisplay.IColor)
pLeaderLineSymbol.Color = pLeaderColor
pSimpleLineCallout.LineSymbol = pLeaderLineSymbol
pSimpleLineCallout.LeaderTolerance = 0.0
pTextBackground = CType(pSimpleLineCallout, esriDisplay.ITextBackground)
pTextSymbol.Background = pTextBackground
pTextSymbol.Color = pTextColor
pTextElement.Symbol = pTextSymbol
pTextElement.Text = s
pTextElement.ScaleText = True
return pTextElement
...
def CreateAnnoFeature(pAnnoFC, pElement, featID, annoclass):
"""Creates new annotation feature, loads the graphic into it,
and inserts the new anno feature into the anno feature class"""
import comtypes.gen.esriGeoDatabase as esriGeoDatabase
import comtypes.gen.esriCarto as esriCarto
#make Insert cursor and FeatureBuffer for use in InsertFeature method
pFCursor = pAnnoFC.Insert(True)
pFeatBuffer = pAnnoFC.CreateFeatureBuffer()
pFeature = CType(pFeatBuffer, esriGeoDatabase.IFeature)
#set annotation of anno feature
pAnnotationFeature = CType(pFeature, esriCarto.IAnnotationFeature2)
pAnnotationFeature.Annotation = pElement
pAnnotationFeature.Status = esriCarto.esriAnnoStatusPlaced
pAnnotationFeature.AnnotationClassID = annoclass
pFeature = CType(pAnnotationFeature, esriGeoDatabase.IFeature)
#insert the new anno feature
pFeatOID = pFCursor.InsertFeature(CType(pFeature, esriGeoDatabase.IFeatureBuffer))
pFCursor.Flush()
mxDoc.ActiveView.PartialRefresh(esriCarto.esriViewGraphics, None, None)
The BuildTextElement()
function contains the code to build either a plain text element or one with a simple line callout format including setting of the anchor point. (See code above for the latter.) The latter results in what looks like a simple line callout, but when the annotation is moved the leader line does not remain anchored on the point feature. The text and leader line are simply combined into a graphic that moves around as a whole with the anno. In the case of the plain text element, I was hoping that a leader line would show, because the simple line callout format is defined in the annotation class symbology.
In this case, how would I set the anchor point?
In fact, in either case the leader line dummy is created--the transparent text box is placed, but there is no leader line. I really don't have a good idea of whether I am going about this in the best way. I can't change the structure of looping through the point features one at a time, but how I go about building the annotation features can certainly change. For instance, using an IFDOGraphicsLayer
and related objects instead of an Insert Cursor might work.
A related desire on my part is to be able to populate attributes of each anno feature right after (as?) I create it. Some of the attribute info will come from the point feature and some will capture the state of some of the properties of the anno feature. I really don't know how to go about this efficiently.
What are some ways I might do this?
Followup Question:
So, where in my code above can I get an ISymbolCollectionElement
and on what?
I gather I should be making an empty text element with only a point location geometry assigned, passing this into CreateAnnoFeature()
, and setting properties there through ISymbolCollectionElement
. Because I create the anno feature as a FeatureBuffer
from the anno feature class, the feature is an annotation feature; however, I don't think a feature supports ISymbolCollectionElement, and the TextElement I pass in is just a plain TextElement. It isn't the element of the anno feature until I set the Annotation property.
Can I get an ISymbolCollectionElement on the TextElement before I set it as the annotation element?
Assuming I can, I guess that all I would have to set is the text, the symbolID, and the anchor point. The leader line, text size, and transparency should be picked up through the symbology defined in the anno feature class.
On a related note, how do I know the SymbolID
to assign?
In my existing anno class properties, the symbology tab shows the symbology that is defined for each of the annotation classes. When I create an anno feature and assign it to an annotation class, does it pick up the annotation class' symbology or do I need to assign the SymbolID
explicitly (hence the first question above)? (Right now, I set the TextElement
properties through ITextSymbol
, so I imagine any link to the SymbolCollection
that may normally exist is broken.)
ISymbolCollectionElement.SharedSymbol(1, pSymbol)
and get backTypeError: object is not callable.
ISymbolCollectionElement.SharedSymbol(1) = pSymbol
[VB6 syntax] gets me "can't assign to a function call."ISymbolCollectionElement.SharedSymbol = 1
gets me "attribute takes exactly 2 arguments (3 given)" The object diagrams showSharedSymbol (in SymbolID: Long): ISymbol
as a property put. The latter doesn't make sense to me for a 2 argument property. So, any insights? (Python of course might be the problem.)ISymbolCollectionElement.SharedSymbol(arg1, arg2)
has both arguments passed ByVal. The pSymbol is a pointer object. Does this create a mismatch with ByVal? (I'm just reaching now.)