ArcGIS Pro SDK (八)地理数据库 8 拓扑

ArcGIS Pro SDK (八)地理数据库 8 拓扑

环境:Visual Studio 2022 + .NET6 + ArcGIS Pro SDK 3.0

1 开放拓扑和进程定义

public void OpenTopologyAndProcessDefinition()
{
    // 从文件地理数据库中打开拓扑并处理拓扑定义。

    using (Geodatabase geodatabase = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(@"C:\TestData\GrandTeton.gdb"))))
        using (Topology topology = geodatabase.OpenDataset<Topology>("Backcountry_Topology"))
    {
        ProcessDefinition(geodatabase, topology);
    }

    // 打开要素服务拓扑并处理拓扑定义。

    const string TOPOLOGY_LAYER_ID = "0";

    using (Geodatabase geodatabase = new Geodatabase(new ServiceConnectionProperties(new Uri("https://sdkexamples.esri.com/server/rest/services/GrandTeton/FeatureServer"))))
        using (Topology topology = geodatabase.OpenDataset<Topology>(TOPOLOGY_LAYER_ID))
    {
        ProcessDefinition(geodatabase, topology);
    }
}

private void ProcessDefinition(Geodatabase geodatabase, Topology topology)
{
    // 类似于Core.Data API中其余Definition对象,打开数据集的定义有两种方式 -- 通过拓扑数据集本身或通过地理数据库。

    using (TopologyDefinition definitionViaTopology = topology.GetDefinition())
    {
        OutputDefinition(geodatabase, definitionViaTopology);
    }

    using (TopologyDefinition definitionViaGeodatabase = 
           geodatabase.GetDefinition<TopologyDefinition>("Backcountry_Topology"))
    {
        OutputDefinition(geodatabase, definitionViaGeodatabase);
    }
}

private void OutputDefinition(Geodatabase geodatabase, TopologyDefinition topologyDefinition)
{
    Console.WriteLine($"拓扑聚类容差 => {topologyDefinition.GetClusterTolerance()}");
    Console.WriteLine($"拓扑Z值聚类容差 => {topologyDefinition.GetZClusterTolerance()}");

    IReadOnlyList<string> featureClassNames = topologyDefinition.GetFeatureClassNames();
    Console.WriteLine($"有 {featureClassNames.Count} 个要素类参与了拓扑:");

    foreach (string name in featureClassNames)
    {
        // 打开每个参与拓扑的要素类。

        using (FeatureClass featureClass = geodatabase.OpenDataset<FeatureClass>(name))
            using (FeatureClassDefinition featureClassDefinition = featureClass.GetDefinition())
        {
            Console.WriteLine($"\t{featureClass.GetName()} ({featureClassDefinition.GetShapeType()})");
        }
    }
}

2 获取拓扑规则

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

    Console.WriteLine($"拓扑定义了 {rules.Count} 条拓扑规则:");
    Console.WriteLine("ID \t 源类 \t 源子类 \t 目标类 \t 目标子类 \t 规则类型");

    foreach (TopologyRule rule in rules)
    {
        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();
    }
}

3 验证拓扑

