ArcGIS Pro SDK - 首次编辑操作失败

问题描述 投票:0回答:1

我创建了一个插件 - 只要按钮处于活动状态,当您创建折线(Plan_Kabler)时,点要素就会沿着线与多边形一起放置。这些是根据折线段定向的。点称为 muffePunktLayer,多边形称为 muffeArealLayer。

编辑或删除折线时,会删除原来的放置,如果编辑了,则沿线放置新的放置;如果删除,则不会放置新的放置。

MuffeTool.cs。下面;

using ArcGIS.Core.Data;
using ArcGIS.Core.Events;
using ArcGIS.Core.Geometry;
using ArcGIS.Core.Internal.Geometry;
using ArcGIS.Desktop.Editing;
using ArcGIS.Desktop.Editing.Events;
using ArcGIS.Desktop.Framework.Contracts;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.Mapping;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace muffe_add_in
{
    internal class MuffeTool : Button
    {
        private SubscriptionToken _rowCreatedEventToken;
        private SubscriptionToken _rowChangedEventToken;
        private SubscriptionToken _rowDeletedEventToken; // New token for RowDeletedEvent
        private bool _isActive = false; // Field to track the active state
        private Dictionary<long, Geometry> _originalGeometries = new Dictionary<long, Geometry>();

        protected override void OnClick()
        {
            _isActive = !_isActive; // Toggle the active state

            if (_isActive)
            {
                // Ensure the subscription logic is awaited properly
                QueuedTask.Run(async () =>
                {
                    try
                    {
                        // Assuming you have a way to access or select the feature layer you're working with
                        var featureLayer = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Plan Kabler");
                        if (featureLayer != null)
                        {
                            // Correctly obtain the spatial reference from the feature layer's dataset
                            var spatialReference = await QueuedTask.Run(() => featureLayer.GetFeatureClass().GetDefinition().GetSpatialReference());

                            _rowCreatedEventToken = RowCreatedEvent.Subscribe((args) => OnRowCreatedOrEdited(args, true), featureLayer.GetTable(), false);
                            _rowChangedEventToken = RowChangedEvent.Subscribe((args) => OnRowCreatedOrEdited(args, false), featureLayer.GetTable(), false);
                            _rowDeletedEventToken = RowDeletedEvent.Subscribe(OnRowDeleted, featureLayer.GetTable(), false); // Subscribe to RowDeletedEvent
                        }
                    }
                    catch (Exception ex)
                    {
                        // Log the exception to the ArcGIS Pro log or show a message box
                        System.Diagnostics.Debug.WriteLine($"Error subscribing to events: {ex.Message}");
                    }
                }).Wait();
                UpdateCaption("Deactivate Muffe");
            }
            else
            {
                // Unsubscribe logic should also be within QueuedTask.Run if it interacts with ArcGIS objects
                QueuedTask.Run(() =>
                {
                    RowCreatedEvent.Unsubscribe(_rowCreatedEventToken);
                    RowChangedEvent.Unsubscribe(_rowChangedEventToken);
                    RowDeletedEvent.Unsubscribe(_rowDeletedEventToken); // Unsubscribe from RowDeletedEvent
                    _rowCreatedEventToken = null;
                    _rowChangedEventToken = null;
                    _rowDeletedEventToken = null;
                }).Wait();
                UpdateCaption("Activate Muffe");
            }
        }

        private void UpdateCaption(string newCaption)
        {
            // Directly update the caption since this method is called within QueuedTask.Run
            this.Caption = newCaption;
        }

        private async void OnRowCreatedOrEdited(RowChangedEventArgs args, bool isInsert)
        {
            if (!_isActive) return; // Check if the script is active before proceeding
            if (!(args.Row is Feature feature)) return;
            if (feature.GetTable().GetName() != "Plan_Kabler") return;

            var featureId = feature.GetObjectID();
            var polyline = feature.GetShape() as Polyline;
            if (polyline == null) return;

            // If there is an existing geometry, delete it
            if (_originalGeometries.ContainsKey(featureId))
            {
                await QueuedTask.Run(async () =>
                {
                    var muffePunktLayer = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Muffe Punkt");
                    var muffeArealLayer = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Muffe Areal");
                    if (muffePunktLayer == null || muffeArealLayer == null) return;

                    // Delete intersecting templates
                    await DeleteIntersectingTemplates(featureId, muffePunktLayer, muffeArealLayer);
                });

                // Remove the old geometry from the dictionary
                _originalGeometries.Remove(featureId);
            }

            // Store the new geometry
            _originalGeometries[featureId] = polyline.Clone();

            await QueuedTask.Run(async () =>
            {
                var muffePunktLayer = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Muffe Punkt");
                var muffeArealLayer = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Muffe Areal");
                if (muffePunktLayer == null || muffeArealLayer == null) return;

                if (isInsert)
                {
                    // Logic for handling row insertions
                    await ApplyTemplateAlongPolyline(polyline, muffePunktLayer, muffeArealLayer);
                }
                else
                {
                    // Logic for handling row updates
                    await ApplyTemplateAlongPolyline(polyline, muffePunktLayer, muffeArealLayer);
                }
            });
        }

        private async void OnRowDeleted(RowChangedEventArgs args)
        {
            if (!_isActive) return; // Check if the script is active before proceeding
            if (!(args.Row is Feature feature)) return;
            if (feature.GetTable().GetName() != "Plan_Kabler") return;

            var featureId = feature.GetObjectID();

            await QueuedTask.Run(async () =>
            {
                var muffePunktLayer = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Muffe Punkt");
                var muffeArealLayer = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Muffe Areal");
                if (muffePunktLayer == null || muffeArealLayer == null) return;

                // Logic for handling row deletions
                await DeleteIntersectingTemplates(featureId, muffePunktLayer, muffeArealLayer);
            });
        }

        private async Task ApplyTemplateAlongPolyline(Polyline polyline, FeatureLayer muffePunktLayer, FeatureLayer muffeArealLayer)
        {
            // Ensure spatial reference is correctly obtained
            var spatialReference = await QueuedTask.Run(() => muffeArealLayer.GetFeatureClass().GetDefinition().GetSpatialReference());

            double interval = 100.0; // Interval in meters
            double distanceToNextPoint = 0; // Start with 0 to place the first point immediately

            foreach (var part in polyline.Parts)
            {
                double distanceCoveredInSegment = 0.0;
                foreach (var segment in part)
                {
                    double segmentLength = CalculateSegmentLength(segment);

                    while (distanceCoveredInSegment + distanceToNextPoint <= segmentLength)
                    {
                        double proportion = distanceToNextPoint / segmentLength;
                        MapPoint position = CalculatePositionAlongSegment(segment, proportion + (distanceCoveredInSegment / segmentLength));
                        double angle = CalculateSegmentAngle(segment);

                        try
                        {
                            // Attempt to create and place the point and polygons at this position
                            await CreateAndPlacePolygons(position, angle, spatialReference, muffeArealLayer);
                            await CreateFeatureInLayer(muffePunktLayer, position);
                        }
                        catch (Exception ex)
                        {
                            // Log or handle the error
                            Console.WriteLine($"Failed to create feature: {ex.Message}");
                        }

                        distanceCoveredInSegment += distanceToNextPoint;
                        distanceToNextPoint = interval; // Reset after placing a point
                    }
                    distanceToNextPoint -= (segmentLength - distanceCoveredInSegment);
                    distanceCoveredInSegment = 0.0;
                }
            }
        }

        private double CalculateSegmentLength(Segment segment)
        {
            return Math.Sqrt(Math.Pow(segment.EndPoint.X - segment.StartPoint.X, 2) + Math.Pow(segment.EndPoint.Y - segment.StartPoint.Y, 2));
        }

        private MapPoint CalculatePositionAlongSegment(Segment segment, double proportion)
        {
            double x = segment.StartPoint.X + (segment.EndPoint.X - segment.StartPoint.X) * proportion;
            double y = segment.StartPoint.Y + (segment.EndPoint.Y - segment.StartPoint.Y) * proportion;
            return MapPointBuilder.CreateMapPoint(x, y, segment.SpatialReference);
        }

        private double CalculateSegmentAngle(Segment segment)
        {
            double dx = segment.EndPoint.X - segment.StartPoint.X;
            double dy = segment.EndPoint.Y - segment.StartPoint.Y;
            return Math.Atan2(dy, dx) * (180 / Math.PI); // Angle in degrees
        }

        private async Task CreateAndPlacePolygons(MapPoint position, double angle, SpatialReference spatialReference, FeatureLayer layer)
        {
            // Adjust the offset distances for left and right polygons
            double offsetDistanceLeft = 5; // Offset distance for left polygons
            double offsetDistanceRight = 10; // Offset distance for right polygons

            // Calculate perpendicular direction for left and right offsets
            double angleLeft = angle - 90; // Perpendicular to the left
            double angleRight = angle + 90; // Perpendicular to the right

            // Create offset points for left and right polygons using the perpendicular angles
            MapPoint offsetPointLeft = RotateAndMovePoint(position, angleLeft, offsetDistanceLeft, spatialReference);
            MapPoint offsetPointRight = RotateAndMovePoint(position, angleRight, offsetDistanceRight, spatialReference);

            // Create polygons based on the direction of the segment
            Polygon orientedPolygonLeft = CreateOrientedPolygon(offsetPointLeft, angle, spatialReference);
            await CreateFeatureInLayer(layer, orientedPolygonLeft);

            Polygon orientedPolygonRight = CreateOrientedPolygon(offsetPointRight, angle, spatialReference);
            await CreateFeatureInLayer(layer, orientedPolygonRight);
        }

        private MapPoint RotateAndMovePoint(MapPoint point, double angleDegrees, double distance, SpatialReference spatialReference)
        {
            double angleRadians = angleDegrees * (Math.PI / 180);

            double offsetX = Math.Cos(angleRadians) * distance;
            double offsetY = Math.Sin(angleRadians) * distance;

            return MapPointBuilder.CreateMapPoint(point.X + offsetX, point.Y + offsetY, spatialReference);
        }

        private async Task CreateFeatureInLayer(FeatureLayer layer, Geometry geometry)
        {
            var editOperation = new EditOperation
            {
                Name = $"Create feature in {layer.Name}"
            };

            editOperation.Create(layer, geometry);

            var result = await editOperation.ExecuteAsync();
            if (!result) // If the result is false, the operation failed
            {
                var errorMessage = editOperation.ErrorMessage;
                // Log the errorMessage or display it to understand why the operation failed
                System.Diagnostics.Debug.WriteLine($"Error creating feature in {layer.Name}: {errorMessage}");
            }
        }

        private Polygon CreateOrientedPolygon(MapPoint centerPoint, double angle, SpatialReference spatialReference)
        {
            double width = 10; // Width of the rectangle
            double height = 5; // Height of the rectangle

            double halfWidth = width / 2;
            double halfHeight = height / 2;

            // Use MapPointBuilder to create points with a spatial reference
            MapPoint[] cornerPoints = new MapPoint[]
            {
                MapPointBuilder.CreateMapPoint(centerPoint.X - halfWidth, centerPoint.Y - halfHeight, spatialReference),
                MapPointBuilder.CreateMapPoint(centerPoint.X + halfWidth, centerPoint.Y - halfHeight, spatialReference),
                MapPointBuilder.CreateMapPoint(centerPoint.X + halfWidth, centerPoint.Y + halfHeight, spatialReference),
                MapPointBuilder.CreateMapPoint(centerPoint.X - halfWidth, centerPoint.Y + halfHeight, spatialReference)
            };

            MapPoint[] rotatedPoints = cornerPoints.Select(point => RotatePoint(point, centerPoint, angle, spatialReference)).ToArray();

            Polygon orientedPolygon = new PolygonBuilder(rotatedPoints.Select(p => new Coordinate2D(p.X, p.Y)), spatialReference).ToGeometry();

            return orientedPolygon;
        }

        private MapPoint RotatePoint(MapPoint point, MapPoint pivot, double angleDegrees, SpatialReference spatialReference)
        {
            double angleRadians = angleDegrees * (Math.PI / 180);

            double cosTheta = Math.Cos(angleRadians);
            double sinTheta = Math.Sin(angleRadians);

            double translatedX = point.X - pivot.X;
            double translatedY = point.Y - pivot.Y;

            double rotatedX = translatedX * cosTheta - translatedY * sinTheta;
            double rotatedY = translatedX * sinTheta + translatedY * cosTheta;

            return MapPointBuilder.CreateMapPoint(rotatedX + pivot.X, rotatedY + pivot.Y, spatialReference);
        }

        private async Task DeleteIntersectingTemplates(long featureId, FeatureLayer muffePunktLayer, FeatureLayer muffeArealLayer)
        {
            if (!_originalGeometries.TryGetValue(featureId, out var originalGeometry))
            {
                return; // If not found, exit the method
            }

            var bufferedGeometry = GeometryEngine.Instance.Buffer(originalGeometry, 10);

            // Use different spatial queries based on the layer type
            await DeleteFeaturesBasedOnLayerType(muffePunktLayer, bufferedGeometry);
            await DeleteFeaturesBasedOnLayerType(muffeArealLayer, bufferedGeometry);
        }

        private async Task DeleteFeaturesBasedOnLayerType(FeatureLayer layer, Geometry bufferedGeometry)
        {
            SpatialQueryFilter spatialQueryFilter = new SpatialQueryFilter
            {
                FilterGeometry = bufferedGeometry,
                SpatialRelationship = SpatialRelationship.Intersects
            };

            List<Geometry> deletedGeometries = new List<Geometry>();
            var featureTable = layer.GetTable();
            if (featureTable == null) return;

            using (var cursor = featureTable.Search(spatialQueryFilter, false))
            {
                while (cursor.MoveNext())
                {
                    var feature = cursor.Current as Feature;
                    deletedGeometries.Add(feature.GetShape().Clone());

                    // Create a separate EditOperation for each feature
                    var editOperation = new EditOperation
                    {
                        Name = $"Delete feature in {layer.Name}"
                    };
                    editOperation.Delete(feature);

                    bool result = await editOperation.ExecuteAsync();
                    if (!result)
                    {
                        System.Diagnostics.Debug.WriteLine($"Failed to delete intersecting feature in {layer.Name}");
                    }
                }
            }
        }
    }
}

无论我如何尝试 - 我无法弄清楚为什么第一次编辑操作总是失败。

我已确定它不是特定层或类似层。

我注意到,如果第一行是 muffePunktLayer 或 muffeArealLayer 是第一行,则在该层中创建的第一个要素将失败。其余要放置的要素成功。

同样,当它被删除或编辑时,第一个要删除的要素无法删除,但随后第一个要放置的要素会成功,这与首次插入折线时不同。 我真的不知道错误可能是什么,并且需要一些帮助。

c# sdk arcgis esri
1个回答
0
投票

我意识到了自己的错误,现在就找到了解决办法。

通过添加延迟》//添加一个小的延迟以确保所有初始化完成 第 153 行和第 305 行等待 Task.Delay(2);”。

我的猜测是,这些操作同时执行得不恰当。

© www.soinside.com 2019 - 2024. All rights reserved.