Skip to content

ProConcepts Topology

UmaHarano edited this page May 6, 2024 · 5 revisions

The Topology API details the workflow for obtaining topologies and their metadata from a geodatabase.

Language:      C#
Subject:       Geodatabase
Contributor:   ArcGIS Pro SDK Team <arcgisprosdk@esri.com>
Organization:  Esri, http://www.esri.com
Date:          04/04/2024
ArcGIS Pro:    3.3
Visual Studio: 2022

In this topic


Abstract

The ArcGIS.Core.Data.Topology namespace provides functionality to work with geodatabase and feature service topologies, including mechanisms to access topology metadata such as rules and errors, validate a topology and perform analytic operations via a topology graph.

Almost all of the methods in the Topology API should be called on the Main CIM Thread (MCT). The API reference documentation on the methods that need to run on the MCT are specified as such. These method calls should be wrapped inside the QueuedTask.Run call. Failure to do so will result in ConstructedOnWrongThreadException being thrown. See Working with multithreading in ArcGIS Pro to learn more.

NOTE: This API doesn’t support topology-related DDL operations (such as adding a new feature class to participate in the topology or adding/removing a topology rule).

Introduction

In geodatabases, a topology is an arrangement that defines how point, line, and polygon features share coincident geometry. Topologies provide a mechanism to perform integrity checks on data and help validate and maintain better feature representations in a geodatabase.

In addition, topologies can be used to model spatial relationships between features. These enable support for various analytic operations, such as finding adjacent features, working with coincident boundaries between features, and navigating along connected features. A typical example of topology is a feature dataset containing public roads, land parcels, and buildings as polygon feature classes and rules: parcels should not be allowed to overlap, neither should public roads and parcels, and buildings should be contained within a parcel. Topologies define these rules and provide validation tools to identify features that violate the rules. Features are explicitly allowed to break the rules through exception settings.

Topologies are implemented as controller datasets in the geodatabase. A feature dataset is used to create a controller dataset, and the feature classes that are to be included in a controller dataset are contained within the feature dataset. Topologies can have multiple feature classes in the same topological role. A feature dataset can have numerous topologies, but a feature class can only belong to one topology. The utility network, network dataset, or parcel fabric are a few examples of other controller datasets.

Map Topology and geodatabase topology differ in the fact that map topology operates on all features in the layers in a map, whereas choosing a geodatabase topology rule limits topological editing to features participating in the selected rule. You can find a sample related to map topology graphs here.

Once the topology has been created with all the feature classes and rules, the topology can be validated. This is not required, but until doing so, the entire topology will be covered by a dirty area and the topological relationships of the features cannot be guaranteed. The areas where the spatial integrity of the topology have not been validated are known as dirty areas.

Topology

The Topology class provides an abstraction of the controller dataset, and the methods in this class provide an entry point to the other areas of the topology API.

As with other datasets in the geodatabase, a Topology object can be obtained by calling Geodatabase.OpenDataset(). The Geodatabase.OpenDataset() routine takes the name of a dataset to open.

public void OpenDataset(Geodatabase geodatabase)
{
    Topology topology = geodatabase.OpenDataset<Topology>("datasetName");
}

The Topology object can be obtained from a feature class as below:

public Topology GetTopologyFromFeatureClass(FeatureClass featureClass)
{
  IReadOnlyList<Dataset> controllerDatasets = featureClass.GetControllerDatasets();
  Topology topology = null;
  foreach (Dataset controllerDataset in controllerDatasets)
  {
    if (controllerDataset is Topology)
    {
      topology = controllerDataset as Topology;
    }
    else
    {
      controllerDataset.Dispose();
    }
  }
  return topology;
}

The topology also contains dirty areas and errors tables, see Validation, Errors, and Exceptions. The errors can be accessed using GetErrors() method.

Most of the methods in this class are described in the sections that follow. Additional methods are detailed below:

  • GetState() returns the state of topology. The states of topology are AnalyzedWithErrors, AnalyzedWithoutErrors, Empty, and Unanalyzed.
  • GetExtent() returns the maximum extent of the union of all the feature classes that participate in the topology. Note: Unlike a geodatabase topology whose extent dynamically reflects that of its constituent participating feature classes, a feature service topology’s extent remains static for the duration of the lifetime of the object. Therefore, a feature service topology’s GetExtent should not be used as input for ValidationDescription.Extent because the extent may have changed since it was opened.
  • GetErrors() returns a list of TopologyError objects as specified in ErrorDescription.

