Thursday 15 June 2017

arcgis 10.0 - Programmatically creating non-feature-linked, simple line callout annotation and setting anchor point?



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.)



Answer



This should be working. When you edit are you using the regular edit tool or the edit annotation tool? The anchor point only sticks with the edit annotation tool.


To optimize your code, I recommend using a symbol from the symbol collection and setting all properties via ISymbolCollectionElement. See http://help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/index.html#//001200000qzm000000 for more details.


No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...