public void ValidateTopology()
{
    using (Geodatabase geodatabase = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(@"C:\TestData\GrandTeton.gdb"))))
        using (Topology topology = geodatabase.OpenDataset<Topology>("Backcountry_Topology"))
    {
        // 如果拓扑当前没有脏区域,调用Validate()将返回一个空的包络线。

        ValidationResult result = topology.Validate(new ValidationDescription(topology.GetExtent()));
        Console.WriteLine($"在未编辑的拓扑上验证后的'受影响区域' => {result.AffectedArea.ToJson()}");

        // 现在创建一个故意违反“PointProperlyInsideArea”拓扑规则的要素。这个动作将创建脏区域。

        Feature newFeature = null;

        try
        {
            // 获取Campsite要素类中ObjectID为2的要素。然后从这个要素稍微修改后创建一个新的几何体,并用它创建一个新的要素。

            using (Feature featureViaCampsites2 = GetFeature(geodatabase, "Campsites", 2))
            {
                Geometry currentGeometry = featureViaCampsites2.GetShape();
                Geometry newGeometry = GeometryEngine.Instance.Move(currentGeometry, (currentGeometry.Extent.XMax / 8),
                                                                    (currentGeometry.Extent.YMax / 8));

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

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

            // 在'Campsites'参与要素类中创建新要素后,拓扑的状态应为“未分析”,因为尚未验证。

            Console.WriteLine($"应用编辑后拓扑状态 => {topology.GetState()}");

            // 现在验证拓扑。结果包络线对应于脏区域。

            result = topology.Validate(new ValidationDescription(topology.GetExtent()));
            Console.WriteLine($"在刚编辑后验证的拓扑上的'受影响区域' => {result.AffectedArea.ToJson()}");

            // 在Validate()之后,拓扑的状态应为“有错误的分析”,因为拓扑当前存在错误。

            Console.WriteLine($"验证拓扑后的拓扑状态 => {topology.GetState()}");

            // 如果没有脏区域,则结果包络线应为空。

            result = topology.Validate(new ValidationDescription(topology.GetExtent()));
            Console.WriteLine($"在刚验证过的拓扑上的'受影响区域' => {result.AffectedArea.ToJson()}");
        }
        finally
        {
            if (newFeature != null)
            {
                geodatabase.ApplyEdits(() =>
                                       {
                                           newFeature.Delete();
                                       });

                newFeature.Dispose();
            }
        }

        // 删除新创建的要素后再次验证。

        topology.Validate(new ValidationDescription(topology.GetExtent()));
    }
}

private Feature GetFeature(Geodatabase geodatabase, string featureClassName, long objectID)
{
    using (FeatureClass featureClass = geodatabase.OpenDataset<FeatureClass>(featureClassName))
    {
        QueryFilter queryFilter = new QueryFilter()
        {
            ObjectIDs = new List<long>() { objectID }
        };

        using (RowCursor cursor = featureClass.Search(queryFilter))
        {
            System.Diagnostics.Debug.Assert(cursor.MoveNext());
            return (Feature)cursor.Current;
        }
    }
}

4 获取拓扑错误

// 获取当前与拓扑相关的所有错误和异常。

IReadOnlyList<TopologyError> allErrorsAndExceptions = topology.GetErrors(new ErrorDescription(topology.GetExtent()));
Console.WriteLine($"错误和异常数目 => {allErrorsAndExceptions.Count}");

Console.WriteLine("源类名称 \t 源对象ID \t 目标类名称 \t 目标对象ID \t 规则类型 \t 是否异常 \t 几何类型 \t 几何宽度 & 高度 \t 规则ID \t");

foreach (TopologyError error in allErrorsAndExceptions)
{
    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}");
}

5 标记和不标记为错误

// 获取所有由于违反“PointProperlyInsideArea”拓扑规则而引起的错误。

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

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

    IReadOnlyList<TopologyError> errorsDueToViolatingPointProperlyInsideAreaRule = topology.GetErrors(errorDescription);
    Console.WriteLine($"有 {errorsDueToViolatingPointProperlyInsideAreaRule.Count} 个要素违反了'PointProperlyInsideArea'拓扑规则.");

    // 将违反“PointProperlyInsideArea”拓扑规则的所有错误标记为异常。

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

    // 现在验证所有违反“PointProperlyInsideArea”拓扑规则的错误是否确实已标记为异常。
    //
    // 默认情况下,ErrorDescription初始化为ErrorType.ErrorAndException。在这里我们想要ErrorType.ErrorOnly。

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

    IReadOnlyList<TopologyError> errorsAfterMarkedAsExceptions = topology.GetErrors(errorDescription);
    Console.WriteLine($"在将所有错误标记为异常后,有 {errorsAfterMarkedAsExceptions.Count} 个要素违反了'PointProperlyInsideArea'拓扑规则.");

    // 最后,通过取消标记为异常将所有异常重置为错误。

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

    IReadOnlyList<TopologyError> errorsAfterUnmarkedAsExceptions = topology.GetErrors(errorDescription);
    Console.WriteLine($"在将所有异常重置为错误后,有 {errorsAfterUnmarkedAsExceptions.Count} 个要素违反了'PointProperlyInsideArea'拓扑规则.");
}

6 探索拓扑图

