using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ArcGIS.Core.CIM; using ArcGIS.Core.Data; using ArcGIS.Core.Geometry; using ArcGIS.Desktop.Catalog; using ArcGIS.Desktop.Core; using ArcGIS.Desktop.Editing; using ArcGIS.Desktop.Extensions; using ArcGIS.Desktop.Framework; using ArcGIS.Desktop.Framework.Contracts; using ArcGIS.Desktop.Framework.Dialogs; using ArcGIS.Desktop.Framework.Threading.Tasks; using ArcGIS.Desktop.Mapping; using System.Windows.Input; namespace ProAppModule1 { internal class MapToolSelect : MapTool { public MapToolSelect() { // select the type of construction tool you wish to implement. // Make sure that the tool is correctly registered with the correct component category type in the daml SketchType = SketchGeometryType.Line; // a sketch feedback is need IsSketchTool = true; // the geometry is needed in map coordinates SketchOutputMode = ArcGIS.Desktop.Mapping.SketchOutputMode.Map; } /// <summary> /// Called when the sketch finishes. This is where we will create the sketch /// operation and then execute it. /// </summary> /// <param name="geometry">The geometry created by the sketch.</param> /// <returns>A Task returning a Boolean indicating if the sketch complete event /// was successfully handled.</returns> protected override Task<bool> OnSketchCompleteAsync(Geometry geometry) { return QueuedTask.Run(() => ExecuteCut(geometry)); } /// <summary> /// Method to perform the cut operation on the geometry and change attributes /// </summary> /// <param name="geometry">Line geometry used to perform the cut against in the polygon features /// in the active map view.</param> /// <returns>If the cut operation was successful.</returns> protected Task<bool> ExecuteCut(Geometry geometry) { if (geometry == null) return Task.FromResult(false); // create a collection of feature layers that can be edited var editableLayers = ActiveMapView.Map.GetLayersAsFlattenedList() .OfType<FeatureLayer>() .Where(lyr => lyr.CanEditData() == true).Where(lyr => lyr.ShapeType == esriGeometryType.esriGeometryPolygon); // ensure that there are target layers if (editableLayers.Count() == 0) return Task.FromResult(false); // create an edit operation EditOperation cutOperation = new EditOperation() { Name = "Cut Elements", ProgressMessage = "Working...", CancelMessage = "Operation canceled.", ErrorMessage = "Error cutting polygons", SelectModifiedFeatures = false, SelectNewFeatures = false }; // initialize a list of ObjectIDs that need to be cut var cutOIDs = new List<long>(); // for each of the layers foreach (FeatureLayer editableFeatureLayer in editableLayers) { // get the feature class associated with the layer Table fc = editableFeatureLayer.GetTable(); // find the field index for the 'Description' attribute int descriptionIndex = -1; descriptionIndex = fc.GetDefinition().FindField("Description"); // find the features crossed by the sketch geometry // use the featureClass to search. We need to be able to search with a recycling cursor // seeing we want to Modify the row results using (var rowCursor = fc.Search(geometry, SpatialRelationship.Crosses, false)) { // add the feature IDs into our prepared list while (rowCursor.MoveNext()) { using (var feature = rowCursor.Current as Feature) { var geomTest = feature.GetShape(); if (geomTest != null) { // make sure we have the same projection for geomProjected and geomTest var geomProjected = GeometryEngine.Instance.Project(geometry, geomTest.SpatialReference); // we are looking for polygons are completely intersected by the cut line if (GeometryEngine.Instance.Relate(geomProjected, geomTest, "TT*F*****")) { var oid = feature.GetObjectID(); // add the current feature to the overall list of features to cut cutOIDs.Add(oid); } } } } } // adjust the attribute before the cut if (descriptionIndex != -1) { var atts = new Dictionary<string, object>(); atts.Add("Description", "Pro Sample"); foreach (var oid in cutOIDs) cutOperation.Modify(editableFeatureLayer, oid, atts); } // add the elements to cut into the edit operation cutOperation.Split(editableFeatureLayer, cutOIDs, geometry); } //execute the operation var operationResult = cutOperation.Execute(); return Task.FromResult(operationResult); } /// <summary> /// Method to override the sketch symbol after collecting the second vertex /// </summary> /// <returns>If the sketch symbology was successfully changed.</returns> protected override async Task<bool> OnSketchModifiedAsync() { // retrieve the current sketch geometry Polyline cutGeometry = await base.GetCurrentSketchAsync() as Polyline; await QueuedTask.Run(() => { // if there are more than 2 vertices in the geometry if (cutGeometry.PointCount > 2) { // adjust the sketch symbol var symbolReference = base.SketchSymbol; if (symbolReference == null) { var cimLineSymbol = SymbolFactory.Instance.ConstructLineSymbol(ColorFactory.Instance.RedRGB, 3, SimpleLineStyle.DashDotDot); base.SketchSymbol = cimLineSymbol.MakeSymbolReference(); } else { symbolReference.Symbol.SetColor(ColorFactory.Instance.RedRGB); base.SketchSymbol = symbolReference; } } }); return true; } } /// <summary> /// Extension method to search and retrieve rows /// </summary> public static class SketchExtensions { /// <summary> /// Performs a spatial query against a feature layer. /// </summary> /// <remarks>It is assumed that the feature layer and the search geometry are using the same spatial reference.</remarks> /// <param name="searchLayer">The feature layer to be searched.</param> /// <param name="searchGeometry">The geometry used to perform the spatial query.</param> /// <param name="spatialRelationship">The spatial relationship used by the spatial filter.</param> /// <returns>Cursor containing the features that satisfy the spatial search criteria.</returns> public static RowCursor Search(this BasicFeatureLayer searchLayer, Geometry searchGeometry, SpatialRelationship spatialRelationship) { RowCursor rowCursor = null; // define a spatial query filter var spatialQueryFilter = new SpatialQueryFilter { // passing the search geometry to the spatial filter FilterGeometry = searchGeometry, // define the spatial relationship between search geometry and feature class SpatialRelationship = spatialRelationship }; // apply the spatial filter to the feature layer in question rowCursor = searchLayer.Search(spatialQueryFilter); return rowCursor; } /// <summary> /// Performs a spatial query against a feature class /// </summary> /// <remarks>It is assumed that the feature layer and the search geometry are using the same spatial reference.</remarks> /// <param name="searchFC">The feature class to be searched.</param> /// <param name="searchGeometry">The geometry used to perform the spatial query.</param> /// <param name="spatialRelationship">The spatial relationship used by the spatial filter.</param> /// <param name="useRecyclingCursor"></param> /// <returns>Cursor containing the features that satisfy the spatial search criteria.</returns> public static RowCursor Search(this Table searchFC, Geometry searchGeometry, SpatialRelationship spatialRelationship, bool useRecyclingCursor) { RowCursor rowCursor = null; // define a spatial query filter var spatialQueryFilter = new SpatialQueryFilter { // passing the search geometry to the spatial filter FilterGeometry = searchGeometry, // define the spatial relationship between search geometry and feature class SpatialRelationship = spatialRelationship }; // apply the spatial filter to the feature layer in question rowCursor = searchFC.Search(spatialQueryFilter, useRecyclingCursor); return rowCursor; } } }