Select to view content in your preferred language

Annotations: Let us use move them with arcpy geometry functions.

217
4
a month ago
Status: Implemented
AlfredBaldenweck
MVP Regular Contributor

Pro 3.1

This may be a duplicate of another Idea but I can't find it.

A while back I had a project whose data I had to shift geographically at the last minute to obscure precise locations. With something like 15 different files of varying geometries, I decide to script it. 

import arcpy

def shift(fc):
    shi = 2000
    with arcpy.da.UpdateCursor(fc, ["SHAPE@XY"]) as cursor:
        for row in cursor:
            try:
                cursor.updateRow([[row[0][0] + shi, row[0][1] +shi]])
            except:
                continue
    return

shift(r"filepath")

 

This worked great! All my data (points, lines, polygons) successfully moved by 2 kilometers to the northeast.

 Unfortunately, it did not work for my many, many annotations. Or rather, the geometry moved, but the text stayed in the same place?

Before the shift:

AlfredBaldenweck_3-1734568999866.png

After the shift

AlfredBaldenweck_1-1734568956206.png

This is kind of screwy.

As I result, I had to undo my shift and manually move all my annotations, which was incredibly time-intensive.

(Yes, I refreshed and re-added the data. Still had this problem)

Please let geometry functions be used for Annotations.

 

4 Comments
HaydenWelch

This is such an irritating and terrible side effect of the annotation fields being run through a translation layer.

There is currently a workaround, but it involves pulling the "Element" blob field out and reading it in as a CIM definition, then updating the Geometry object in that CIM def then re-writing the element field.

bad part about that is that the geometry field of the annotation is not updated during that process unless you manually do it during the update.

Did some messing around, and while the CIM definition is indeed stored there, updating is is a bit odd.

HaydenWelch_0-1735308801665.png

It's read as a readonly memoryview. You can lop off the first 8 and last 11 bytes to get a serializable bytestring, then load that in (I had to roll my own Decoder using some of the functions in .cimloader.jsontocim), but the final output is something like the far right of that image. You can then write the data back to the field using an update cursor, but you need to write the raw bytes, because a memoryview will be referencing garbage collected data after it leaves scope.

It does seem to update something, but whatever those leading and trailing bytes are seem to be important and even adding them back to the bytearray after updating the CIM object just destroys the Annotation. It successfully updates the field, but the annotations vanish from the map and can only be deleted by removing them from the table directly.

There is also a Point object stored in the leader point field of the Annotaiton CIM that could theoretically be moved too, but as far as I can tell, there's no way to apply the updates to an existing Annotation. You could still use this to run some validations using the expanded information in the CIM, but that's much less useful than being able to directly update the CIM in place.

AlfredBaldenweck

Oh weird. Cool to know, though. Annotations are such awful things-- I'm just glad I never had to deal with the old Coverage Annotations-- you set the symbology (including size) fresh for each map.

CraigWilliams
Status changed to: Implemented

I'm marking this implemented but it's a bit different than the approach being used here. The shape field of an annotation object is just the bounds of the item. Changing it does not affect the annotation in the way you're expecting. At ArcGIS Pro 3.4 we introduced a new ANNO@ token to work with an Annotation object in cursors and modify properties using the arcpy CIM access. Use that path to make modifications you want to make.

AlfredBaldenweck

Hmm. That'll be useful when my organization gets there. Thanks!