Topology Definition

The TopologyDefinition class provides metadata information about the topology dataset. It contains a schema record of all the properties specified during the topology creation, such as cluster tolerance, topology rules, and feature classes that participate in the topology.

Similar to other definition classes in the geodatabase API, the TopologyDefinition class can be accessed via the topology dataset itself or from the geodatabase.

  • Get the TopologyDefinition from the topology dataset – Usually this is used when the dataset is already open, and the reference is accessible

    using (Topology topology = geodatabase.OpenDataset<Topology>("datasetName"))
    using (TopologyDefinition definitionViaTopology = topology.GetDefinition())
    {
    
    }
  • Get the TopologyDefinition from a geodatabase – Usually this is used when it is not anticipated that the topology will be opened

    using (Geodatabase geodatabase = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri("topology // dataset // location"))))
    using (TopologyDefinition definitionViaGeodatabase = geodatabase.GetDefinition<TopologyDefinition>("topologyName"))
    {
    
    }

The methods of the TopologyDefinition class are described below:

  • GetClusterTolerance() returns the cluster tolerance of the topology. The cluster tolerance is the distance within which edges or vertices are determined to be coincident.
  • GetFeatureClassNames() returns the name of all the feature classes that participate in the topology.
  • GetRules() returns the set of TopologyRule objects defined for the topology.
  • GetZClusterTolernace() returns the Z cluster tolerance of the topology. The z–tolerance is used to distinguish the z–height or elevation of vertices within the tolerance of one another.

Rules

A topology rule defines the spatial relationship between features. The spatial relationship could be the relationship between features within a feature class, between features in different feature classes, or between features with different subtypes.

The topology rules are accessed from the topology definition by calling TopologyDefinition.GetRules().

using (TopologyDefinition topologyDefinition = topology.GetDefinition())
{
  IReadOnlyList<TopologyRule> rules = topologyDefinition.GetRules();

  foreach (TopologyRule rule in rules)
  {
    Console.WriteLine($@"{rule.ID} {rule.RuleType} {rule.OriginClass} {rule.OriginSubtype.GetName()}");
  }
}

The members of TopologyRule class are explained as:

  • DestinationClass specifies the name of the destination feature class to which this topology rule is assigned.
  • DestinationSubtype defines the Subtype in the destination feature class.
  • ID specifies the ID of the topology rule.
  • OriginClass specifies the name of the origin feature class to which this topology rule is assigned.
  • OriginSubtype defines the SubType in the origin feature class.
  • RuleType stipulates the type of this topology rule.

Validation, Errors, and Exceptions

Topology validation is the process of checking the features to identify any violations of the rules that have been defined for the topology. Dirty areas represent the areas where the spatial integrity of the topology has not been validated. The entire dataset is considered dirty when the topology is first created. Then, dirty areas are created whenever a feature’s geometry or subtype is modified or topology rules are added or removed. A topology validation operation also cleans up the dirty areas.

During the validation process, a series of spatial tasks identify shared features and maintain coincidence between adjacent features. The procedure involves snapping features together within a cluster tolerance and testing for rule violations. Any feature that violates one or more topology rules will result in a topology error, which can be marked as an exception. For example, if a rule is defined on a parcel feature class so that it Must Not Have Gaps, and there is a gap between two parcels, the width of the gap and the size of the cluster tolerance determine if the error should be fixed automatically during validation, or if an error is created that must be marked as an exception or addressed differently.

The ValidateInEditOperation method on the Topology class ensures data integrity by validating the features in a topology against a set of topology rules for the area specified in ValidationDescription. The ValidationDescription class is used to describe the details of a ValidateInEditOperation operation, and it accepts an Envelope as a parameter.

If writing a CoreHost application, use the Validate() base method on the Topology class. This routine provides its own transaction management; therefore, it cannot be issued inside an edit operation created by Geodatabase.ApplyEdits().

Using the Asynchronous Service

When called on a feature service, the ValidateInEditOperation method makes a call to a REST endpoint, which runs synchronously. This routine has low processing overhead. Unfortunately, it is also susceptible to server timeouts if processing takes too long. To work around this, the Validate REST endpoint provides an asynchronous version of this routine. Processing is offloaded from to the Geoprocessing Server. While this does cause a performance hit, the Geoprocessing Server is designed to handle long-running jobs and is not susceptible to timeouts. With this service, ArcGIS Pro will poll the service until the operation completes. Because many evaluation calls run against a large number of features, the asynchronous version is the default.

