Library Reference  

Geodatabase

Library dependencies: System, SystemUI, Geometry, Display, Server, Output

The Geodatabase library provides the programming API for the geodatabase. The geodatabase is a repository of geographic data built on standard industry relational and object relational database technology. The objects within the library provide a unified programming model for all supported data sources within ArcGIS. The Geodatabase library defines many of the interfaces that are implemented by data source providers higher in the architecture.

The geodatabase can be extended by developers to support specialized types of data objects (Features, Classes, and so on). In addition it can have custom vector data sources added using the plug-in data source objects. The native data types supported by the geodatabase cannot be extended.

The objects that implement this functionality are grouped into a number of library subsystems. These library subsystems are:

Core geodatabase



The diagram shown above is a simplified view of the most important geodatabase objects, which are summarized as follows:

Workspaces


A Workspace is a container of spatial and nonspatial datasets such as feature classes, raster datasets, and tables. It provides methods to instantiate existing datasets and to create new datasets.

Workspaces are classified into types specified by the esriWorkspaceType enumerator: esriFileSystemWorkspace, esriLocalDatabaseWorkspace, and esriRemoteDatabaseWorkspace.

Shapefiles and ArcInfo workspaces are examples of esriFileSystemWorkspace. A personal geodatabase stored in Microsoft Access or a File Geodatabase is an example of an esriLocalDatabaseWorkspace. An enterprise geodatabase stored in an RDBMS, such as Oracle, DB2, SQL Server, or Informix, and accessed via ArcSDE is an example of an esriRemoteDatabaseWorkspace.

Other workspace types include:

A WorkspaceFactory is a dispenser of workspaces and allows a client to connect to a workspace specified by a set of connection properties. A workspace represents a database or a data source that contains one or more datasets. Examples of datasets include tables, feature classes, and relationship classes.

A WorkspaceFactory is a cocreatable, singleton object. A WorkspaceFactory maintains a pool of currently connected, active workspaces that are being referenced by the application. Connection properties are specified using a PropertySet object and can be saved to a connection file.

A WorkspaceFactory also supports methods that can be used to browse and manage file system workspaces and methods to manage connection files for remote database workspaces.

This example connects to an ArcSDE for Oracle geodatabase.


SdeWorkspaceFactory sdeWorkspaceFactory = new SdeWorkspaceFactory();
PropertySet pset = new PropertySet();
pset.setProperty("SERVER", "zinc");
pset.setProperty("INSTANCE", "9192");
pset.setProperty("USER", "gdb");
pset.setProperty("PASSWORD", "gdb");
pset.setProperty("VERSION", "sde.DEFAULT");			
Workspace featureWksp = new Workspace(sdeWorkspaceFactory.open(pset, 0));

The IFeatureWorkspace interface is used to access and manage datasets that are a key component of a feature-based geodatabase: Tables and ObjectClasses, FeatureClasses, FeatureDatasets, and RelationshipClasses.

All of the open methods (such as openTable) take a dataset name as input. When working with an ArcSDE geodatabase, the name may be fully qualified (for example, "database.owner.tablename" or "owner.tablename") using the qualification character appropriate to the underlying database (see ISQLSyntax). If the input name is not fully qualified, then it is qualified using the currently connected user for the workspace.

When working with geodatabases (Personal, File or ArcSDE), the workspace keeps a running object table of instantiated datasets. Multiple calls to open an already instantiated dataset will return a reference to the already instantiated dataset.

The openTable method can be used to open any existing table or object class in the workspace given its fully qualified name. The table object returned will always support the ITable interface. The returned table object will support additional interfaces depending on the type of table—for example, ObjectClasses will additionally support the IObjectClass interface.

The openFeatureClass method can be used to open any existing feature class in the workspace given its fully qualified name. Note that every feature class in a geodatabase has a unique fully qualified name, and the openFeatureClass method can be used to directly open FeatureClasses that are part of a FeatureDataset.

This example opens a shapefile as a feature class.

static void openShapeFile() throws Exception {
	ShapefileWorkspaceFactory shapefileWorkspaceFactory = new ShapefileWorkspaceFactory();	
	Workspace featureWksp = new Workspace(shapefileWorkspaceFactory.openFromFile("c:/Data/usa", 0));
	FeatureClass fclass = new FeatureClass(featureWksp.openFeatureClass("states.shp"));
	System.out.println("There are" + fclass.featureCount(null) + " states");}

Applications can use the ISQLSyntax interface to help them construct SQL queries and where clauses that are database-system independent.

getSpecialCharacter can be used to return the DBMS-dependent character that represents an SQL special character, including the following:

Applications should use the parseTableName and parseColumnName methods to split the fully qualified name for a dataset or for a column in a table into its components (database, owner, table, column). Applications that wish to be RDBMS independent should not assume that “.” is the delimiter used to separate the components of a fully qualified dataset name. Note that both the IDataset.getName method for a dataset in a geodatabase and the IDatasetName.getName property for a dataset name object return the fully qualified name for the dataset (the name object for the dataset, obtained using the IDataset.getFullName property itself).

Applications should use the qualifyTableName and qualifyColumnName methods to construct fully qualified dataset and column names.

Editing data

Applications can use specified transactions to manage direct updates, for example, any feature classes that is tagged as not requiring an edit session can be updated outside of an edit session. When using transactions to manage direct updates, applications are responsible for discarding any cached row objects at transaction boundaries.

ITransactions is an optional interface that allows an application to explicitly control database transactions. The interface does not support nested transactions.

Applications should not use transactions when performing updates within an edit session (for information on edit sessions, see the documentation below). In the context of an edit session, transactions are managed by the workspace and automatically started and stopped as needed.

Applications should be aware that DDL operations made through the ArcObjects geodatabase data access objects (for example, deleting a feature dataset or creating a new feature class) use database transactions to ensure integrity of the data dictionary tables and commit the transaction at the end of the operation. Applications should not invoke DDL operations within an application transaction—application transactions should be restricted to DML operations (such as data updates).

Applications can make use of Geodatabase edit sessions to manage database transactions. It is possible to start an edit session in one of two modes. The IWorkspaceEdit interface allows the application to start and stop edit sessions in the versioned edit session mode only.

A versioned edit session is begun using the startEditing method. The withUndoRedo parameter can be used to suppress undo/redo logging if the workspace supports such suppression. Shapefiles support suppression of Undo/Redo logging, but ArcSDE does not.

The IMultiuserWorkspaceEdit interface also allows the application to start and stop edit sessions during which the objects in a geodatabase can be updated. This differs from the IWorkspaceEdit interface in that the IMultiuserWorkspaceEdit interface is only supported by workspaces that support both edit session modes, namely ArcSDE geodatabases. For this reason it is only recommended that the IMultiuserWorkspaceEdit interface be used if it is the intent to control the edit session mode used when editing. By default, through the startEditing method, an edit session will be started in the Versioned edit session mode.Therefore at this time it is necessary to use the IWorkspaceEdit interface to edit local workspaces.

If the IMultiuserWorkspaceEdit interface is used the edit session can be started in either versioned or non versioned edit session modes. If the edit session mode is set to versioned the only changes to data that an application sees within an edit session are changes that are made by that application. Changes made by other concurrently executing applications (if allowed) are not seen until the edit session is saved or discarded. On the other hand, editing within an edit session in non versioned mode is the equivalent of performing standard database transactions. You still perform the edits within a standard edit session however when you’ve finished, the changes are committed as a single transaction by saving. If you don’t want to commit the changes, you abort the edit session without saving. Each transaction can include as few or as many operations as required, provided they fall within a single edit session.

Editing in a non versioned edit session mode alters the data source directly, it doesn’t store the changes in delta tables like versioned editing does. This avoids the overhead of managing these extra tables, and allows you to easily adapt non-ESRI applications so that they can read and edit the data. However, the drawback is that since you edit the data source directly, you cannot undo or redo an individual edit if you make a mistake. The only way to undo edits is to undo all edits by quitting the edit session without saving. You can perform non versioned edits on simple data only: points, lines, polygons, annotation, and relationships. You cannot perform non versioned edits on complex data such as feature classes in a topology or geometric network.

If undo/redo facilities are required when editing in a versioned edit session mode, all related changes to objects in the database within an edit session should be grouped into edit operations. An edit operation is begun using the startEditOperation method.

Applications are responsible for calling the abortEditOperation method to abort an edit operation if errors are detected within the methods executed for an edit operation.

Applications are responsible for calling stopEditOperation to mark the end of a successfully completed edit operation. Completed edit operations can be thought of as being pushed onto an undo stack.

The undoEditOperation can be used to roll the state of the edit session back to what it was prior to the execution of the edit operation at the top of the undo stack. Undoing an edit operation pops the edit operation from the Undo stack and adds it to a Redo stack.

The redoEditOperation rolls the state of the edit session forward to what it was after the execution of the edit operation at the top of the Redo stack, pops the redone edit operation from the Redo stack, and pushes it back onto the Undo stack. Performing a new edit operation clears the Redo stack.

The stopEditing method is used to end an edit session. The saveEdits parameter controls whether or not edits are saved or discarded. A multiversioned database can support multiple concurrent edit sessions on the same version of the database. In such a scenario, stopEditing will return an error code of FDO_E_VERSION_REDEFINED if it detects that the database state associated with the version being edited is no longer the same as it was at the beginning of the edit session (indicating that the version was modified by some other edit session). In this case, the application is responsible for calling the IVersionEdit.reconcile method to reconcile the edit session against the current state of the version being edited. stopEditing may be called again after reconciliation.

The geodatabase guarantees “unique instancing” of row objects retrieved from the database within an edit session. Any data access call that retrieves a nonrecycling object with a particular object ID will return the in-memory instance of the object if the object has already been instantiated by the application. Such behavior is needed to ensure application correctness when updating complex object models—for example, models with relationship-based messaging or models with network features where updates to the geometry of a feature affect the geometry of topologically related features.

