ArcObject 实现拓扑创建和检查
拓扑(Topology)是在同一个要素集(FeatureDataset)下的要素类(Feature Class)之间的拓扑关系的集合。所以要参与一个拓扑的所有要素类,必须在同一个要素集内(也就是具有同一的空间参考),这样进行的拓扑检查才是精确的。一个要素集可以有多个拓扑,但每个要素类最多只能参与一个拓扑,一个拓扑中可以定义多个规则。
进行拓扑分析的过程:
1 建立拓扑(添加拓扑规则)
2 验证拓扑
3 编辑过程中保证拓扑的正确
4 查询系统中存在的拓扑
1 建立拓扑的方法:
(1)利用 ArcCatalog 桌面建立。
(2)使用程序(ArcEngine)开发建立拓扑。
在 Engine 中建立拓扑的实现接口是ITopologyContainer::CreateTopology
,ITopologyContainer::CreateTopology
方法用来创建拓扑。
接口说明:
ITopologyContainer
是用来创建、添加、管理拓扑的平台容器。通过本接口用户可以了解当前要素集的拓扑信息,如名称;如果用户仅出于得到要素集拓扑信息的目的,建议不用本接口打开拓扑,而使用IFeatureDatasetNames::TopologyNames
方法就可以快速获取。一旦使用CreateTopology
方法创建拓扑后,本拓扑的参数便不能更改,如想变更则需删除或重新建立新的拓扑。
DefaultClusterTolerance
是建立拓扑默认的“容差”。
MaximumClusterTolerance
表示最大“容差”。
MinimumClusterTolerance
表示最小“容差”。
如果用户在创建拓扑时使用的容差参数位于最大、最小之间 则参数有效,如位于最大与最小之间则按照极值(最大、最小)来处理;注意默认的容差是与最小容差一样的;
建立拓扑规则:
使用ITopologyRuleContainer
接口。
接口说明:
IRule
接口是Engine
中的定义规则的接口,其中它的Type
属性有以下几种:
建立拓扑规则我们将使用esriRTTopology
;并通过ITopologyRule
接口来实现拓扑规则。
并通过ITopologyRuleContainer
接口将所定义的规则添加到当前的拓扑中(AddRule方法)。
ITopology
接口和ITopologyRuleContainer
接口共同继承于Topology
类,所以可以QI。
(1)当要素集中的所有要素都已经参加建立其它拓扑的时候,使用已使用的要素类新建立拓扑会产生错误。
(2)当要素已参加网络分析(Geometry Network)运算的时候,建立拓扑也会出现错误。
(3)目标要素类是一个注记层或多维图层,不能建立拓扑。
(4)目标要素类已被注册为有版本,不能建立拓扑。
2 进行拓扑验证(validate)的方法:
(1)在ArcCatalog建立好拓扑后,可以选择直接验证。
(2)也可以在ArcMap中对建立好的拓扑进行验证(要素必须在编辑状态 Start Edit),然后可以进行拓扑的编辑。
(3)利用程序进行验证。
ITopology
有一个方法ValidateTopology
用来验证指定区域内的拓扑。需要注意 没有版本的拓扑可以在 任何时候验证。而有版本的拓扑必须在编辑回话中验证。
通过验证后,当前的拓扑就可以检查出相应的拓扑错误,并生成拓扑图层ITopologyLayer
,ITopologyLayer
其实也是一个layer
,继承与layer
类。
接口说明:
ITopologyLayer
接口具有一个渲染Renderer
的方法,可以根据拓扑错误信息(esriTopologyRenderer)进行分类渲染。
3 拓扑编辑
当对拓扑分析验证后,就需要对当前的拓扑错误进行编辑更改。在进行拓扑编辑的时候,我们需要知道一个概念,就是TopologyGraph
。
什么是TopologyGraph呢?其实就是在进行拓扑分析后,我们要对错误的线、点(因为多边形也是有线和点组成的)进行编辑,来修改当前的要素错误。在编辑前要生成TopologyGraph拓扑图,在拓扑图上显示了需要修改的Nodes、Edges等信息,我们所做的编辑其实就是在TopologyGraph上进行的。上面我提到过ITopologyLayer,当ITopologyLayer建立后,其实在内存中就存在了Cache缓存。拓扑图TopologyGraph的建立就是需要这个ITopologyLayer的Cache来完成的,方法如下:
//topoLayer 是一个打开的拓扑图层
ITopologyGraph pTG=topoLayer.Topology.Cache;
pTG.Build(pA.Extent,false);建立拓扑图,其中pA.Extent是所要分析的范围;
接口说明:
在接口的属性方法中,我们可以看到很多对要素编辑的操作。那就对了,对拓扑编辑其实很大一部分是利用这个方法来完成的。如:切割、重建、删除等等。
HitTest
方法来获取所点击的Edges
、Nodes
。
ITopologyNode
和ITopologyEdge
获取边、点 同样实现相应的编辑操作。
ITopologyErrorFeature
接口可以获取 产生错误的要素Feature,它继承与Feature类。
ITopologyErrorSelection
接口操作当前TopologyLayer
层下的错误是否可用。
ITopologyExtension
是当前拓扑的扩展,包括可以设置错误要素的样式symbol等。
注意:本接口只能在 Desktop 许可下获取,Engine 不能应用。那么有人会问:“我们对拓扑图层、拓扑要素进行更改颜色,Engine就做不了了?”,当然不是,我前面提到过的ITopologyLayer
接口
利用这个接口我们同样可以对图层进行Renderer
渲染,具体操作请参考help帮助文档!topologyelements (Node and Edges)
当编辑结束后,我们就可以提交更改了。
ITopologyGraph:Post方法 提交所做的编辑。
部分编辑的代码:
Set pTopologyGraph = pTopology.Cache
pTopologyGraph.Build
pMxDoc.ActiveView.Extent, False
Set pEnumTopologyEdge = pTopologyGraph.Edges
Set pTopologyEdge = pEnumTopologyEdge.Next
bsel = pTopologyGraph.Select(esriTopologySelectionResultNew, pTopologyEdge)
Set pAffine = New AffineTransformation2DpAffine.Move 10, 10
pTopologyGraph.TransformSelection esriTransformForward, pAffine, False
pTopologyGraph.Post pGDset.Extent
提交编辑,这样整个拓扑建立、验证、编辑 整个流程就完成了。
如果我们想对这个拓扑进行查看 ,可以使用ITopologyWorkspace::OpenTopology
方法来打开拓扑。
附录:
一、拓扑数据图层(ItopologyLayer)中拓扑错误类型:作为图层可以进行renderer渲染,以下也是可以渲染得类型;
esriTRAreaErrors
0 Area Error renderer.
esriTRLineErrors
1 Line Error renderer.
esriTRPointErrors
2 Point Error renderer.
esriTRAreaExceptions
3 Polygon Exception renderer.
esriTRLineExceptions
4 Line Exception renderer.
esriTRPointExceptions 5 Point Exception renderer.
esriTRDirtyAreas
6 Dirty Area renderer
二、Geodatabase的Topology规则:
多边形topology规则
1.must not overlay:单要素类,多边形要素相互不能重叠
2.must not have gaps:单要素类,连续连接的多边形区域中间不能有空白区(非数据区)
3.contains point:多边形+点,多边形要素类的每个要素的边界以内必须包含点层中至少一个点
4.boundary must be covered by:多边形+线,多边形层的边界与线层重叠(线层可以有非重叠的更多要素)
5.must be covered by feature class of:多边形+多边形,第一个多边形层必须被第二个完全覆盖(省与全国的关系)
6.must be covered by:多边形+多边形,第一个多边形层必须把第二个完全覆盖(全国与省的关系)
7.must not overlay with:多边形+多边形,两个多边形层的多边形不能存在一对相互覆盖的要素
8.must cover each other:多边形+多边形,两个多边形的要素必须完全重叠
9.area boundary must be covered by boundary of:多边形+多边形,第一个多边形的各要素必须为第二个的一个或几个多边形完全覆盖
10.must be properly inside polygons:点+多边形,点层的要素必须全部在多边形内
11.must be covered by boundary of:点+多边形,点必须在多边形的边界上
线topology规则
1.must not have dangle:线,不能有悬挂节点
2.must not have pseudo-node:线,不能有伪节点
3.must not overlay:线,不能有线重合(不同要素间)
4.must not self overlay:线,一个要素不能自覆盖
5.must not intersect:线,不能有线交(不同要素间)
6.must not self intersect:线,不能有线自交
7.must not intersect or touch interrior:线,不能有相交和重叠
8.must be single part:线,一个线要素只能由一个path组成
9.must not covered with:线+线,两层线不能重叠
10.must be covered by feature class of:线+线,两层线完全重叠
11.endpoint must be covered by:线+点,线层中的终点必须和点层的部分(或全部)点重合
12.must be covered by boundary of:线+多边形,线被多边形边界重叠
13.must be covered by endpoint of:点+线,点被线终点完全重合
14.point must be covered by line:点+线,点都在线上
实现代码如下:
public void ValidateTopology(ITopology topology, IEnvelope envelope)
{
// Get the dirty area within the provided envelope.
IPolygon locationPolygon = new PolygonClass();
ISegmentCollection segmentCollection = (ISegmentCollection)locationPolygon;
segmentCollection.SetRectangle(envelope);
IPolygon polygon = topology.get_DirtyArea(locationPolygon);
// If a dirty area exists, validate the topology. if (!polygon.IsEmpty)
{ // Define the area to validate and validate the topology.
IEnvelope areaToValidate = polygon.Envelope;
IEnvelope areaValidated = topology.ValidateTopology(areaToValidate);
}
}
public void AddRuleToTopology(ITopology topology, esriTopologyRuleType ruleType,
String ruleName, IFeatureClass featureClass)
{
// Create a topology rule.
ITopologyRule topologyRule = new TopologyRuleClass();
topologyRule.TopologyRuleType = ruleType;
topologyRule.Name = ruleName;
topologyRule.OriginClassID = featureClass.FeatureClassID;
topologyRule.AllOriginSubtypes = true;
// Cast the topology to the ITopologyRuleContainer interface and add the rule.
ITopologyRuleContainer topologyRuleContainer = (ITopologyRuleContainer)topology;
if (topologyRuleContainer.get_CanAddRule(topologyRule))
{
topologyRuleContainer.AddRule(topologyRule);
}
else
{
throw new ArgumentException("Could not add specified rule to the topology.");
}
}
public void AddRuleToTopology(ITopology topology, esriTopologyRuleType ruleType,
String ruleName, IFeatureClass originClass, int originSubtype, IFeatureClass
destinationClass, int destinationSubtype)
{
// Create a topology rule.
ITopologyRule topologyRule = new TopologyRuleClass();
topologyRule.TopologyRuleType = ruleType;
topologyRule.Name = ruleName;
topologyRule.OriginClassID = originClass.FeatureClassID;
topologyRule.AllOriginSubtypes = false;
topologyRule.OriginSubtype = originSubtype;
topologyRule.DestinationClassID = destinationClass.FeatureClassID;
topologyRule.AllDestinationSubtypes = false;
topologyRule.DestinationSubtype = destinationSubtype;
// Cast the topology to the ITopologyRuleContainer interface and add the rule.
ITopologyRuleContainer topologyRuleContainer = (ITopologyRuleContainer)topology;
if (topologyRuleContainer.get_CanAddRule(topologyRule))
{
topologyRuleContainer.AddRule(topologyRule);
}
else
{
throw new ArgumentException("Could not add specified rule to the topology.");
}
}
// 按照arcToolbox 应该拆成两个方法,一个是CreateTopology 一个是AddFeatureClassToTopology
public void CreateTopology()
{
// Open the workspace and the required datasets.
Type factoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.AccessWorkspaceFactory");
IWorkspaceFactory workspaceFactory = new AccessWorkspaceFactory();
IWorkspace workspace = workspaceFactory.OpenFromFile(@"E:\123.mdb",0);
IFeatureWorkspace featureWorkspace = (IFeatureWorkspace)workspace;
IFeatureDataset featureDataset = featureWorkspace.OpenFeatureDataset("地籍信息_H_3207");
IFeatureClass DLJXPl = featureWorkspace.OpenFeatureClass("DLJX_H_3207");
IFeatureClass DLTBPg = featureWorkspace.OpenFeatureClass("DLTB_H_3207");
IFeatureClass QSZDPg = featureWorkspace.OpenFeatureClass("QSZD_H_3207");
IFeatureClass DXJZDPt = featureWorkspace.OpenFeatureClass("DXJZD_H_3207");
IFeatureClass DXJZXPl = featureWorkspace.OpenFeatureClass("DXJZX_H_3207");
IFeatureClass JBNTBHPKPg = featureWorkspace.OpenFeatureClass("JBNTBHPK_H_3207");
IFeatureClass JBNTBHTBPg = featureWorkspace.OpenFeatureClass("JBNTBHTB_H_3207");
IFeatureClass JBNTBHQPg = featureWorkspace.OpenFeatureClass("JBNTBHQ_H_3207");
IFeatureClass JZDPt = featureWorkspace.OpenFeatureClass("JZD_H_3207");
IFeatureClass JZXPl = featureWorkspace.OpenFeatureClass("JZX_H_3207");
// Attempt to acquire an exclusive schema lock on the feature dataset.
ISchemaLock schemaLock = (ISchemaLock)featureDataset;
try
{
schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);
// Create the topology.
ITopologyContainer2 topologyContainer = (ITopologyContainer2)featureDataset;
ITopology topology = topologyContainer.CreateTopology("Landbase_Topology", topologyContainer.DefaultClusterTolerance, -1, "");
// Add feature classes and rules to the topology.
topology.AddClass(DLJXPl, 5, 1, 1, false);
topology.AddClass(DLTBPg, 5, 1, 1, false);
topology.AddClass(QSZDPg, 5, 1, 1, false);
topology.AddClass(DXJZDPt, 5, 1, 1, false);
topology.AddClass(DXJZXPl, 5, 1, 1, false);
topology.AddClass(JBNTBHPKPg, 5, 1, 1, false);
topology.AddClass(JBNTBHTBPg, 5, 1, 1, false);
topology.AddClass(JBNTBHQPg, 5, 1, 1, false);
topology.AddClass(JZDPt, 5, 1, 1, false);
topology.AddClass(JZXPl, 5, 1, 1, false);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTLineNoSelfIntersect, "不存在自相交的线", DLJXPl);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTLineNoOverlap, "无重合的线", DLJXPl);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTLineCoveredByAreaBoundary, "线被区域的边线覆盖", DLJXPl, 1, DLTBPg, 1);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTAreaNoGaps, "面之间不存在间隙", DLTBPg);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTAreaNoOverlap, "面不自重叠", DLTBPg);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTAreaCoveredByAreaClass, "必须在另一个面的内部", DLTBPg, 1, QSZDPg, 1);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTPointCoveredByLineEndpoint,"必须被其他图层的端点覆盖", DXJZDPt, 1, DXJZXPl, 1);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTLineNoSelfOverlap,"线不自重叠",DXJZXPl);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTLineNoSelfIntersect, "不存在自相交的线", DXJZXPl);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTLineEndpointCoveredByPoint, "线的端点必须被点图层覆盖", DXJZXPl, 1, DXJZDPt, 1);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTAreaNoOverlap, "面与面不重叠", JBNTBHPKPg);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTAreaBoundaryCoveredByAreaBoundary, "面边界被面边界覆盖", JBNTBHPKPg, 1, JBNTBHTBPg, 1);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTAreaNoOverlap, "面与面不重叠", JBNTBHQPg);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTPointCoveredByLineEndpoint, "必须被其他图层的端点覆盖", JZDPt, 1, JZXPl, 1);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTLineNoSelfOverlap, "线不自重叠", JZXPl);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTLineNoOverlap, "无重合的线", JZXPl);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTLineEndpointCoveredByPoint, "线的端点必须被点图层覆盖", JZXPl, 1, JZDPt,1);
AddRuleToTopology(topology, esriTopologyRuleType.esriTRTPointProperlyInsideArea, "点必须在面图层内部", JZDPt, 1, DLTBPg, 1);
// Get an envelope with the topology's extents and validate the topology.
IGeoDataset geoDataset = (IGeoDataset)topology;
IEnvelope envelope = geoDataset.Extent;
ValidateTopology(topology, envelope);
}
catch (COMException comExc)
{
throw new Exception(String.Format( "Error creating topology: {0} Message: {1}", comExc.ErrorCode, comExc.Message), comExc);
}
finally
{
schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
}
}