[GIS] ArcGIS 10.1 – c# custom geoprocessing function tool – how to populate a polyline output parameter

arcgis-10.1arcobjectsgeoprocessing

I'm at 10.1SP1 with VS2010.

In my tool's ParameterInfo property I create an output GPParameter object of IGPParameterEdit.DataType GPLineTypeClass.

Then in the Execute() method I do work that creates an IPolyline that I want to stuff into that parameter's IGPParameter.Value property as an IGPValue.

PROBLEM 1: Using the typical

IGPValue outValue = gputils.MakeGPValueFromObject(myIPolyline) 

using the GPUtilities class's IGPUtilities interface causes an E_FAIL COM error. huh. How does one debug that? Well, try another way –

So I tried this instead:

IGPLine2 outValue = new GPLineClass();
outValue.Polyline = myIPolyline;
gpUtils.PackGPValue((IGPValue)outValue, outputParam);

PROBLEM 2: But I get an invalid cast exception on that first line:

System.InvalidCastException: Unable to cast COM object of type 'ESRI.ArcGIS.Geoprocessing.GPLineClass' to interface type 'ESRI.ArcGIS.Geoprocessing.IGPLine2'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{1248339D-7B76-4A43-8FE0-752B1BF9D39A}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))

GPLineClass cannot be cast to IGPLine2. What! Documentation says this class implements this interface. I CAN cast it to IGPLine, but that interface is ridiculous – I'd have to disassemble the IPolyline and assign each vertex, and it won't work for multipart geometries.

SO – what is the secret sauce to stuff an IPolyline into an output parameter's value? And what the heck is up with that IGPLine2 thing?

Thanks!

Best Answer

If it weren't for that apparent bug casting a GPLine to IGPLine2, I would just return the polyline geometry in a GPLine output parameter.

Whatever. There is thankfully newish stuff in ArcObjects and the Silverlight API (3.1) for serializing to and deserializing from JSON (which of course the GP framework would have done under the hood anyway if I had been able to use a GPLine output parameter). It is a little inconvenient if you wanted to use it from a Desktop client, a minor thing in my case.

in the GP Tool, at the end of the Execute() method, serialize the geometry to JSON and assign it to the output parameter:

string traceresultjson = JsonSerializeGeometry(traceresult as IGeometry);
IGPString outValue = new GPStringClass();
outValue.Value = traceresultjson;
_gpUtilities.PackGPValue((IGPValue)outValue, resultParam);

Where _gpUtilities is an IGPUtilities instance of GPUtilitiesClass, and resultParam is the tool's output GPString IGPParameter set up in the tool's ParameterInfo property, and my JsonSerializeGeometry() method looks like:

protected string JsonSerializeGeometry(IGeometry geom)
{
    //see http://nicogis.blogspot.com/2012/04/json-arcobjects.html
    string json = "";
    IJSONWriter jsonWriter = new JSONWriterClass();
    jsonWriter.WriteToString();
    IJSONSerializer jsonSerializer = new JSONSerializerGdbClass();
    jsonSerializer.InitSerializer(jsonWriter, null);
    ((IExternalSerializerGdb)jsonSerializer).WriteGeometry(null, geom);
    json = Encoding.UTF8.GetString(jsonWriter.GetStringBuffer());
    Marshal.FinalReleaseComObject(jsonSerializer);
    jsonSerializer = null;
    Marshal.FinalReleaseComObject(jsonWriter);
    jsonWriter = null;

    return json;
}

In the Silverlight app consuming the service using the Geoprocessor class, deserialize the result in the Geoprocessor's ExecuteCompleted event handler:

string json = ((GPString)e.Results.OutParameters[0]).Value;
Geometry geom = Geometry.FromJson(json);
Graphic g = new Graphic() { Geometry = geom };