REST Web services


In this topic


About REST services

Representational State Transfer (REST) services contain resources and operations. ArcGIS Server provides a set of prepackaged service types, such as map and geocode, and a common REST service handler by which they can be accessed. Custom server object extensions (SOEs) can also be accessed using the ArcGIS Server REST service handler and provide a hierarchy of resources and operations.  
This topic discusses the implementation and use of a custom REST SOE. The following are some key items to remember in the context of this topic:
  • REST SOEs can only extend map services.
  • Implementation of the IRESTRequestHandler interface is required to enable REST access to an SOE.
  • The ESRI.ArcGIS.SOESupport library makes REST implementation easier.
To illustrate this topic, you will proceed through building the basic framework for a REST SOE. The SOE extends a map service to find features in all feature layers near a user-defined location. The name of the SOE is FindNearFeaturesRESTSOE and it exposes the hierarchy shown in the following illustration:

 
The uniform resource locators (URLs) for the resources and operations are shown in the following table:
#
URL
1
http://<service-url>/exts/ FindNearFeaturesRESTSOE
2
http:// <service-url>/exts/ FindNearFeaturesRESTSOE /customLayers/3
3
http:// <service-url>/exts/ FindNearFeaturesRESTSOE /customLayers/3/findNearFeatures    
The <service-url> is the URL to the map service being extended, for example, http://webservername/arcgis/rest/Texas/MapServer. 
The following describes the information in the preceding table:
  • 1—Simple Hypertext Transfer Protocol (HTTP) GET request on the first URL returns information about the root resource, in this case, the SOE. 
  • 2—Returns information about a feature layer with ID = 3. 
  • 3—Executes the findNearFeatures operation on features in the layer with ID = 3. This SOE has one child resource and one operation. Real-world SOEs can have operations off the root resource. 
