ProConcepts Editing Annotation - Esri/arcgis-pro-sdk GitHub Wiki

This concepts document covers special considerations for authors of editing tools for annotation. It augments the overall annotation concepts covered in ProConcepts Annotation.

ArcGIS.Core.dll

ArcGIS.Desktop.Editing.dll

Language:      C#
Subject:       Annotation
Contributor:   ArcGIS Pro SDK Team <[email protected]>
Organization:  Esri, http://www.esri.com
Date:          10/06/2024
ArcGIS Pro:    3.4
Visual Studio: 2022

In this topic

Overview

This concepts document augments the overall annotation concepts covered in ProConcepts Annotation. Annotation features differ from other geodatabase features in a few small but fundamental ways. It is important to keep these in mind when developing custom editing tools that create or modify annotation features.

Firstly an annotation feature class stores polygon geometry. This polygon is the bounding box of the text of the annotation feature. The bounding box is calculated from the text string, font, font size, angle orientation and other text formatting attributes of the feature. It is automatically updated by the application each time the annotation attributes are modified. You should never need to access or modify an annotation feature's polygon shape.

The text attributes of an annotation feature are represented as a CIMTextGraphic. The CIMTextGraphic contains the text string, text formatting attributes (such as alignment, angle, font, color, etc), and other information (such as callouts and leader lines). It also has a shape which represents the baseline geometry that the annotation text string sits upon. For annotation features this CIMTextGraphic shape can be a point, polyline (typically a two point line or Bezier curve), multipoint or geometryBag. It is this shape that you will typically interact with when developing annotation tools. For example when creating annotation features, the geometry passed to the EditOperation.Create method is the CIMTextGraphic geometry.

Another key concept when interacting with annotation is the annotation feature class schema. By default, annotation feature classes are created with a series of fields with descriptive information about the feature and its symbolization. While these fields are created for new feature classes, not all these fields are required. Optional text formatting attributes can be deleted by the user if they exist; they are no longer designated as protected or system fields. In ArcGIS Pro, the only fields guaranteed to exist in an annotation schema are AnnotationClassID, SymbolID, Element, FeatureID or FeatureGlobalID (if using a GlobalID relationship), ZOrder and Status along with the system ObjectID and Shape fields. All other fields which store text formatting attributes (such as TextString, FontName, VerticalAlignment, HorizontalAlignment etc) are optional. They are not guaranteed to exist in the physical schema. Additionally, the ArcGIS Pro annotation model no longer has Bold and Italic fields. They have been replaced with a FontStyle field. When the annotation descriptive fields exist, they are kept in sync with the contents of the CIMTextGraphic of the AnnotationFeature. Updating the CIMTextGraphic will update the field in the row corresponding to the property. Likewise, updating the field value will update the CIMTextGraphic. If you update both the field and the CIMTextGraphic in one operation and they conflict, the CIMTextGraphic will take precedence.

Due to the complexities of having to cater for possible differences in the annotation schema, the recommended way to access or modify annotation features is to use the AnnotationProperties class which is obtained from an Inspector object.

Annotation Label and Symbol Classes

The final concept specific to annotation feature classes is the label and symbol classes defined at the feature class level. Each annotation feature class has a set of label and symbol classes associated with it. Label classes are synonymous with annotation classes in ArcGIS 10x. A label class can contain a query expression and placement properties (to include a labeling expression) to define how a subset of annotation in the feature class display and the default symbology to be applied when creating new annotation.

For example, if you have an annotation feature class for cities, you could have label classes of varying text sizes and scale ranges for small, medium, and large cities—all managed within a single annotation feature class. Label classes save you from having to define and maintain multiple annotation feature classes.

An annotation feature class also contains a collection of one or more text symbols that you define. Every time you create a new annotation feature, you assign it one of these predefined symbols. The symbol contains properties that describe how the annotation feature is drawn, such as font, size, and color. For example, if you have annotation for small, medium, and large cities, create three text symbols of varying font sizes to assign to the annotation. Each annotation feature stores a value in the SymbolID field which refers to one of these three text symbols. Because the annotation feature stores the symbol ID of the predefined symbol rather than each individual symbol property, ArcGIS is able to reduce storage requirements and maximize display and query performance. Committing to a limited list of symbols can help you promote standards for any new annotation features you create.

You can retrieve the label and symbol classes for an annotation feature class by accessing its AnnotationFeatureClassDefinition. This can be achieved using code similar to the following

   // must be executed on the MCT - wrap in a QueuedTask.Run

   // get the anno layer
   AnnotationLayer annoLayer = CurrentTemplate.Layer as AnnotationLayer;
   if (annoLayer == null)
     return false;

   // get the anno feature class
   var fc = annoLayer.GetFeatureClass() as ArcGIS.Core.Data.Mapping.AnnotationFeatureClass;
   if (fc == null)
     return false;

   // get the featureclass definition which contains the label and symbol collections
   var annoDefinition = fc.GetDefinition() as ArcGIS.Core.Data.Mapping.AnnotationFeatureClassDefinition;
   //Get the CIM definitions for the labelling and symbol identifiers
   var labels = annoDefinition.GetLabelClassCollection();
   var symbols = annoDefinition.GetSymbolCollection();

