staticvoidOpenTopologyAndProcessDefinition(){// Open a geodatabase topology from a file geodatabase and process the topology definition.using(Geodatabasegeodatabase=newGeodatabase(newFileGeodatabaseConnectionPath(newUri(@"C:\TestData\GrandTeton.gdb"))))using(ArcGIS.Core.Data.Topology.Topologytopology=geodatabase.OpenDataset<ArcGIS.Core.Data.Topology.Topology>("Backcountry_Topology")){ProcessDefinition(geodatabase,topology);}// Open a feature service topology and process the topology definition.conststringTOPOLOGY_LAYER_ID="0";using(Geodatabasegeodatabase=newGeodatabase(newServiceConnectionProperties(newUri("https://sdkexamples.esri.com/server/rest/services/GrandTeton/FeatureServer"))))using(ArcGIS.Core.Data.Topology.Topologytopology=geodatabase.OpenDataset<ArcGIS.Core.Data.Topology.Topology>(TOPOLOGY_LAYER_ID)){ProcessDefinition(geodatabase,topology);}staticvoidProcessDefinition(Geodatabasegeodatabase,ArcGIS.Core.Data.Topology.Topologytopology){// Similar to the rest of the Definition objects in the Core.Data API, there are two ways to open a dataset's // definition -- via the Topology dataset itself or via the Geodatabase.using(TopologyDefinitiondefinitionViaTopology=topology.GetDefinition()){OutputDefinition(geodatabase,definitionViaTopology);}using(TopologyDefinitiondefinitionViaGeodatabase=geodatabase.GetDefinition<TopologyDefinition>("Backcountry_Topology")){OutputDefinition(geodatabase,definitionViaGeodatabase);}}staticvoidOutputDefinition(Geodatabasegeodatabase,TopologyDefinitiontopologyDefinition){Console.WriteLine($"Topology cluster tolerance => {topologyDefinition.GetClusterTolerance()}");Console.WriteLine($"Topology Z cluster tolerance => {topologyDefinition.GetZClusterTolerance()}");IReadOnlyList<string>featureClassNames=topologyDefinition.GetFeatureClassNames();Console.WriteLine($"There are {featureClassNames.Count} feature classes that are participating in the topology:");foreach(stringnameinfeatureClassNames){// Open each feature class that participates in the topology.using(FeatureClassfeatureClass=geodatabase.OpenDataset<FeatureClass>(name))using(FeatureClassDefinitionfeatureClassDefinition=featureClass.GetDefinition()){Console.WriteLine($"\t{featureClass.GetName()} ({featureClassDefinition.GetShapeType()})");}}}}
Get topology rules
staticvoidGetTopologyRules(){using(Geodatabasegeodatabase=newGeodatabase(newFileGeodatabaseConnectionPath(newUri(@"C:\TestData\GrandTeton.gdb"))))using(ArcGIS.Core.Data.Topology.Topologytopology=geodatabase.OpenDataset<ArcGIS.Core.Data.Topology.Topology>("Backcountry_Topology")){using(TopologyDefinitiontopologyDefinition=topology.GetDefinition()){IReadOnlyList<TopologyRule>rules=topologyDefinition.GetRules();Console.WriteLine($"There are {rules.Count} topology rules defined for the topology:");Console.WriteLine("ID \t Origin Class \t Origin Subtype \t Destination Class \t Destination Subtype \t Rule Type");foreach(TopologyRuleruleinrules){Console.Write($"{rule.ID}");Console.Write(!String.IsNullOrEmpty(rule.OriginClass)?$"\t{rule.OriginClass}":"\t\"\"");Console.Write(rule.OriginSubtype!=null?$"\t{rule.OriginSubtype.GetName()}":"\t\"\"");Console.Write(!String.IsNullOrEmpty(rule.DestinationClass)?$"\t{rule.DestinationClass}":"\t\"\"");Console.Write(rule.DestinationSubtype!=null?$"\t{rule.DestinationSubtype.GetName()}":"\t\"\"");Console.Write($"\t{rule.RuleType}");Console.WriteLine();}}}}
Validate topology
staticvoidValidateTopology(){using(Geodatabasegeodatabase=newGeodatabase(newFileGeodatabaseConnectionPath(newUri(@"C:\TestData\GrandTeton.gdb"))))using(ArcGIS.Core.Data.Topology.Topologytopology=geodatabase.OpenDataset<ArcGIS.Core.Data.Topology.Topology>("Backcountry_Topology")){// If the topology currently does not have dirty areas, calling Validate() returns an empty envelope.ValidationResultresult=topology.Validate(newValidationDescription(topology.GetExtent()));Console.WriteLine($"'AffectedArea' after validating a topology that has not been edited => {result.AffectedArea.ToJson()}");// Now create a feature that purposely violates the "PointProperlyInsideArea" topology rule. This action will// create dirty areas.FeaturenewFeature=null;try{// Fetch the feature in the Campsites feature class whose objectID is 2. Then create a new geometry slightly// altered from this and use it to create a new feature.using(FeaturefeatureViaCampsites2=GetFeature(geodatabase,"Campsites",2)){GeometrycurrentGeometry=featureViaCampsites2.GetShape();GeometrynewGeometry=GeometryEngine.Instance.Move(currentGeometry,(currentGeometry.Extent.XMax/8),(currentGeometry.Extent.YMax/8));using(FeatureClasscampsitesFeatureClass=featureViaCampsites2.GetTable())using(FeatureClassDefinitiondefinition=campsitesFeatureClass.GetDefinition())using(RowBufferrowBuffer=campsitesFeatureClass.CreateRowBuffer()){rowBuffer[definition.GetShapeField()]=newGeometry;geodatabase.ApplyEdits(()=>{newFeature=campsitesFeatureClass.CreateRow(rowBuffer);});}}// After creating a new feature in the 'Campsites' participating feature class, the topology's state should be // "Unanalyzed" because it has not been validated.Console.WriteLine($"The topology state after an edit has been applied => {topology.GetState()}");// Now validate the topology. The result envelope corresponds to the dirty areas.result=topology.Validate(newValidationDescription(topology.GetExtent()));Console.WriteLine($"'AffectedArea' after validating a topology that has just been edited => {result.AffectedArea.ToJson()}");// After Validate(), the topology's state should be "AnalyzedWithErrors" because the topology currently has errors.Console.WriteLine($"The topology state after validate topology => {topology.GetState()}");// If there are no dirty areas, the result envelope should be empty.result=topology.Validate(newValidationDescription(topology.GetExtent()));Console.WriteLine($"'AffectedArea' after validating a topology that has just been validated => {result.AffectedArea.ToJson()}");}finally{if(newFeature!=null){geodatabase.ApplyEdits(()=>{newFeature.Delete();});newFeature.Dispose();}}// Validate again after deleting the newly-created feature.topology.Validate(newValidationDescription(topology.GetExtent()));}}
Process topology errors
staticvoidProcessTopologyErrors(){using(Geodatabasegeodatabase=newGeodatabase(newFileGeodatabaseConnectionPath(newUri(@"C:\TestData\GrandTeton.gdb"))))using(ArcGIS.Core.Data.Topology.Topologytopology=geodatabase.OpenDataset<ArcGIS.Core.Data.Topology.Topology>("Backcountry_Topology")){// Get all the errors and exceptions currently associated with the topology.IReadOnlyList<TopologyError>allErrorsAndExceptions=topology.GetErrors(newErrorDescription(topology.GetExtent()));Console.WriteLine($"errors and exceptions count => {allErrorsAndExceptions.Count}");Console.WriteLine("OriginClassName \t OriginObjectID \t DestinationClassName \t DestinationObjectID \t RuleType \t IsException \t Shape type \t Shape width & height \t Rule ID \t");foreach(TopologyErrorerrorinallErrorsAndExceptions){Console.WriteLine($"'{error.OriginClassName}' \t{error.OriginObjectID}\t '{error.DestinationClassName}' \t "+$"{error.DestinationObjectID}\t{error.RuleType}\t{error.IsException}\t{error.Shape.GeometryType}\t "+$"{error.Shape.Extent.Width},{error.Shape.Extent.Height}\t{error.RuleID}");}}}
Mark and unmark errors or exceptions
staticvoidMarkAndUnmarkAsErrors(ArcGIS.Core.Data.Topology.Topologytopology){// Get all the errors due to features violating the "PointProperlyInsideArea" topology rule.using(TopologyDefinitiontopologyDefinition=topology.GetDefinition()){TopologyRulepointProperlyInsideAreaRule=topologyDefinition.GetRules().First(rule =>rule.RuleType==TopologyRuleType.PointProperlyInsideArea);ErrorDescriptionerrorDescription=newErrorDescription(topology.GetExtent()){TopologyRule=pointProperlyInsideAreaRule};IReadOnlyList<TopologyError>errorsDueToViolatingPointProperlyInsideAreaRule=topology.GetErrors(errorDescription);Console.WriteLine($"There are {errorsDueToViolatingPointProperlyInsideAreaRule.Count} feature violating the 'PointProperlyInsideArea' topology rule.");// Mark all errors from features violating the 'PointProperlyInsideArea' topology rule as exceptions.foreach(TopologyErrorerrorinerrorsDueToViolatingPointProperlyInsideAreaRule){topology.MarkAsException(error);}// Now verify all the errors from features violating the 'PointProperlyInsideArea' topology rule have indeed been// marked as exceptions.//// By default, ErrorDescription is initialized to ErrorType.ErrorAndException. Here we want ErrorType.ErrorOnly.errorDescription=newErrorDescription(topology.GetExtent()){ErrorType=ErrorType.ErrorOnly,TopologyRule=pointProperlyInsideAreaRule};IReadOnlyList<TopologyError>errorsAfterMarkedAsExceptions=topology.GetErrors(errorDescription);Console.WriteLine($"There are {errorsAfterMarkedAsExceptions.Count} feature violating the 'PointProperlyInsideArea' topology rule after all the errors have been marked as exceptions.");// Finally, reset all the exceptions as errors by unmarking them as exceptions.foreach(TopologyErrorerrorinerrorsDueToViolatingPointProperlyInsideAreaRule){topology.UnmarkAsException(error);}IReadOnlyList<TopologyError>errorsAfterUnmarkedAsExceptions=topology.GetErrors(errorDescription);Console.WriteLine($"There are {errorsAfterUnmarkedAsExceptions.Count} feature violating the 'PointProperlyInsideArea' topology rule after all the exceptions have been reset as errors.");}}
Explore topology graph
staticvoidExploreTopologyGraph(){using(Geodatabasegeodatabase=newGeodatabase(newFileGeodatabaseConnectionPath(newUri(@"C:\TestData\GrandTeton.gdb"))))using(ArcGIS.Core.Data.Topology.Topologytopology=geodatabase.OpenDataset<ArcGIS.Core.Data.Topology.Topology>("Backcountry_Topology")){// Build a topology graph using the extent of the topology dataset.topology.BuildGraph(topology.GetExtent(),(topologyGraph)=>{using(Featurecampsites12=GetFeature(geodatabase,"Campsites",12)){IReadOnlyList<TopologyNode>topologyNodesViaCampsites12=topologyGraph.GetNodes(campsites12);TopologyNodetopologyNodeViaCampsites12=topologyNodesViaCampsites12[0];IReadOnlyList<TopologyEdge>allEdgesConnectedToNodeViaCampsites12=topologyNodeViaCampsites12.GetEdges();IReadOnlyList<TopologyEdge>allEdgesConnectedToNodeViaCampsites12CounterClockwise=topologyNodeViaCampsites12.GetEdges(false);foreach(TopologyEdgeedgeConnectedToNodeViaCampsites12inallEdgesConnectedToNodeViaCampsites12){TopologyNodefromNode=edgeConnectedToNodeViaCampsites12.GetFromNode();TopologyNodetoNode=edgeConnectedToNodeViaCampsites12.GetToNode();boolfromNodeIsTheSameAsTopologyNodeViaCampsites12=(fromNode==topologyNodeViaCampsites12);booltoNodeIsTheSameAsTopologyNodeViaCampsites12=(toNode==topologyNodeViaCampsites12);IReadOnlyList<FeatureInfo>leftParentFeaturesBoundedByEdge=edgeConnectedToNodeViaCampsites12.GetLeftParentFeatures();foreach(FeatureInfofeatureInfoinleftParentFeaturesBoundedByEdge){EnsureShapeIsNotEmpty(featureInfo);}IReadOnlyList<FeatureInfo>leftParentFeaturesNotBoundedByEdge=edgeConnectedToNodeViaCampsites12.GetLeftParentFeatures(false);foreach(FeatureInfofeatureInfoinleftParentFeaturesNotBoundedByEdge){EnsureShapeIsNotEmpty(featureInfo);}IReadOnlyList<FeatureInfo>rightParentFeaturesBoundedByEdge=edgeConnectedToNodeViaCampsites12.GetRightParentFeatures();foreach(FeatureInfofeatureInfoinrightParentFeaturesBoundedByEdge){EnsureShapeIsNotEmpty(featureInfo);}IReadOnlyList<FeatureInfo>rightParentFeaturesNotBoundedByEdge=edgeConnectedToNodeViaCampsites12.GetRightParentFeatures(false);foreach(FeatureInfofeatureInfoinrightParentFeaturesNotBoundedByEdge){EnsureShapeIsNotEmpty(featureInfo);}}}});}staticvoidEnsureShapeIsNotEmpty(FeatureInfofeatureInfo){using(Featurefeature=featureInfo.GetFeature()){// Process feature ...}}}
Find closest element from a topology graph
staticvoidFindClosestElement(){using(Geodatabasegeodatabase=newGeodatabase(newFileGeodatabaseConnectionPath(newUri(@"C:\TestData\GrandTeton.gdb"))))using(ArcGIS.Core.Data.Topology.Topologytopology=geodatabase.OpenDataset<ArcGIS.Core.Data.Topology.Topology>("Backcountry_Topology")){// Build a topology graph using the extent of the topology dataset.topology.BuildGraph(topology.GetExtent(),(topologyGraph)=>{MapPointqueryPointViaCampsites12=null;using(Featurecampsites12=GetFeature(geodatabase,"Campsites",12)){queryPointViaCampsites12=campsites12.GetShape()asMapPoint;}doublesearchRadius=1.0;TopologyElementtopologyElementViaCampsites12=topologyGraph.FindClosestElement<TopologyElement>(queryPointViaCampsites12,searchRadius);IReadOnlyList<FeatureInfo>parentFeatures=topologyElementViaCampsites12.GetParentFeatures();Console.WriteLine("The parent features that spawn 'topologyElementViaCampsites12' are:");foreach(FeatureInfoparentFeatureinparentFeatures){Console.WriteLine($"\t{parentFeature.FeatureClassName}; OID: {parentFeature.ObjectID}");}TopologyNodetopologyNodeViaCampsites12=topologyGraph.FindClosestElement<TopologyNode>(queryPointViaCampsites12,searchRadius);if(topologyNodeViaCampsites12!=null){// There exists a TopologyNode nearest to the query point within searchRadius units.}TopologyEdgetopologyEdgeViaCampsites12=topologyGraph.FindClosestElement<TopologyEdge>(queryPointViaCampsites12,searchRadius);if(topologyEdgeViaCampsites12!=null){// There exists a TopologyEdge nearest to the query point within searchRadius units.}});}}