ProConcepts Annotation - Esri/arcgis-pro-sdk GitHub Wiki
Functionality that uses the fine-grained Annotation API. Annotation API functionality is found in ArcGIS.Core.dll. The Annotation API is commonly used in conjunction with geodatabase, map authoring, and editing.
ArcGIS.Core.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
- The ArcGIS.Core.Data.Mapping API for annotation builds upon the ArcGIS.Core.Data API and a solid understanding of ArcGIS.Core.Data API concepts is recommended.
- The ArcGIS.Core.Data.DDL API provides a limited amount of geodatabase DDL (data definitnion language) functionality. Operations such as creating tables and feature classes including annotation feature classes can be accomplished using .NET classes. Operations such as creating and and deleting fields are also supported. Refer to the DDL Concepts for more information regarding DDL using the Pro SDK. Other operations such as modifying existing fields, enabling attachments, and so on, need to be performed using the [Geoprocessing API](ProConcepts-Geoprocessing or the user interface of ArcGIS Pro.
- Almost all of the methods in the ArcGIS.Core.Data.Mapping API should be called on the MCT. The triple-slash documentation on the methods that need to run on the MCT are specified as such. These method calls should be wrapped inside the QueuedTask.Run call. Failure to do so will result in ConstructedOnWrongThreadException being thrown.
Annotation feature classes are collections of text features with a common set of attribute columns and class level properties. The class level properties define behavioral aspects of the feature class like editing behavior, update behavior, and assist in minimizing data size by offering a shared collection of text symbols that features may reference. In ArcGIS Pro, the annotation API is designed for use with annotation feature classes that have been upgraded to the new annotation model introduced for ArcGIS Pro.
In the ArcGIS.Core.Data.Mapping API, the AnnotationFeatureClass class inherits from the FeatureClass class. Obtaining AnnotationFeatureClass objects is similar to obtaining FeatureClass objects as shown in the following examples:
- From the Geodatabase object:
using (AnnotationFeatureClass annoFeatureClass =
geodatabase.OpenDataset<AnnotationFeatureClass>("AnnotationFeatureClassName"))
{
}
In the case of a Geodatabase object representing a FeatureService, the OpenDataset can be called with the string representation of the ID for the annotation feature class.
using (AnnotationFeatureClass annoFeatureClass =
geodatabase.OpenDataset<AnnotationFeatureClass>("1"))
{
}
- From the AnnotationLayer object:
ArcGIS.Desktop.Mapping.Layer selectedLayer = MapView.Active.GetSelectedLayers().FirstOrDefault();
if (selectedLayer is ArcGIS.Desktop.Mapping.AnnotationLayer)
{
using (Table table = (selectedLayer as AnnotationLayer).GetTable())
{
if (table is AnnotationFeatureClass)
{
AnnotationFeatureClass annoFeatureClass = table as AnnotationFeatureClass;
}
}
}
- From the AnnotationFeature object:
ArcGIS.Desktop.Editing.Events.RowChangedEvent.Subscribe(args =>
{
Row row = args.Row;
if (row is AnnotationFeature)
{
using (AnnotationFeatureClass annoFeatureClass = row.GetTable() as AnnotationFeatureClass)
{
}
}
}
You can open a AnnotationFeatureClass object using the following code:
using (Table table = geodatabase.OpenDataset<Table>("FeatureClassName"))
{
}
The table is an ArcGIS.Core.Data.Table reference, but in reality, it's an ArcGIS.Core.Data.Mapping.AnnotationFeatureClass object. You could cast the table reference as an AnnotationFeatureClass, and the cast would work as expected.
You can open a AnnotationFeatureClass object using the following code:
using (FeatureClass featureClass = geodatabase.OpenDataset<FeatureClass>("FeatureClassName"))
{
}
The featureClass is an ArcGIS.Core.Data.FeatureClass reference, but in reality, it's an ArcGIS.Core.Data.Mapping.AnnotationFeatureClass object. You could cast the featureClass reference as an AnnotationFeatureClass, and the cast would work as expected.
Annotation feature classes have their own Definition object, AnnotationFeatureClassDefinition, which provides access to properties unique to the AnnotationFeatureClass. AnnotationFeatureClassDefinition inherits from FeatureClassDefinition to provide access to general properties of the feature class but adds the annotation specific properties. The additional annotation specific properties include reference scale, reference scale unit, general placement properties, the label class collection which defines the annotation subclasses, and the symbol collection. It also includes annotation editing options such as whether or not to automatically create new feature-linked annotation, whether or not to update annotation when the shape of a linked feature is updated, whether or not to require a symbol ID be used at all times, or whether or not symbol overrides are allowed.
- Opening an AnnotationFeatureClassDefinition from a Geodatabase. A definition object contains metadata information about the dataset and is typically used when it is not anticipated that the dataset will be opened.
AnnotationFeatureClassDefinition definition =
geodatabase.GetDefinition<AnnotationFeatureClassDefinition>("AnnotationFeatureClassName");
- Opening the AnnotationFeatureClassDefinition from the dataset. Use this when the dataset is already open and the reference is accessible.
ArcGIS.Desktop.Mapping.Layer selectedLayer = MapView.Active.GetSelectedLayers().FirstOrDefault();
if (selectedLayer is ArcGIS.Desktop.Mapping.AnnotationLayer)
{
using (AnnotationFeatureClass annoFC = (selectedLayer as AnnotationLayer).GetTable() as AnnotationFeatureClass)
{
AnnotationFeatureClassDefinition definition = annoFC.GetDefinition();
}
}
When working with an annotation feature class, the features returned via queries are of type AnnotationFeature. AnnotationFeature extends the capabilities of Feature with annotation-specific functionality. Primarily, it provides access to the CIMTextGraphic in the annotation feature. The CIMTextGraphic is the primary object that is modified when working with annotation. Additionally the AnnotationFeature provides convenience Set and Get methods for the AnnotationClassID, the LinkedFeatureID, and the annotation Status. These are simpler to update via the AnnotationFeature than via lookup of their Field index when using a Feature object. If the annotation feature class is established with a GlobalID based relationship class, the LinkedFeatureID will be of type System.GUID, otherwise it will be a Long.
Unlike a regular Feature, the Shape of an AnnotationFeature is not routinely updated via GetShape and SetShape. Instead, the AnnotationFeature manages the shape when the CIMTextGraphic of the annotation is updated. The Shape is set to be a bounding polygon of the CIMTextGraphic.
Another concept to keep in mind when dealing with annotation is the 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 fields are required. 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 a 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. If you are writing tools that create or modify annotation features, it is essential to take these changes and important concept into account.
The CIMTextGraphic is the primary object modified while manipulating an AnnotationFeature. While the AnnotationFeature API provides GetGraphic and SetGraphic methods which appear to take any CIMGraphic, only CIMGraphics of type CIMTextGraphic can be used.
There are three main properties of the CIMTextGraphic: the Text, the Shape, and the Symbol. The Text is the text string drawn by the text graphic and updates to the string are common in Annotation editing workflows. The Text string can also contain Text formatting tags for inline styling changes to the annotation. For instance, the color of a subset of characters in the string can be changed. To change the color of a single word in the middle of three words, use syntax as demonstrated below to change the word 'Special' to red.
My <CLR red="255" green="0" blue="0">Special</CLR> Text
The Shape of the CIMTextGraphic is the location the text is drawn at. This may be a MapPoint but is typically a Polyline. GeometryBag is occasionally used for text strings that have individual word placements (multi-part text). A Multipoint is used in the case where each character has a specific placement, this type is produced by the label engines when placing curved text. When updating the Text of a CIMTextGraphic with a Multipoint or GeometryBag shape, the Geometry will be synchronized with the characters and/or words in the string to maintain data integrity.
The symbol of a CIMTextGraphic is a CIMSymbolReference which contains the CIMTextSymbol used to draw the graphic and optionally an identifier in the SymbolName property for the symbol referenced from the symbol collection. The symbol collection is used by reference from the AnnotationFeatureClassDefinition to minimize the storage for each feature persisted. By referencing a shared symbol, the amount of repeated symbol information stored for each AnnotationFeature is reduced. However, CIMTextSymbol properties can be overridden to deviate from the referenced symbol collection symbol. The AnnotationFeature manages these overridden by comparing edits made to the CIMTextGraphic's symbol to the symbol referenced in the SymbolName property of the CIMSymbolReference. If the feature can be stored with overrides, it will be. Otherwise, the CIMTextGraphic will be disconnected from the symbol collection and the annotation feature will store the entire CIMTextSymbol. In most cases, this allows for CIMTextSymbol properties to be freely changed without worrying about storage implications. 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 be rejected.
- Opening a cursor on an AnnotationFeatureClass and making updates to the CIMTextGraphic of the AnnotationFeature is demonstrated below. Note that this example changes the text symbol height and the symbol in the symbol collection that is being referenced. The symbol ID of the symbol collection is an integer but the SymbolName property is a string, so it must be set as a string.
QueryFilter qf = new QueryFilter();
qf.WhereClause = "OBJECTID < 100";
//Note: this is a non-recycling cursor off the Table, ~not~ the layer
using (RowCursor rowCursor = featureClass.Search(qf, false))
{
geodatabase.ApplyEdits(() =>
{
while (rowCursor.MoveNext())
{
using (AnnotationFeature annoFeat = rowCursor.Current as AnnotationFeature)
{
CIMTextGraphic textGraphic = annoFeat.GetGraphic() as CIMTextGraphic;
CIMSymbolReference symbolRef = textGraphic.Symbol;
symbolRef.SymbolName = "1"; //change the symbol being referred to by the CIMTextGraphic
CIMTextSymbol textSymbol = symbolRef.Symbol as CIMTextSymbol;
textSymbol.Height = 6; //change the height of the text.
annoFeat.SetGraphic(textGraphic);
annoFeat.Store();
}
}
});
}
- Using a RowBuffer and creating a new AnnotationFeature in an AnnotationFeatureClass is demonstrated below. The CIMTextGraphic is created and the position, text string, and CIMTextSymbol properties are specified. The text symbol from the symbol collection is referenced by ID. The symbol ID of the symbol collection is an integer but the SymbolName property is a string, so it must be set as a string. The AnnotationFeature will optimize the storage of the feature with overrides on the Store() call.
static void InsertAnno(string textString, MapPoint mapPoint, int symbolID, AnnotationFeatureClass featureClass)
{
var annoFCDef = featureClass.GetDefinition();
var symCol = annoFCDef.GetSymbolCollection();
//get the text symbol from the symbol collection
var symbolIdentifier = (from s in symCol where s.ID == symbolID select s).FirstOrDefault();
var txtSymbol = symbolIdentifier.Symbol;
//Create the row buffer
using (RowBuffer rowBuffer = featureClass.CreateRowBuffer())
{
Feature feature = featureClass.CreateRow(rowBuffer) as Feature;
AnnotationFeature annoFeat = feature as AnnotationFeature;
annoFeat.SetStatus(AnnotationStatus.Placed);
annoFeat.SetAnnotationClassID(0);
//Setup the text graphic
CIMTextGraphic cimTextGraphic = new CIMTextGraphic();
cimTextGraphic.Text = textString;
cimTextGraphic.Shape = mapPoint;
//Setup the symbol reference with the symbol id and the text symbol
var symbolRef = new CIMSymbolReference();
symbolRef.SymbolName = symbolID.ToString();
symbolRef.Symbol = txtSymbol;
//Set the symbol reference on the graphic, push it back into the feature, and store.
cimTextGraphic.Symbol = symbolRef;
annoFeat.SetGraphic(cimTextGraphic);
feature.Store();
}
}
The ProGuide Annotation Construction Tools and ProGuide Annotation Editing Tools give examples of building construction and editing tools for annotation. Refer to ProConcepts Editing Annotation for additional reference information relevant for annotation editing customizations.