ArcGIS provides a REST SOE template (currently only available in C#) that is integrated with Visual Studio. For more information refer to SOE Web services.

REST SOE service descriptions

REST SOEs must provide a schema to let ArcGIS Server and the client know what resources and operations it provides. JavaScript Object Notation (JSON) is used to define the format of a REST service schema and in most cases, the communication format between the client and server. The following code example shows the schema for FindNearFeaturesRESTSOE:
 
[JavaScript]
{
    "name": "FindNearFeaturesRESTSOE", "isCollection": false, "resources": [{
        "name": "customLayers", "isCollection": true, "operations": [{
            "name": "findNearFeatures", "parameters": ["location", "distance"], 
                "supportedOutputFormats": ["json", "html"]
        }
        ]
    }
    ]
}
Within the REST SOE, the service schema is returned from a call to IRESTRequestHandler.GetSchema. The definition of IRESTRequestHandler is provided in the following code example. The ESRI.ArcGIS.SOESupport library provides a set of convenient classes to assist you in the implementation of these methods. 
[C#]
public interface IRESTRequestHandler
{
    string GetSchema();

    byte[] HandleRESTRequest(string capabilities, string resourceName, string
        operationName, string operationInput, string outputFormat, out string
        responseProperties);
}
[VB.NET]
Public Interface IRESTRequestHandler

Function GetSchema() As String

    Function HandleRESTRequest(ByVal capabilities As String, ByVal resourceName As String, ByVal operationName As String, ByVal operationInput As String, ByVal outputFormat As String, <System.Runtime.InteropServices.Out()> ByRef responseProperties As String) As Byte()
        End Interface
On each request to the SOE, both methods previously listed are called. First, GetSchema is called to validate the in-bound URL and refers to a valid resource, then HandleRESTRequest is called to execute the request. The URL is broken into a resource name and an operation name. 
As previously mentioned, REST SOEs normally return JSON documents. The document syntax depends on the implementation of the resource or operation. The FindNearFeatures SOE supports the following three requests:
  1. http://<service-url>/exts/FindNearFeaturesRESTSOE—Returns an array of customLayer objects, one for each layer in the map service. See the following code example:
     
[JavaScript]
{
    "customLayers": [ < customLayer1 > ,  < customLayer2 > ]
}
A customLayer is defined as shown in the following code example:
[JavaScript]
{
    "name":  < layerName > , "id":  < layerId > , "extent":  < envlope > 
}
  1. http://<service-url>/exts/FindNearFeaturesRESTSOE/customLayers/<layerId>Returns information about the layer with the defined ID. See the following code example:
[JavaScript]
{
    "name":  < layerName > , "id":  < layerId > , "extent":  < envlope > 
}
  1. http://<service-url>/exts/FindNearFeaturesRESTSOE/customLayers/<layerId>/findNearFeaturesRequires two input parameters, a location and a distance. It buffers the location using the specified distance and searches for features in the layer with the defined ID. 

    The input location and distance are specified as query parameters in the URL, such as shown in the following URL:
    • http://<service-url>/exts/FindNearFeaturesRESTSOE/customLayers/1/findNearFeatures
      ?location={"x":570768.61635393905,"y":4906395.7492997088}&distance=100
The operation returns a feature collection. The syntax of the response is shown in the following code example:
[JavaScript]
{
    "displayFieldName": "<displayFieldName>", "fieldAliases": {
        "<fieldName1>": "<fieldAlias1>", "<fieldName2>": "<fieldAlias2>"
    }
    , "geometryType": "<geometryType>", "spatialReference":  < spatialReference > , 
        "features": [ < feature1 > ,  < feature2 > ]
}
Each feature is defined as a JSON object with two properties, a geometry and a set of attributes. See the following code example:
[JavaScript]
{
    "geometry":  < geometry > , "attributes": {
        "name1":  < value1 > , "name2":  < value2 > , 
    }
}

Using SOESupport

Basically, a REST SOE requires the implementation of the GetSchema and HandleRESTRequest methods. Implementation of GetSchema is trivial, since it only returns a string that describes the service. Implementation of HandleRESTRequest is more involved, since it needs to deserialize JSON input, execute the business logic, and generate a JSON response. When ArcGIS Server calls HandleRESTRequest, the library calls the appropriate handler function (by using the schema and the HandleRESTRequest parameters). Basically, as a developer, you are only responsible for defining the schema and writing the handler functions.

Creating the resources schema

The schema is created by FindNearFeaturesRESTSOE by using the classes, RestResource and RestOperation, as shown in the following code example:
[C#]
private RestResource CreateRestSchema()
{
    RestResource soeResource = new RestResource("FindNearFeaturesRESTSOE", false,
        RootSOE, c_cap_getInfo);

    RestResource customLayerResource = new RestResource("customLayers", true,
        CustomLayer, c_cap_getInfo);

    RestOperation findNearFeatsOp = new RestOperation("findNearFeatures", new
        string[]
    {
        "location", "distance"
    }
    , new string[]
    {
        "json", "html"
    }
    , FindNearFeatures, c_cap_findFeatures);

    customLayerResource.operations.Add(findNearFeatsOp);

    soeResource.resources.Add(customLayerResource);

    return soeResource;
}
[VB.NET]
Private Function CreateRestSchema() As RestResource
    Dim soeResource As New RestResource("FindNearFeaturesRESTSOE", False, RootSOE, c_cap_getInfo)
    Dim customLayerResource As New RestResource("customLayers", True, CustomLayer, c_cap_getInfo)
    Dim findNearFeatsOp As New RestOperation("findNearFeatures", New String() { "location", "distance" }, New String() { "json", "html" }, FindNearFeatures, c_cap_findFeatures)
    customLayerResource.operations.Add(findNearFeatsOp)
    soeResource.resources.Add(customLayerResource)
    Return soeResource
End Function
The parameters for the RestResource constructor are the name, whether it is a collection or not, the handler function, and the capability required to access the resource. A RestOperation is defined by passing the name of the operation, a list of parameter names, a list of supported formats, the handler function, and the capability required to execute the operation. Capabilities, such as getInfo or findFeatures in this example, are not required.
A RestResource has a collection of child resources and a collection of operations. The preceding code example adds the operation findNearFeatures to the customLayerResource operation collection. It also adds the customLayerResource to the SOE's resource collection (soeResource’s children resources collection).

Using the SoeRestImpl class

A REST SOE using SOESupport uses an instance of the SoeRestImpl class. This class has code to do the following:
  • Validate the schema
  • Validate the resourceName and operationName of the HandleRESTRequest calls
  • Validate capabilities
  • Logging of service calls and responses
  • Error handling

SoeRestImpl implements IRESTRequestHandler. Normally, the SOE class contains an instance of the SoeRestImpl class and keeps a reference to the IRESTRequestHandler interface. The following code example shows the constructor of the FindNearFeaturesRESTSOE class:
[C#]
public class FindNearFeaturesRESTSOE: [...]
{
    private const string c_soe_name = "FindNearFeaturesRESTSOE";
    private ServerLogger logger;
    private IRESTRequestHandler reqHandler;

    public FindNearFeaturesRESTSOE()
    {
        logger = new ServerLogger();

        RestResource rootResource = CreateRestSchema();

        SoeRestImpl restImpl = new SoeRestImpl(c_soe_name, rootResource);
        reqHandler = (IRESTRequestHandler)restImpl;
    }
    . . .
[VB.NET]
Public Class FindNearFeaturesRESTSOE
    Private Const c_soe_name As String = "FindNearFeaturesRESTSOE"
    Private logger As ServerLogger
    Private reqHandler As IRESTRequestHandler

    Public Sub New()
        logger = New ServerLogger()
        Dim rootResource As RestResource = CreateRestSchema()
        Dim restImpl As New SoeRestImpl(c_soe_name, rootResource)
        reqHandler = CType(restImpl, IRESTRequestHandler)
    End Sub
The schema is created, then SoeRestImpl is created, passing the schema. Finally, the class keeps a reference to the interface as a member variable. The SOE class forwards calls of GetSchema and HandleRESTRequest to the SoeRestImpl instance. See the following code example:
[C#]
public string GetSchema()
{
    return reqHandler.GetSchema();
}

public byte[] HandleRESTRequest(string capabilities, string resourceName, string
    operationName, string operationInput, string outputFormat, string
    requestProperties, out string responseProperties)
{
    return reqHandler.HandleRESTRequest(capabilities, resourceName, operationName,
        operationInput, outputFormat, requestProperties, out responseProperties);
}
[VB.NET]
Public Function GetSchema() As String
    Return reqHandler.GetSchema()
End Function


Public Function HandleRESTRequest(ByVal capabilities As String, ByVal resourceName As String, ByVal operationName As String, ByVal operationInput As String, ByVal outputFormat As String, ByVal requestProperties As String, <System.Runtime.InteropServices.Out()> ByRef responseProperties As String) As Byte()
    Return reqHandler.HandleRESTRequest(capabilities, resourceName, operationName, operationInput, outputFormat, requestProperties, responseProperties)
End Function

Using the handler functions

The handler functions (RootSOE, CustomLayer, FindNearFeatures) are perhaps the most important aspect of the schema graph. They will be called when a request for a given resource or operation is handled by the SOE. The signature of the handler functions are defined by two delegates in SOESupport. See the following code example:
[C#]
public delegate byte[] ResourceHandler(NameValueCollection boundVariables, string
    outputFormat, string requestProperties, out string responseProperties);

public delegate byte[] OperationHandler(NameValueCollection boundVariables,
    JsonObject operationInput, string outputFormat, string requestProperties, out
    string responseProperties);
[VB.NET]
Public Delegate Function ResourceHandler(ByVal boundVariables As NameValueCollection, ByVal outputFormat As String, ByVal requestProperties As String, <System.Runtime.InteropServices.Out()> ByRef responseProperties As String) As Byte()
Public Delegate Function OperationHandler(ByVal boundVariables As NameValueCollection, ByVal operationInput As JsonObject, ByVal outputFormat As String, ByVal requestProperties As String, <System.Runtime.InteropServices.Out()> ByRef responseProperties As String) As Byte()
The following code example shows the handler function for the root resource (soeResource). Its signature matches the ResourceHandler delegate. Recalling this function returns a JSON document with an array of customLayer objects.
[C#]
private byte[] RootSOE(NameValueCollection boundVariables, string outputFormat,
    string requestProperties, out string responseProperties)
{
    responseProperties = null;

    CustomLayerInfo[] layerInfos = GetLayerInfos();

    JsonObject[] jos = new JsonObject[layerInfos.Length];

    for (int i = 0; i < layerInfos.Length; i++)
        jos[i] = layerInfos[i].ToJsonObject();

    JsonObject result = new JsonObject();
    result.AddArrayAddMember("customLayers", jos);

    string json = result.ToJson();

    return Encoding.UTF8.GetBytes(json);
}
[VB.NET]
Private Function RootSOE(ByVal boundVariables As NameValueCollection, ByVal outputFormat As String, ByVal requestProperties As String, <System.Runtime.InteropServices.Out()> ByRef responseProperties As String) As Byte()
    responseProperties = Nothing
    Dim layerInfos() As CustomLayerInfo = GetLayerInfos()
    Dim jos(layerInfos.Length - 1) As JsonObject
    For i As Integer = 0 To layerInfos.Length - 1
        jos(i) = layerInfos(i).ToJsonObject()
    Next i
    Dim result As New JsonObject()
    result.AddArrayAddMember("customLayers", jos)
    Dim json As String = result.ToJson()
    Return Encoding.UTF8.GetBytes(json)
End Function
Handler functions will normally be wrapper functions that deserialize JSON input, call a business logic function, then serialize the output to JSON. The preceding function has no input, calls GetLayerInfos, then serializes the response to JSON.
The next resource exposed is customLayers, a collection resource. Service requests address resources within the collection by ID. In the customLayers resource case, think of the resource as having the following "template" URL:
  • http://<service-url>/exts/FindNearFeaturesRESTSOE/customLayers/{customLayersID}—And that a request for the customLayer has an ID of 3 as shown in the following URL:
    • http://<service-url>/exts/FindNearFeaturesRESTSOE/customLayers/3—Yields to an invocation of the handler function for the customLayers resource, where an entry in boundVariables is called "customLayersID" and its value is 3. The following code example shows the handler function:
[C#]
private byte[] CustomLayer(NameValueCollection boundVariables, string outputFormat,
    string requestProperties, out string responseProperties)
{
    responseProperties = null;

    //The LayerID.
    int layerID = Convert.ToInt32(boundVariables["customLayersID"]);

    //Execute.
    CustomLayerInfo layerInfo = GetLayerInfo(layerID);

    string json = layerInfo.ToJsonObject().ToJson();

    return Encoding.UTF8.GetBytes(json);
}
[VB.NET]
Private Function CustomLayer(ByVal boundVariables As NameValueCollection, ByVal outputFormat As String, ByVal requestProperties As String, <System.Runtime.InteropServices.Out()> ByRef responseProperties As String) As Byte()
    responseProperties = Nothing
    'The LayerID.
    Dim layerID As Integer = Convert.ToInt32(boundVariables("customLayersID"))
    'Execute.
    Dim layerInfo As CustomLayerInfo = GetLayerInfo(layerID)
    Dim json As String = layerInfo.ToJsonObject().ToJson()
    Return Encoding.UTF8.GetBytes(json)
End Function
The final handler function for an operation is FindNearFeatures. In this case, the signature has an extra parameter for the operationInput of type JsonObject. This JsonObject contains members for the location and distance inputs (query parameters in the URL). See the following code example:
[C#]
private byte[] FindNearFeatures(NameValueCollection boundVariables, JsonObject
    operationInput, string outputFormat, string requestProperties, out string
    responseProperties)
{

    responseProperties = null;

    //The LayerID.
    int layerID = Convert.ToInt32(boundVariables["customLayersID"]);

    //The location.
    JsonObject jsonPoint;
    if (!operationInput.TryGetJsonObject("location", out jsonPoint))
        throw new ArgumentNullException("location");

    IPoint location = Conversion.ToGeometry(jsonPoint,
        esriGeometryType.esriGeometryPoint)as IPoint;
    if (location == null)
        throw new ArgumentException("FindNearFeatures: invalid location", "location")
            ;

    //The distance.
    double ? distance;
    if (!operationInput.TryGetAsDouble("distance", out distance) ||
        !distance.HasValue)
        throw new ArgumentException("FindNearFeatures: invalid distance", "distance")
            ;

    //Execute asking the map server to generate JSON directly (not an IRecordSet).
    byte[] result = FindNearFeatures(layerID, location, distance.Value);

    return result;
[VB.NET]
Private Function FindNearFeatures(ByVal boundVariables As NameValueCollection, ByVal operationInput As JsonObject, ByVal outputFormat As String, ByVal requestProperties As String, <System.Runtime.InteropServices.Out()> ByRef responseProperties As String) As Byte()
    responseProperties = Nothing
    'The LayerID.
    Dim layerID As Integer = Convert.ToInt32(boundVariables("customLayersID"))
    'The location.
    Dim jsonPoint As JsonObject
    If (Not operationInput.TryGetJsonObject("location", jsonPoint)) Then
        Throw New ArgumentNullException("location")
    End If
    Dim location As IPoint = TryCast(Conversion.ToGeometry(jsonPoint, esriGeometryType.esriGeometryPoint), IPoint)
    If location Is Nothing Then
        Throw New ArgumentException("FindNearFeatures: invalid location", "location")
    End If
    'The distance.
    Dim distance? As Double
    If (Not operationInput.TryGetAsDouble("distance", distance)) OrElse (Not distance.HasValue) Then
        Throw New ArgumentException("FindNearFeatures: invalid distance", "distance")
    End If
    'Execute asking the map server to generate JSON directly (not an IRecordSet).
    Dim result() As Byte = FindNearFeatures(layerID, location, distance.Value)
    Return result
Assume the following Web request is received by the SOE:
  • http://<service-url>/exts/FindNearFeaturesRESTSOE/customLayers/1/findNearFeatures
    ?location={"x":570768.61,"y":4906395.74}&distance=100
SOESupport creates a JSonObject instance that corresponds to the following JSON document:
[JavaScript]
{
    "location": {
        "x": 570768.61, "y": 4906395.74
    }
    , "distance": 100
}
The value of the location parameter is another JsonObject. The code example uses the TryGetJsonObject method to get that object, which is converted into an IPoint instance (an ArcObjects Point instance) using Conversion.ToGeometry. When the distance parameter is retrieved, the business function is called. This function uses a map service method that is capable of generating JSON output directly. In real-world SOEs, you might want to access fine-grained ArcObjects rather than using the coarse-grained methods of the map service.

Using the JsonObject class

JsonObject is a class in SOESupport that can be used to serialize and deserialize JSON documents. You can create an instance by using a string that contains JSON. See the following code example:
[C#]
string json_feature = 
    "{\"geometry\" : {\"x\" : -118.15, \"y\" : 33.80, \"spatialReference\" : { \"wkid\" : 4326 } }, \"attributes\" : {\"OWNER\" : \"Joe Smith\",\"VALUE\" : 94820.37, \"APPROVED\" : true,\"LASTUPDATE\" : 1227663551096}}";
JsonObject aFeature = new JsonObject(json_feature);
[VB.NET]
Dim json_feature As String = "{""geometry"" : {""x"" : -118.15, ""y"" : 33.80, ""spatialReference"" : { ""wkid"" : 4326 } }, ""attributes"" : {""OWNER"" : ""Joe Smith"",""VALUE"" : 94820.37, ""APPROVED"" : true,""LASTUPDATE"" : 1227663551096}}"
Dim aFeature As New JsonObject(json_feature)
You can also create an empty instance, then start adding members. See the following code example:
[C#]
JsonObject sr = new JsonObject();
sr.AddLong("wkid", 4326);
JsonObject pt = new JsonObject();
pt.AddDouble("x", 1.0);
pt.AddDouble("y", 1.0);
pt.AddObject("spatialReference", sr);
JsonObject jo = new JsonObject();
jo.AddLong("long", 1);
jo.AddString("str", "foo");
//Array with jo and w/nested array (also with jo).
jo.AddArray("arr", new object[]
{
    1, "a", aFeature, new object[]
    {
        2, pt
    }
}

);
jo.AddObject("doubles", new double[]
{
    1.0, 2.0, 3.0
}

);
jo.AddJsonObject("obj", aFeature);
jo.AddJsonObject("nestedJO", pt);
[VB.NET]
JsonObject sr = New JsonObject();
sr.AddLong("wkid", 4326);
JsonObject pt = New JsonObject();
pt.AddDouble("x", 1.0);
pt.AddDouble("y", 1.0);
pt.AddObject("spatialReference", sr);
JsonObject jo = New JsonObject();
jo.AddLong("long", 1);
jo.AddString("str", "foo");
'Array with jo and w/nested array (also with jo).
jo.AddArray("arr", New Object[] { 1, "a", aFeature, New Object[] { 2, pt} });
jo.AddObject("doubles", New Double[] { 1.0, 2.0, 3.0 });
jo.AddJsonObject("obj", aFeature);
jo.AddJsonObject("nestedJO", pt);
To access members of an instance, use the TryGetMember method. See the following code example:
[C#]
JsonObject jsonPoint;
if (!operationInput.TryGetMember("location", out jsonPoint))
    throw new ArgumentNullException("location");
[VB.NET]
Dim jsonPoint As JsonObject
If (Not operationInput.TryGetMember("location", jsonPoint)) Then
    Throw New ArgumentNullException("location")
End If
You can serialize a JsonObject to a string by calling ToJson() and finally, it implements IEnumerator; therefore, you can enumerate the members (for example, with a foreach).

Handling exceptions

REST SOEs need to send a JSON error document when problem occurs. They should also add an informative error message to the server's log. All exceptions thrown by a handler function are caught by the code in SoeRestImpl. This code adds the log entry and creates the JSON error from the exception. As a developer, throw exceptions to indicate invalid conditions.

Logging with ServerLogger and AutoTimer

SOESupport includes two classes—ServerLogger and AutoTimer—that can be used for logging. FindNearFeaturesRESTSOE creates an instance of ServerLogger in its constructor and keeps a reference to it. The Construct method shows an example of how to use the two classes. See the following code example:
[C#]
//IObjectConstruct.
public void Construct(IPropertySet props)
{
    AutoTimer timer = new AutoTimer();
    logger.LogMessage(ServerLogger.msgType.infoSimple, "Construct",  - 1, c_soe_name
        + " Construct has started.");
    configProps = props;
    logger.LogMessage(ServerLogger.msgType.infoSimple, "Construct",  - 1,
        timer.Elapsed, c_soe_name + " Construct has completed.");
}
[VB.NET]
'IObjectConstruct.

Public Sub Construct(ByVal props As IPropertySet)
    Dim Timer As New AutoTimer()
    logger.LogMessage(ServerLogger.msgType.infoSimple, "Construct", -1, c_soe_name & " Construct has started.")
    configProps = props
    logger.LogMessage(ServerLogger.msgType.infoSimple, "Construct", -1, Timer.Elapsed, c_soe_name & " Construct has completed.")
End Sub
The AutoTimer constructor starts a timer. The first LogMessage invocation adds a log entry indicating the Construct method has started. The second LogMessage invocation adds a log entry indicating Construct has finished, but uses the AutoTimer.Elapsed to add the elapsed time (seconds) to the log entry. Adding elapsed times to the log is not a requirement, but can help you find and fix performance problems. Adding log entries when a handler function is called, is handled by the SoeRestImpl class. 

Registering the SOE with COM

The SOE is hosted in a Component Object Model (COM) visible dynamic-link library (DLL). Copy and register the DLL on all server object container (SOC) machines by issuing the following command:
regasm FindNearFeaturesRESTSoe.dll /codebase
Do the following to strongly sign the library to register it:
  1. Open the project properties.
  2. Click the Signing tab, select the Sign the assembly check box, then select New in the combo box. The codebase parameter registers the full path to the library so the SOE does not need to be added to the global assembly cache (GAC).

Registering the SOE with ArcGIS Server

The SOE also needs to be registered with ArcGIS Server. This step must be done after it has been COM registered on all SOC machines, since the geographic information system (GIS) server creates an instance of it using COM.  
A simple console application that uses ArcObjects and the Server library can be used to register (or unregister) the SOE as a map server extension. You only need to register the SOE with ArcGIS Server once.  
It is important that the class identifier (CLSID), Name, and capabilities in the registration code match those in the SOE project.

Adding the SOE to a service

To use the SOE, enable the extension on a map service using ArcGIS Server Manager or ArcCatalog. See the following screen shot:
Once it is running, access the service in the ArcGIS Server REST Services Directory. The service appears as an entry at the end of the page for the Map resource on which it is enabled. See the following screen shot:
When you click FindNearFeaturesRestSOE on the preceding screen shot, a set of Hypertext Markup Language (HTML) pages are generated by the ArcGIS Server REST HTTP Handler based on the schema returned by the SOE. The page shows the root resource and lists each feature layer in the map service. See the following screen shot: 

 
When you click a layer, information about the layer appears with a link to the findNearFeatures supported operation. See the following screen shot: 

When you click the findNearFeatures operation, a set of HTML inputs appear depending on the inputs of the SOE, then you can go to the FindFeatures operation. To use it, specify a JSON point as the location, with the following syntax: 
  • {x: <double>, y: <double>}
See the following screen shot:
The results can be returned in a variety of formats, depending on the support within the SOE. In the following example, the output format is JSON: