Updating Annotation with Arcpy in ArcGIS Pro

678
1
09-27-2022 12:31 PM
David_Colombini
New Contributor II

Hello,

 

I'm trying to figure out how to use an updatecursor to update an annotation feature class (specifically moving the annotation, not updating the actual text).  There's little documentation on this, but from what I can find I think both the shape field and the element fields need to be updated?   I'm trying to use an update cursor with hard-coded values first just to see if the annotation updates (it's a single feature), then change it to be more dynamic later on.  

I'm also testing with edit sessions to see if that does anything but nothing seems to update.  I don't get any errors either, but it seems like the update cursor is not changing anything.  Any ideas?

UPDATE:  It's definitely an issue with the blob field.  Geometry field is updating fine (tested on another polygon layer), but converting the element field to bytes, making changes, and converting back to blob is something I haven't seen any documentation on.  Does anyone know the proper encoding?  I'm using UTF-8 by default but playing around with different options.

   This is my code so far: 

import arcpy, os, json

aprx = arcpy.mp.ArcGISProject(r"C:\MyProject\MyProject.aprx")

for m in aprx.listMaps():
print("Map: {0} Layers".format(m.name))
for lyr in m.listLayers():
if lyr.name == 'testAnno':
cursor = arcpy.da.SearchCursor(lyr, ['SHAPE@','Element'])
for row in cursor:
#print(row[1].tobytes())
print(row[1].tobytes())
print(row[0])
s = row

#view the
print(s[0].JSON)
print(s[1].tobytes())

#hard coded data:

origjson = """{"rings":[[[-75.764363411999966,45.158486504000052],[-73.469080190999989,45.158486504000052],[-73.469080190999989,42.846093335000035],[-75.764363411999966,42.846093335000035],[-75.764363411999966,45.158486504000052]],[[-61.499009848999947,43.715848636000032],[-63.210088402999986,42.185967841000036],[-64.347457697999971,43.458046171000035],[-73.427031740999951,38.791915476000042],[-73.428038225999956,38.791551154000047],[-73.431213794999962,38.792105457000048],[-73.433067308999966,38.794742874000065],[-73.432513005999965,38.797918442000025],[-73.430882073999953,38.799407634000033],[-64.353259686999934,43.46453534300008],[-64.751373533999981,43.909801450000032],[-63.040294979999942,45.439682245000029],[-61.499009848999947,43.715848636000032]]],"spatialReference":{"wkid":4326,"latestWkid":4326}}"""

newjson = """{"rings":[[[-90.580491480999967,44.665072753000061],[-90.580491480999967,42.460838942000066],[-90.579865532999975,42.461430743000051],[-90.577483941999958,42.461614847000078],[-73.428077403999964,38.799780517000045],[-73.427236678999975,38.799506056000041],[-73.425022048999949,38.797163643000033],[-73.425112405999982,38.793941327000027],[-73.427454818999934,38.791726697000058],[-73.42983640999995,38.791542593000031],[-90.57924294899999,42.453376923000064],[-90.58008367299999,42.453651384000068],[-90.580491480999967,42.454082722000066],[-90.580491480999967,42.352679584000043],[-92.875774701999944,42.352679584000043],[-92.875774701999944,44.665072753000061],[-90.580491480999967,44.665072753000061]]],"spatialReference":{"wkid":4326,"latestWkid":4326}}"""

newbytes = """b'\n\x011 \x02*M-92.890214218719606,42.748368221326849;-90.578362387719665,42.7483682213268492\x04test8\x00A\x00\x00\x00\x00\x00\x00\x00\x00z2{"x":-73.4289569073265227,"y":38.7956615549338792}\x81\x01iR\xb2{\xdd\xe5d@\xca\x01\x99\x02{"type":"CIMSimpleLineCallout","lineSymbol":{"type":"CIMLineSymbol","symbolLayers":[{"type":"CIMSolidStroke","enable":true,"capStyle":"Round","joinStyle":"Round","lineStyle3D":"Strip","miterLimit":10,"width":1,"color":{"type":"CIMRGBColor","values":[0,0,0,100]}}]},"autoSnap":true}'"""

origbytes = """b'\n\x011 \x02*M-63.778802929164698,43.241781972225155;-61.466951098164685,43.2417819722251692\x04test8\x00A\x00\x00\x00\x00\x00\x00\x00\x00z2{"x":-73.4289569073265227,"y":38.7956615549338792}\x81\x01iR\xb2{\xdd\xe5d@\xca\x01\x99\x02{"type":"CIMSimpleLineCallout","lineSymbol":{"type":"CIMLineSymbol","symbolLayers":[{"type":"CIMSolidStroke","enable":true,"capStyle":"Round","joinStyle":"Round","lineStyle3D":"Strip","miterLimit":10,"width":1,"color":{"type":"CIMRGBColor","values":[0,0,0,100]}}]},"autoSnap":true}'"""

d = json.loads(s[0].JSON)#getting first record, assuming only 1 anno here for testing

workspace = os.path.dirname(r"C:\Users\dcolombini\Documents\ArcGIS\Projects\MyProject\MyProject.aprx")
# Start an edit session. Must provide the workspace.
edit = arcpy.da.Editor(workspace)

# Edit session is started without an undo/redo stack for versioned data

# Set multiuser_mode to False if working with unversioned enterprise gdb data
edit.startEditing(with_undo=False, multiuser_mode=False)

# Start an edit operation
edit.startOperation()




print('\n UPDATING ANNO \n')

ESBlob = bytearray(origbytes, 'utf-8')
dj = json.dumps(origjson)

# Create update cursor for feature class

for lyr in m.listLayers():
if lyr.name == 'testanno':

for m in aprx.listMaps():
print("Map: {0} Layers".format(m.name))
for lyr in m.listLayers():
if lyr.name == 'testAnno':
print('updating anno layer')
with arcpy.da.UpdateCursor(lyr, ['SHAPE@JSON','Element'], "", sr) as cursor:
for row in cursor:
#print(row)
row[0] = dj #features[0].projectAs('4326')
row[1] = ESBlob
cursor.updateRow(row)



print('\n UPDATE COMPLETE \n')
#del aprx

# Stop the edit operation.
edit.stopOperation()

# Stop the edit session and save the changes
edit.stopEditing(save_changes=True)


print('\n Checking new values \n')
for m in aprx.listMaps():
print("Map: {0} Layers".format(m.name))
for lyr in m.listLayers():
if lyr.name == 'testAnno':
cursor = arcpy.da.SearchCursor(lyr, ['SHAPE@JSON','Element'])
for row in cursor:
print(row[1].tobytes())
print(row[0])
0 Kudos
1 Reply
RogerDunnGIS
Occasional Contributor II

These are just some ideas from looking at your code.  It doesn't appear that "Python" was chosen as the formatting language in the code block, the indentation is missing, and the code isn't documented.  This all makes it a little hard to read.  So I'll go with what I can see.  One hint that I can give you is that if this code is working inside a Notebook, you can use "CURRENT" instead of the name of the project file you are working in and not care about the name and path of the ArcGIS Pro project itself.

Although Python is able to display bytes as a string using print() and implicit representation, it doesn't mean that the resulting string can be converted back into bytes by escaping it or removing special characters.  The variable newbytes for instance, is assigned a string, but I bet that's not how it's stored, but rather, how it's represented as a string in Python.

The numbers (e.g. 42.748368221326849) are probably being stored in actual binary form in the geodatabase, not as binary representations of the string of the number.  The string "42.748368221326849" would take something in the neighborhood of 19 bytes (ASCII) or 38 bytes (UTF-8) to store, but the number 42.748368221326849 would only take 4 bytes (if a float) or 8 bytes (if a double) to store.  Bytes are the raw material that everything in computers are based on, and many of what we see and experience with a computer is not stored as string-bytes.  The color red, for example, is hardly ever stored in bytes as "red", "RGB(255, 0, 0)", but in binary as 111111110000000000000000 in three bytes.

Hopefully this guides you a solution, but I've just not done anything with annotation in Python before.

Let us know and good luck!

0 Kudos