This document is archived and information here might be outdated.  Recommended version.


Creating features (ArcObjects .NET 10.5 SDK)

Creating features


Summary
This topic explains how to create features in a geodatabase feature class. Two methods are shown, one that creates an individual feature using the IFeatureClass.CreateFeature and IFeature.Store methods, and one that uses an insert cursor to execute a bulk load.

In this topic


About creating features

The functionality available for feature creation is dependent on the following:
  • The license level the code runs on and the type of geodatabase it runs against.
  • The type of features that are being created—whether they are simple features, network features, created in a topology, and so on. For example, an ArcGIS Desktop Standard or Advanced license is required to create features in the following:
    • Geometric network or topology
    • Dimension feature class
    • Annotation feature class
    • ArcSDE geodatabase, whether at the personal, workgroup, or enterprise level
The following are two primary ways to create features in the geodatabase: 
The following are the main differences between the two primary ways to create features in a geodatabase:
  • Calling IFeature.Store results in all object behavior being triggered. This includes—but is not limited to—behavior specific to network or annotation features and features that participate in a topology, as well as events, such as those from the IObjectClassEvents interface.
  • Insert cursors are used to bulk insert features in the geodatabase. Using an insert cursor and a feature buffer offers significantly faster performance for simple data loading and creation than creating features and storing them. The one catch is that when using the IFeatureCursor.InsertFeature method, complex behavior and the triggering of events is not guaranteed.
  • The IObjectClassInfo and IWorkspaceEditControl interfaces can be used to enforce the calling of IFeature.Store by insert cursors, but this negates any performance advantages they offer.
If more than one simple feature is being created, always use an insert cursor for performance considerations.

Using the CreateFeature and Store methods

The CreateFeature method is used to create individual features in the geodatabase. It has the effect of assigning a system-generated ObjectID (OID), which is the value of the OIDField. Use the IFeature.Store method to store this new feature in the database.
This basic process to create a feature is used independently of whether the feature is created in a point, line, polygon feature class, or in a feature class participating in a geometric network or topology. See the following:
  • Creating the feature—Calling the CreateFeature method on a feature class has the same effect as calling the CreateRow method on the ITable interface, except that the IFeatureClass method returns a reference to the IFeature interface representing the returned row object. The OID of the feature is available once the CreateFeature method is called.
  • Create the geometry for the feature—One of the fundamental differences between a feature and a row object is the association between a feature and a geometry. There are many different ways to create the geometry for the feature. For more information on creating geometries, see How to create a polyline and How to create a polygon.
  • Store the geometry in the feature—Use the IFeature.Shape property to store the geometry with the feature.
  • Set the subtype, initialize any default values, and set other field values—After calling CreateFeature, the default subtype value is not automatically set nor are default values initialized. For features without a subtype that have defined default values, calling IRowSubtypes.InitDefaultValues initializes the default values. For features created within a feature class containing subtypes, use IRowSubtypes.SubtypeCode to set the subtype value for features before calling IRowSubtypes.InitDefaultValues to ensure the default values are set for the particular subtype. After initializing the default values, the remaining field values can be set through the IFeature.Value property. If the field values are not correct for the type of field, an error is raised. However, if the field is too small to accept the defined value, an error is raised after the call to the Store method.
  • Store the feature—The feature is not written to the database until the IFeature.Store method has been called. Once Store has been called, all subsequent queries in the same edit session, using the geodatabase application programming interface (API), reflects the modified state of the feature.
Once the Store method is called, the following actions are triggered:
  • Any complex behavior inherent to the feature occurs. For example, network features create corresponding network elements, topologies create dirty areas over edited features, and the OnNew method is called on custom features that override the IRowEvents interface.
  • If the class the feature belongs to has a class extension that implements the IObjectClassEvents interface, its IObjectClassEvents.OnCreate method is called. This is often used to set an object's field values to application-defined variables.
  • The IObjectClassEvents interface of a feature class exposes events. If the OnCreate event has listeners, the event is raised, triggering all event handlers.
  • If the feature's class participates in a relationship class with notification and the related class has a class extension that implements the IRelatedObjectClassEvents interface, the RelatedObjectCreated method is triggered on the extension.