The example below shows a simple non versioned edit session on an ArcSDE workspace. Note that if the user chooses to undo the edit operation, there will be no outstanding edits, so the prompt to save the work will not appear. For this reason, all object editing should be done within an edit session. The geodatabase data access APIs (such as IRow.store, ITable.update, and ITable.insert) will fail if you attempt to use them outside of an edit session on object and feature classes that are marked as requiring an edit session to ensure unique instancing semantics. Use IObjectClassInfo2.canBypassEditSession to determine the situation.

    static void sdeWorkspaceEdit() throws Exception {
SdeWorkspaceFactory sdeWorkspaceFactory = new SdeWorkspaceFactory();
PropertySet pset = new PropertySet();
pset.setProperty("SERVER", "pine");
pset.setProperty("INSTANCE", "9192");
pset.setProperty("USER", "gdb");
pset.setProperty("PASSWORD", "gdb");
pset.setProperty("VERSION", "sde.DEFAULT");

Workspace featureWksp = new Workspace(sdeWorkspaceFactory.open(pset, 0));
FeatureClass fclass = new FeatureClass(featureWksp.openFeatureClass("VTEST.States"));
IMultiuserWorkspaceEdit pMWorkspaceEdit = new IMultiuserWorkspaceEditProxy(featureWksp);
if (pMWorkspaceEdit.supportsMultiuserEditSessionMode(esriMultiuserEditSessionMode.esriMESMNonVersioned)) {
pMWorkspaceEdit.startMultiuserEditing(esriMultiuserEditSessionMode.esriMESMNonVersioned);
Feature feature = (Feature) fclass.getFeature(1);
feature.delete();
boolean[] bHasEdits = new boolean[1];
featureWksp.hasEdits(bHasEdits);
if (bHasEdits[0]) {
featureWksp.stopEditing(true);
}
}
}

The rules for correct object editing on a geodatabase are summarized below:

  1. All object editing should be done within an edit session.
  2. Group changes into edit operations.
  3. Discard all references to row objects retrieved at the edit session boundary (on startEditing). If references to row objects will be maintained across edit operations, then discard all references and refetch objects in response to the undo, redo, and abort edit operation calls made by the application, as well as the reconcile call made within an edit session on versioned databases. In the context of ArcMap, these calls are made by the editor, which broadcasts corresponding editor events via the IEditorEvents and IEditorEvents2 interfaces. Personal and ArcSDE geodatabase workspaces support the IWorkspaceEditEvents and the IVersionEvents outbound interfaces and directly broadcast these events.
  4. Use nonrecycling search cursors to fetch objects that are to be updated (using any of the search, getRow, or getRows methods supported by tables, feature classes, and selection sets). Recycling cursors should only be used for drawing and read-only access to object states.
  5. Always fetch all properties of the objects to be edited. Query filters should always use “*” for the subfields property (attempts to instantiate nonrecycling cursors with less than all fields will still result in all row object fields being hydrated).
  6. After changing a row object, call the IRow.store method to mark the object as changed and trigger propagation of the onChanged message; propagate messages to related objects by calling the IRow.store method on the object. Delete objects by calling the IRow.delete method on the object, which triggers the onDelete message. Stored and deleted objects within an edit operation are automatically and periodically flushed to the underlying database as needed to ensure read/query consistency and update efficiency. Use the set versions of these methods (for example, IRowEdit.deleteSet) if updates or deletions are being made to a set of objects in order to increase performance.
  7. Update and insert cursors are bulk data-loading and data-update APIs designed to perform direct updates and inserts outside of an edit session on simple data during the data-loading phase of a project. Following the editing rules on this page will help ensure geodatabase integrity, particularly where custom objects and geodatabase behavior is concerned. Avoid using these APIs in editing applications. Using these APIs within an edit session or on complex objects (objects with nonsimple row or feature behavior, or on objects participating in composite relationships or relationships with notification) negates any performance advantages they may have.

Workspace extensions

A workspace representing a geodatabase can have one or more workspace extensions. A workspace extension extends the functionality of a workspace in some way, for example, by managing a new type of custom dataset or by maintaining custom data dictionary information on datasets. A workspace extension is usually used in conjunction with an application or editor extension that acts as the client of the workspace extension.

The Workspace instantiates all WorkspaceExtensions that are registered in the component category CATID_GeodatabaseWorkspaceExtensions at connect time. An application extension can find a workspace extension by its well-known GUID and invoke methods supported by the extension as appropriate. It will also instantiate all workspace extensions registered with a workspace using IWorkspaceExtensionManager.registerExtension.

IWorkspaceExtension is a mandatory interface that must be supported by all workspace extensions. The GUID property returns the well-known GUID for the extension and is guaranteed to be unique. The Name property is the name of the extension. The get PrivateDatasetNames and getDataDictionaryNames properties return the names of tables and datasets that are private to the extension and will not be exposed to browsing clients by the workspace.

Datasets


A Dataset is an abstract class that represents a named collection of data in a workspace. Datasets may contain other datasets. All datasets support the IDataset interface and may optionally support other interfaces, including IDatasetEdit, ISchemaLock, and IMetadata.

Examples of datasets include tables, feature classes, relationship classes, feature datasets, topologies, and geometric networks. Datasets appear as items in the ArcCatalog treeview under their workspace:

Feature datasets are collections of feature classes and may also contain relationship classes, geometric networks, or topologies. Feature classes that store simple features can be organized either inside or outside a feature dataset. Those outside a feature dataset are called standalone feature classes. Feature classes that store topological features, for example, those participating in geometric networks or topologies, must be contained within a feature dataset to ensure a common spatial reference. A FeatureDataset is a dataset that exists only in a geodatabase workspace; all the datasets contained in the FeatureDataset are also part of the same geodatabase.

When programming with feature classes, you need to remember that the feature class may or may not belong to a feature dataset.

This code to get the workspace for a feature class assumes a feature dataset exists and therefore may fail.


FeatureClass fclass = new FeatureClass(featureWksp.openFeatureClass("LOBUSER.States"));
FeatureDataset dataset = new FeatureDataset(fclass.getFeatureDataset());
Workspace workspace = new Workspace(dataset.getWorkspace());

This piece of code will work for both standalone feature classes and those in feature datasets.

workspace = new Workspace(fclass.getWorkspace());

Each dataset in a geodatabase must have a unique name. In particular, each feature class in a geodatabase must have a unique name independent of the feature dataset that contains it. Note that this is different from a file system model, where two folders may contain files with the same local name within the folder.

Table, object class, and feature class


A Table object has one or more columns, referred to as fields, and contains an unordered collection of rows. For each field, each row has exactly one value in the data type of the field.

A Table is a Dataset; properties such as the name of the table, the persistable name object for the table, and the workspace containing the table may be obtained via the IDataset interface. In relational terms, a Table object represents an RDBMS table or view. In objected-oriented terms, a Table object represents an ObjectClass or a RelationshipClass in a geodatabase. A Table object hands out Row objects that support application- callable methods, depending on the type of data stored in the table. The Name property of a Table, accessible via the IDataset interface, returns its fully qualified name. The level of qualification may vary depending on the host DBMS. For example, a table named "pipes" owned by a user named "gas" may be called "pipes" on Access, "gas.pipes" on Oracle, and "mydb.gas.pipes" on SQL Server. The parseTableName method on the ISQLSyntax interface supported by the table’s workspace can be used to split the fully qualified name into its components.

An object class is a table whose rows represent entities, modeled as objects with properties and behavior. The row objects handed out by an object class support the IRow and the IObject interfaces.

An object class may participate in any number of relationship classes that relate its instances to objects (entities) in other object classes. An object class may contain a discriminating field, referred to as the subtype field, that may be used to partition its instances into a number of subtypes. All subtypes share the same field definition and are stored in the same table; however, individual subtypes may differ in the default values and domains assigned to fields. The subtyping mechanism can also be used in defining attribute, connectivity and topology rules that apply to the instances of the object class. The subtyping mechanism is a lightweight alternative to creating multiple subclasses, and each is represented by its own object class.

An object class has a nonnegative object class ID that is unique within the geodatabase. This ID is assigned to the object class at the time that it is created or at the time that an existing table in the RDBMS is registered with the geodatabase as an object class. The Name of the object class is the same as the name of the table in the DBMS in which the objects in the object class are stored; it follows the same fully qualified naming conventions.

An object class may have an getAliasName property that is stored as part of its definition by the geodatabase. The getAliasName may be retrieved and used for display purposes by applications.

An object class may have a ModelName property that is stored as part of its definition by the geodatabase. The model name is not exposed to end users and, if defined, can be used as a search key to find an object class by a standard model name that is adhered to across databases. A model name must be unique across the workspace.

Although all objects in a feature class or object class must have the same behavior and attributes, not all objects have to share the same default values and validation rules. You can group features and objects into subtypes. Subtypes differentiate objects based on their rules.

A FeatureClass is an ObjectClass whose objects are features, that is, a feature class is a collection of spatial entities, modeled as objects with properties and behavior. All of the features in a feature class share the same attribute schema (they have the same set of named fields). The row objects handed out by a feature class support the IRow, IObject, and IFeature interfaces.

A feature class has one distinguished field of type Geometry, referred to as the shape field. The shape field stores the geometry (referred to as the shape property) for the features in the FeatureClass.

This code example uses the Workspace object to return a FeatureClass object.


AccessWorkspaceFactory accessWorkspaceFactory = new AccessWorkspaceFactory();	
Workspace featureWksp = new Workspace(accessWorkspaceFactory.openFromFile("c:/Data/usa/usa.mdb", 0));
FeatureClass fclass = new FeatureClass(featureWksp.openFeatureClass("states"));

Rows, objects and features


 

A RowBuffer is a transient object that is capable of holding the state of a row but has no object identity. It is used primarily during data loading as the argument to the insertRow method on an insert cursor. A Row- Buffer is obtained from a Table using the createRowBuffer method.

The IRowBuffer interface contains methods to access the state (the set of field values) for a row buffer. These methods take as an argument the numeric index of the field to be accessed.

A Row object is an instantiated software object that represents a persistent row in a Table. A row object is normally obtained from a cursor on a table (for example, ICursor.nextRow) or fetched directly given its object ID (for example, ITable.getRow).