public void ExploreTopologyGraph()
{
    using (Geodatabase geodatabase = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(@"C:\TestData\GrandTeton.gdb"))))
        using (Topology topology = geodatabase.OpenDataset<Topology>("Backcountry_Topology"))
    {
        // 使用拓扑数据集的范围构建拓扑图。

        topology.BuildGraph(topology.GetExtent(),
                            (topologyGraph) =>
                            {
                                using (Feature campsites12 = GetFeature(geodatabase, "Campsites", 12))
                                {
                                    IReadOnlyList<TopologyNode> topologyNodesViaCampsites12 = topologyGraph.GetNodes(campsites12);

                                    TopologyNode topologyNodeViaCampsites12 = topologyNodesViaCampsites12[0];

                                    IReadOnlyList<TopologyEdge> allEdgesConnectedToNodeViaCampsites12 = topologyNodeViaCampsites12.GetEdges();
                                    IReadOnlyList<TopologyEdge> allEdgesConnectedToNodeViaCampsites12CounterClockwise = topologyNodeViaCampsites12.GetEdges(false);

                                    System.Diagnostics.Debug.Assert(allEdgesConnectedToNodeViaCampsites12.Count == allEdgesConnectedToNodeViaCampsites12CounterClockwise.Count);

                                    foreach (TopologyEdge edgeConnectedToNodeViaCampsites12 in allEdgesConnectedToNodeViaCampsites12)
                                    {
                                        TopologyNode fromNode = edgeConnectedToNodeViaCampsites12.GetFromNode();
                                        TopologyNode toNode = edgeConnectedToNodeViaCampsites12.GetToNode();

                                        bool fromNodeIsTheSameAsTopologyNodeViaCampsites12 = (fromNode == topologyNodeViaCampsites12);
                                        bool toNodeIsTheSameAsTopologyNodeViaCampsites12 = (toNode == topologyNodeViaCampsites12);

                                        System.Diagnostics.Debug.Assert(fromNodeIsTheSameAsTopologyNodeViaCampsites12 || toNodeIsTheSameAsTopologyNodeViaCampsites12,
                                                                        "连接到'topologyNodeViaCampsites12'的每个边的FromNode或ToNode应与'topologyNodeViaCampsites12'本身相同。");

                                        IReadOnlyList<FeatureInfo> leftParentFeaturesBoundedByEdge = edgeConnectedToNodeViaCampsites12.GetLeftParentFeatures();
                                        foreach (FeatureInfo featureInfo in leftParentFeaturesBoundedByEdge)
                                        {
                                            System.Diagnostics.Debug.Assert(!String.IsNullOrEmpty(featureInfo.FeatureClassName));
                                            System.Diagnostics.Debug.Assert(featureInfo.ObjectID > 0);
                                            EnsureShapeIsNotEmpty(featureInfo);
                                        }

                                        IReadOnlyList<FeatureInfo> leftParentFeaturesNotBoundedByEdge = edgeConnectedToNodeViaCampsites12.GetLeftParentFeatures(false);
                                        foreach (FeatureInfo featureInfo in leftParentFeaturesNotBoundedByEdge)
                                        {
                                            System.Diagnostics.Debug.Assert(!String.IsNullOrEmpty(featureInfo.FeatureClassName));
                                            System.Diagnostics.Debug.Assert(featureInfo.ObjectID > 0);
                                            EnsureShapeIsNotEmpty(featureInfo);
                                        }

                                        IReadOnlyList<FeatureInfo> rightParentFeaturesBoundedByEdge = edgeConnectedToNodeViaCampsites12.GetRightParentFeatures();
                                        foreach (FeatureInfo featureInfo in rightParentFeaturesBoundedByEdge)
                                        {
                                            System.Diagnostics.Debug.Assert(!String.IsNullOrEmpty(featureInfo.FeatureClassName));
                                            System.Diagnostics.Debug.Assert(featureInfo.ObjectID > 0);
                                            EnsureShapeIsNotEmpty(featureInfo);
                                        }

                                        IReadOnlyList<FeatureInfo> rightParentFeaturesNotBoundedByEdge = edgeConnectedToNodeViaCampsites12.GetRightParentFeatures(false);
                                        foreach (FeatureInfo featureInfo in rightParentFeaturesNotBoundedByEdge)
                                        {
                                            System.Diagnostics.Debug.Assert(!String.IsNullOrEmpty(featureInfo.FeatureClassName));
                                            System.Diagnostics.Debug.Assert(featureInfo.ObjectID > 0);
                                            EnsureShapeIsNotEmpty(featureInfo);
                                        }
                                    }
                                }
                            });
    }
}

