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!
