Click here to get the sample associated with this walkthrough.
In this topic
- Project description
- Concepts
- Design
- Requirements
- Implementation
- Creating a Web site
- Adding references to ESRI assemblies in the project
- Naming the Web service
- Creating the SchoolDistrict class
- Creating the Web service method
- Validating input parameters
- Connecting to the GIS server
- Geocoding the input address
- Buffering the result and querying the SchoolDistricts
- Testing the Web service
- Creating a client application
- Deployment
- Additional resources
Project description
The purpose of this topic is to create an ASP.NET Web service using Visual Studio and uses ArcObjects to locate all of the school districts within a specified distance of a specified address. The Web service returns a .NET array of application defined school district objects.
This Web service is intended to be called by other programs and an example of such a client program is a .NET Windows application. This topic also provides an example of how a client application consumes this Web service.
Concepts
A Web service is a set of related application functions that can be programmatically invoked over the Internet. The function can be one that solves a particular application problem or performs another type of geographic information system (GIS) function. In this example, the Web service finds all of the toxic waste sites within a certain distance of an address. Web services can be implemented using the native Web service framework of your Web server, such as ASP.NET Web service (WebMethod).
When using native frameworks, such as ASP.NET, to create and consume your application Web services, use native or application defined types as both arguments and return values from your Web methods. Clients of the Web service will not be ArcObjects applications, and as such, your Web service should not expect ArcObjects types as arguments and should not directly return ArcObjects types.
Any development language that can use standard Hypertext Transfer Protocol (HTTP) to invoke methods can consume this Web service. The Web service consumer can get the methods and types exposed by the Web service through its Web Service Description Language (WSDL). As you progress through this topic, you will notice where special attributes need to be added to your methods and classes for expression in WSDL and serialized as Extensible Markup Language (XML).
Design
Web services are stateless applications and this example is no different because it makes stateless use of the GIS server. ArcObjects on the server is used to locate an address and query a feature class. To support this application, you need to add a pooled geocode server object to your GIS Server using ArcCatalog.
The Web service connects to the GIS server and uses an instance of the geocode server object to locate the address supplied to the Web method from the calling application. To buffer the resultant point and use that buffered geometry to query toxic waste sites, use the geocode server's server context. Since the geodatabase address locator is stored in the same geodatabase as the feature class containing the toxic waste sites, you can use the fine grained objects associated with the geocode server to get a reference to that workspace.
Requirements
This topic requires ArcGIS Server and ArcGIS Desktop to be installed and running. The machine on which you develop this Web service must have the ArcGIS Server Web Application Developer Framework (ADF) installed.
A geocode server object must be configured and running on your GIS server that uses the RoadCenterLinelocator installed with the samples. In ArcCatalog, create a connection to your GIS server and use the Add Server Object command to create a server object with the following properties:
- Name—RoadCenterLine_Locator
- Type—GeocodeServer
- Description—Geocode server object for BloomFieldTownship
- Locator—<ArcGIS Developer Kit install location>\Samples\data\BloomfieldTownship\ RoadCenterline_Locator.loc
- Batch size—10 (default)
- Pooling—Web service that makes stateless use of the server object. Accept the defaults for the pooling model (pooled server object with minimum instances = 1, max instances = 2).
Accept the defaults for the remainder of the configuration properties. See the following screen shot:
- After creating the server object, start it.
- Right-click the server object, then click Properties to verify that it is correctly configured and that the geocoding properties show.
The following ArcObjects assemblies are used in this example:
- ESRI.ArcGIS.ADF.ArcGISServer
- ESRI.ArcGIS.ADF.ArcGISServer.Local
- ESRI.ArcGIS.ADF.Connection.Core
- ESRI.ArcGIS.ADF.Connection.Local
- ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer
- ESRI.ArcGIS.ADF.Core
- ESRI.ArcGIS.Geodatabase
- ESRI.ArcGIS.Geometry
- ESRI.ArcGIS.Location
- ESRI.ArcGIS.Server
- ESRI.ArcGIS.System
The development environment does not require ArcGIS licensing; however, connecting to a server and using a geocode server object does require that the GIS server is licensed. None of the assemblies used require an extension license.
The integrated development environment (IDE) used in this example is Visual Studio 2008 and all IDE specific steps assume that is the IDE you are using.
Implementation
All code written in this example is in C#; however, you can write this Web service using VB .NET. To start, create a project in Visual Studio.
Creating a Web site
Do the following steps to create a Web site:
- Start Visual Studio.
- Click the File menu, choose New, then Web Site. The New Web Site dialog box appears.
See the following screen shot: - On the New Web Site dialog box, click ASP.NET Web Service, then select Visual C# from the Language drop-down list.
- Type http://localhost/GeocodeSearch for the Web service name.
- Click OK. A blank Web service application is created.
- In the Solution Explorer, right-click Service.asmx and click Rename.
- Type SchoolDistrictLocator.asmx as the new name.
Adding references to ESRI assemblies in the project
To program using ArcGIS Server, add references to the ESRI assemblies that contain proxies to the ArcObjects components the Web service uses. These assemblies are installed when you installed the ArcGIS Server Web ADF.
Do the following steps to add references:
- In the Solution Explorer, click the project and click Add Reference. See the following screen shot:
- When you click Add Reference in the Solution Explorer, the Add Reference dialog box appears. Double-click the following assemblies to add to the project:
- ESRI.ArcGIS.ADF.ArcGISServer
- ESRI.ArcGIS.ADF.ArcGISServer.Local
- ESRI.ArcGIS.ADF.Connection.Core
- ESRI.ArcGIS.ADF.Connection.Local
- ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer
- ESRI.ArcGIS.ADF.Core
- ESRI.ArcGIS.Geodatabase
- ESRI.ArcGIS.Geometry
- ESRI.ArcGIS.Location
- ESRI.ArcGIS.Server
- ESRI.ArcGIS.System
- Click OK.
- Add using statements to add the assemblies' namespaces to the application. In the Solution Explorer, right-click SchoolDistrictLocatorService.asmx and click View Code. The code for the implementation of the Web service appears.
- Add the using statements in the following code example at the top of the code window:
See the following screen shot of the Add Reference dialog box:
Naming the Web service
Add the following code example to name the class and its constructor to ToxicSiteLocations:
[C#]
public class SchoolDistrictLocatorService: System.Web.Services.WebService
{
public SchoolDistrictLocatorService(){}
- See the following code example that shows what the code resembles:
[System.Web.Services.WebService(Namespace =
"http://localhost/ArcGIS_SchoolDistrictGeocode_WebService/")
][System.Web.Services.WebServiceBinding(ConformsTo =
System.Web.Services.WsiProfiles.BasicProfile1_1)]
public class SchoolDistrictLocatorService: System.Web.Services.WebService
{
public SchoolDistrictLocatorService(){}
- The Web service exposes a method that is accessible via HTTP-based Simple Object Access Protocol (SOAP) requests and returns all school districts within a specified distance for a specified address. The method types must be declared as public and support the [WebMethod] attribute.
Creating the SchoolDistrict class
Before you create a method, do the following steps to define your school distrcit class that will be returned as a result of the method:
- In the Solution Explorer, right-click the ArcGIS_Geocode_Search project, then click Add New Item. The Add New Item dialog box appears. See the following screen shot:
- Click the Class template under the Visual Studio installed templates area.
- Type SchoolDistrict.cs in the Name text box.
- Click Add.
- Click Yes when prompted to add the class to the App_Code folder. This adds a new class (SchoolDistrict) to the project and opens the code for the class with some automatically generated code. See the following code example for the SchoolDistrict class:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Serialization;
public class SchoolDistrict
{
public string SchoolDescrip;
public SchoolDistrict(){}
public SchoolDistrict(string sSchoolDescription)
{
SchoolDescrip = sSchoolDescription;
}
}
- Results returned from Web services must be serializable in XML. The SchoolDistrict type must be marked as serializable as XML by including the XmlInclude attribute. Because the XmlInclude attribute is defined in the System.Xml.Serialization namespace, add a using statement for that assembly. Classes that have the XmlInclude attribute can be serialized into XML. Returned custom types by a Web service must have the XmlInclude attribute.
- Add the following using statement to your code:
using System.Xml.Serialization;
- Add the following attribute to your class declaration:
[XmlInclude(typeof(SchoolDistrict))]
- See the following code example that shows what the code resembles:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Serialization;
[XmlInclude(typeof(SchoolDistrict))]
public class SchoolDistrict
{
public string SchoolDescrip;
public SchoolDistrict(){}
public SchoolDistrict(string sSchoolDescription)
{
SchoolDescrip = sSchoolDescription;
}
}
The Web service returns the following information:
- Name of the SchoolDistrict within the specified distance of input address.
Creating the Web service method
Now that you have defined your SchoolDistrict class, implement your Web service method. As previously described, the goal of this Web service is to expose a method (accessible via HTTP-based SOAP requests) that returns all toxic site locations within a specified distance for a specified address. These types of methods must be declared as public and support the [WebMethod] attribute.
.NET methods within a class that have the [WebMethod] attribute set are XML Web service methods and are callable from remote Web clients.
Create a method called FindSchoolDistrictLocationsSoap that takes as arguments full address and search distance and returns an array of SchoolDistrict objects. This method ultimately opens a cursor on a feature class in a geodatabase.
The FindSchoolDistrictLocationsSoap Web method returns an array of SchoolDistrict objects, rather than a generic collection, such as an ArrayList. If the method did not have a return type of SchoolDistrict , the custom class is not expressed in the Web service's WSDL.
Do the following steps to create a Web service method:
- In the Solution Explorer, right-click SchoolDistrictLocatorService.asmx, then click View Code. The implementation code for this Web service appears.
- Add the following code example:
[System.Web.Services.WebMethod(Description =
"Locates SchoolDistrict within the specified distance of the input address." +
"Output is returned in an array. To see an example, input '1575 Apple Ln', '48304', '10000.'" + "Note that this method implements its functionality using ArcObjects.")]
public SchoolDistrict[] FindSchoolDistrictLocationsArcObjects(string FullAddress,
string Distance)
{
// Declare server context variables
ESRI.ArcGIS.Server.IServerContext mapServerContext = null;
ESRI.ArcGIS.Server.IServerContext geocodeServerContext = null;
ESRI.ArcGIS.Server.IServerContext geometryServerContext = null;
try{}
catch {}
return SchoolDistrictsArray;
}
Validating input parameters
The Web method verifies that the provided parameters are valid and if not, returns null. Type the following code example:
[C#]
// Check to make sure all inputs are specified
if (FullAddress == null || string.IsNullOrEmpty(Distance.ToString()) ||
System.Convert.ToDouble(Distance) == 0.0)
{
return null;
}
Connecting to the GIS server
The Web service uses objects in the GIS server; therefore, add code to connect to the GIS server and get the IServerObjectManager interface. In this example, the GIS server is running on a machine named localhost.
Do the following steps to connect to the GIS server and get the reference to IServerObjectManager:
[C#]
// Open a connection to ArcGIS Server
ESRI.ArcGIS.ADF.Identity adfIdentity = new ESRI.ArcGIS.ADF.Identity("username",
"password", "domainOrmachine");
ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection agsServerConnection = new
ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection("localhost", adfIdentity);
agsServerConnection.Connect();
// Get a reference to the server object manager to use in creating server objects
ESRI.ArcGIS.Server.IServerObjectManager serverObjectManager =
agsServerConnection.ServerObjectManager;
- The Web service uses the RoadCenterline_Locator server object you created with ArcCatalog. Get a server object by requesting to the GIS server a server context containing the object. Add the following code example to get the server object's context:
// Create server context for the RoadCenterline_Locator geocoding service. We will use this to geocode
// the passed-in address.
geocodeServerContext = serverObjectManager.CreateServerContext(
"RoadCenterline_Locator", "GeocodeServer");
- Release the server object's context when you are finished using it. Ensure that your method calls ReleaseContext on the server context when the method no longer needs the server object or any other objects running in the context.
Ensure the context is released in case there is an error. The remainder of the code runs within the previously added try and catch block statements. If an error occurs in the code in the try block, the code in the catch block is executed; therefore, add the call to release the context at the end of the try block and in the catch block.
- Add the following code example to the catch block:
geocodeServerContext.ReleaseContext();
- Add the following code example to the try block:
// Release the server context.
geocodeServerContext.ReleaseContext();
Geocoding the input address
Now that you have connected to the GIS server and have a context containing the geocode server object, add the code to use the server object to geocode the input address, then store the resulting point as aoLocationPoint. Add the following code example to the try block:
[C#]
// Create a server context for and get a reference to the RoadCenterline_Locator geocode service
geocodeServerContext = serverObjectManager.CreateServerContext(
"RoadCenterline_Locator", "GeocodeServer");
ESRI.ArcGIS.Location.IGeocodeServer geocodeServer =
geocodeServerContext.ServerObject as ESRI.ArcGIS.Location.IGeocodeServer;
// Instantate and populate a property set holding geocode operation input parameters
ESRI.ArcGIS.esriSystem.IPropertySet aoInputPropertySet =
geocodeServerContext.CreateObject("esriSystem.PropertySet")as
ESRI.ArcGIS.esriSystem.IPropertySet;
aoInputPropertySet.SetProperty("Single Line Input", FullAddress);
// Execute the geocoding operation
ESRI.ArcGIS.esriSystem.IPropertySet aoResultsPropertySet =
geocodeServer.GeocodeAddress(aoInputPropertySet, null);
// Retrieve the geocoded point from the operation results
ESRI.ArcGIS.Geometry.IPoint aoLocationPoint = aoResultsPropertySet.GetProperty(
"Shape")as ESRI.ArcGIS.Geometry.IPoint;
- A PropertySet is a generic class that is used to hold a set of properties. PropertySet properties are stored as name and value pairs. Examples for the use of a property set are to hold the required properties for opening an SDE workspace or geocoding an address.
Buffering the result and querying the SchoolDistricts
Buffer this point and use the resulting geometry to query the SchoolDistricts from the
SchoolTaxDistrict feature class.
- Add the following code example to the try block to buffer the point, open the feature class, then query it:
// Create a server context for and get a reference to the geometry service. We will use this
// to execute buffer and project operations.
geometryServerContext = serverObjectManager.CreateServerContext("Geometry",
"GeometryServer");
// The point generated by the geocode operation is in the geocode server object's server context.
// It will be used in a geometry service operation, thus it must be available in the geometry
// server objects's server context. SaveObject serializes the ArcObjects point to a string.
// LoadObject deserializes the string to create an object in server context.
ESRI.ArcGIS.Geometry.IPoint aoLocationPointInGeometryServer =
(ESRI.ArcGIS.Geometry.IPoint)geometryServerContext.LoadObject
(geocodeServerContext.SaveObject(aoLocationPoint));
ESRI.ArcGIS.Geometry.IGeometryServer geometryServer =
geometryServerContext.ServerObject as ESRI.ArcGIS.Geometry.IGeometryServer;
// Create a spatial reference in which to execute the buffer operation. This spatial
// reference's projection is optimized to minimize distortion in the vicinity of the
// operation.
ESRI.ArcGIS.Geometry.ISpatialReference aoBufferSpatialReference =
Utility.CreateOperationSpatialReference(aoLocationPointInGeometryServer,
geometryServerContext);
// Store output spatial reference for use in the buffer operation. Also use to assign spatial reference
// to buffer generated by operation.
ESRI.ArcGIS.Geometry.ISpatialReference outputSpatialReference =
aoLocationPointInGeometryServer.SpatialReference;
// Create a server context for and get a reference to the BloomfieldTownship map service. We will
// use this to access map information and query features.
mapServerContext = serverObjectManager.CreateServerContext("BloomfieldTownship",
"MapServer");
ESRI.ArcGIS.Carto.IMapServer2 mapServer = mapServerContext.ServerObject as
ESRI.ArcGIS.Carto.IMapServer2;
// Get a reference to the map service's server info object
ESRI.ArcGIS.Carto.IMapServerInfo mapServerInfo = mapServer.GetServerInfo
(mapServer.DefaultMapName);
// Create a spatial reference environemnt for use by the ConvertUnitType method
ESRI.ArcGIS.Geometry.SpatialReferenceEnvironment spatialReferenceEnvironment =
geometryServerContext.CreateObject("esriGeometry.SpatialReferenceEnvironment")as
ESRI.ArcGIS.Geometry.SpatialReferenceEnvironment;
// Convert the map service's units to the type required by the buffer operation
ESRI.ArcGIS.Geometry.IUnit bufferUnit = Utility.ConvertUnitType
(mapServerInfo.MapUnits, spatialReferenceEnvironment);
// Package the geocoded point and buffer distance in arrays to pass to the buffer
// operation
ESRI.ArcGIS.Geometry.IGeometryArray aoInputGeometryArray =
geometryServerContext.CreateObject("esriGeometry.GeometryArray")as
ESRI.ArcGIS.Geometry.IGeometryArray;
aoInputGeometryArray.Add(aoLocationPointInGeometryServer);
ESRI.ArcGIS.esriSystem.IDoubleArray distancesArray =
geometryServerContext.CreateObject("esriSystem.DoubleArray")as
ESRI.ArcGIS.esriSystem.IDoubleArray;
distancesArray.Add(System.Convert.ToDouble(Distance));
// Execute the buffer operation
ESRI.ArcGIS.Geometry.IGeometryArray aoBufferGeometryArray = geometryServer.Buffer
(aoLocationPointInGeometryServer.SpatialReference, aoBufferSpatialReference,
outputSpatialReference, distancesArray, bufferUnit, false, aoInputGeometryArray);
// Retrieve the buffer geometry from the operation results
ESRI.ArcGIS.Geometry.IPolygon aoBuffer = aoBufferGeometryArray.get_Element(0)as
ESRI.ArcGIS.Geometry.IPolygon;
// Assign output spatial reference to buffer geometry
aoBuffer.SpatialReference = outputSpatialReference;
// The polygon generated by the buffer operation is in the geometry server object's server context.
// It will be used in a query operation against a map service, thus it must be available in the map
// server objects server context. SaveObject serializes the ArcObjects polygon to a string.
// LoadObject deserializes the string to create an object in server context.
ESRI.ArcGIS.Geometry.IPolygon aoBufferInMapServer = (ESRI.ArcGIS.Geometry.IPolygon)
mapServerContext.LoadObject(geometryServerContext.SaveObject(aoBuffer));
Add code to find the school districts within the buffered geometry by querying the featureclass. Add the following code to search the featureclass and retrieve a recordset.
[C#]
// Retrieve the map service's MapLayerInfos object to use in accessing information about
// the service's layers
ESRI.ArcGIS.Carto.IMapLayerInfos mapLayerInfos = mapServerInfo.MapLayerInfos;
// Retrieve the layer ID and geometry field name of the SchoolDistrict sites layer. This is the layer
// we will query to find SchoolDistrict sites within the buffer.
int SchoolDistrictsLayerID = - 1;
string geometryFieldName = null;
for (int i = 0; i < mapLayerInfos.Count; i++)
{
ESRI.ArcGIS.Carto.IMapLayerInfo mapLayerInfo = mapLayerInfos.get_Element(i);
if (mapLayerInfo.Name == "SchoolTaxDistrict")
{
SchoolDistrictsLayerID = mapLayerInfo.ID;
for (int j = 0; j < mapLayerInfo.Fields.FieldCount; j++)
{
ESRI.ArcGIS.Geodatabase.IField field = mapLayerInfo.Fields.get_Field(j);
if (field.Type ==
ESRI.ArcGIS.Geodatabase.esriFieldType.esriFieldTypeGeometry)
{
geometryFieldName = field.Name;
break;
}
}
break;
}
}
// Instantiate and initialize a spatial filter with the buffer geometry and SchoolDistrict sites
// layer geometry field name
ESRI.ArcGIS.Geodatabase.ISpatialFilter aoSpatialFilter =
mapServerContext.CreateObject("esriGeodatabase.SpatialFilter")as
ESRI.ArcGIS.Geodatabase.ISpatialFilter;
aoSpatialFilter.Geometry = aoBufferInMapServer;
aoSpatialFilter.SpatialRel =
ESRI.ArcGIS.Geodatabase.esriSpatialRelEnum.esriSpatialRelIntersects;
aoSpatialFilter.GeometryField = geometryFieldName;
// Execute the query
ESRI.ArcGIS.Geodatabase.IRecordSet aoRecordSet = mapServer.QueryFeatureData
(mapServer.DefaultMapName, SchoolDistrictsLayerID, aoSpatialFilter);
- Add code to loop through the features returned by the query.
Because the number of features returned by the query is unknown until the cursor has been exhausted, you cannot declare your SchoolDistrict array, as its size is unknown. Store these SchoolDistrict objects in an ArrayList collection, then copy the contents of that ArrayList to an array of SchoolDistrict objects that is returned by the method.
- Add the following code example to the try block:
#region Package function output based on SchoolDistrict site data
// Retrieve the School Description type fields
int schoolDscrpIndex = - 1;
int currentIndex = 0;
for (int i = 0; i < aoRecordSet.Fields.FieldCount; i++)
{
ESRI.ArcGIS.Geodatabase.IField field = aoRecordSet.Fields.get_Field(i);
if (field.Name.ToUpper() == "SCHLDSCRP")
{
schoolDscrpIndex = currentIndex;
break;
}
currentIndex++;
}
System.Collections.ArrayList SchoolDistrictsList = new System.Collections.ArrayList()
;
SchoolDistrict SchoolDistrict = null;
// Get a cursor to loop through the rows corresponding to SchoolDistrict site results
ESRI.ArcGIS.Geodatabase.ICursor SchoolDistrictCursor = aoRecordSet.get_Cursor(false);
ESRI.ArcGIS.Geodatabase.IRow SchoolDistrictRow = SchoolDistrictCursor.NextRow();
// For each SchoolDistrict site record, instantiate a new SchoolDistrict object and add it to the SchoolDistrict sites
// list object
while (SchoolDistrictRow != null)
{
SchoolDistrict = new SchoolDistrict((string)SchoolDistrictRow.get_Value
(schoolDscrpIndex));
SchoolDistrictsList.Add(SchoolDistrict);
SchoolDistrictRow = SchoolDistrictCursor.NextRow();
}
// Copy the SchoolDistrict sites list to an array to be returned as the function result
SchoolDistrict[] SchoolDistrictsArray = new
SchoolDistrict[SchoolDistrictsList.Count];
SchoolDistrictsList.CopyTo(SchoolDistrictsArray);
#endregion
return SchoolDistrictsArray;
- Your FindSchoolDistrictLocationsArcObjectsmethod resembles the following code example:
[System.Web.Services.WebMethod(Description =
"Locates SchoolDistrict within the specified distance of the input address." +
"Output is returned in an array. To see an example, input '1575 Apple Ln', '48304', '10000.'" + "Note that this method implements its functionality using ArcObjects.")]
public SchoolDistrict[] FindSchoolDistrictLocationsArcObjects(string FullAddress,
string Distance)
{
// Declare server context variables
ESRI.ArcGIS.Server.IServerContext mapServerContext = null;
ESRI.ArcGIS.Server.IServerContext geocodeServerContext = null;
ESRI.ArcGIS.Server.IServerContext geometryServerContext = null;
// Make sure all inputs were specified
if (FullAddress == null || string.IsNullOrEmpty(Distance.ToString()) ||
System.Convert.ToDouble(Distance) == 0.0)
{
return null;
}
try
{
// Connect to ArcGIS Server
ESRI.ArcGIS.ADF.Identity adfIdentity = new ESRI.ArcGIS.ADF.Identity(
"username", "password", "domainOrmachine");
ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection agsServerConnection = new
ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection("localhost",
adfIdentity);
agsServerConnection.Connect();
// Get a reference to the server object manager to use in creating server objects
ESRI.ArcGIS.Server.IServerObjectManager serverObjectManager =
agsServerConnection.ServerObjectManager;
#region Geocode the input address
// Create a server context for and get a reference to the RoadCenterline_Locator geocode service
geocodeServerContext = serverObjectManager.CreateServerContext(
"RoadCenterline_Locator", "GeocodeServer");
ESRI.ArcGIS.Location.IGeocodeServer geocodeServer =
geocodeServerContext.ServerObject as ESRI.ArcGIS.Location.IGeocodeServer;
// Instantate and populate a property set holding geocode operation input parameters
ESRI.ArcGIS.esriSystem.IPropertySet aoInputPropertySet =
geocodeServerContext.CreateObject("esriSystem.PropertySet")as
ESRI.ArcGIS.esriSystem.IPropertySet;
aoInputPropertySet.SetProperty("Single Line Input", FullAddress);
// Execute the geocoding operation
ESRI.ArcGIS.esriSystem.IPropertySet aoResultsPropertySet =
geocodeServer.GeocodeAddress(aoInputPropertySet, null);
// Retrieve the geocoded point from the operation results
ESRI.ArcGIS.Geometry.IPoint aoLocationPoint =
aoResultsPropertySet.GetProperty("Shape")as ESRI.ArcGIS.Geometry.IPoint;
#endregion
#region Buffer the geocoded point
// Create a server context for and get a reference to the geometry service. We will use this
// to execute buffer and project operations.
geometryServerContext = serverObjectManager.CreateServerContext("Geometry",
"GeometryServer");
// The point generated by the geocode operation is in the geocode server object's server context.
// It will be used in a geometry service operation, thus it must be available in the geometry
// server objects's server context. SaveObject serializes the ArcObjects point to a string.
// LoadObject deserializes the string to create an object in server context.
ESRI.ArcGIS.Geometry.IPoint aoLocationPointInGeometryServer =
(ESRI.ArcGIS.Geometry.IPoint)geometryServerContext.LoadObject
(geocodeServerContext.SaveObject(aoLocationPoint));
ESRI.ArcGIS.Geometry.IGeometryServer geometryServer =
geometryServerContext.ServerObject as
ESRI.ArcGIS.Geometry.IGeometryServer;
// Create a spatial reference in which to execute the buffer operation. This spatial
// reference's projection is optimized to minimize distortion in the vicinity of the
// operation.
ESRI.ArcGIS.Geometry.ISpatialReference aoBufferSpatialReference =
Utility.CreateOperationSpatialReference(aoLocationPointInGeometryServer,
geometryServerContext);
// Store output spatial reference for use in the buffer operation. Also use to assign spatial reference
// to buffer generated by operation.
ESRI.ArcGIS.Geometry.ISpatialReference outputSpatialReference =
aoLocationPointInGeometryServer.SpatialReference;
// Create a server context for and get a reference to the BloomfieldTownship map service. We will
// use this to access map information and query features.
mapServerContext = serverObjectManager.CreateServerContext(
"BloomfieldTownship", "MapServer");
ESRI.ArcGIS.Carto.IMapServer2 mapServer = mapServerContext.ServerObject as
ESRI.ArcGIS.Carto.IMapServer2;
// Get a reference to the map service's server info object
ESRI.ArcGIS.Carto.IMapServerInfo mapServerInfo = mapServer.GetServerInfo
(mapServer.DefaultMapName);
// Create a spatial reference environemnt for use by the ConvertUnitType method
ESRI.ArcGIS.Geometry.SpatialReferenceEnvironment spatialReferenceEnvironment
= geometryServerContext.CreateObject(
"esriGeometry.SpatialReferenceEnvironment")as
ESRI.ArcGIS.Geometry.SpatialReferenceEnvironment;
// Convert the map service's units to the type required by the buffer operation
ESRI.ArcGIS.Geometry.IUnit bufferUnit = Utility.ConvertUnitType
(mapServerInfo.MapUnits, spatialReferenceEnvironment);
// Package the geocoded point and buffer distance in arrays to pass to the buffer
// operation
ESRI.ArcGIS.Geometry.IGeometryArray aoInputGeometryArray =
geometryServerContext.CreateObject("esriGeometry.GeometryArray")as
ESRI.ArcGIS.Geometry.IGeometryArray;
aoInputGeometryArray.Add(aoLocationPointInGeometryServer);
ESRI.ArcGIS.esriSystem.IDoubleArray distancesArray =
geometryServerContext.CreateObject("esriSystem.DoubleArray")as
ESRI.ArcGIS.esriSystem.IDoubleArray;
distancesArray.Add(System.Convert.ToDouble(Distance));
// Execute the buffer operation
ESRI.ArcGIS.Geometry.IGeometryArray aoBufferGeometryArray =
geometryServer.Buffer(aoLocationPointInGeometryServer.SpatialReference,
aoBufferSpatialReference, outputSpatialReference, distancesArray,
bufferUnit, false, aoInputGeometryArray);
// Retrieve the buffer geometry from the operation results
ESRI.ArcGIS.Geometry.IPolygon aoBuffer = aoBufferGeometryArray.get_Element(0)
as ESRI.ArcGIS.Geometry.IPolygon;
// Assign output spatial reference to buffer geometry
aoBuffer.SpatialReference = outputSpatialReference;
// The polygon generated by the buffer operation is in the geometry server object's server context.
// It will be used in a query operation against a map service, thus it must be available in the map
// server objects server context. SaveObject serializes the ArcObjects polygon to a string.
// LoadObject deserializes the string to create an object in server context.
ESRI.ArcGIS.Geometry.IPolygon aoBufferInMapServer =
(ESRI.ArcGIS.Geometry.IPolygon)mapServerContext.LoadObject
(geometryServerContext.SaveObject(aoBuffer));
#endregion
#region Find SchoolDistrict sites within the buffer
// Retrieve the map service's MapLayerInfos object to use in accessing information about
// the service's layers
ESRI.ArcGIS.Carto.IMapLayerInfos mapLayerInfos = mapServerInfo.MapLayerInfos;
// Retrieve the layer ID and geometry field name of the SchoolDistrict sites layer. This is the layer
// we will query to find SchoolDistrict sites within the buffer.
int SchoolDistrictsLayerID = - 1;
string geometryFieldName = null;
for (int i = 0; i < mapLayerInfos.Count; i++)
{
ESRI.ArcGIS.Carto.IMapLayerInfo mapLayerInfo = mapLayerInfos.get_Element
(i);
if (mapLayerInfo.Name == "SchoolTaxDistrict")
{
SchoolDistrictsLayerID = mapLayerInfo.ID;
for (int j = 0; j < mapLayerInfo.Fields.FieldCount; j++)
{
ESRI.ArcGIS.Geodatabase.IField field =
mapLayerInfo.Fields.get_Field(j);
if (field.Type ==
ESRI.ArcGIS.Geodatabase.esriFieldType.esriFieldTypeGeometry)
{
geometryFieldName = field.Name;
break;
}
}
break;
}
}
// Instantiate and initialize a spatial filter with the buffer geometry and SchoolDistrict sites
// layer geometry field name
ESRI.ArcGIS.Geodatabase.ISpatialFilter aoSpatialFilter =
mapServerContext.CreateObject("esriGeodatabase.SpatialFilter")as
ESRI.ArcGIS.Geodatabase.ISpatialFilter;
aoSpatialFilter.Geometry = aoBufferInMapServer;
aoSpatialFilter.SpatialRel =
ESRI.ArcGIS.Geodatabase.esriSpatialRelEnum.esriSpatialRelIntersects;
aoSpatialFilter.GeometryField = geometryFieldName;
// Execute the query
ESRI.ArcGIS.Geodatabase.IRecordSet aoRecordSet = mapServer.QueryFeatureData
(mapServer.DefaultMapName, SchoolDistrictsLayerID, aoSpatialFilter);
#endregion
#region Package function output based on SchoolDistrict site data
// Retrieve the School Description type fields
int schoolDscrpIndex = - 1;
int currentIndex = 0;
for (int i = 0; i < aoRecordSet.Fields.FieldCount; i++)
{
ESRI.ArcGIS.Geodatabase.IField field = aoRecordSet.Fields.get_Field(i);
if (field.Name.ToUpper() == "SCHLDSCRP")
{
schoolDscrpIndex = currentIndex;
break;
}
currentIndex++;
}
System.Collections.ArrayList SchoolDistrictsList = new
System.Collections.ArrayList();
SchoolDistrict SchoolDistrict = null;
// Get a cursor to loop through the rows corresponding to SchoolDistrict site results
ESRI.ArcGIS.Geodatabase.ICursor SchoolDistrictCursor =
aoRecordSet.get_Cursor(false);
ESRI.ArcGIS.Geodatabase.IRow SchoolDistrictRow =
SchoolDistrictCursor.NextRow();
// For each SchoolDistrict site record, instantiate a new SchoolDistrict object and add it to the SchoolDistrict sites
// list object
while (SchoolDistrictRow != null)
{
SchoolDistrict = new SchoolDistrict((string)SchoolDistrictRow.get_Value
(schoolDscrpIndex));
SchoolDistrictsList.Add(SchoolDistrict);
SchoolDistrictRow = SchoolDistrictCursor.NextRow();
}
// Copy the SchoolDistrict sites list to an array to be returned as the function result
SchoolDistrict[] SchoolDistrictsArray = new
SchoolDistrict[SchoolDistrictsList.Count];
SchoolDistrictsList.CopyTo(SchoolDistrictsArray);
#endregion
return SchoolDistrictsArray;
}
catch (System.Exception exception)
{
System.Diagnostics.Debug.WriteLine("Exception: " + exception.Message);
}
finally
{
// Make sure server contexts are released
if (mapServerContext != null)
mapServerContext.ReleaseContext();
if (geocodeServerContext != null)
geocodeServerContext.ReleaseContext();
if (geometryServerContext != null)
geometryServerContext.ReleaseContext();
}
return null;
}
- The Web service is ready to test. Click Build, then Build Solution to compile the project. If necessary, fix any errors.
Testing the Web service
If you run the Web service in Visual Studio, a Web browser opens and lists the FindToxicLocations method that you can invoke in the Web browser. Do the following steps to test the Web service:
- Click the Debug menu, choose Start Debugging or press F5. See the following screen shot:
- When you run your Web service in Visual Studio, it opens in a Web browser and shows the Web service's methods. Click the links on the Web browser windows and type the method's input to evoke the methods. Click ServiceDescription to review the WSDL for the Web service.
- When the Web browser appears, click FindSchoolDistrictLocationsArcObjects. See the following screen shot:
- When you click FindSchoolDistrictLocationsArcObjects on the preceding Web browser window, the FindSchoolDistrictLocationsArcObjectsWeb browser window appears. See the following screen shot:
- Type the following values for each parameter on the preceding FindToxicLocations Web browser window:
- Address—1575 Apple Ln
- Distance—10000
- Click Invoke. A new Web browser appears with the results returned from the method in XML.
Creating a client application
Since your Web service exposes a language neutral interface that can be called using HTTP, your Web service can be called from any language that understands HTTP and WSDL. An elaborate client application can be a Web application, desktop application, or another Web service.
The following is a C# code example of how this type of application can call the Web service method. Rather than describe this type of application in detail, assume that this is a Windows desktop application that contains a button called btnCallWS whose Click event calls into your Web service. The code for this can resemble the following, assuming you have added your Web service as a Web reference called SchoolLocation:
[C#]
private void btnCallWS_Click(object sender, System.EventArgs e)
{
SchoolLocation.SchoolLocator toxloc = new SchoolLocation.SchoolLocator();
object[] objs = toxloc.FindSchoolDistrictLocationsArcObjects("2111 Division St",
10000);
for (int i = 0; i < objs.Length; i++)
{
SchoolLocation.SchoolDistrict schoolLoc = objs[i] as
SchoolLocation.SchoolDistrict;
// Do something with the toxic site object.
}
}
Deployment
Presumably you developed this Web service using your development Web server. To deploy this Web service on your production Web server, use the built-in Visual Studio tools to copy the Web site.
Do the following steps to deploy your Web service:
- In the Solution Explorer, click ToxicLocations.
- Click Web site, then click Copy Web site.
See the following screen shot: - Use the Copy Web Site dialog box to specify a connection to the Web server to which you want to copy the Web site.
Additional resources
This topic includes functionality and programming techniques covering various aspects of ArcObjects, ArcGIS Server ArcObjects API, and Web controls. Use the Developer Help system to get a better understanding of core ArcGIS Server programming concepts, and programming guidelines for working with server contexts and ArcObjects running within those contexts.
This topic uses the ArcGIS Server Web ADF to provide the GIS server connection object for this Web service. If you are unfamiliar with ASP.NET Web development, refer to your .NET developer documentation to become more familiar with Web application development.
ArcGIS Server applications exploit the rich GIS functionality of ArcObjects. This Web service is no exception. It includes the use of ArcObjects to work with the components of a GeocodeServer to locate an address, manipulate geometries, and perform spatial queries against feature classes in a geodatabase. For more information about these aspects of ArcObjects, see the Developer Help system on the Location, GeoDatabase, and Geometry object libraries.