Hi everyone.
We need to export a feature layer in the map to a shapefile, but applying a clipping operation with a code-built polygon. The original layer must not be modified.
We've tried running the "analysis.Clip" tool. Unlike the old "arcpy.Clip_analysis", the ArcGIS Pro SDK version seems to require providing the clipping polygon (second GP value) as a FeatureLayer.
We think we need to either:
We could find a way to do either one in C#. Maybe you can provide a sample or have other ideas. We would prefer not to create any intermediate visible layers in the Map.
Thanks!
Solved! Go to Solution.
try this:
internal class Button1 : Button
{
protected async void OnClickClip()
{
var tool_name = "analysis.Clip";
var extent = MapView.Active.Extent;
var sel_layer = MapView.Active.Map.GetLayersAsFlattenedList()
.OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "GreatLakes");
if (sel_layer == null) return;
var gdb = Project.Current.DefaultGeodatabasePath;
var out_fc = System.IO.Path.Combine(gdb, "clipped_lakes_out");
var val_array = await QueuedTask.Run(() =>
{
var rect = GeometryEngine.Instance.Scale(extent, extent.Center, 0.5, 0.5) as Envelope;
var clip_poly = PolygonBuilderEx.CreatePolygon(rect, rect.SpatialReference);
var geom = new List<object>() { clip_poly };
return Geoprocessing.MakeValueArray(new object[] { sel_layer, geom, out_fc });
});
Geoprocessing.ExecuteToolAsync(tool_name, val_array,
null, null, null, GPExecuteToolFlags.InheritGPOptions);
}
protected override void OnClick()
{
OnClickClip();
}
}
A feature layer is a temporary construct within an ArcMap/Pro session, and that is absolutely the way I would go. Really easy to call in arcpy via the "Make Feature Layer" tool. There has always been a little bit of inconsistency in both ArcMap and ArcGIS Pro about which tools require Feature Layers as inputs, and while it's a little bit of a faff, it's realistically only a single extra line of code
Thanks, Richard.
In the end, my solution is quite complex, but I don't see a shorter way. Basically:
public static async Task<Layer> CreateLayerFromPolygon(string tempDirectory, Polygon clipArea)
{
GPExecuteToolFlags flags = GPExecuteToolFlags.GPThread; // instruct the tool run non-blocking GPThread
// Create empty feature class
var valueArrayCreateFCArea = Geoprocessing.MakeValueArray(tempDirectory, "clip_area.shp", "POLYGON");
IGPResult gpResult = await Geoprocessing.ExecuteToolAsync("management.CreateFeatureclass", valueArrayCreateFCArea, null, null, null, flags);
if (!Utils.VerifyGPToolResult(gpResult))
return null;
// Load feature class as layer
Uri clipAreaFile = new Uri(Path.Combine(tempDirectory, "clip_area.shp"));
Layer clipLayer = null;
await QueuedTask.Run(() =>
clipLayer = LayerFactory.Instance.CreateLayer(clipAreaFile, Utils.getActiveMap())
);
if (clipLayer == null)
return null;
// Add clip area to clip layer
var editOp = new EditOperation();
editOp.Create(clipLayer, clipArea);
if (!editOp.Execute())
return null;
return clipLayer;
}
Layer clipLayer = await Utils.CreateLayerFromPolygon(tempDirectory, clipArea);
GPExecuteToolFlags flags = GPExecuteToolFlags.GPThread;
var valueArray = await QueuedTask.Run(() =>
{
List<string> inlayers = new List<string>();
inlayers.Add(layerNameToBeClipped);
string outpath = Path.Combine(outShpDirectory, layerNameToBeClipped + ".shp");
return Geoprocessing.MakeValueArray(inlayers, clipLayer, outpath);
});
IGPResult gpResult = await Geoprocessing.ExecuteToolAsync("analysis.Clip", valueArray, null, null, null, flags);
try this:
internal class Button1 : Button
{
protected async void OnClickClip()
{
var tool_name = "analysis.Clip";
var extent = MapView.Active.Extent;
var sel_layer = MapView.Active.Map.GetLayersAsFlattenedList()
.OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "GreatLakes");
if (sel_layer == null) return;
var gdb = Project.Current.DefaultGeodatabasePath;
var out_fc = System.IO.Path.Combine(gdb, "clipped_lakes_out");
var val_array = await QueuedTask.Run(() =>
{
var rect = GeometryEngine.Instance.Scale(extent, extent.Center, 0.5, 0.5) as Envelope;
var clip_poly = PolygonBuilderEx.CreatePolygon(rect, rect.SpatialReference);
var geom = new List<object>() { clip_poly };
return Geoprocessing.MakeValueArray(new object[] { sel_layer, geom, out_fc });
});
Geoprocessing.ExecuteToolAsync(tool_name, val_array,
null, null, null, GPExecuteToolFlags.InheritGPOptions);
}
protected override void OnClick()
{
OnClickClip();
}
}
Thanks, Charles!! That's a much better solution 🙂
I just needed to pass my clip area to the GP as a List, like you do. I guess the docs could be more clear about accepting clip features as a list, but not a single feature. Anyway, thanks a lot!