With Pro 3.0 I am trying to implement the SelectLayerByLocation geoprocessing tool. I have objects/classes that I want to pass in to the tool. I've looked at the community samples and the ESRI forums and I must be missing something because this throws an exception
System.InvalidOperationException
HResult=0x80131509
Message=convert unsupported type
Source=ArcGIS.Desktop.GeoProcessing
object[] listOfParameter = { SelectedProximityLayer, "WITHIN_A_DISTANCE_GEODESIC", featOrigin, "100 NAUTICALMILES", "NEW_SELECTION", "NOT_INVERT" };
var parameters = Geoprocessing.MakeValueArray(listOfParameter); //exception here
var gpResult = await Geoprocessing.ExecuteToolAsync("SelectLayerByLocation_management", parameters);
With ArcMap you could call out a native .NET API to do this and I can't find anything similar with Pro:
Dim selLyrLoc As New SelectLayerByLocation
selLyrLoc.in_layer = pPointProxLayer 'the selection will be applied to this layer
selLyrLoc.overlap_type = "WITHIN_A_DISTANCE_GEODESIC" 'we want features within a geodesic distance
selLyrLoc.select_features = pFeatSelOrigin 'this is the source feature
selLyrLoc.search_distance = "100 NAUTICALMILES" 'search within 100 NM of the source
selLyrLoc.selection_type = "NEW_SELECTION" 'new selection
selLyrLoc.invert_spatial_relationship = "NOT_INVERT" 'do not invert selection
'run the geoprocessor
Dim res As ESRI.ArcGIS.Geoprocessing.IGeoProcessorResult = CType(GP.Execute(selLyrLoc, Nothing), ESRI.ArcGIS.Geoprocessing.IGeoProcessorResult)
I see that the parameter list is a string list but how do I feed my objects /classes to any geoprocessing tool?
Solved! Go to Solution.
Abel, Looking at the tool help regarding input parameters, as written, this does looks correct to me also
//gp
List<string> vals= new List<string>();
vals.Add(SelectedProximityLayer.URI);
vals.Add(...);
...
await Geoprocessing.ExecuteToolAsync("SelectLayerByLocation_management",
parameters)
Here is my stab at this (which does work for me):
Given these inputs (using Crimes and Fire stations point data sets from the community samples data):
I have this code - as Gintatuatas mentioned I just pass the param values straight to MakeValueArray:
internal class TestGP : Button
{
protected async override void OnClick()
{
var sel_tool_name = "SelectLayerByLocation_management";
var input_layer = MapView.Active.Map.GetLayersAsFlattenedList()
.OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Crimes");
if (input_layer == null) return;
var sel_layer = MapView.Active.Map.GetLayersAsFlattenedList()
.OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Fire_Stations");
if (sel_layer == null) return;
var parameters =
Geoprocessing.MakeValueArray(
input_layer, "WITHIN_A_DISTANCE_GEODESIC", sel_layer, "1000 Feet",
"NEW_SELECTION", "NOT_INVERT");
await Geoprocessing.ExecuteToolAsync(sel_tool_name, parameters);
//test params by showing the tool dialog
//await Geoprocessing.OpenToolDialogAsync(sel_tool_name, parameters);
}
(these are my parameters returned from Geoprocessing.MakeValueArray:)
After looking at the documentation and my prior work with ArcMap I see where I erred. The parameter Selecting Features is defined as a Feature Layer and I was trying to pass it a Feature object. All I had to do was select my feature in the layer and pass it the layer like so:
//clear the origin layer of ALL selections
SelectedOriginLayer.ClearSelection();
//select ONLY the origin feature in the origin layer
var qf = new QueryFilter();
qf.WhereClause = "FID=1";
Selection selOrigin = SelectedOriginLayer.Select(qf, SelectionCombinationMethod.New);
//clear the proximity layer of ALL selections
SelectedProximityLayer.ClearSelection();
//gp
List<object> values = new List<object>();
values.Add(SelectedProximityLayer); //in_layer: the selection will be applied to this layer
values.Add("WITHIN_A_DISTANCE_GEODESIC"); //overlap_type: we want features within a geodesic distance
values.Add(SelectedOriginLayer); //select_features: this is the source (the original)
values.Add("100 NAUTICALMILES"); //search_distance: search within 100 NM of the source
values.Add("NEW_SELECTION"); //selection_type: new selection
values.Add("NOT_INVERT"); //invert_spatial_relationship: do not invert selection
var parameters = Geoprocessing.MakeValueArray(values);
IGPResult gpResult = await Geoprocessing.ExecuteToolAsync("SelectLayerByLocation_management", parameters);
Unfortunately it didn't work. I have been trying for 2 days to get this to work and its a bit disappointing to me that at Pro 3.0 that geometry objects are not supported with geoprocessing. At least the ones I have tested. This is what ended up working for me after numerous tests (why are we still relying on strings when we have classes and objects?):
//clear the origin layer of ALL selections
SelectedOriginLayer.ClearSelection();
//select ONLY the origin feature in the origin layer
var qf = new QueryFilter();
qf.WhereClause = "FID=1";
Selection selOrigin = SelectedOriginLayer.Select(qf, SelectionCombinationMethod.New);
//clear the proximity layer of ALL selections
SelectedProximityLayer.ClearSelection();
//gp
List<string> vals= new List<string>();
vals.Add(SelectedProximityLayer.URI); //in_layer: the selection will be applied to this layer
vals.Add("WITHIN_A_DISTANCE_GEODESIC"); //overlap_type: we want features within a geodesic distance
vals.Add(SelectedOriginLayer.URI); //select_features: this is the source (the original)
vals.Add("100 NAUTICALMILES"); //search_distance: search within 100 NM of the source
vals.Add("NEW_SELECTION"); //selection_type: new selection
vals.Add("NOT_INVERT"); //invert_spatial_relationship: do not invert selection
IGPResult gpResult = await Geoprocessing.ExecuteToolAsync("SelectLayerByLocation_management", vals);
The way to set geoprocessing parameters is like this:
var parameters = Geoprocessing.MakeValueArray(SelectedProximityLayer, "WITHIN_A_DISTANCE_GEODESIC", featOrigin, "100 NAUTICALMILES", "NEW_SELECTION", "NOT_INVERT");
There is no need to form object or string array. Geoprocessing MakeValueArray knows how to convert each parameter depending on it type.
@GKmieliauskas that is exactly what I have in my original post. It doesnt work. I also posted the error.
Abel, Looking at the tool help regarding input parameters, as written, this does looks correct to me also
//gp
List<string> vals= new List<string>();
vals.Add(SelectedProximityLayer.URI);
vals.Add(...);
...
await Geoprocessing.ExecuteToolAsync("SelectLayerByLocation_management",
parameters)
Here is my stab at this (which does work for me):
Given these inputs (using Crimes and Fire stations point data sets from the community samples data):
I have this code - as Gintatuatas mentioned I just pass the param values straight to MakeValueArray:
internal class TestGP : Button
{
protected async override void OnClick()
{
var sel_tool_name = "SelectLayerByLocation_management";
var input_layer = MapView.Active.Map.GetLayersAsFlattenedList()
.OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Crimes");
if (input_layer == null) return;
var sel_layer = MapView.Active.Map.GetLayersAsFlattenedList()
.OfType<FeatureLayer>().FirstOrDefault(l => l.Name == "Fire_Stations");
if (sel_layer == null) return;
var parameters =
Geoprocessing.MakeValueArray(
input_layer, "WITHIN_A_DISTANCE_GEODESIC", sel_layer, "1000 Feet",
"NEW_SELECTION", "NOT_INVERT");
await Geoprocessing.ExecuteToolAsync(sel_tool_name, parameters);
//test params by showing the tool dialog
//await Geoprocessing.OpenToolDialogAsync(sel_tool_name, parameters);
}
(these are my parameters returned from Geoprocessing.MakeValueArray:)
Let me test this. The only difference I see is that I am using BasicFeatureLayer. Not sure why though. I think the example I was following used that class. Now that I look at the differences, the FeatureLayer class has more methods. So let me change to that class and test again.
@GKmieliauskas @CharlesMacleod well that seemed to work. thanks!!!
The only thing I did different was change my classes from BasicFeatureLayer to FeatureLayer.
I wonder if the GP tools just dont work with the BasicFeatureLayer class.
Actually I found the possible source of all my problems. I suspect that many other devs might also make this simple mistake. The function MakeValueArray takes as its input a variable number of objects as an array. I thought that if i passed it a List of Objects it would work as well. It does compile so merrily I went along. However, it is reading the List of Objects as a single object. What I needed to do was convert that List of Objects to an Array of objects AND then pass that to the GP tool.
Example:
//create the parameter list for the GP
List<object> vals = new List<object>();
vals.Add(lyrProximity); //in_layer: the selection will be applied to this layer
vals.Add("WITHIN_A_DISTANCE_GEODESIC"); //overlap_type: we want features within a geodesic distance
vals.Add(lyrOrigin); //select_features: this is the source
vals.Add(radiusInNm.ToString() + " NAUTICALMILES"); //search_distance: search within xNM of the source
vals.Add("NEW_SELECTION"); //selection_type: new selection
vals.Add("NOT_INVERT"); //invert_spatial_relationship: do not invert selection
//var parameters = Geoprocessing.MakeValueArray(vals); //compiles but does not work
var parameters = Geoprocessing.MakeValueArray(vals.ToArray()); //compiles and works
IGPResult gpResult = await Geoprocessing.ExecuteToolAsync("SelectLayerByLocation_management", parameters);
Actually I found the possible source of all my problems. I suspect that many other devs might also make this simple mistake.
//create the parameter list for the GP
List<object> vals = new List<object>();
vals.Add(lyrProximity); //in_layer: the selection will be applied to this layer
vals.Add("WITHIN_A_DISTANCE_GEODESIC"); //overlap_type: we want features within a geodesic distance
vals.Add(lyrOrigin); //select_features: this is the source
vals.Add(radiusInNm.ToString() + " NAUTICALMILES"); //search_distance: search within xNM of the source
vals.Add("NEW_SELECTION"); //selection_type: new selection
vals.Add("NOT_INVERT"); //invert_spatial_relationship: do not invert selection
//var parameters = Geoprocessing.MakeValueArray(vals); //compiles but does not work
var parameters = Geoprocessing.MakeValueArray(vals.ToArray()); //compiles and works
IGPResult gpResult = await Geoprocessing.ExecuteToolAsync("SelectLayerByLocation_management", parameters);