3

I'm currently using the Statistics geoprocessing tool (called Summary Statistics in the UI) from ArcObjects. This tool accepts two "complex" (for lack of a better term) arguments:

  • Case fields parameter: a list of attributes that are used for grouping the statistics
  • Statistics field parameter: a list of pairs of attribute and statistic type, indicating what to calculate for the given attribute

The only way I've been able to get it to work from ArcObjects so far is by passing strings in the format that you can see logged in the results window when you run it manually:

  • List (case fields parameter): a semicolon separating each element ("ELEMENT1;ELEMENT2")
  • List of pairs (statistics field parameter): the pair itself separated by a space, then each pair separated by a semicolon ("PAIR1ITEM1 PAIR1ITEM2;PAIR2ITEM1 PAIR2ITEM2")

Like this:

var statsGP = new Statistics()
{
    in_table = @"some\gdb\path\afeatureclass",
    out_table = @"some\gdb\path\outputtable",
    statistics_fields = "OBJECTID COUNT;SOME_OTHER_ATTR SUM",
    case_field = "GROUP_ATTR1;GROUP_ATTR2"
};
var GP = new Geoprocessor();
GP.Execute((IGPProcess)statsGP, null);

The Python examples show that you just pass in a list, but .NET arrays (a close equivalent) don't seem to work:

var statsGP = new Statistics()
{
    in_table = @"some\gdb\path\afeatureclass",
    out_table = @"some\gdb\path\outputtable",
    statistics_fields = new string[][] {new string[]{"OBJECTID", "COUNT"}, new string[]{"SOME_OTHER_ATTR","SUM"}},
    case_field = new string[]{"GROUP_ATTR1", "GROUP_ATTR2"}
};

I get the error System.Runtime.InteropServices.SafeArrayTypeMismatchException: Specified array was not of the expected type.. I also tried ESRI's VarArray as a wild stab in the dark after trying to figure out some example Java code:

static IVariantArray MakeVarArray(params object[] elements)
{
    var result = new VarArray();
    foreach (var e in elements)
    {
        result.Add(e);
    }

    return result;
}

...

var statsGP = new Statistics()
{
    in_table = @"some\gdb\path\afeatureclass",
    out_table = @"some\gdb\path\outputtable",
    statistics_fields = MakeVarArray(MakeVarArray("OBJECTID", "COUNT"), MakeVarArray("OBJECTID", "COUNT")),
    case_field = MakeVarArray("GROUP_ATTR1", "GROUP_ATTR2")
};

But it just gave a different error:

ERROR 000622: Failed to execute (Summary Statistics). Parameters are not valid.
ERROR 000623: Invalid value type for parameter statistics_fields.

Is there a cleaner way to pass complex arguments into a geoeprocessing tool in ArcObjects? I really don't want to litter my code with string building code.

1

You can use the IGpValueTableObject to be Using value tables:

A value table is a flexible object that can be used as input for a multivalue parameter. Examples of multivalue parameter values focus on the text value of the parameter that can become difficult to use when there are numerous values with complex paths. The value table is used to organize the values into a table so values can be easily added or removed, eliminating the complexity of parsing a multivalue text string. The table can be thought of as a virtual matrix of values that is not persisted as an actual table because it is a device for managing many values in a program.

1

This post was helpful in helping me solve a similar problem. I can confirm that this works using the resources linked to by Rich. I'm using ArcGIS Desktop 10.4.1.

The updated resource link (claims to target 10.5) is here: http://desktop.arcgis.com/en/arcobjects/latest/net/84349562-e062-44ee-8db0-9fcdcd64708b.htm

I initially missed the need for the ValueTableObject SetColumns method for parameters with "spaces" in them (ex. "columnName SUM"). It appears that every "space" is considered a delimiter for a new column...

The code below only does SUM statistics, but it could be modified for more flexibility.

In my case, I broke the GP tool run into pieces:

            string pathToOutputFGDBTable = null;
            string pathToInputFGDBTable = null;
            List<string> listStatisticsFields = new List<string>();
            List<string> listCaseFields = new List<string>();
            bool blnRunSuccess = false;

            pathToOutputFGDBTable = "FGDBPath\\outputTableName";
            pathToInputFGDBTable = "FGDBPath\\inputTableName;
            listStatisticsFields.Clear();
            listStatisticsFields.Add("DoubleTypeColumnToSummarize");
            listCaseFields.Clear();
            listCaseFields.Add("CaseColumn1");
            listCaseFields.Add("CaseColumn2");

            blnRunSuccess = runStatisticsGPTool(pathToInputFGDBTable, pathToOutputFGDBTable, listStatisticsFields, listCaseFields);

Then in the function holding the GP execute tool:

    private bool runStatisticsGPTool(string inputTable, string outputTable, List<string> listSumFields, List<string> listCaseFields)
    {
        bool ifSucceeded = false;

        IGeoProcessor2 gp = (IGeoProcessor2)new GeoProcessor();
        gp.OverwriteOutput = true;
        IGeoProcessorResult result = new GeoProcessorResult();

        IVariantArray parameters = new VarArrayClass();
        object sev = null;

        try
        {
            parameters.Add(inputTable);
            parameters.Add(outputTable);
            IGpValueTableObject vtSumFieldsObject = getValueTableObjectForStatistics(listSumFields,"SUM");
            parameters.Add(vtSumFieldsObject);
            IGpValueTableObject vtCaseFieldsObject = getValueTableObjectForStatistics(listCaseFields, null);
            parameters.Add(vtCaseFieldsObject);

            //execute tool
            result = gp.Execute("Statistics_analysis", parameters, null);

            //I'm not sure that this loop actually does anything in my implementation...
            while (result.Status == esriJobStatus.esriJobExecuting)
            {
                Thread.Sleep(1000);
            }

            string strStatusMessage = gp.GetMessages(ref (sev));
            ifSucceeded = true;

        }
        catch (Exception ex)
        {
            //string exMessage = ex.Message;
            //string gpErrorMessage = gp.GetMessages(ref sev);
            ifSucceeded = false;
        }
        return ifSucceeded;
    }

And finally in the function generating the Value Table:

    private GpValueTableObject getValueTableObjectForStatistics(List<string> listStatisticsFields, string statisticsType)
    {
        IGpValueTableObject vtObject = new GpValueTableObject();
        if (statisticsType != null)
        {
            vtObject.SetColumns(2);
        }
        else
        {
            vtObject.SetColumns(1);
        }
        object row = "";
        foreach (string field in listStatisticsFields)
        {
            if (statisticsType != null)
            {
                row = field + " " + statisticsType;
            }
            else
            {
                row = field;
            }
            vtObject.AddRow(ref row);
        }
        return (GpValueTableObject)vtObject;
    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.