Once retrieved, clients may query the row object for additional interfaces and invoke methods on the row object. The CLSID property of a Table determines the type of row object returned by the Table. A new persistent row object is created using the ITable.createRow method. The act of creating the row assigns it identity. Note that applications should use the createRow method to create new persistent row objects, as opposed to directly cocreating the row objects. The latter will not create a row in the underlying persistent store.

A Row has a set of Fields. The set of Fields for a Row is the same as the set of Fields for its Table. In particular, the numeric index of a field in the Fields collection of its table is the same as the numeric index of the field in the Fields collection of the row, which is the same as the numeric index used to access the value of the field from the row. This means that application programs can and should cache field numeric indexes using the findField method on the Table object, rather than invoking the findField method once per row returned by a Cursor.

This example shows the creation of a row, then an update, followed by the deletion of the row.

Table table = new Table(wksp.openTable("STATES"));

int i = table.findField("STATE_NAME");
wksp.startEditing(false);
wksp.startEditOperation();

Row row = new Row(table.createRow());br>row.setValue(i, "Exploits");
row.store();

row.setValue(i, "Badger");
row.store();
row.delete(); wksp.stopEditOperation();
wksp.stopEditing(false);

An ObjectClass is a Table whose Row objects represent entities. The Row objects handed out by an ObjectClass support the IRow and the IObject interface and are referred to as Object objects or simply as Objects. An alternative name for Object object in this context is Entity object.

The IObject interface is almost identical to IRow, from which it inherits. The only additional property is a direct link to the object class.

The instances of an ObjectClass may be partitioned into a number of subtypes. The IRowSubtypes interface on an Object contains methods that allow determination and modification of the Subtype to which an Object belongs and allow initialization or resetting of the field values of an Object to the default values defined for its subtype.

It is important to note that when Objects are programmatically created via the createRow method on the ITable interface (or for features with the createFeature method on the IFeatureClass interface), the default subtype is not automatically set, nor are the default values initialized. When using ArcMap, these tasks are automatically performed. However, if you are programmatically creating an Object (or Feature) that has default values, the following code fragment indicates the proper sequence that should be followed.


FeatureClass fclass = new FeatureClass(wksp.openFeatureClass("GDB.STATES"));
wksp.startEditing(false);
wksp.startEditOperation();
Feature feature = (Feature) fclass.createFeature();
wksp.stopEditOperation();
wksp.stopEditing(false);

// Get the default subtype code for the feature class
int defaultSubtype = fclass.getDefaultSubtypeCode(); 

//Set the subtype and initialize the default values for the feature
feature.setSubtypeCode(defaultSubtype); 
feature.initDefaultValues();

A Feature is a spatial Object. It is also a member of a feature class, being a row in the feature class table. A feature has an associated shape, the type of which is defined by the feature class. The possible shape objects are Point, Multipoint, Multipatch, Polyline, and Polygon—these are all objects in the Geometry object model.

Mostly you will deal with simple features, but there are various special kinds as defined by the esriFeatureType enumeration. These include annotation, dimension, raster catalog, and various network features.

The IFeature interface extends IObject and IRow, from which it inherits. The additional facilities are to do with the shape of the feature. You can use the getShape/setShapeByRef property to get or set the shape. This can be much more convenient than the alternative of using the getValue property since you don’t have to work out the index of the shape field.

Query, cursors, and selection


Cursors

A Cursor is a data-access object that can either be used to iterate over the set of rows in a table or query or insert new rows into a table. There are three forms of Cursor, referred to as search, insert, and update cursors. Each of these types of cursors is returned by the corresponding method (search, insert, or update) on a Table or FeatureClass object. The search and update methods take a QueryFilter as input, which can be used to restrict the set of rows returned.

A Search cursor can be used to retrieve rows specified by a query filter; it supports a nextRow method. An Update cursor can be used to positionally update and delete rows specified by a query filter; it supports the nextRow, updateRow, and deleteRow method. An Insert cursor is used to insert rows into a table and supports the insertRow method. All of these methods are available in the single ICursor interface—it is your responsibility to make the calls appropriate to the type of cursor.

The nextRow method on a search or update cursor returns the next row in the result set to the application. The row object returned is allocated and hydrated by the cursor, and a reference to it is handed to the application. To retrieve all rows in a result set containing N rows, the application must make N calls to nextRow.

Cursors are forward only; they do not support backing up and retrieving rows that have already been retrieved or making multiple passes over data. If an application needs to make multiple passes over the data, the application needs to reexecute the query that returned the cursor. If both executions of the query are made within the same edit session (or database transaction with the appropriate level of isolation), the application is guaranteed not to see any changes made to the data by other concurrently executing applications.

This example shows a very simple cursor operation. It prints out the value of the first field for each row in a table.

Cursor cursor = new Cursor(table.ITable_search(null, false));
IRow row = cursor.nextRow();
while (row != null) {				
	System.out.println(row.getValue(0));
	row = cursor.nextRow();
}

Note that no data is fetched from the database until the NextRow method is called.

A Cursor has a recycling property that controls how it allocates row objects. Recycling cursors allocate a single row object and rehydrate it on each fetch. They can be used to optimize read-only access, for example, when drawing. It is illegal to maintain a reference on a row object returned by a recycling cursor across multiple calls to nextRow on the cursor. Row objects returned by a recycling cursor should not be modified. Nonrecycling cursors return a separate row object on each fetch. The objects returned by a nonrecycling cursor may be modified (setting the IRow.value property or any other custom ancestor supported by the Row) and stored with polymorphic behavior. The geodatabase guarantees “unique instance semantics” on nonrecycling row objects fetched during an edit session. If the row object to be retrieved by a call to nextRow has already been instantiated in the calling application, then a reference to the existing row object will be returned.

All Row objects retrieved from a Table using a Cursor logically contain the same ordered set of fields, and this set is the same as the ordered set of fields for the Cursor and the Table. In particular, the numeric index of a field in the Fields collection of the table is the same as the numeric index of the field in the Fields collection of the cursor, which is the same as the numeric index of the field for the row. So, the findField method needs to be used only once per table or cursor. If the query filter used in generating a cursor does not include certain fields, then the resulting row objects will still logically contain these fields; however, they will not have hydrated values for these fields. If an application accesses these field values for the row, a variant of type empty (VT_EMPTY) will be returned. Note that this value is different from the Null value (VT_NULL) that is returned when the value of a fetched field is null.