The following code example demonstrates how to use the IFeatureClass.CreateFeature method and IFeature.Store to create a feature in a feature class containing pipes:
[C#]
public static void CreateFeature(IFeatureClass featureClass, IPolyline polyline)
{
    // Build the feature.
    IFeature feature=featureClass.CreateFeature();
    feature.Shape=polyline;

    // Apply the appropriate subtype to the feature.
    ISubtypes subtypes=(ISubtypes)featureClass;
    IRowSubtypes rowSubtypes=(IRowSubtypes)feature;
    if (subtypes.HasSubtype)
    {
        // In this example, the value of 3 represents the polymer vinyl chloride (PVC) subtype.
        rowSubtypes.SubtypeCode=3;
    }

    // Initialize any default values the feature has.
    rowSubtypes.InitDefaultValues();

    // Update the value on a string field that indicates who installed the feature.
    int contractorFieldIndex=featureClass.FindField("CONTRACTOR");
    feature.set_Value(contractorFieldIndex, "K Johnston");

    // Commit the new feature to the geodatabase.
    feature.Store();
}
[VB.NET]
Public Shared Sub CreateFeature(ByVal featureClass As IFeatureClass, ByVal polyline As IPolyline)
' Build the feature.
Dim feature As IFeature=featureClass.CreateFeature()
feature.Shape=polyline

' Apply the appropriate subtype to the feature.
Dim subtypes As ISubtypes=CType(featureClass, ISubtypes)
Dim rowSubtypes As IRowSubtypes=CType(feature, IRowSubtypes)
If subtypes.HasSubtype Then
    ' In this example, the value of 3 represents the Cross subtype.
    rowSubtypes.SubtypeCode=3
End If

' Initialize any default values the feature has.
rowSubtypes.InitDefaultValues()

' Update the value on a string field that indicates who installed the feature.
Dim contractorFieldIndex As Integer=featureClass.FindField("CONTRACTOR")
feature.Value(contractorFieldIndex)="K Johnston"

' Commit the new feature to the geodatabase.
feature.Store()
End Sub
It is not necessary to explicitly call Connect on network features, or create and associate network elements with network features. All network behavior is handled by the object behavior when Store is called on the feature. This is also the case with features in a topology; dirty area creation is handled internally when Store is called.
Store should not be called inside edit events, such as OnCreateFeature, OnChangeFeature, or OnDeleteFeature. Any changes made within these events' handlers are automatically persisted.
When working with a versioned feature class, only call CreateFeature in an edit session. An edit session can be started by calling IWorkspaceEdit.StartEditing or IMultiuserWorkspaceEdit.StartMultiuserEditing. All edits to features that participate in a topology, geometric network, terrain, or representation must be performed in an edit session and bracketed in an edit operation.

Using insert cursors

An insert cursor is used to insert rows into a table and supports the InsertRow method. Insert cursors are used for bulk row insertion. Using an insert cursor offers significantly faster performance for loading data into simple tables and feature classes than the alternative of making multiple calls to CreateRow on the table, then calling the Store method on the new row.
Insert cursors on tables that implement custom behavior through participation in topologies, geometric networks, custom features, and some class extensions will internally use the CreateRow and Store methods to achieve polymorphism (there is no difference in performance in these cases).
The InsertRow method takes a row buffer as an argument. Applications obtain a row buffer using the CreateRowBuffer method on the table object where rows are inserted. Each call to InsertRow on the cursor creates a row in the database whose initial values are set to the values in the input row buffer. A single row buffer can be reused for multiple InsertRow calls. The ObjectID for the created row is returned by the InsertRow method.
Remember, if the geometries being inserted are in a different spatial reference than the feature class being inserted into, those geometries will have to be projected, which could result in a performance hit. To avoid this, make sure the geometries being inserted are using the same spatial reference as the feature class they will be inserted into.
Passing a true value to the UseBuffering parameter of the ITable.Insert method returns an insert cursor that buffers rows on the client side and sends them to the server in batches for increased performance. Always enable buffering, except when inserting rows outside an edit session (buffering can only be leveraged during an edit session).
The application is responsible for calling the Flush method on the insert cursor after all rows have been inserted. If a call to the Flush method is not made, the cursor flushes its buffers on destruction (when the application releases all references on the cursor). However, relying on the destructor to flush the insert cursor does not give the application the chance to detect errors that can arise on the call to the Flush method, for example, if the storage for the table in the underlying database fills up. The next code example provides a basic template to check if a flush is successful.
If a spatial cache is used, it should be disabled before bulk loading features using an insert cursor. For more information, see ISpatialCacheManager.
The following code example uses a feature buffer to insert a set of new features into a feature class of roads. The number of features is determined by the size of the geometryList parameter, and the features take their Shape properties sequentially from the list. All  features have their "TYPE" value set to "Primary Highway." These inserts are stored in a buffer and are only flushed when Flush is called.
[C#]
public static void InsertFeaturesUsingCursor(IFeatureClass featureClass, List <
    IGeometry > geometryList)
{
    using(ComReleaser comReleaser=new ComReleaser())
    {
        // Create a feature buffer.
        IFeatureBuffer featureBuffer=featureClass.CreateFeatureBuffer();
        comReleaser.ManageLifetime(featureBuffer);

        // Create an insert cursor.
        IFeatureCursor insertCursor=featureClass.Insert(true);
        comReleaser.ManageLifetime(insertCursor);

        // All of the features to be created are classified as Primary Highways.
        int typeFieldIndex=featureClass.FindField("TYPE");
        featureBuffer.set_Value(typeFieldIndex, "Primary Highway");
        foreach (IGeometry geometry in geometryList)
        {
            // Set the feature buffer's shape and insert it.
            featureBuffer.Shape=geometry;
            insertCursor.InsertFeature(featureBuffer);
        }

        // Flush the buffer to the geodatabase.
        insertCursor.Flush();
    }
}
[VB.NET]
Public Shared Sub InsertFeaturesUsingCursor(ByVal featureClass As IFeatureClass, _
                                            ByVal geometryList As List(Of IGeometry))
Using comReleaser As ComReleaser=New ComReleaser()
' Create a feature buffer.
Dim featureBuffer As IFeatureBuffer=featureClass.CreateFeatureBuffer()
comReleaser.ManageLifetime(featureBuffer)

' Create an insert cursor.
Dim insertCursor As IFeatureCursor=featureClass.Insert(True)
comReleaser.ManageLifetime(insertCursor)

' All of the features to be created are classified as Primary Highways.
Dim typeFieldIndex As Integer=featureClass.FindField("TYPE")
featureBuffer.Value(typeFieldIndex)="Primary Highway"
For Each geometry As IGeometry In geometryList
    ' Set the feature buffer's shape and insert it.
    featureBuffer.Shape=geometry
    insertCursor.InsertFeature(featureBuffer)
Next

' Flush the buffer to the geodatabase.
insertCursor.Flush()
End Using
End Sub

Using load-only mode to insert rows

The IFeatureClassLoad interface is an optional interface supported by feature classes in ArcSDE, and feature classes and tables in file geodatabases. It can be used to enable "load-only mode," which can improve the performance of data loading.
Only use load-only mode when inserting new rows or features. Do not use load-only mode when editing existing features.
With ArcSDE, putting a feature class in load-only mode disables updating of the spatial index while inserting features. In a file geodatabase, putting a feature class or table in load-only mode disables the updating of spatial and attribute indexes while inserting rows or features. Taking the feature class or table out of load-only mode rebuilds the indexes.
While a feature class or table is in load-only mode, other applications cannot work with the data. Place a feature class or table in load-only mode only after acquiring an exclusive schema lock on the feature class via the ISchemaLock interface.
Applications are responsible for ensuring there are no outstanding cursors on a class when load-only mode is enabled or disabled. Any cursors instantiated before enabling load-only mode should be flushed (if necessary) and destroyed using Marshal.ReleaseComObject, or a managing ComReleaser before enabling load-only mode, and any cursors instantiated in load-only mode should be flushed and destroyed using Marshal.ReleaseComObject or a managing ComReleaser before disabling load-only mode. Failure to do so could result in unexpected errors.
The following code example is a modified version of the previous insert cursor example, with load-only mode enabled before the inserts take place:
[C#]
public static void LoadOnlyModeInsert(IFeatureClass featureClass, List < IGeometry >
    geometryList)
{
    // Cast the feature class to the IFeatureClassLoad interface.
    IFeatureClassLoad featureClassLoad=(IFeatureClassLoad)featureClass;

    // Acquire an exclusive schema lock for the class.
    ISchemaLock schemaLock=(ISchemaLock)featureClass;
    try
    {
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);

        // Enable load-only mode on the feature class.
        featureClassLoad.LoadOnlyMode=true;
        using(ComReleaser comReleaser=new ComReleaser())
        {
            // Create the feature buffer.
            IFeatureBuffer featureBuffer=featureClass.CreateFeatureBuffer();
            comReleaser.ManageLifetime(featureBuffer);

            // Create an insert cursor.
            IFeatureCursor insertCursor=featureClass.Insert(true);
            comReleaser.ManageLifetime(insertCursor);

            // All of the features to be created are classified as Primary Highways.
            int typeFieldIndex=featureClass.FindField("TYPE");
            featureBuffer.set_Value(typeFieldIndex, "Primary Highway");

            foreach (IGeometry geometry in geometryList)
            {
                // Set the feature buffer's shape and insert it.
                featureBuffer.Shape=geometry;
                insertCursor.InsertFeature(featureBuffer);
            }

            // Flush the buffer to the geodatabase.
            insertCursor.Flush();
        }
    }
    catch (Exception)
    {
        // Handle the failure in a way appropriate to the application.
    }
    finally
    {
        // Disable load-only mode on the feature class.
        featureClassLoad.LoadOnlyMode=false;

        // Demote the exclusive schema lock to a shared lock.
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
    }
}
[VB.NET]
Public Shared Sub LoadOnlyModeInsert(ByVal featureClass As IFeatureClass, ByVal geometryList As List(Of IGeometry))
' Cast the feature class to the IFeatureClassLoad interface.
Dim featureClassLoad As IFeatureClassLoad=CType(featureClass, IFeatureClassLoad)

' Acquire an exclusive schema lock for the class.
Dim schemaLock As ISchemaLock=CType(featureClass, ISchemaLock)
Try
schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock)

' Enable load-only mode on the feature class.
featureClassLoad.LoadOnlyMode=True
Using ComReleaser As ComReleaser=New ComReleaser()
' Create the feature buffer.
Dim featureBuffer As IFeatureBuffer=featureClass.CreateFeatureBuffer()
ComReleaser.ManageLifetime(featureBuffer)

' Create an insert cursor.
Dim insertCursor As IFeatureCursor=featureClass.Insert(True)
ComReleaser.ManageLifetime(insertCursor)

' All of the features to be created are classified as Primary Highways.
Dim typeFieldIndex As Integer=featureClass.FindField("TYPE")
featureBuffer.Value(typeFieldIndex)="Primary Highway"
For Each geometry As IGeometry In geometryList
    ' Set the feature buffer's shape and insert it.
    featureBuffer.Shape=geometry
    insertCursor.InsertFeature(featureBuffer)
Next

' Flush the buffer to the geodatabase.
insertCursor.Flush()
End Using
Catch exc As Exception
' Handle the failure in a way appropriate to the application.
Finally
' Disable load-only mode on the feature class.
featureClassLoad.LoadOnlyMode=False

' Demote the exclusive schema lock to a shared lock.
schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock)
End Try
End Sub

