我创建了一个插件 - 只要按钮处于活动状态,当您创建折线(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 是第一行,则在该层中创建的第一个要素将失败。其余要放置的要素成功。
同样,当它被删除或编辑时,第一个要删除的要素无法删除,但随后第一个要放置的要素会成功,这与首次插入折线时不同。 我真的不知道错误可能是什么,并且需要一些帮助。
我意识到了自己的错误,现在就找到了解决办法。
通过添加延迟》//添加一个小的延迟以确保所有初始化完成 第 153 行和第 305 行等待 Task.Delay(2);”。
我的猜测是,这些操作同时执行得不恰当。