private void EnsureShapeIsNotEmpty(FeatureInfo featureInfo)
{
    using (Feature feature = featureInfo.GetFeature())
    {
        System.Diagnostics.Debug.Assert(!feature.GetShape().IsEmpty, "要素的形状不应为空。");
    }
}

7 找到最近的元素

public void FindClosestElement()
{
    using (Geodatabase geodatabase = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(@"C:\TestData\GrandTeton.gdb"))))
        using (Topology topology = geodatabase.OpenDataset<Topology>("Backcountry_Topology"))
    {
        // 使用拓扑数据集的范围构建拓扑图。

        topology.BuildGraph(topology.GetExtent(), 
                            (topologyGraph) =>
                            {
                                MapPoint queryPointViaCampsites12 = null;

                                using (Feature campsites12 = GetFeature(geodatabase, "Campsites", 12))
                                {
                                    queryPointViaCampsites12 = campsites12.GetShape() as MapPoint;
                                }

                                double searchRadius = 1.0;

                                TopologyElement topologyElementViaCampsites12 = 
                                    topologyGraph.FindClosestElement<TopologyElement>(
                                    queryPointViaCampsites12, searchRadius);

                                System.Diagnostics.Debug.Assert(
                                    topologyElementViaCampsites12 != null, "在searchRadius范围内应该有一个与'queryPointViaCampsites12'对应的拓扑元素.");

                                IReadOnlyList<FeatureInfo> parentFeatures = topologyElementViaCampsites12.GetParentFeatures();

                                Console.WriteLine("生成'topologyElementViaCampsites12'的父要素:");
                                foreach (FeatureInfo parentFeature in parentFeatures)
                                {
                                    Console.WriteLine($"\t{parentFeature.FeatureClassName}; OID: {parentFeature.ObjectID}");
                                }

                                TopologyNode topologyNodeViaCampsites12 = topologyGraph.FindClosestElement<TopologyNode>(queryPointViaCampsites12, searchRadius);

                                if (topologyNodeViaCampsites12 != null)
                                {
                                    // 在searchRadius单位内存在一个最近的TopologyNode。
                                }

                                TopologyEdge topologyEdgeViaCampsites12 = topologyGraph.FindClosestElement<TopologyEdge>(queryPointViaCampsites12, searchRadius);

                                if (topologyEdgeViaCampsites12 != null)
                                {
                                    // 在searchRadius单位内存在一个最近的TopologyEdge。
                                }
                            });
    }
}
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ArcGIS Pro SDK是用于创建ArcGIS Pro加载项的开发工具包。您可以使用ArcGIS Pro SDK来扩展ArcGIS Pro的功能,添加自定义工具、面板、任务和其他功能。\[1\] 要安装ArcGIS Pro SDK 3.0,您需要使用Visual Studio 2022,并从Visual Studio Marketplace搜索并安装以下三个扩展:ArcGIS Pro SDK for .NET,ArcGIS Pro SDK for .NET(Utilities),ArcGIS Pro SDK for .NET(Migration)\[1\]。请注意,ArcGIS Pro SDK for .NET扩展只能集成到Visual Studio 2022中,建议使用版本17.2或更高版本\[2\]。 ArcGIS Pro SDK for .NET提供了三个不同的扩展名(.vsix文件):ArcGIS Pro SDK for .NET用于创建ArcGIS Pro加载项的工程和项模板的集合,ArcGIS Pro SDK for .NET(Utilities)用于帮助创建ArcGIS Pro加载项的实用程序的集合,ArcGIS Pro SDK for .NET(Migration)用于将ArcGIS Pro SDK 2.x扩展模块迁移到ArcGIS Pro SDK 3.0 for .NET\[3\]。 总结来说,ArcGIS Pro SDK是用于创建ArcGIS Pro加载项的开发工具包,您可以使用它来扩展ArcGIS Pro的功能。要安装ArcGIS Pro SDK 3.0,您需要使用Visual Studio 2022,并从Visual Studio Marketplace安装相应的扩展。 #### 引用[.reference_title] - *1* *2* *3* [VS2022中ArcGIS Pro SDK for .NET安装和卸载指南](https://blog.csdn.net/u012685544/article/details/126317090)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值