Using buffering and flushing

When buffering is used with an insert cursor, a call to InsertRow does not write a new row to the database management system (DBMS), but to a client-side buffer. The rows contained in the buffer are written to the DBMS as a batched operation when ICursor.Flush is called, or when the buffer reaches its maximum size. Because a DBMS write can occur on the Flush call or the InsertRow call (if the buffer has reached its maximum size), both of these calls should have appropriate error handling.
Buffering can only be leveraged during an edit session.
There are other events that can trigger a flush, such as the destruction of the cursor (as previously mentioned). Inside an edit session, stopping an edit operation flushes versioned data, and stopping an edit session flushes non-versioned data. Also, creating a search or update cursor inside an edit operation flushes any rows that have already been cached.
The following illustration shows how buffering works in an enterprise geodatabase:


See Also:

How to create a polyline
How to create a polygon




To use the code in this topic, reference the following assemblies in your Visual Studio project. In the code files, you will need using (C#) or Imports (VB .NET) directives for the corresponding namespaces (given in parenthesis below if different from the assembly name):

Additional Requirements
  • When creating simple features in an ArcSDE geodatabase, ArcGIS Desktop requires a Standard or an Advanced license, while ArcGIS Engine requires the Geodatabase Update extension.

Development licensing Deployment licensing
ArcGIS Desktop Basic ArcGIS Desktop Basic
ArcGIS Desktop Standard ArcGIS Desktop Standard
ArcGIS Desktop Advanced ArcGIS Desktop Advanced
Engine Developer Kit Engine