You can use values in these collections to set the AnnotationClassID and SymbolID fields when creating new annotation features if required.

Of course, over time, you may discover that the text symbols you created do not contain the properties you need for one or more annotation features. For example, you may require a smaller font size to fit annotation into a congested area. One approach is to create a new text symbol with the new properties, then assign the new text symbol to the annotation features. However, creating a new symbol for every unique set of properties you require could result in a long list of symbols that is difficult to work with.

Instead ArcGIS Pro allows you to modify symbol properties on a feature-by-feature basis. When editing, you can select annotation and change any symbol property for that annotation. These overrides are managed by comparing edits made to the CIMTextGraphic's symbol to the symbol referenced. If the feature can be stored with overrides, it will be. This allows for CIMTextSymbol properties to be freely changed without worrying about storage implications. If the overrides cannot be stored separately, the CIMTextGraphic will be disconnected from the symbol collection and the annotation feature will store the entire CIMTextSymbol. When this happens, the symbol is referred to as 'bloated'. Note that AnnotationFeatureClassDefinition properties can designate that overrides are not allowed or that all symbols must reference a symbol collection symbol. If those properties are set and edits are made that violate those constraints, the edits will fail.

Accessing Annotation

Since so many of the text formatting fields in the data schema for an annotation feature class are optional, you should not use the Inspector[fieldName] methodology for accessing and updating annotation data. Instead, use the AnnotationProperties class to access the baseline geometry and other text formatting properties of an annotation feature. The AnnotationProperties class is retrieved via the Inspector.GetAnnotationProperties method. If you are modifying the annotation attributes, update the required properties on the AnnotationProperties object, then use the Inspector.SetAnnotationProperties method to assign any altered properties back on to the Inspector object.

Use this pattern for creating annotation features which require attributes different from default template values, modifying annotation attributes and also for creating annotation templates. Snippets covering all three scenarios are displayed below.

Note: The AnnotationFeatureClassDefinition AreSymbolOverridesAllowed() method can be accessed to verify if symbol overrides are allowed on the given annotation feature class. If symbol override are not allowed, edits to the annotation text graphic will fail. Similarly the AnnotationFeatureClassDefinition IsSymbolIDRequired() method should also be checked to determine if all symbolds must reference a symbol collection symbol. If symbol IDs are required, edits to the annotation text graphic which cause a disconnect from the symbol collection will also fail.

This code snippets below assume that annoFeatureClassDef.AreSymbolOverridesAllowed() is true and annoFeatureClassDef.IsSymbolIDRequired() is false.

Creating Annotation Features

If you wish to create construction tools for annotation features which assign attribute overrides then use the overload on the EditOperation.Create method. This takes an Inspector object as a parameter, allowing you to override the default properties of an annotation template. The general pattern for creating features in this scenario should be as follows: obtain the inspector object from the CurrentTemplate, assign the AnnotationClassID and SymbolID fields (required fields), retrieve the AnnotationProperties class, update the necessary attributes including the shape, assign the AnnotationProperties back to the inspector and call EditOperation.Create. The following code illustrates this.

   // must be executed on the MCT - wrap in a QueuedTask.Run

   // use the template's inspector object
   var inspector = CurrentTemplate.Inspector;

   // use the inspector[fieldName] methodology to set the AnnotationClassid and SymbolID fields
   //   these are fixed fields in the annotation schema and are guaranteed to exist. 
   inspector["AnnotationClassID"] = label.ID;
   // set the symbolID too
   inspector["SymbolID"] = symbolID;


   // get the annotation properties
   var annoProperties = inspector.GetAnnotationProperties();
                                               
   // use the annotation properties to set the other attributes
   annoProperties.TextString = "My annotation feature";
   annoProperties.Color = ColorFactory.Instance.GreenRGB;
   annoProperties.VerticalAlignment = ArcGIS.Core.CIM.VerticalAlignment.Top;
   annoProperties.Underline = true;

   // set the geometry to be the sketched geometry
   // when creating annotation features the shape to be passed 
   //    in the create operation is the CIMTextGraphic shape
   annoProperties.Shape = geometry;

   // set the annotation properties back on the inspector
   inspector.SetAnnotationProperties(annoProperties);

   // Create an edit operation
   var createOperation = new EditOperation();
   createOperation.Name = string.Format("Create {0}", CurrentTemplate.Layer.Name);
   createOperation.SelectNewFeatures = true;

   // create and execute using the inspector
   createOperation.Create(CurrentTemplate.Layer, inspector);
   return createOperation.Execut();

Modifying Annotation Features

Modifying annotation features should follow similar patterns to modifying point, line or polygon features. That is; create an Inspector object, load the appropriate feature, modify the necessary attributes, then call EditOperation.Modify passing the updated Inspector object. The only difference for annotation features is that you use the AnnotationProperties class to modify the annotation specific attributes. This is illustrated below.

   // must be executed on the MCT - wrap in a QueuedTask.Run

   // load the inspector with the feature
   var insp = new Inspector();
   insp.Load(annoLayer, oid);

   // get the annotation properties
   var annoProperties = insp.GetAnnotationProperties();
   // get the cimTextGraphic geometry
   Geometry textGraphicGeometry = annoProperties.Shape;

   // increase the font size
   annoProperties.FontSize = 48;

   // rotate the cimTextGraphic - but only if it's a polyline
   Polyline baseLine = textGraphicGeometry as Polyline;
   if (baseLine != null)
   {
      // rotate the baseline 90 degrees
      var origin = GeometryEngine.Instance.Centroid(baseLine);
      Geometry rotatedBaseline = GeometryEngine.Instance.Rotate(baseLine, origin, System.Math.PI / 2);

      // set the updated geometry back to the annotation properties
      annoProperties.Shape = rotatedBaseline;
   }

   // assign the annotation properties back to the inspector
   insp.SetAnnotationProperties(annoProperties);

   // create the edit operation
   op = new EditOperation();
   op.Name = "Update annotation baseline";
   op.SelectModifiedFeatures = true;
   op.SelectNewFeatures = false;
   
   // call modify
   op.Modify(insp);

   // call execute

Similar to editing of other geometry types, you can load multiple annotation features into the inspector, assign values via the AnnotationProperties class and have those values be applied to all features loaded in the inspector.

If the property you are wishing to modify does not exist on the AnnotationProperties class - for example you wish to add a callout or leader line or modify some more specific text formatting attribute you can still access the CIMTextGraphic itself by using the TextGraphic property. Once you have modified the CIMTextGraphic, use the AnnotationProperties.LoadFromTextGraphic method to assign the text graphic back to the AnnotationProperties, followed by setting the annotation properties back on the Inspector.

   // must be executed on the MCT - wrap in a QueuedTask.Run

   // load the inspector with the feature
   var insp = new Inspector();
   insp.Load(annoLayer, oid);

   // get the annotation properties
   var annoProperties = insp.GetAnnotationProperties();
   // get the cimTextGraphic 
   CIMTextGraphic textGraphic = annoProperties.TextGraphic;

   // change text
   textGraphic.Text = "Hello world";

   // set x,y offset via the symbol
   var symbol = textGraphic.Symbol.Symbol;
   var textSymbol = symbol as CIMTextSymbol;
   textSymbol.OffsetX = 2;
   textSymbol.OffsetY = 3;

   textSymbol.HorizontalAlignment = HorizontalAlignment.Center;

   // load the updated textGraphic
   annoProperties.LoadFromTextGraphic(textGraphic);

   // assign the annotation properties back to the inspector
   insp.SetAnnotationProperties(annoProperties);

   // create the edit operation
   op = new EditOperation();
   op.Name = "Update annotation baseline";
   op.SelectModifiedFeatures = true;
   op.SelectNewFeatures = false;
   
   // call modify
   op.Modify(insp);

   // call execute

Creating Annotation Templates

The Inspector and AnnotationProperties classes are also the recommended pattern for creating annotation templates. Rather than dealing directly with the CIM as in previous releases, the CreateTemplate method facilitates template creation with a populated Inspector object. You can also easily assign the template name, description, tags, default tool and tool filter with the same call. This extension method can be used for all geometry types; it is not specifically for creation of annotation templates.

   // must be executed on the MCT - wrap in a QueuedTask.Run

   // load the schema
   insp = new Inspector();
   insp.LoadSchema(annoLayer);

   // set up the AnnotationClassID, SymbolID fields - required fields
   // ok to access them this way - they are guaranteed to exist
   insp["AnnotationClassID"] = label.ID;
   insp["SymbolID"] = symbolID;

   // set up some text properties
   AnnotationProperties annoProperties = insp.GetAnnotationProperties();
   annoProperties.FontSize = 36;
   annoProperties.TextString = "My Annotation feature";
   annoProperties.VerticalAlignment = VerticalAlignment.Top;
   annoProperties.HorizontalAlignment = HorizontalAlignment.Justify;

   // assign the properties back to the inspector
   insp.SetAnnotationProperties(annoProperties);

   // set up tags
   var tags = new[] { "Annotation", "tag1", "tag2" };

   // set up default tool - use daml-id rather than guid
   string defaultTool = "esri_editing_SketchStraightAnnoTool";

   // tool filter is the tools to filter OUT
   var toolFilter = new[] { "esri_editing_SketchCurvedAnnoTool" };

   // create a new CIM template  - new extension method
   var newTemplate = annoLayer.CreateTemplate("My new template", "sample description", insp, defaultTool, tags, toolFilter);

The ProGuide Annotation Construction Tools and ProGuide Annotation Editing Tools give examples of building construction and editing tools using concepts discussed above. Refer to ProConcepts Annotation for general reference information on Annotation.

⚠️ **GitHub.com Fallback** ⚠️