The ServiceSynchronizationType parameter on TopologyValidationDescription allows control over which type of the service is called. This uses the ServiceSynchronizationType enum, which has two values: Synchronous and Asynchronous. It's important to note that from a C# developer's perspective, this routine is still a synchronous C# method, regardless of this parameter. Like other methods in the API, it blocks the current thread until the operation completes.

Validation Results

A ValidationResult describes the result from the validate operation. Areas that violate the topology rules result in topology errors, and these errors need to be fixed.

If a topology does not have dirty areas, calling the validate method returns an empty envelope

ValidationResult result = topology.Validate(new ValidationDescription(topology.GetExtent()));
if (result.AffectedArea.IsEmpty)
{
  // Topology has not been edited to create dirty areas
}

This code example shows how to create a new feature in the feature class to make a dirty area that violates the PointProperlyInsideArea topology rule:

Feature feature = GetFeature("feature class name", “OID”);
Geometry currentGeometry = feature.GetShape();
Geometry newGeometry = GeometryEngine.Instance.Move(currentGeometry, (currentGeometry.Extent.XMax / 8), (currentGeometry.Extent.YMax / 8));

using (FeatureClass featureClass = feature.GetTable())
using (FeatureClassDefinition definition = featureClass.GetDefinition())
using (RowBuffer rowBuffer = featureClass.CreateRowBuffer())
{
  rowBuffer[definition.GetShapeField()] = newGeometry;

  geodatabase.ApplyEdits(() =>
  {
    featureClass.CreateRow(rowBuffer);
  });
}

After creating a new feature, the topology’s state should be “Unanalyzed” because it has not been validated:

TopologyState topologyState = topology.GetState(); // Unanalyzed

Validate the topology:

// Validate from CoreHost
ValidationResult result = topology.Validate(new ValidationDescription(topology.GetExtent()));

// Alternatively, validate from an ArcGIS Pro add-in
// ValidationResult result = topology.ValidateInEditOperation(new ValidationDescription(topology.GetExtent()));

if (!result.AffectedArea.IsEmpty)
{
  // After Validate(), the topology’s state should be “AnalyzedWithErrors” because the topology currently has errors.
  topologyState = topology.GetState();
}

Errors

The ErrorDescription describes how topology errors should be retrieved from an area of interest defined in the Extent. The ErrorType specifies the type of TopologyError to be retrieved, and the RuleType defines the type of TopologyRule that is violated by the features.

Get all the errors and exceptions due to features violating the PointProperlyInsideArea topology rule by calling Topology.GetErrors()

TopologyRule pointProperlyInsideAreaRule = topology.GetDefinition().GetRules().First(rule => rule.RuleType == TopologyRuleType.PointProperlyInsideArea);

ErrorDescription errorDescription = new ErrorDescription(topology.GetExtent())
{
  TopologyRule = pointProperlyInsideAreaRule
};

IReadOnlyList<TopologyError> errorsDueToViolatingPointProperlyInsideAreaRule = topology.GetErrors(errorDescription);

Mark all errors from features violating the PointProperlyInsideArea topology rule as exceptions using MarkAsExceptionInEditOperation() method (Use the base Topology.MarkAsException() routine only in CoreHost applications)

foreach (TopologyError error in errorsDueToViolatingPointProperlyInsideAreaRule)
{
  topology.MarkAsException(error);

  // Alternatively, mark exception from the ArcGIS Pro add-in
  // topology.MarkAsExceptionInEditOperation(error);
}

By default, ErrorDescription is initialized to ErrorType.ErrorAndException. Here we want ErrorType.ErrorOnly by skipping the exceptions

ErrorDescription errorDescription = new ErrorDescription(topology.GetExtent())
{
  ErrorType = ErrorType.ErrorOnly,
  TopologyRule = pointProperlyInsideAreaRule
};

IReadOnlyList<TopologyError> errorsAfterMarkedAsExceptions = topology.GetErrors(errorDescription);

Reset all the exceptions as errors by un-marking them as exceptions using UnmarkAsExceptionInEditOperation() method (again, in CoreHost use the core Topology.UnmarkAsException routine).

foreach (TopologyError error in errorsDueToViolatingPointProperlyInsideAreaRule)
{
  topology.UnmarkAsException(error);

  // Alternatively, unmark exception from the ArcGIS Pro add-in
  // topology.UnmarkAsExceptionInEditOperation(error);
}

Graph and Elements

Each geodatabase topology has one associated topology graph. The topology graph is an in-memory planar representation of the geometries in the feature classes participating in the topology. When a TopologyGraph is built via Topology.BuildGraph(), spatial relationships between features are discovered, analyzed, and established to form an in-memory graph of topological elements.

The lifetime of the TopologyGraph as input for callBack is scoped to that of the client callback itself. Once the execution flow has returned from the callback, the TopologyGraph object will be disposed, and attempting to use the TopologyGraph object outside of the callback scope will raise an ArcGIS.Core.ObjectDisconnectedException.

A TopologyElement can be either a TopologyNode or a TopologyEdge, corresponding to points and lines in the feature space. The topology graph can be traversed by iterating through the connectivity between the topology edges and topology nodes.

Build a topology graph using the extent of the topology dataset by calling Topology.BuildGraph()

// Get a feature from the feature class that participated in topology using an ObjectID
Feature feature = GetFeature("feature class name", ObjectID);

// Build a topology graph using the extent of the topology dataset
topology.BuildGraph(topology.GetExtent(), (topologyGraph) =>
{
  // Get all topology edges via the feature 
  IReadOnlyList<TopologyEdge> allTopologyEdgesViaFeature = topologyGraph.GetEdges(feature);

  // Get all topology nodes via the feature 
  IReadOnlyList<TopologyNode> allTopologyNodesViaFeature = topologyGraph.GetNodes(feature);
  TopologyNode topologyNodeViaFeature = allTopologyNodesViaFeature[0];

  // Get all edges connected to node in clockwise order via a feature 
  IReadOnlyList<TopologyEdge> allEdgesConnectedToNodeViaFeature = topologyNodeViaFeature.GetEdges();

  // Get all edges connected to node in counterclockwise order via a feature 
  IReadOnlyList<TopologyEdge> allEdgesConnectedToNodeViaFeatureCounterClockwise = topologyNodeViaFeature.GetEdges(false);
});

Iterates through the topology edges to access topology nodes and parent features

foreach (TopologyEdge edgeConnectedToNodeViaFeature in allEdgesConnectedToNodeViaFeature)
{
  // From and to nodes from a topology edge
  TopologyNode fromNode = edgeConnectedToNodeViaFeature.GetFromNode();
  TopologyNode toNode = edgeConnectedToNodeViaFeature.GetToNode();

  // Left and right parent features bounded by the edge
  IReadOnlyList<FeatureInfo> leftParentFeaturesBoundedByEdge = edgeConnectedToNodeViaFeature.GetLeftParentFeatures();
  IReadOnlyList<FeatureInfo> rightParentFeaturesBoundedByEdge = edgeConnectedToNodeViaFeature.GetRightParentFeatures();

  // Left and right parent features not bounded by the edge
  IReadOnlyList<FeatureInfo> leftParentFeaturesNotBoundedByEdge = edgeConnectedToNodeViaFeature.GetLeftParentFeatures(false);
  IReadOnlyList<FeatureInfo> rightParentFeaturesNotBoundedByEdge = edgeConnectedToNodeViaFeature.GetRightParentFeatures(false);
}

The FindClosestElement<T> method discovers a topological element in the topology graph closest to the location specified by MapPoint and within the search radius.

// Topology node nearest to the query point within specified search radius of 1 unit
TopologyNode topologyNodeWithInSearchRadius = topologyGraph.FindClosestElement<TopologyNode>(feature.GetShape() as MapPoint, 1.0);

// Topology edge nearest to the query point within specified search radius of 2 units
TopologyEdge topologyEdgeWithInSearchRadius = topologyGraph.FindClosestElement<TopologyEdge>(feature.GetShape() as MapPoint, 2.0);

Developing with ArcGIS Pro

    Migration


Framework

    Add-ins

    Configurations

    Customization

    Styling


Arcade


Content


CoreHost


DataReviewer


Editing


Geodatabase

    3D Analyst Data

    Plugin Datasources

    Topology

    Object Model Diagram


Geometry

    Relational Operations


Geoprocessing


Knowledge Graph


Layouts

    Reports


Map Authoring

    3D Analyst

    CIM

    Graphics

    Scene

    Stream

    Voxel


Map Exploration

    Map Tools


Networks

    Network Diagrams


Parcel Fabric


Raster


Sharing


Tasks


Workflow Manager Classic


Workflow Manager


Reference

Clone this wiki locally