Geometry task

The Geometry task provides a range of methods for performing geometric operations on input geometries. With the Geometry task, you can calculate area and length, generate a buffer, retrieve label points, project geometries into a different spatial reference, simplify geometries, and determine the spatial relationship between geometries. To use a Geometry task in your application, you need to implement the task's execution logic and provide a way for users to specify input geometry and view the task's results.

This topic focuses on the Geometry task's methods. It is assumed that you are familiar with implementing code for capturing input geometries and defining elements for displaying results such as GraphicsLayers and MapTips. For further information about those topics, see Creating a map, Using a Draw surface, Query task, Find task, and Identify task.

NoteNote:

Additional examples of the Geometry task functionality can be found in the Interactive SDK.

Operations

Each operation provided by the Geometry task has a method to invoke the operation, an event that is fired when the operation is complete, a property that is updated when the operation is complete, and a particular type of results. For each operation, the method to invoke it follows the form <operation name>Async, the event fired upon completion is called <operation name>Completed, and the property updated is named <operation name>LastResult. For instance, the method to initiate a buffer operation is BufferAsync, the completed event is BufferCompleted, and the results property is BufferLastResult.

For each operation, results are wrapped in an event arguments object and passed to the second parameter of the completed event handler. Attaching an event handler to the completed event allows you to manipulate results as soon as a geometric operation is complete. Additionally, the <operation name>LastResult property is updated with the same results. This property allows you to use Silverlight data binding to automatically update controls with the new results and provides an easy way to store the collection of results for later reference (for example, when selecting a corresponding item from a ComboBox).

The geometric operations, their arguments, and their results are summarized in the following table:

Operation

Argument Type

Argument Description

Results Type

Results Description

AreasAndLengths

List<Graphic>

Polygon graphics from which to calculate areas and perimeters.

AreasAndLengths

Two lists of doubles—one containing the input polygons' areas, the other containing their lengths.

Buffer

BufferParameters

Parameters object containing the graphics to buffer, the input and output spatial references, the buffer distances and units, and whether to union overlapping result buffers.

List<Graphic>

Graphics containing the buffer geometries.

LabelPoints

List<Graphic>

Graphics for which to retrieve the label points.

List<Graphic>

Graphics containing the label points.

Lengths

List<Graphic>

Polyline graphics of which to calculate the lengths.

List<double>

List of doubles containing the lengths of the input polygons.

Project

List<Graphic>, Spatial Reference

Graphics to be projected and spatial reference into which to project graphics.

List<Graphic>

Graphics containing the projected geometries.

Relation

List<Graphic>, List<Graphic>, GeometryRelation, String

The two lists contain the graphics to compare—graphics in one list will be compared to graphics in the second list. The GeometryRelation parameter contains the relationship to check for. If a custom relationship is desired (GeometryRelation set to esriGeometryRelationRelation), the fourth parameter will specify the Shape Comparison Language string that defines the custom relationship.

List<GeometryRelationPair>

A list of GeometryRelationPairs that indicates the pairs of graphics from the input lists for which the specified spatial relationship is true. Each pair contains the indexes of the graphics from each of the two input lists to which the relationship applies.

For example, suppose the operation's graphics input is a list with two points and a list with two polygons, where the first point in the list is within both polygons, while the second point is within the first polygon. Suppose also that the input spatial relationship is esriGeometryRelationWithin. The results will contain three GeometryRelationPairs—one for each of the three cases where there is a point within a polygon. One pair will contain indexes of 0 and 0 (first point within first polygon), another will contain indexes of 0 and 1 (first point within second polygon), and the last will contain indexes of 1 and 0 (second point within first polygon).

Simplify

List<Graphic>

Graphics containing the geometries to simplify.

GraphicsEventArgs

Graphics containing the simplified geometries.

Use

To use any of the Geometry task's operations, you first need to instantiate a GeometryService object and specify the geometry service to use for performing geometric operations. As with all tasks, the Geometry task is declared and initialized in the code-behind because tasks alone do not define any user interface (UI), but rather encapsulate pieces of execution logic. XAML is intended for implementation of an application's presentation layer, while the code-behind is where business logic is implemented.

The geometry service can be specified by passing the service's URL to the task's constructor as shown below. To find the URL, you can use the ArcGIS Services Directory. See Discovering Services for more information.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");

To programmatically manipulate an operation's results as soon as the operation is complete, wire a handler for the operation's completed event to the task. For instance, to do this for the AreasAndLengths operation, declare a method to handle the event as follows:

private void GeometryService_AreasAndLengthsCompleted(object sender, AreasAndLengthsEventArgs args)
{
}

Then, in the block of code where you've initialized the GeometryService object, associate the handler with it as follows:

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.AreasAndLengthsCompleted += GeometryService_AreasAndLengthsCompleted;

The following sections show how to use each Geometry task operation, including snippets of .NET code for executing the operation and handling its results. These samples assume that code for retrieving operation input and specifying a place for operation output (for example, a GraphicsLayer declared in XAML) has already been implemented. All the code is written in C#.

AreasAndLengths

The AreasAndLengths operation calculates the areas and perimeters of a list of polygon graphics. Since GraphicsLayers store their features as lists of graphics, a common use pattern for this operation is to retrieve a GraphicsLayer and pass its graphics collection to the operation as shown in the following code. The code assumes that you've already created a Map containing a GraphicsLayer. The Map has an x:Name of MyMap and the GraphicsLayer has an ID of MyPolygonGraphicsLayer.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.AreasAndLengthsCompleted += GeometryService_AreasAndLengthsCompleted;
GraphicsLayer graphicsLayer = MyMap.Layers["MyPolygonGraphicsLayer"] as GraphicsLayer;
geometryService.AreasAndLengthsAsync(graphicsLayer.Graphics.ToList());

The areas and lengths returned by the operation are in two lists of doubles stored in the Results.Areas and Results.Lengths properties of the result object. The result object is accessible through either the second parameter of an AreasAndLengthsCompleted event handler or the AreasAndLengthsLastResult property of the GeometryService object. The following code shows accessing results in an event handler. The example iterates through the results, building a string with the calculated areas and perimeters. The returned values are at the same indexes in their respective lists as the input graphics from which the values were calculated.

private void GeometryService_AreasAndLengthsCompleted(object sender, AreasAndLengthsEventArgs args)
{
  string results = "";
  for (int i = 0; i < args.Results.Areas.Count; i++)
  {
    results += string.Format("Graphic {0}: Area = {1}, Perimeter = {2}\n", 
                             i, args.Results.Areas[i], args.Results.Lengths[i]);
  }
}

Buffer

The Buffer operation creates polygon graphics with geometries that are calculated by applying a buffer of a specified distance around a list of input graphics. The operation takes a BufferParameters object as its input, which, in addition to the graphics to be buffered, specifies the buffer distances and units, the spatial references to use during the operation and for the output graphics, and whether to union overlapping output buffers.

The following code assumes that you've already created a Map with an x:Name of MyMap. A BufferParameters object is initialized such that the buffer operation's units will be miles, its spatial reference will be World Hotine, the output's spatial reference will match that of MyMap, and overlapping output features will be unioned.

NoteNote:

The operation spatial reference here, World Hotine, is well suited to minimizing projection distortion in geometric operations when the location of the operation can be anywhere in the world. Other projections will be more suitable when the possible locations for the operation are limited to a smaller area.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.BufferCompleted += GeometryService_BufferCompleted;

BufferParameters bufferParameters = new BufferParameters()
{
  Unit = LinearUnit.StatuteMile,
  BufferSpatialReference = new SpatialReference(54025),
  OutSpatialReference = MyMap.SpatialReference,
  UnionResults = true
};

Each distance specified on the Buffer Parameters object is applied to each graphic that is buffered. The following code specifies distances of 10 and 20 miles.

TipTip:

The Distances property is read-only, so distances must be specified through the Add or AddRange methods.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.BufferCompleted += GeometryService_BufferCompleted;

BufferParameters bufferParameters = new BufferParameters()
{
  Unit = LinearUnit.StatuteMile,
  BufferSpatialReference = new SpatialReference(54025),
  OutSpatialReference = MyMap.SpatialReference,
  UnionResults = true
};

bufferParameters.Distances.AddRange(new double[] { 10, 20 });

Since GraphicsLayers store their features as lists of graphics, a common use of this operation is to buffer all the features in a GraphicsLayer as shown in the following code. The code assumes that you've already created a GraphicsLayer with an ID of MyGraphicsLayer. Once all the buffer parameters have been specified, executing the operation is done by passing the BufferParameters object to the BufferAsync method.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.BufferCompleted += GeometryService_BufferCompleted;

BufferParameters bufferParameters = new BufferParameters()
{
  Unit = LinearUnit.StatuteMile,
  BufferSpatialReference = new SpatialReference(54025),
  OutSpatialReference = MyMap.SpatialReference,
  UnionResults = true
};

bufferParameters.Distances.AddRange(new double[] { 10, 20 });
GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;
bufferParameters.Features.AddRange(graphicsLayer.Graphics);

geometryService.BufferAsync(bufferParameters);

The operation returns a list of graphics containing the buffer geometries in the Results property of the result object. The result object is accessible through either the second parameter of a BufferCompleted event handler or the BufferLastResult property of the GeometryService object. The following code shows accessing results in an event handler. The example iterates through the results, applying a symbol to each and adding it to a GraphicsLayer. The code assumes that you've already created both a Map containing a GraphicsLayer and a symbol. The Map has an x:Name of MyMap, the GraphicsLayer has an ID of MyGraphicsLayer, and the symbol has an x:Key of DefaultBufferSymbol.

void GeometryService_BufferCompleted(object sender, GraphicsEventArgs args)
{
  GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;

  foreach (Graphic graphic in args.Results)
  {
    graphic.Symbol = DefaultBufferSymbol;
    graphicsLayer.Graphics.Add(graphic);
  }
}

LabelPoints

The LabelPoints operation creates points that are well suited to be label locations for a list of input graphics. Since GraphicsLayers store their features as lists of graphics, a common use pattern for this operation is to retrieve a GraphicsLayer and pass its graphics collection to the operation as shown in the following code. The code assumes that you've already created a Map containing a GraphicsLayer. The Map has an x:Name of MyMap and the GraphicsLayer has an ID of MyGraphicsLayer.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.LabelPointsCompleted += GeometryService_LabelPointsCompleted;
GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;
geometryService.LabelPointsAsync(graphicsLayer.Graphics.ToList());

The operation returns a list of graphics containing the label points in the Results property of the result object. The result object is accessible through either the second parameter of a LabelPointsCompleted event handler or the LabelPointsLastResult property of the GeometryService object. The following code shows accessing results in an event handler. The example iterates through the results, applying a symbol to each and adding it to a GraphicsLayer. The code assumes that you've already created both a Map containing a GraphicsLayer and a symbol. The Map has an x:Name of MyMap, the GraphicsLayer has an ID of MyResultsGraphicsLayer, and the symbol has an x:Key of DefaultMarkerSymbol.

void GeometryService_LabelPointsCompleted(object sender, GraphicsEventArgs args)
{
  GraphicsLayer graphicsLayer = MyMap.Layers["MyResultsGraphicsLayer"] as GraphicsLayer;

  foreach (Graphic graphic in args.Results)
  {
    graphic.Symbol = DefaultMarkerSymbol;
    graphicsLayer.Graphics.Add(graphic);
  }
}

Lengths

The Lengths operation calculates the length of each polyline graphic in a list. Since GraphicsLayers store their features as lists of graphics, a common use pattern for this operation is to retrieve a GraphicsLayer and pass its graphics collection to the operation as shown in the following code. The code assumes that you've already created a Map containing a GraphicsLayer. The Map has an x:Name of MyMap and the GraphicsLayer has an ID of MyPolylineGraphicsLayer.

GeometryService geometryService = 
  new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/" +
    "rest/services/Geometry/GeometryServer");
geometryService.LengthsCompleted += GeometryService_LengthsCompleted;
GraphicsLayer graphicsLayer = MyMap.Layers["MyPolylineGraphicsLayer"] as GraphicsLayer;
geometryService.LengthsAsync(graphicsLayer.Graphics.ToList());

The lengths returned by the operation are in a list of doubles stored in the Results property of the result object. The result object is accessible through either the second parameter of a LengthsCompleted event handler or the LengthsLastResult property of the GeometryService object. The following code shows accessing results in an event handler. The example simply iterates through the results, building a string with the calculated lengths. The returned values are at the same index in the results list as the input graphics from which the value was calculated.

private void GeometryService_LengthsCompleted(object sender, LengthsEventArgs args)
{
  string results = "";
  for (int i = 0; i < args.Results.Count; i++)
  {
    results += string.Format("Graphic {0}: Length = {1}\n", i, args.Results[i]);
  }
}

Project

The Project operation projects a list of graphics into a new spatial reference. Since GraphicsLayers store their features as lists of graphics, a common use pattern for this operation is to retrieve a GraphicsLayer and pass its graphics collection to the operation as shown in the following code. This can be useful, for instance, before using the AreasAndLengths or Lengths operations, since those operations calculate distances in the units of the input graphics' spatial reference. The output spatial reference must also be specified when invoking the Project operation. The code assumes that you've already created a Map containing a GraphicsLayer. The Map has an x:Name of MyMap and the GraphicsLayer has an ID of MyGraphicsLayer. The spatial reference specified is the World Hotine projection.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.ProjectCompleted += GeometryService_ProjectCompleted;
GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;
geometryService.ProjectAsync(graphicsLayer.Graphics.ToList(), new SpatialReference(54025));

The operation returns a list of graphics having geometries that are in the spatial reference that you specified in the call to ProjectAsync. The graphics are accessible through either the second parameter of a ProjectCompleted event handler or the ProjectLastResult property of the GeometryService object. The following code shows passing the results to an AreasAndLengths operation. When coupled with a call to ProjectAsync in the previous code, the dimensions calculated by the AreasAndLengths operation will be in meters.

private void GeometryService_ProjectCompleted(object sender, ESRI.ArcGIS.Client.Tasks.GraphicsEventArgs args)
{
  GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
    "Geometry/GeometryServer");
  geometryService.AreasAndLengthsCompleted += GeometryService_AreasAndLengthsCompleted;
  geometryService.AreasAndLengthsAsync(args.Results);
}

Relation

The Relation operation determines whether a spatial relationship is true between two sets of input graphics. Since GraphicsLayers store their features as lists of graphics, one common use of this operation is to retrieve two GraphicsLayers and pass their graphics collections to the operation along with the relationship to be tested. When the operation executes, it will check each geometry in the first list against each geometry in the second list to see whether the specified relationship is true. The following code shows this and assumes that you've already created a Map containing two GraphicsLayers. The Map has an x:Name of MyMap and the GraphicsLayers have IDs of MyPointGraphicsLayer and MyPolygonGraphicsLayer.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.RelationCompleted += GeometryService_RelationCompleted;
GraphicsLayer pointGraphicsLayer = MyMap.Layers["MyPointGraphicsLayer"] as GraphicsLayer;
GraphicsLayer polygonGraphicsLayer = MyMap.Layers["MyPolygonGraphicsLayer"] as GraphicsLayer;
geometryService.RelationAsync(pointGraphicsLayer.Graphics.ToList(), polygonGraphicsLayer.Graphics.ToList(),
  GeometryRelation.esriGeometryRelationWithin, null);

To test a relationship that is not included in the GeometryRelation enumeration, you can define the GeometryRelation parameter as esriGeometryRelationRelation and specify the fourth parameter as a Shape Comparison Language string that defines the desired relationship. For instance, the following example checks the polygons in the MyPolygonGraphicsLayer GraphicsLayer to see whether they share boundaries but not interiors. That is, that the polygons intersect such that the geometry of their intersection is a line.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.RelationCompleted += GeometryService_RelationCompleted;
GraphicsLayer polygonGraphicsLayer = MyMap.Layers["MyPolygonGraphicsLayer"] as GraphicsLayer;
string sclRelationship = "dim(g1.boundary, g2.boundary) = linear AND intersect(g1.interior, g2.interior) = false";
geometryService.RelationAsync(polygonGraphicsLayer.Graphics.ToList(), polygonGraphicsLayer.Graphics.ToList(),
  GeometryRelation.esriGeometryRelationRelation, sclRelationship);

The operation returns a list of results that contains the indexes of the pairs of features for which the specified relationship is true. This information is encapsulated as a list of GeometryRelationPair objects that is stored in the Results property of the result object. The result object is accessible through either the second parameter of a RelationCompleted event handler or the RelationLastResult property of the GeometryService object. Each GeometryRelationPair object contains two properties: Graphic1Index and Graphic2Index. Graphic1Index stores the index of the graphic in the first input list of graphics, while Graphic2Index stores the index of the graphic in the second input list of graphics. The following code shows accessing results in an event handler. The code assumes that a Relation similar to the one in the first snippet above has been executed and iterates through the results, building a string indicating which points are within which polygon.

private void GeometryService_RelationCompleted(object sender, RelationEventArgs args)
{
  string results = "";
  for (int i = 0; i < args.Results.Count; i++)
  {
    results += string.Format("Point {0} is within polygon {1}.\n", 
                             args.Results[i].Graphic1Index, 
                             args.Results[i].Graphic2Index);
  }
}

Simplify

The Simplify operation converts the geometries of a list of input graphics from complex to simple. This is important when using geometries for spatial queries because the results may not be accurate if the input geometries are not simplified. Simplifying geometries can also improve rendering performance. Since GraphicsLayers store their features as lists of graphics, a common use pattern for this operation is to retrieve a GraphicsLayer and pass its graphics collection to the operation as shown in the following code. The code assumes that you've already created a Map containing a GraphicsLayer. The Map has an x:Name of MyMap and the GraphicsLayer has an ID of MyGraphicsLayer.

GeometryService geometryService = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
  "Geometry/GeometryServer");
geometryService.SimplifyCompleted += GeometryService_SimplifyCompleted;
GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;
geometryService.SimplifyAsync(graphicsLayer.Graphics.ToList());

The operation returns a list of graphics containing the simplified geometries in the Results property of the result object. The result object is accessible through either the second parameter of a SimplifyCompleted event handler or the SimplifyLastResult property of the GeometryService object. The following code shows accessing results in an event handler. The example iterates through the results, applying a symbol to each and adding it to a GraphicsLayer. The code assumes that you've already created both a Map containing a GraphicsLayer and a symbol. The Map has an x:Name of MyMap, the GraphicsLayer has an ID of MyResultsGraphicsLayer, and the symbol has an x:Key of DefaultSymbol.

void GeometryService_SimplifyCompleted(object sender, GraphicsEventArgs args)
{
  GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;

  foreach (Graphic graphic in args.Results)
  {
    graphic.Symbol = DefaultSymbol;
    graphicsLayer.Graphics.Add(graphic);
  }
}

6/23/2013