The updateRow method can be used to update the row at the current position of an update cursor (making a call to nextRow on a cursor returns a Row and positions the cursor on that Row). After fetching a Row object using nextRow, the application can modify the Row as needed and then call updateRow, passing in the modified Row. This is an alternative to calling store on the retrieved row. Using a recycling update cursor can be faster than calling Store on the rows returned by a search cursor when performing direct updates outside an edit session on simple data. If the row objects for the table are not simple (they don't have custom behavior or participate in composite relationships or relationships with notification), then calling updateRow on the cursor will generate a call to Store on the row object to trigger the custom behavior, and there will be no performance gain.

The deleteRow method can be used to delete the row at the current position of an Update cursor (that is, to delete the Row returned by the last call to NextRow on this cursor). After fetching the Row object using NextRow, the application should call deleteRow on the cursor to delete the row. The application is responsible for discarding the husk deleted row object. Using a recycling update cursor to delete rows can be faster then calling Delete on the rows returned by a Search cursor when performing direct updates outside an edit session on simple data. If the row objects for the table are not simple (they don't have custom behavior) or participate in composite relationships or relationships with notification, then DeleteRow on the cursor will generate a call to Delete on the row object in order to trigger custom behavior, and there will be no performance gain.

Insert cursors are used to bulk insert rows. Using an insert cursor offers significantly faster performance for data loading into simple tables and feature classes (tables whose CLSID is esriGeoDatabase.Row, esriGeoDatabase.Object, or esriGeoDatabase.Feature) than the alternative: making multiple calls to createRow on the table followed by calling store on the created row.

Insert cursors on tables that contain non-simple objects, such as internally use the createRow and store methods to achieve polymorphism, and there is no difference in performance in these cases. The insertRow method takes a RowBuffer as an argument.

Applications obtain a RowBuffer using the createRowBuffer method on the Table object into which rows are to be inserted. Each call to insertRow on the cursor creates a new row in the database whose initial values are set to the values in the input row buffer. The object ID for the created row is returned by the insertRow method.

The useBuffering method argument to the Insert method on a table returns an insert cursor that buffers rows on the clients and sends them to the server in batches for increased performance. The application is responsible for calling flush on the insert cursor after all rows have been inserted. If a call to flush is not made, the cursor will flush 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 may arise on the call to flush (for example, if the tablespace [disk] for the Table in the underlying database fills up).

The FeatureCursor object is a kind of Cursor object. It performs in the same way, except it is based on a feature class rather than a generic table.

The IFeatureCursor interface provides access to a set of features in a feature class. It operates in the same way as ICursor, although it does not inherit from that interface. This saves you from having to use Query-Interface when dealing with features rather than rows.

All the discussion for Cursor objects is appropriate to feature cursors—there is a direct correspondence between the methods on the various interfaces.

Query filters and spatial filters

A QueryFilter object specifies a filter for tabular data based on attribute values. It is used to restrict the set of rows or the set of columns retrieved from a single table or feature class. The primary use of a query filter is to specify the set of rows to be returned when opening a cursor on a Table. It is also used in a number of other cases where a subset of the data in a table needs to be specified.

Some scenarios of using a QueryFilter include opening a cursor on some of the rows in a table, deleting some features meeting certain criteria, counting the number of features satisfying a condition, and defining which features will be rendered on the map.

The example below shows how to select features for the State of California. This code will work on any feature layer with a STATE_NAME attribute —QueryFilters are not specific to any particular dataset.


FeatureClass fclass = new FeatureClass(wksp.openFeatureClass("states.shp"));
			
QueryFilter qfilter = new QueryFilter();
qfilter.setWhereClause("STATE_NAME = 'California'");

There is no need to specify a WhereClause if you just want to filter the fields of data. You can also normally use the VB keyword "Nothing" in place of a QueryFilter for those methods that require one—for example, to count the features in a feature class.


SelectionSet selection = new SelectionSet(fclass.select(qfilter, esriSelectionType.esriSelectionTypeHybrid, esriSelectionOption.esriSelectionOptionNormal, wksp));
System.out.println("Number of selected features = " + selection.getCount());

You can use the SubFields property to improve performance when using query filters. The performance gain comes from just fetching the field values that you require rather than all the data for each row. The default value for SubFields is "*", which indicates that all field values will be returned. It isn't necessary to set the subfields when the query filter is used in a context in which no attribute values are fetched, for example, when selecting features.

A QueryFilter has properties named getSubFields and getWhereClause and represents a subset of the single table queries that may be made against a table in a SQL database using the SQL SELECT statement. QueryFilters map on to simple SQL select statements of the form SELECT <field names> FROM <table name> WHERE <where-clause that references only table name>.

The SQL syntax used to specify the setWhereClause of QueryFilter objects is the same as that of the underlying database holding the data. An application can use the ISqlSyntax interface on a Workspace to determine information about the SQL syntax used, such as the delimiter character used in qualifying table and field names and the identifier quote character. This information is available for the different types of workspaces (ArcSDE for Oracle, ArcSDE for SqlServer, Access, shapefile, coverage, and others). Unlike QueryDef objects, QueryFilter objects are supported across all the workspace types, including shapefiles and coverages.

A SpatialFilter is a QueryFilter that includes both spatial and attribute constraints. A SpatialFilter can be used to restrict the set of features retrieved from a feature class using both spatial and attribute restrictions. A spatial filter has a single query geometry that specifies the geometry against which the features in the feature class will be tested. Because ArcObjects supports a number of different geometry types, including both single and multipart geometries and geometry collections, one way of expressing a complex spatial query is by building an appropriate query geometry to pass as input to the spatial filter.

A spatial filter has a single geometric shape that is used in the query. You can form more complicated spatial queries by using several spatial filters in succession.

You can use spatial filters everywhere that query filters are used, as long as the dataset to be queried has a spatial field. Some example tasks might be:

The ISpatialFilter interface is used to define a query with geographic criteria.

You must always set these three properties: setGeometryByRef, setGeometryField, and setSpatialRel. The GeometryEx method may be used to set the query geometry in the case of large query geometries where the application is willing to surrender ownership of the geometry to the filter. In this case, the filter may modify (project) the query geometry in place if the spatial reference of the query geometry is different from the native spatial reference of the feature class or the requested output spatial reference. The spatial reference in which the features should be returned by the query is specified using the setOutputSpatialReferenceByRef property in the IQueryFilter interface.

This simple example shows a simple selection of features that intersect a given shape. It assumes an existing feature layer and a valid geometry pointer (perhaps derived from end user input).


SpatialFilter sfilter = new SpatialFilter();
IGeometry geometry = null;
sfilter.setGeometryByRef(geometry);
sfilter.setGeometryField(flayer.getFeatureClass().getShapeFieldName());
sfilter.setSpatialRel(esriSpatialRelEnum.esriSpatialRelIntersects);
flayer.selectFeatures(sfilter, esriSelectionResultEnum.esriSelectionResultNew, false);

ISpatialFilter inherits the members of IQueryFilter—the example above could be extended by setting the WhereClause property on the spatial filter.

The SpatialRel property takes an enumeration that defines the relationship between the query geometry and the target feature geometry; this must be satisfied for the target feature to be returned by the query. The spatial relationships supported are the basic Clementini relationships, specified as part of the OpenGIS® Simple Feature data-access standard.

The five basic Clementini relationships are Disjoint, Touches, Overlaps, Crosses, and Within. esriSpatialRelIntersects, esriSpatialRelTouches, esriSpatialRelCrosses, esriSpatialRelOverlaps, esriSpatialRelWithin, and esriSpatialRelContains map to the corresponding Clementini relationships. Intersects maps to Not(Disjoint), Contains(a,b) maps to Within(b,a), and the rest correspond directly to the Clementini relationship.

esriSpatialRelEnvelopeIntersects is True if the envelope of the query geometry intersects the envelope of the target geometry.

esriSpatialRelIndexIntersects may be specified as the filter spatial relationship if the application is prepared to deal with features that do not intersect the query geometry, as long as all features that do intersect the query geometry are returned. This is a hint to the database that only the primary filter based on the spatial index needs to be applied; this results in faster query execution. This can be appropriate for drawing applications that rely on clipping to do the secondary filtering.

esriSpatialRelRelate may be specified as the filter spatial relationship if the application wishes to directly specify the relationships between the topological interior, boundary, and exterior of the query geometry and the topological interior, boundary, and exterior of the target geometry, using the dimensionally extended nine-intersection model. The spatial relationships between the components are specified using a string of nine characters that is set as the value for the esriSpatialRelDescription property of the filter.

The characters are drawn from the alphabet {T, F, *} and indicate the dimension of the point set resulting from the intersection of the two components that map to that character position. F indicates no intersection, T indicates intersection, and * indicates don’t care. The mapping of components to character position in the string is shown in the diagram to the left. The character string is constructed by reading out the entries in the 3 x 3 matrix in the order left to right and top to bottom.

The values in this diagram translate into the nine-character string reading from left to right and top to bottom (TT*TT***).

Some of the spatial relationships exposed to the end user in the TODO ArcMap Select By Location dialog box do not correspond directly to the basic Clementini relationships described above. These spatial relationships can be implemented using the Clementini spatial filter relationships combined with preprocessing and post processing. Preprocessing is used to assemble the appropriate query geometry (for example, in the case of distance-based relationships, using buffer). Post processing can be used to further restrict retrieved geometries returned by the Clementini operator. The following table shows examples of such processing: relationships between the query and target geometries.

The SpatialRelDescription property is only used when SpatialRel is set to esriSpatialRelRelation. You can use it to define various complex spatial relationships.

The SearchOrder property determines whether the spatial part of the query is performed before the attribute part of the query. By default, the spatial relationship is tested first, but in the case of queries where the attribute criteria are much more specific than the spatial, it is better to change the SearchOrder. An example of this kind of query might be "find all worldwide cities with population greater than a million that are not in Spain".

If you want to want to query a feature class based on a collection of shapes, for example, "select the cities that are within the selected states", you have several different options. One option is to apply successive spatial filters for each query shape. Another option is to make a single multipart query shape from the collection of original query shapes, then use a single spatial filter.

The following example shows how to form a single geometry from the selected features in a layer.


EnumFeatureGeometry enumGeom = new EnumFeatureGeometry();
enumGeom.bindGeometrySource(null, flayer.getSelectionSet());
GeometryEnvironment geomFactory = new GeometryEnvironment();
IGeometry geom = geomFactory.createGeometryFromEnumerator(enumGeom);

Selections

A SelectionSet object allows an application to reference a selected set of rows all belonging to a single table or feature class.

Selection sets are normally used throughout ArcObjects when a temporary subset of rows or features is required for some operation. It is important to note that the selection set only applies to a single table; you cannot sensibly combine two selection sets from different tables.

A selection set can be based on either a set of ObjectIDs that correspond to the selected rows, or on an actual set of row objects, instantiated and referenced by the selection set. In either case, the selection set provides methods to iterate over the set of row objects in the selection. The SelectionType property of a selection set, specified by an application at the time that it creates the selection set, determines the type of representation (object IDs or Row object references) used by the SelectionSet.

A selection set is typically created from a table or feature class using the Select method on the table. A query filter is used to specify the subset of rows to include in the selection set:


SelectionSet selection = new SelectionSet(fclass.select(qfilter, esriSelectionType.esriSelectionTypeHybrid, esriSelectionOption.esriSelectionOptionNormal, wksp));

An application can create multiple SelectionSets on a Table or FeatureClass coclass. The SelectionSets reference their target table, but the latter have no knowledge of the selection sets that reference them. Applications are responsible for associating the created selection sets with the target table as appropriate. For example, a FeatureLayer in TODO ArcMap holds a reference to a geodatabase FeatureClass and also to a SelectionSet that it creates on the feature class—at draw time the selected features are retrieved and drawn in a distinguished manner.

The ISelectionSet interface is used to manage and query the selection set.

The search method is used to iterate over the rows in the selection set and returns a cursor. Note that the Search method takes a query filter that can be used to further restrict the set of rows in the selection that are returned for ID set selections. Using a query filter with hybrid selections will force the representation of the selection to become an ID set selection.

The select method is used to create a new selection based on a subset of the current selection using a query filter to specify the restriction. The add, addList, and removeList methods can be used to alter the selection set by adding and removing rows specified by object IDs. The combine method can be used to combine two SelectionSets using the standard set operations of union, intersection, difference, and symmetric difference. Only use Combine on two selection sets from the same target—it doesn't make sense to mix lists of IDs from different datasets.

The ISelectionSet2 interface provides a method update that creates an update cursor on the selection set; this can be used to update and delete rows from the table or feature class of the selection set.

This example returns the average population of the selected features in a counties feature layer; it illustrates a transition from using TODO ArcMap objects to using the geodatabase data-access objects.


SelectionSet sset = new SelectionSet(flayer.getSelectionSet());
ICursor[] fcursor = new ICursor[1]; 
sset.search(null, true, fcursor);

DataStatistics dstats = new DataStatistics();
dstats.setCursorByRef(fcursor[0]);
dstats.setField("POP1990");
System.out.println(dstats.getStatistics().getMean());

As described in this section, the main interface for geodatabase selection sets is ISelectionSet. There are, however, some other similarly named interfaces in ArcObjects—the following table summarizes these other interfaces.

QueryDef

A QueryDef object represents a database query on one or more tables or feature classes.

A QueryDef may be evaluated, resulting in the execution of the query on the database server. The results of the query are returned to the application as a cursor. The application can iterate over the cursor to fetch the row objects in the result set of the query. The row objects returned by a cursor on a QueryDef are always of type esriGeoDatabase.Row— they never have custom behavior or support additional interfaces, even if the table names specified in the QueryDef correspond to tables representing ObjectClasses with behavior. The row objects returned by evaluating a QueryDef are read-only—these row objects do not reference a parent table, and the Store method may not be called on them. Attempting to Store a row object returned by evaluating a QueryDef will result in an error.

The primary use of QueryDefs is to directly evaluate database queries on arbitrary tables. They can be used to join tables with the assurance that the join query will execute in the underlying RDBMS. All of the tables in a QueryDef must belong to the same workspace (RDBMS). A QueryDef may include geometry fields in the specification of the list of fields to be returned but may not include geometry fields in the where clause specification unless the underlying DBMS is a spatially extended DBMS that supports geometric types, and unless the geometry fields for feature classes are using those native DBMS geometry types for storage.

The IQueryDef interface is used to set up and define the query and also provides an Evaluate method that is used to execute the query, returning a cursor.

This example shows how to create a QueryDef that defines a join between USA counties and states. A valid pointer to the workspace containing the data is assumed.


QueryDef qdef = new QueryDef(fworkspace.createQueryDef()); //QueryDef qdef = (QueryDef) fworkspace.createQueryDef();

qdef.setTables("Counties, States"); // TODO does not seem to be working for shapefiles or mdb
qdef.setSubFields("COUNTIES.Shape, COUNTIES.NAME, STATES.STATE_ABBR");
qdef.setWhereClause("COUNTIES.STATE_FIPS = STATES.STATE_FIPS");

Note that QueryDef objects cannot be cocreated. They can only be created from the IFeatureWorkspace interface. This guarantees that all tables in the query are within the same workspace.

The setSubFields property is optional when creating QueryDef objects. The default value is “*”, which means that all fields are returned.

The openFeatureQuery method on a workspace (available in the IFeatureWorkspace interface) can be used to create a FeatureClass that is based on a QueryDef. Such a feature class may be added to a Map (as a FeatureLayer) and may be used to visually represent the results of a database join query. Such a FeatureClass is similar in concept to an ArcSDE view.

The following example adds a new layer to a map based on the QueryDef created in the previous example. The IQueryDef.setSubFields property must define one and only one spatial field in order to create the feature class.


FeatureDataset fdataset = new FeatureDataset(fworkspace.openFeatureQuery("My counties join", qdef));
FeatureClass fclass = null;
if (fdataset.getClassCount() != 1) {
	System.out.println("Failed to create feature class by query");
	System.exit(0);
}

fclass = new FeatureClass(fdataset.esri_getClass(0));

Map map = new Map();
FeatureLayer flayer = new FeatureLayer();
flayer.setFeatureClassByRef(fclass);
map.addLayer(flayer);

The SQL syntax used with QueryDef objects is the same as that of the underlying database holding the data. An application can use the ISQLSyntax interface on a Workspace to determine information about the SQL syntax for the database, such as the delimiter character used in qualifying table and field names and the identifier quote character.

QueryDefs represent a subset of the queries that may be made against an SQL database using the SQL SELECT statement. QueryDefs map onto simple SQL select statements of the form: SELECT <field names> FROM <list of table names> WHERE <where-clause referencing only tables in from list of tables>.

QueryDefs do not guarantee to support SQL statements that do not map onto the above simple form. In particular, QueryDefs do not guarantee to support ORDER BY and GROUP BY clauses embedded within the WhereClause property, nested SELECT statements or correlated subqueries in the WhereClause property, AS keywords embedded in the SubFields property, the use of table aliases in the Tables property, the use of Aggregate functions (for example, MIN, MAX, and SUM), or the use of DISTINCT clauses. Support for such capabilities is not guaranteed across all configurations, and applications that rely on such capabilities risk failure.

Relationships


A RelationshipClass is an association between two object classes; one is the origin class and the other the destination class. The relationship class represents a set of relationships between the objects belonging to two classes.

You can create a relationship class with either IRelationshipClassContainer or IFeatureWorkspace. RelationshipClass objects implement IDataset (useful for getting the name or the workspace), but they do not implement IClass (unless they are attributed). This is because a nonattributed relationship class does not have any fields of its own.

The IRelationshipClass interface provides information about a relationship class, functionality to create and delete individual relationships, and methods to find related objects. The members of this interface can be split into three logical groups: the properties that correspond to how the relationship class was created, the object-to-object methods that deal with individual relationships, and the relationship rules methods.

The getOriginPrimaryKey, getOriginForeignKey, get DestinationPrimaryKey, and get DestinationForeignKey properties can be somewhat confusing—their uses are different depending on whether the relationship class is attributed.

The object-to-object methods, such as getObjectsRelatedToObjectSet, make use of the ISet interface, which manipulates a set of generic objects. When adding objects to a set with a cursor, make sure that the cursor recycling is turned off, as shown in this example (which deletes all the relationships for features with areas less than a certain value).


QueryFilter qfilter = new QueryFilter();
qfilter.setWhereClause("SHAPE.AREA < 25");
FeatureCursor fcursor = new FeatureCursor(fclass.search(qfilter, false));
Set fset = new Set();
Feature feature = (Feature) fcursor.nextFeature();
while(feature != null) {
	fset.add(feature);
	feature = (Feature) fcursor.nextFeature();
	if (feature != null)
		System.out.println("State name = " + feature.getValue(feature.getFields().findField("STATE_NAME")));
}
fset.reset();

System.out.println("number of features = " + fset.getCount());

RelationshipClass rclass = new RelationshipClass(fworkspace.openRelationshipClass("GDB.CitiesInStates"));	

Set relatedFeatures = (Set) rclass.getObjectsRelatedToObjectSet(fset);
Object rfeat = relatedFeatures.next();
if (rfeat != null) {
	int cityIndex = (new Feature(rfeat)).getFields().findField("CITY_NAME");
	int stateIndex = (new Feature(rfeat)).getFields().findField("STATE_NAME");
	while (rfeat != null) {
		System.out.println( (new Feature(rfeat)).getValue(stateIndex) + " = "
				+ (new Feature(rfeat)).getValue(cityIndex));
		rfeat = relatedFeatures.next();
	}
}

rclass.deleteRelationshipsForObjectSet(fset); 

The identify dialog in ArcMap allows you to discover objects related to other objects through a relationship class:

When using CreateRelationship, remember that this operation will write a value into the foreign key field. Therefore, it is possible that you could overwrite, and therefore delete, an existing relationship. Similarly, DeleteRelationship will remove the foreign key value, so that field must allow null values unless you want to ensure that all objects in the class belong to relationships.

The ArcMap editor's property inspector allows you to both discover, add and remove relationships for an object:


The IRelationshipClass2 interface was added to provide a method to get matching objects.

An AttributedRelationshipClass is a special kind of relationship class and is also a kind of table known as the relationship table. For nonattributed relationship classes, the relationships are stored with the objects themselves in the foreign key values. For attributed relationship classes, the relationships are defined by the objects in conjunction with the rows in the relationship table.

A good way of testing whether you have an AttributedRelationshipClass object is as follows:


if {pRelClass instanceof ITable}
  System.out.println("Attributed Relationship Class");

The IRelationshipClass.isAttributed property only returns True if there are extra relationship attributes beyond those required to relate the objects. The IRelationshipClass.getRelationship method is useful for accessing the relationship attributes.

A relationship represents a pair of related objects or features. Relationship is an abstract class that covers SimpleRelationship and AttributedRelationship objects.

The IRelationship interface provides read-only information about a relationship. It is most useful with attributed relationships since it can form a bridge between the attribute information, which is in row form, and the related objects. When dealing with relationships, you will normally use the IRelationshipClass interface rather than IRelationship.

The SimpleRelationship object represents a pair of related geodatabase objects or features. There are no attribute values associated with the relationship. You should not cocreate a simple relationship. Instead, use IRelationshipClass.createRelationship.

The AttributedRelationship object is a kind of row that represents a pair of related objects or features with extra information about the pairing. The extra information is stored in the row.

You should not cocreate an attributed relationship. Instead, use IRelationshipClass.createRelationship. The IRelationshipClassEvents Interface provides information as to when two objects are related or unrelated and when attributes on an attributed relationship are modified. You can use this interface to listen to the events on the relationship class you care about.

Domains and validation rules


 

Rules are associated with object classes and are used during the process of validating objects within an object class. There are four categories of rules that are subclassed from the Rule abstract class. They are attribute rules (AttributeRule), relationship rules (RelationshipRule), topology rules (TopologyRule), and connectivity rules (ConnectivityRule), further broken down into JunctionConnectivityRule and EdgeConnectivityRule.

For a detailed discussion on topology rules, see the Topology section in this document.

Associating a rule with a class does not guarantee that all objects within the class will always be valid; the validation process still needs to be run through the Editor toolbar or with IValidation.validate. Through the IValidation interface (found on the Object class), the set of currently defined rules can be accessed, new rules can be added, and objects can be validated.

IRule is a generic interface that supports validation rules on an object class. Use this interface when you want to determine the type of rule and the helpstring associated with it.

Helpstring displays the message associated with the rule. This message is displayed during the process of validating a single feature when that feature is found to be invalid. The helpstring of the first (of possibly many) validation rule that is found to be invalid is displayed through the ArcMap user interface.

Type specifies the type of rule (attribute, relationship, or connectivity) and can be used to determine what validation rule object you are holding. Alternatively, you can attempt to probe for the appropriate interfaces (for example, if the Rule supports IAttributeRule, then it is an AttributeRule).

The following code extracts the rules defined for a layer and prints the type of the rule and helpstring associated with the rule.


FeatureClass fclass = new FeatureClass(fworkspace.openFeatureClass("GDB.States"));

IEnumRule rules = fclass.getRules();
IRule rule = rules.next();

while(rule != null) {
	if (rule instanceof IAttributeRule) {
		System.out.println("Attribute rule - " + rule.getType() + "-" + rule.getHelpstring());
	}
	else if (rule instanceof IRelationshipRule) {
		System.out.println("Relationship rule - " + rule.getType() + "-" + rule.getHelpstring());
	}
	else if (rule instanceof IJunctionConnectivityRule) {
		System.out.println("AttJunctionConnectivity rule - " + rule.getType() + "-" + rule.getHelpstring());
	}
	else if (rule instanceof IEdgeConnectivityRule) {
		System.out.println("EdgeConnectivity rule - " + rule.getType() + "-" + rule.getHelpstring());
	}
	rule  = rules.next();
}

Creating a class extension can extend the types of rules that can be defined for an object class. By implementing IObjectClassValidation (along with IClassExtension), any type of custom validation rule can be coded.

Attribute rules and domains

An attribute rule applies an attribute domain to a field of an object class.

The AttributeRule class is used to define attribute-specific rules on an object class. This type of rule applies a specified domain to a field name with a specific subtype. Domains can be used to limit the attribute values to a set of valid values or to a range of values. Domains can also define how values in the field are distributed during a split. The process of associating a domain with a field in an object class creates an AttributeRule as a side effect—thus, it is generally not the case that users must explicitly create AttributeRules.

A domain is used to specify the permissible values that a field in an object class may take.

A Domain is an abstract class that defines an interface used by RangeDomain and CodedValueDomain coclasses to constrain the permissible values that may be associated with a particular field on an object or feature class. Domains are assigned on a subtype basis.

The IDomain interface provides access to the common properties shared across both types of Domains. Each of the properties are read-write except for the Type property. When creating and assigning a Domain to a particular field, the client is required to set the Name and FieldType properties.

A range domain is used to specify the legal minimum and maximum values that a field may have.

A coded value domain is used to specify a set of permissible values that a field may take.

Domains are used by the ArcMap property inspector to constrain the values that the user can enter for a field, as well as during the validation process within the geodatabase.

A Domain may be shared by any number of fields. Domains are associated with a Field object. Domains are added to a dataset at the workspace level through IWorkspaceDomains.addDomain.

RangeDomains may be associated with fields that are either numeric fields (such as esriFieldTypeSmallInteger or esriFieldTypeDouble) or date fields. RangeDomains may not be associated with string or character fields (esriFieldTypeString).

CodedValueDomains store a set of (value, name) pairs that represent the discrete values that a field may take. The value is what is actually persisted inside a field; the name is what is displayed by the ArcMap property inspector. The name can be considered to be a human-readable string that describes what the value represents. In contrast to RangeDomains, CodedValueDomains may also be associated with string fields (esriFieldTypeString)—the value may be a string.

The ArcMap identify dialog and editor's property inspector make use of coded value domains to display the human-readable description of values for an attribute. The object inspector also provides a list of valid values for users to select when editing an attribute with a coded value domain, as shown below:

The following code fragment demonstrates how a user could use these properties in order to display all the (value, name) pairs associated with a CodedValueDomain.


CodedValueDomain domain = (CodedValueDomain) fld.getDomain();			
Object value = null;
String name = null;
for (int i = 0; i < domain.getCodeCount(); i++) {
	value = domain.getValue(i);
	name = domain.getName(i);
	System.out.println("value: " + value + " name: " + name );
}

Domains are managed at the workspace level. The following graphic is the ArcCatalog user interface for creating, deleting and modifying domains:


Relationship rules

A RelationshipRule constrains the cardinality between two subtypes that participate in a RelationshipClass. Thus, if the RelationshipClass is a one-to-many relationship, a RelationshipRule may, for example, constrain the cardinality between two subtypes to be one–three. The RelationshipRule may not conflict with the RelationshipClass. For example, a one-to-many RelationshipClass may not have any associated relationship rules that constrain the cardinality to be two to two. One RelationshipRule is necessary for each subtype pair that participates in the RelationshipClass.

The IRelationshipRule interface inherits from IRule. This interface provides access to the various parameters of a relationship rule that are used to refine the cordialities between subtypes participating in the RelationshipClass. Use this interface when you want to set or retrieve these parameters.

DestinationMaximumCardinality and DestinationMinimumCardinality are only applicable in one-to-many and many-to-many relationships.

OriginMaximumCardinality and OriginMinimumCardinality are only applicable in many-to-many relationships.

Connectivity rules

A connectivity rule constrains the type of network connectivity that may be established between edges and junctions in the geometric network.

Within a geometric network, any edge may connect to any junction. ConnectivityRules are used to constrain the permissible connectivity between edges and junctions. There are two types of connectivity rules that can be applied. JunctionConnectivityRules are placed on junction object classes and determine the valid types of edges that can be connected. EdgeConnectivityRules are placed on edge object classes and determine the valid types of junction or edges (through a junction) that can be connected. Connectivity rules can only be established between network feature classes.

The JunctionConnectivityRule class is a type of ConnectivityRule that constrains the possible valid network connections that may exist between a pair of edge and junction subtypes. It may also constrain the cardinality of the connectivity relationships.

The IJunctionConnectivityRule interface inherits from IConnectivityRule. This interface defines the junction connectivity properties and the valid types of edges that can connect to them. Use this interface when you want to define or manipulate rules between an edge and a junction.

The EdgeConnectivityRule class is a type of ConnectivityRule that defines the permissible relationship between two edge features. In addition, it specifies all the valid junctions that may exist at the connection point between the two edges. It is also possible to specify the default junction that will be placed at the point of connectivity between the two edges.

The IEdgeConnectivityRule interface inherits from IConnectivityRule. This interface defines the two types of edges (they can be of the same type) and the valid junctions that can exist between them.

DefaultJunctionClassID and DefaultJunctionSubtypeCode define the default junction that will be added at the location where connectivity is established between two edges of the specified type.

The number of junctions associated with the EdgeConnectivityRule is unlimited. Junctions are managed through this interface and are accessed on an index basis (the JunctionCount property and the JunctionClassID and JunctionSubtypeCode index-based properties). It is not possible to remove a junction from the rule; if this is required, the rule must be deleted and then re-created, less the junction, to be deleted.

The following graphic illustrates the ArcCatalog user interface for configuring connectivity rules for a geometric network:

 

Geometric network


A geometric network is a type of graph that is uniquely associated with a logical network, which represents network topology.

GeometricNetworks are responsible for detecting and maintaining network connectivity among a set of feature classes that participate in the network. When new network features are created, the GeometricNetwork is responsible for detecting endpoint coincidence (in the case of edge features) with other network features, then communicating with the logical network in order to establish network connectivity.

The GeometricNetwork is also responsible for managing and validating all connectivity rules.

The most common type of third-party client applications that will consume the IGeometricNetwork interface are custom network solvers. The associated logical network may be accessed through the Network property.

The direct accessibility of the logical network obviates the need to expose the functionality of the logical network through redundant convenience methods at the geometric network level. Functionality that is related to the mapping between geometry (found at the feature level) and network elements (found at the logical network level) is necessarily supported outside of the logical network. This is because the logical network does not have any understanding of feature geometry, only logical connectivity.

You can determine the network elements associated with a network feature at a given location through the EdgeElement and JunctionElement properties. If there is more than one network feature of the appropriate type at the location, then the edge or junction element that corresponds to the first one encountered is returned.

Complementary functionality is also provided where, for a given edge or junction element, the associated feature geometry is returned via the GeometryForEdgeEID and GeometryForJunctionEID properties, respectively.

In contrast to the results returned by the EdgeElement and JunctionElement methods, the GeometryForEdgeEID and GeometryForJunctionEID properties return an unambiguous result, as only one piece of geometry corresponds to a given EID. An EID is an element ID for an element in a logical network.

A final method found on IGeometricNetwork that may commonly be called by third-party client applications is SearchForNetworkFeature. This method, given a point location and a feature type, will return all the network features that are found within the machine precision of this point. If more than one network feature is coincident with the point, then all are returned. The returned network features may span different feature classes; the only restriction is that all features must be of the same feature type.

The IGeometricNetworkErrorDetection interface identifies errors between a geometric network and its logical network.

In order to maintain correct network connectivity in large production environments, it is necessary to have a collection of tools that will enable the user to detect a variety of connectivity problems within a geometric network. In production environments, it is often impractical to drop the network and rebuild when connectivity problems are encountered during general editing of the network. For this reason, it is necessary to provide a set of tools that will enable the end user to detect and repair such problems.

Philosophically, there should not be any need for such tools—the network should always be correct. From this standpoint, the geodatabase will not waver. However, there are certain circumstances where this may be violated:

The CreateErrorTable method is used to create a table that can be used to persist information related to corrupt network features (using a fixed table schema) with the specified name. Such network error information can only be persisted by the geometric network in a table with this schema. This table is user managed and should remain unversioned.

The DeleteNetworkElements method takes an ISet of ISelectionSets. All of the network features contained in the various selection sets will have their network elements deleted from the logical network. The primary reason why one would want to do this is to correct the geometry of an edge feature that was loaded (prior to ArcGIS 8.1) with corrupt polyline geometry.

If network connectivity errors are found in the geometric network, they can generally be corrected through the use of the RebuildConnectivity method on the IGeometricNetworkConnectivity interface. This method takes an envelope that should contain all the network features for which connectivity should be rebuilt.

A graph is a set of topologically related feature classes. A Graph is an abstract class that factors behavior and attribution common to the different types of topological collections within the geodatabase.

The IGraph interface specifies the attributes and properties expected on all the different types of topological collections within the geodatabase. These attributes and methods are not unique to a particular type of topology. It is not expected that third-party client applications will call many of these methods. The primary clients of these methods on this interface are ArcMap, ArcCatalog, and the polymorphic implementations of the features managed by the Graph.

Network features

NetworkFeature is an abstract component that supports network connectivity and participates in a geometric network.

The INetworkFeature interface is supported at the NetworkFeature level within the geodatabase; all features participating in a GeometricNetwork support this interface. Because each NetworkFeature may either be enabled (can trace through) or disabled (cannot trace through) within the logical network, the Enabled property is read–write. It is important to note that although a complex edge may correspond to one or more network elements, setting the Enabled property will either enable or disable all associated network elements. It is not possible to individually set the enabled or disabled status of an individual network element associated with a complex network feature through this interface.

When a NetworkFeature is being created and added to a GeometricNetwork coclass, the GeometricNetwork will call the createNetworkElements method on the NetworkFeature. It will not be necessary for a custom feature developer to override this method in their implementation.

It is important to note that if network features are being programmatically created (for example, using a sequence similar to IFeatureClass.createFeature, setting the feature’s Shape, then calling Store on the feature), the network feature’s spatial reference must match that of the containing FeatureClass. More specifically, if you call IGeometry.project on the geometry prior to it being set as the feature’s Shape, you must take care to ensure that the SpatialReference that is being passed as an argument to Project match that of the FeatureClass. It is not always the case that the Map’s SpatialReference is the correct one to use (for example, the Map may contain two FeatureDatasets with differing SpatialReferences).

Junction features are used to maintain network integrity within a geometric network. They are found at the locations that correspond to the endpoints of edge features. They may also be freestanding (unconnected to any edge feature) or connected to complex edges at midspan.

The SimpleJunctionFeature class represents simple junctions on a network that may be added to GeometricNetworks.

The SimpleJunctionFeature class can be aggregated, and possibly overridden, by custom feature developers.

Simple junction features have point geometry and may be connected to any number of other edge features.

A simple junction feature may not be directly connected to another junction feature without an intervening edge feature.

The ISimpleJunctionFeature interface contains three properties that are unique to simple junctions.

The EdgeFeatureCount property and EdgeFeature property array are used to specify the connected edge features to the client. The index for EdgeFeature is zero-based.

The following code fragment shows how a client might use this information to display the object IDs of the connected edge features.


SimpleJunctionFeature simpleJunction = new SimpleJunctionFeature(feat); 
SimpleEdgeFeature edgeFeature = null;
for(int i = 0; i < simpleJunction.getEdgeFeatureCount(); i++) {
	edgeFeature = (SimpleEdgeFeature) simpleJunction.getEdgeFeature(i);
	System.out.println("EdgeFeature " + edgeFeature.getOID());
}

Edge features correspond to features with polyline geometry that are part of the network topology within a geometric network. They have two or more connected junction features—one at each location corresponding to the endpoints of their polyline geometries. Complex edges may also have any number of connected mid-span junction features. An EdgeFeature is an abstract class.


The IEdgeFeature interface must be supported by both simple and complex edges. This interface is found on the EdgeFeature abstract class. The various properties found on this interface are intended to facilitate network feature navigation for client applications.

The FromToJunctionEIDs property hands back both the FROM and TO junction EIDs; it is more efficient to access this property than to call FromJunctionEID and ToJunctionEID. It is important to note that these properties are generally computationally expensive. For certain clients (that is, those that do not require access to the geometry, attributes, or feature class associated with the network feature), it may prove more advantageous to directly utilize the logical network when performing navigation between large numbers of network features. For example, this is the case with network solvers.

The update method is reserved for internal consumption (during the process of updating the shape and storing the result); there is no need for clients to call this method directly.

Simple edge features correspond to features with polyline geometry that are part of the network topology within a geometric network. They have two connected junction features—one at each location corresponding to the endpoints of their polyline geometries. Junction features connected at mid-span are not allowed. If you attempt to connect a junction at mid-span on a SimpleEdgeFeature, a split operation occurs (the original SimpleEdgeFeature is deleted and replaced by two new SimpleEdgeFeatures that are commonly connected at the junction feature that caused the subdivision).

Complex edge features correspond to features with polyline geometry that are part of the network topology within a geometric network. They have two or more connected junction features—one at each location corresponding to the endpoints of their polyline geometries. They may also have any number of connected mid-span junction features.

Connecting a junction feature to a ComplexEdgeFeature does not result in a physical subdivision of the edge; instead, it results in a logical subdivision (that is, new edge elements in the logical network that are associated with the complex edge).

The geometry of ComplexEdgeFeatures may not be self-intersecting; there may be discontinuities with the geometry (they may be multipart), and the geometry may not have the same start and stop vertex (that is, a closed loop).

The IComplexEdgeFeature interface is supported on a ComplexEdgeFeature. The GeometryForEID property allows clients to obtain the portion of the complex edge’s geometry that corresponds to a specified EID. This is useful for network solvers in particular. The JunctionFeature property array is a mechanism for clients to obtain all the junction features that are associated with the complex edge.

 

Topology


A topology is a collection of simple feature classes within the same feature dataset that participate in topological relationships with a set of rules that govern those relationships. Topologies can have multiple feature classes in the same topological role. A feature dataset may have multiple topologies but a feature class can only belong to one topology. Each topology has one associated topology graph. The topology graph is a planar representation of the geometries in the feature classes participating in a geodatabase topology.

When new features are created, edited or deleted, the topology is responsible for creating or modifying a dirty area that will encompass the envelope of the feature. A dirty area is a special type of feature under which, the state of the topology is unknown:

Features that are covered by dirty areas can still be edited and queried, but their topological relationships cannot be guaranteed to be correct. A dirty area must be validated in order to discover the topology of its underlying features.

The Topology object is not cocreateable, topologies must be created through another method call, ITopologyContainer.createTopology.

The ITopology interface provides properties of a topology and methods for adding feature classes and validating dirty areas.

The following code shows you how to obtain a reference to a topology in your feature workspace:


FeatureDataset fdataset = new FeatureDataset(fworkspace.openFeatureDataset("GDB.USA"));
Topology topology = (Topology) fdataset.getTopologyByName("GDB.USA_Topology");

The AddClass method is used to add a feature class to the topology, with the specified weight and ranks.

Non-simple feature classes, such as annotation, dimension and geometric network feature classes cannot be added to a Topology. Object classes or tables and versioned simple feature classes, cannot be added to a Topology. After a populated feature class is added to a topology that has already been validated, in whole or in part, the state of the topology will be changed and a dirty area corresponding to the extent of the feature class will be created. If an unpopulated feature class is added to a topology, the topology’s state will not change and no dirty area will be created. The AddClass method cannot be called on versioned topologies in ArcSDE, but can be called on non-versioned topologies in ArcSDE and topologies in a personal geodatabase.

Topologies support the IFeatureClassContainer interface that can be used to return the feature classes participating in the topology.

The Cache property returns a reference to the topology graph of the Topology. The topology graph can be used for working with topological primitives such as edges and nodes. For more information, see the discussion on the topology graph later in this section.

The ClusterTolerance property returns the tolerance that was specified when the topology was built. The ClusterTolerance of the topology cannot be changed. In order to modify the tolerance, the Topology must be deleted and rebuilt with the new ClusterTolerance.

The DirtyArea method returns the dirty area polygon of the topology. The DirtyArea property requires an IPolygon object as input. The IPolygon can correspond to the extent of the topology or a subset of the extent. If there is no dirty area intersecting the specified area, an empty polygon is returned. The dirty area polygon that is returned may be a multipart polygon.

The following code example demonstrates how to obtain the DirtyArea for the entire Topology.


Polygon polygon = new Polygon();
polygon.setRectangle(topology.getExtent());
Polygon dirtyPoly = (Polygon) topology.getDirtyArea(polygon);

Each topology has a maximum number of errors that can be generated on Validate, which can be determined through the MaximumGeneratedErrorCount property. MaximumGeneratedErrorCount can only be specified when a Topology is created programmatically. All topologies created with the New Topology wizard in ArcCatalog have a MaximumGeneratedErrorCount of –1, indicating no limit to the number of errors that can be generated. As with the ClusterTolerance, the MaximumGeneratedErrorCount property cannot be changed, a topology must be deleted and rebuilt in order to specify a new value.

The RemoveClass method removes the specified class from the topology. While RemoveClass will remove any topology rules and errors associated with the class, it will not result in the creation of a dirty area, nor will the state of the topology change.

The State property indicates the current status of the Topology; whether the topology has been validated and if so, whether any topology errors have been discovered.

validateTopology validates the dirty area of the topology in the area specified by the areaToValidate envelope. validateTopology evaluates all the rules and produces any topology errors corresponding to areas in which a rule has been violated. The validateTopology function returns the envelope of the validated area. If an empty envelope is supplied, validateTopology will return an empty validated area. validateTopology can be performed outside of an edit session on topologies within personal geodatabases or on topologies within ArcSDE geodatabases that have not been registered as versioned. Once a topology is registered as versioned, validateTopology must be performed within an edit session and bracketed within an edit operation. The IWorkspaceEdit or IEditor interfaces can be used to manage edit sessions and edit operations.

An entire topology can be validated by supplying the envelope of the extent of the topology, demonstrated by the following code example.


topology.validateTopology(topology.getExtent());

By supplying a polygon object to the ITopology.getDirtyArea property, the dirty area at a particular location can be returned. The envelope of the returned dirty area polygon can then be passed into validateTopology.


Polygon polygon1 = new Polygon();
polygon.setRectangle(topology.getExtent());
Polygon dirtyPoly1 = (Polygon) topology.getDirtyArea(polygon1);
topology.validateTopology(dirtyPoly1.getEnvelope());

The ITopologyRuleContainer interface provides access to members for adding, removing and returning topology rules from a topology. This interface also provides access to members that control the promotion and demotion of topology errors and exceptions.

The isCanAddRule method returns a boolean value indicating if the topology rule is valid with respect to the existing rules. This method will return false if:

The addRule method is used for adding a new rule to a topology. The addition of the new rule results in a dirty area created for the extent of the entire topology and a change to the state of the topology to esriTSUnanalyzed.

The deleteRule method removes the specified rule from the topology, resulting in a dirty area created for the extent of the entire topology and a change to the state of the topology to esriTSUnanalyzed.

The demoteFromRuleException method will demote the specified exception from being an exception to an error feature. On a topology in an ArcSDE geodatabase, demoteFromRuleException, must be called from within an edit session and edit operation.

The promoteToRuleException method will promote an error feature from an error to an exception. On a topology in an ArcSDE geodatabase, promoteToRuleException, must be called from within an edit session and edit operation.


The ITopologyClass interface provides read-only access to the properties of feature classes in a Topology. Most of these properties are specified when the feature class is added to the Topology.

The ITopologyContainer interface can be used to manage and create topologies within a feature dataset. If your intention is to simply browse for the set of topologies in a feature dataset, it is not necessary to open the feature dataset and call the methods on ITopologyContainer. The IFeatureDatasetNames2.getTopologyNames method can be used to efficiently obtain this information. Careful consideration should be given to specifying the parameters when creating a topology. Once the topology is built, none of the parameters can be modified. In order to change properties such as cluster tolerance, the topology must be deleted and rebuilt with the new parameters.

The ITopologyProperties interface provides access to additional properties of a topology not supplied through the ITopology interface such the enumeration of feature classes and spatial reference of the Topology.

The ITopologyWorkspace interface provides access to the openTopology method that allows you open a topology within a workspace given the topology’s name. Use this interface to open a topology when you only have a reference to a workspace object. For ArcSDE geodatabases, the fully qualified name can be used to return topologies owned by specific users. If multiple topologies with the same name and owned by different users exist within the geodatabase, openTopology will return the topology owned by the connected user if an unqualified name is supplied.

The ITopologyErrorFeature interface provides access to the read-only properties of topology error features. Topology error features represent violations of topology rules and are discovered during the validation process. Error features cannot be edited directly, so while you can QueryInterface for interfaces such as IFeature on a TopoloyErrorFeature, calling methods such as IFeature.getValue or IFeature.store will fail. The only modification that can be made to a topology error feature is to mark it as an exception using the ITopologyRuleContainer.promoteToRuleException. Conversely, exceptions can be marked as an error by passing it as an argument to ITopologyRuleContainer.demoteFromRuleException.

The OriginID and DestinationID properties represent the object class IDs of the origin and destination feature classes of the topology rule that the error feature is a violation of. The OriginOID and DestinationOID properties represent the object IDs of the origin and destination features that created the topology error.

In general, all topology errors for which only the origin class has been specified will return values for the OriginID and OriginOID properties and a value of zero for the DestinationID and DestinationOID properties. The exception is for topology errors generated from the esriTRTNoGaps rule, which will return a value of zero for the Origin as well as Destination ID and OID properties. In addition, topology rules whose origin and destination feature class have been specified will generally return zero for the Destination feature class properties. The exceptions to this rule are:

The exception to both of these statements is the esriTRTAreaAreaCoverEachOther rule, which may generate topology errors referencing either the Origin or Destination feature class.

The ErrorID of a topology error feature is not unique across all topology error features within the topology, but is unique for each topology error feature geometry type. While a topology error feature with polygon geometry may have the same ErrorID as a topology error feature with point geometry, the ErrorID will be unique for all topology error features with polygon geometry. Combining the ErrorID and ShapeType of a topology error feature will result in a unique value within the topology.

The TopologyRule class is used to define the permissible spatial relationships between features within a topology. Topology rules can be defined with a single feature class or between two feature classes. Topology rules can also be defined to the subtype level of a feature class. Topology rules have an origin and a destination feature class, either of which can be set to the subtype level. Depending on the type of topology rule that is implemented, the destination feature class properties may be irrelevant.

The ITopologyRule is the main interface for creating and returning information about a topology rule. Use this interface to create a new topology rule or return the properties of existing rules.

The isAllDestinationSubtypes property specifies if the rule applies to all subtypes in the destination feature class. By default isAllDestinationSubtypes is False and the DestinationSubtype property (get/set) points to the default subtype. If isAllDestinationSubtypes is explicitly set to False, the DestinationSubtype property (get/set) must be set or the rule will be invalid. isAllDestinationSubtypes returns the opposite value of the isDestinationSubtypeSpecified property. If the topology rule is a single feature class rule, isAllDestinationSubtypes is set to True once the rule is added to the Topology.

isAllOriginSubtypes specifies if the rule applies to all subtypes in the origin feature class. By default isAllOriginSubtypes is False and the OriginSubtype property (get/set) points to the default subtype. If isAllOriginSubtypes is explicitly set to False, the OriginSubtype property must be set or the rule will be invalid. isAllOriginSubtypes returns the opposite value of the isOriginSubtypeSpecified property.

The DestinationClassID property (get/set) corresponds to the ObjectClassID of the destination feature class of the topology rule. If the topology rule is a single feature class rule, the DestinationClassID does not need to be set, it will be equal to the OriginClassID by default and if set, will be ignored when the rule is added to the Topology. The OriginClassID property corresponds to the ObjectClassID of the origin feature class of the topology rule. Every topology rule will have an OriginClassID.

OriginSubtype and DestinationSubtype correspond to the origin and destination subtypes of the topology rule. Setting the OriginSubtype or DestinationSubtype will result in the corresponding SubtypeSpecified property returning True. If AllOriginSubtypes or AllDestinationSubtypes is set to True, the value specified for OriginSubtype or DestinationSubtype will be ignored. The ErrorShapeTypes method indicates the error shape types that can be produced by the topology rule. If the rule supports a particular shape type, it will return a value of True.

The GUID property is read-only and returns the unique, geodatabase-controlled value that is set when the rule is added to the topology using ITopologyRuleContainer.addRule. The GUID value can be used to return its associated topology rule using ITopologyRuleContainer.getRulebyGUID. The Name property can be used to assign a user specified string to each rule. By default, the Name property is empty.

The TopologyRuleType property is used to return or set the type of topology rule from the esriTopologyRuleType constants. Every topology has one inherent rule, the esriTRTFeatureLargerThanClusterTolerance rule, which is added implicitly when the topology is created.

As mentioned previously, the Cache property on ITopology returns a reference to the topology graph of the Topology. The topology graph can be used for working with topological primitives such as edges and nodes.

A topology graph is a cached planar representation of the feature geometries participating in a topology, where the topological relationships are known. There is only one graph per topology. It is derived from the Topology object and cannot be cocreated. The TopologyGraph object was designed to allow editing of a topology without breaking adjacency or connectivity of the participating features, also known as “Shared Editing”. For example, TopologyGraph can be used to edit the shared boundaries of two polygons without breaking the adjacency of their geometries. Additionally, use it to move a set of overlapping routes while keeping all features coincident. There are a number of properties and methods that can be used on the TopologyGraph object and its components to:

  • Navigate through it
  • Modify the geometry of the topology elements
  • Discover the relationship between features
  • Modify the connectivity between features
  • A TopologyGraph object is accessed from a Topology object and is constructed for a given extent. It references the topology primitives that are cached. The topology primitive geometries present in memory have been modified, via cracking and clustering, during a round trip to the topology engine. However, it is important to understand that these geometries are not updated on disk yet. They are updated only if the topology elements are modified and those edits are posted back to disk. For example, one could programmatically move a topology node, which is shared by several features. This edit is cached in memory until the ITopologyGraph.post method is called. That method will propagate the edit to all the features sharing that topology node; the geometries of those features will be updated.

    The TopologyGraph object is derived from a Topology object and is a singleton. As previously described, you can access a topology's topology graph using the ITopology.getCache method. The topology graph is empty initially and must be built to contain the topology primitives.

    For a given topology, the topology graph is built on demand with a user-defined envelope bounding its included features. At construction time, the feature geometries are sent to the topology engine, where they are cracked and clustered using the cluster tolerance for the topology. Cracking and clustering is a process where vertices are introduced at each feature intersection and the new and existing vertices are grouped into clusters. This process is necessary to remove ambiguities between features and discover their topological relationships. After cracking and clustering, the features are, at minimum, separated by the cluster tolerance of the given topology. For a second time, the topological relationships between features are discovered and sent back to the client code. The topology primitives (geometry and topological relationships) are persisted in memory for further uses.

    There are two types of topology primitives, or elements, that are cached—nodes and edges. A TopologyNode is an object holding a reference to an IPoint geometry. It is created at each feature's endpoints as well as at the intersections of feature geometries participating in the topology. The node also contains topological relationships information, such as incident TopologyEdges and corresponding TopologyParents. A TopologyParent is a pointer to a feature class coupled with a feature OID. This couple uniquely identifies a feature part of the topology.

    A TopologyEdge is defined as an object holding a reference to an IPolyline geometry (always single part) formed between two TopologyNodes. The edge also references the topological relationships information, such as FromTopologyNode, ToTopologyNode, and TopologyParents. The TopologyEdge also allows its left and right polygons to be identified when appropriate. At any time the relationship between nodes, edges, and features can be accessed using the TopologyGraph API. The diagrams below give representations of the TopologyGraph object.

    In this diagram, two polyline features are used to construct the topology graph. The TopologyGraph is persisted in memory and contains nine topology primitives: five nodes and four edges. Each TopologyNode knows which features were used to construct it; those features are called TopologyParents. For example, the parents of the center TopologyNode are the two polyline features. The TopologyNode also knows which TopologyEdges are connected to it. The edges, in turn, are aware of their TopologyParents, as well as the FromTopologyNode and the ToTopologyNode.

    In this diagram, a polyline feature overlaps the boundaries of two adjacent polygon features. In this case, the TopologyGraph contains five topology primitives: two nodes and three edges. Each TopologyNode has three TopologyParents—the polyline and both polygons. The black arrow shows the orientation of the polyline feature. The red arrows show the orientation of the TopologyEdges. For example, the central TopologyEdge has three parents, the polyline and both polygons. Using this TopologyEdge and the ITopologyEdge.getLeftParents or ITopologyEdge.getRightParents method, you can access the blue and green polygons. The orientation of the edge determines which polygon is the left or right parent. A TopologyEdge also knows which TopologyNodes are associated with it. You could use this information to navigate in the TopologyGraph. A topology element can have 1-N TopologyParents. The parents are, in fact, the features that share geometries at a given location.

    The ITopologyGraph interface exposes several methods for interacting with the elements of a topology and, therefore, the underlying features participating in the topology. You can access all or selected elements of a TopologyGraph. To access:

    Once you’ve accessed the topology elements, you can work with their associated topological relationships. Three methods are available. To retrieve

    As discussed earlier, the features associated with a topology element are known as its TopologyParents. Three methods are available providing access to:

    Topology elements can be moved or transformed in a variety of ways. The methods used vary according to the type of transformation. If you are