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;
}
}
}