This walkthrough is for developers who need to build and deploy a server object extension for use in server applications. It describes the process of extending the MapServer to provide methods for performing a specific type of spatial analysis on the features in one layer of the map. The scenario consists of four parts:
This scenario is also included as a developer sample, ArcGIS_Spatial_Query_Server_Object_Extension.
The sample code is located in:<install
location>\DeveloperKit\SamplesNET\Server\Web_Applications
The purpose of this scenario is to create a server object extension
using C# to extend a MapServer server object. The server object extension
exposes methods to clip the geometries of polygons in one of the layers in the
map to a buffer around a user-defined point. Summary statisitcs are then
provided using the area of the clipped polygons based on the unique values of a
specified field. Both the layer to perform the analysis on, and the field to
summarize statistics on will be a property of the server object extension.
As part of this scenario, you'll create a custom administration property page for ArcCatalog that will allow you to set the layer and field properties of the server object extension, based on the MapServer object's map document. Finally, you'll create a client Web application that consumes your server object extension. This application will utilize the Web ADF controls and capabilities.
Both coarse-grained calls to remote ArcObjects, such as the methods on the MapServer and GeocodeServer, as well as fine-grained calls to remote ArcObjects, such as looping through all the vertices of a polygon, are exposed through the ArcGIS Server ArcObjects API and can be used in your application. However, it’s important to note that when making a call against an object running in the server from your Web application, you are making that call across processes. The Web application is running in one process, while the object is running in another process.
Calls to objects across processes are significantly slower than calls to objects in the same process. It’s also likely that your Web application is running on a Web server that is actually a different machine from the one the object is running on, so the calls are not only cross process but also cross machine.
If your application requires making a large number of fine-grained ArcObjects calls, there are two strategies you can employ to extend the GIS server with your own object that expose coarse-grained interfaces: create utility COM objects or create server object extensions.
Extending a server object has the following advantages over creating a generic COM object:
However, unlike utility COM objects, server object extensions are reigstered and configured with speficic server objects and are not for add-hoc use or use with an empty server context. See the sample ArcGIS_Spatial_Query COM Utility for an example of extending the GIS server with utility COM object.
The server object extension satisfies application requirements for functionality that requires making a large number of fine-grained ArcObjects calls within the ArcGIS Server container process (ArcSOC.exe). It this scenario these calls include looping through features, getting their geometry, clipping the geometry, summarizing the areas based on an attribute, creating a graphic for each feature, and so on. Since the caller of the server object extension is free to specify a buffer distance that may include a large number of features, the number of features that would be analyzed is indeterminate, which could easily result in thousands of fine-grained ArcObjects calls.
The server object extension in this scenario extends the MapServer
with specialized functionality exposed as a stateless method on a custom
interface. As a result, you should configure the server object
extension with a pooled MapServer.
The server object extension is also designed to store two custom
properties with the MapServer configuration for which the extension is
enabled. These properties will be settable through a custom ArcCatalog
property page for the server object extension and used during execution:
The Web application is designed to make stateless use of the GIS server.
It uses events on the Web ADF Map control to get a point from the user,
gains access to server context using the MapResource for an ArcGIS Server Local
data source, then uses the point as input to the server object extension
to perform the analysis. To support this application, you need to add a pooled
map server object with the server object extension enabled to your ArcGIS
Server using ArcCatalog.
The Web application will use the Web ADF framework to manage the connection to
the GIS server, and the Web ADF controls will provide the basic
mapping functionality required for this application. You will add a new
tool to the Toolbar control that allows the user to click the map as input to
the analysis. The results are displayed on the map as a set of graphics
and summarized in a table on the Web page.
The requirements for working through this scenario are that you have ArcGIS Server and ArcGIS Desktop installed and running. The machine on which you develop the Web application must, at a minimum have the Web ADF, .NET SDK, and IIS installed.
The following ArcObjects .NET assemblies will be used to build the server object extension and property page:
The following .NET framework assembly will also be required:
The following ArcObjects and Web ADF .NET assemblies will be used to build the
Web application:
The development environment does not require any ArcGIS licensing; however, connecting to a server and using a map server object does require that the GIS server is licensed to run ArcObjects in the server. None of the assemblies or object libraries used require an extension license.
The IDE used in this example is Visual Studio .NET 2005, and all IDE specific steps will assume that it is the IDE you are using.
The code for this scenario is divided into four parts; all are written in C#. The first is the implementation of the server object extension, which will run within the GIS server, and exposes the methods that extend the MapServer. The second is the server object extension's property page that will be registered with the ArcCatalog desktop application.
The third is a utility application that will register the server object extension with the GIS Server. The final part illustrates how to create a Web application that makes use of the server object extension and the Web ADF controls and APIs.
The instructions below will be easiest to follow as a narrative description of the sample code for this scenario. You may wish to open the sample in Visual Studio and read the rest of this section as a walk through of the sample.
The first step is to create a new project that will contain interfaces implemented by server object extension classes and utilized by clients.
You will create a C# class named VegSOEInterfacesCSharp that contains the two interfaces to implement within the server object extension classes in another project. Why are the interfaces for the server object extension stored in a separate assembly? Client applications only need the interfaces, registered as COM types, to interact with the server object extension. The business logic for the server object extension only needs to reside on the GIS Server and does not need to be provided to the client. As a result, the assembly provided to a client application only contains interfaces, no business logic. Much like working with ArcObjects remotely via ArcGIS Server, a client will use a custom server object extension interface to work with server object extension classes (objects) remotely via ArcGIS Server.
Add the new class to the VegSOEInterfacesCSharp project.
You'll need to add references to assemblies that contain the ArcObjects required for the interface definitions in this class file. Because the class contains interfaces that will be exposed as COM types, it will need to reference some additional assemblies to manage interoperability.
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.Geodatabase;
Add the namespace VegSOEInterfacesCSharp. Within the namespace definition, add
two interfaces: IVegUtilsSOE and IVegResultsSOE.
The IVegUtilsSOE interface will define the basic framework to be implemented by
the VegUtilsSOE class, namely a method to accepts a user provided ArcObjects
object of type IPoint and a double value to define the distance around the
point to generate a buffer. An ArcGIS Server client work with a reference to
the VegUtilsSOE object running within the server object container via the
sumVegetationType method and the IVegUtilsSOE interface. The pattern will
follow standard programming techniques for working with ArcObjects (COM
objects) remotely via ArcGIS Server. The sumVegetationType method will create a
VegResultsSOE object in the server container process, update it's properties
and return to the client a reference to the remote VegResultsSOE COM object via
the IVegResultsSOE interface.
The IVegResultsSOE interface will define the basic framework to be implemented
by the VegResultsSOE class, namely two properties to store a reference to an
array of graphic elements and a recordset.
Since both interfaces will be registered (added to the registry) with COM, they
should define a unique GUID using the COM attribute GuidAttribute. The GUID
will be used to uniquely identify an application, component, class, interface,
etc. within the Windows registry. There are many options for generating a
unique GUID. You can create a unique GUID using the .NET System.Guid struct.
The call to System.Guid.NewGuid().ToString() will return a usable GUID string.
In addition, a number of Web sites and services provide GUID generating
capabilities.
namespace VegSOEInterfacesCSharp
{
[GuidAttribute("70a7fbc8-ab8e-4e3c-810f-4e0f46f62e49")]
public interface IVegUtilsSOE
{
IVegResultsSOE sumVegetationType(IPoint pPoint, double dDistance);
}
[GuidAttribute("3c09de7a-c0ce-4c6f-b1b6-8100e712c1b2")]
public interface IVegResultsSOE
{
IGraphicElements ResGraphics
{
get;
set;
}
IRecordSet Stats
{
get;
set;
}
}
}
In the VegSOEInterfacesCSharp project, open the AssemblyInfo.cs file and change it to reflect the following settings. It is important to set the ComVisibleAttribute to true and specify a unique GUID for the GuidAttribute. Since the assembly will be used to generate a type library, these attributes will enable the interfaces to be registered with COM and will define a unique identifier.
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("VegSOEInterfacesCSharp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("ESRI")]
[assembly: AssemblyProduct("VegSOEInterfacesCSharp")]
[assembly: AssemblyCopyright("Copyright 2006")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisibleAttribute(true)]
[assembly: GuidAttribute("3AAEB4BE-263F-380D-89FD-D9717DE4BFF9")]
You will create a C# class named VegSOECSharp that represents the server object extension. This server object extension will provide a method that performs the spatial query and returns an object of type VegResultsSOE that contains an array of graphic elements and a recordset. Before creating the server object extension itself, you'll create the VegResultsSOE (vegetation results) class. This class is merely used as a complex type to store the results of the server object extension in a single object for use by the consuming client (e.g. Web application).
Add the new class to the VegSOECSharp project.
You'll need to add references to assemblies that contain the ArcObjects required for this class. In addition, because this is a COM object for use in the GIS server, you'll need to add some additional references to support calling a managed .NET component from a COM client. The .NET component is the custom server object extension assembly and the COM client is the container process, ArcSOC.exe. The COM client uses a COM callable wrapper to work with the .NET component. The technology that permits .NET components and COM clients to work together is known as COM Interop.
using System; using System.Runtime.InteropServices; using System.EnterpriseServices; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.Geodatabase;
Define the public COM class to contain implementation of the VegResultsSOE
object. Since this class will be exposed to COM, a couple of COM
attributes need to be added to the class definition;
specifically AutomationProxy,
ClassInterface and GuidAttribute. AutomationProxy determines if an
object should be marshaled using the automation marshaler (true) or
custom marshaled (false). We'll use the automation marshaler.
ClassInterface defines how interfaces to the COM class are generated in the
type library and exposed to a COM client. In this case, we want to
support COM versioning and explictly define the default interface by which a
COM client will access the class. Setting the ClassInterface
attribute to ClassInterfaceType.None requires that we define an explict
interface (IVegResultsSOE) to work with our class (VegResultsSOE). The
GuidAttribute should reference a unique GUID to identify our
class.
Our class should derive from the ServiceComponent class to be
hosted by COM clients. In addition, we will implement the IVegResultsSOE
interface as a default interface to interact with our COM object. Two
properties will be defined to get\set the graphics elements and recordset,
ResGraphics and Stats, respectively.
namespace VegSOECSharp
{
[AutomationProxy(true), ClassInterface(ClassInterfaceType.None),GuidAttribute("05922ca8-1a80-4502-8bbf-b3c0637b80ac")]
public class VegResultsSOE: ServicedComponent, VegSOEInterfacesCSharp.IVegResultsSOE
{
private IGraphicElements m_resGraphics;
private IRecordSet m_resStats;
public IGraphicElements ResGraphics
{
get{ return m_resGraphics; }
set{ m_resGraphics = (IGraphicElements) value; }
}
public IRecordSet Stats
{
get{ return m_resStats; }
set{ m_resStats = (IRecordSet) value; }
}
}
}
The first step is to add the new class to the project.
Now that you have implemented the results class, you can implement your server object extension class. A server object extension extends a server object with additional interfaces to provide more specialized functionality. In this example, the server object extension implements the custom interface IVegUtilsSOE.
using System; using System.Runtime.InteropServices; using System.EnterpriseServices; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.Display; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.Server;
Define the public COM class to contain the implementation of the VegUtilsSOE object. Since this class will be exposed to COM, a couple of COM attributes need to be added to the class definition; specifically AutomationProxy, ClassInterface and GuidAttribute. AutomationProxy determines if an object should be marshaled using the automation marshaler (true) or custom marshaled (false). We'll use the automation marshaler. ClassInterface defines how interfaces to the COM class are generated in the type library and exposed to a COM client. In this case, we want to support COM versioning and explictly define the default interface by which a COM client will access the class. Setting the ClassInterface attribute to ClassInterfaceType.None requires that we define an explict interface (IVegResultsSOE) to work with our class (VegResultsSOE). The GuidAttribute should reference a unique GUID to identify our class.
A set of member variables should be created to store a reference an IServerObjectHelper (IServerObjectExtension implmementation), an ILayer and two strings to account for layer name and field name (IVegUtilSOE implmementation) and an ILog (ILogSupport implementation). Polulating these variables will be discussed in the next section.
namespace VegSOECSharp
{
[AutomationProxy(true), ClassInterface(ClassInterfaceType.None), GuidAttribute("87176523-1ede-4fe6-abe0-66481ac7d04b")]
public class VegUtilsSOE : ServicedComponent, VegSOEInterfacesCSharp.IVegUtilsSOE, IServerObjectExtension, IObjectConstruct, ILogSupport, IObjectActivate
{
private IServerObjectHelper m_SOH;
private ILayer m_layer;
private string m_layerName;
private string m_fieldName;
private ILog m_log;
The following class and interfaces need to be implemented:
A discussion and appropriate implementation code is provided below:
The VegUtilsSOE class should derive from the ServicedComponent class to be hosted by COM clients (e.g. ArcSOC.exe). No additional code is necessary.
If you want your server object extension to log messages to the GIS server's log
file, your server object extension should implement ILogSupport. ILogSupport is
an optional interface for server object extensions that has a single
InitLogging method. InitLogging is called when the server object extension is
created and hands back a reference to the GIS server's log object via the log
argument. Once you have a reference to the server log, you will often call a
single method, AddMessage(), to add information to the log. The AddMessage()
method has three parameters: level, code, and message.
The level is the level of detail of the message in relation to other messages.
Levels are classified from 1 to 5 and termed, in order, Error, Warning, Normal,
Detailed, and Debug. ArcGIS Server log file settings determine which messages
are included in the server log.
The code is the result code associated with the message. The code is an
arbitrary integer value to uniquely define the source of the message. Codes 0 -
5999 are utilized by the SOM. Codes 6000 and above can be generated by any
serviced component (e.g. MapServer, GeocodeServer, custom component, etc.).
The message is the custom string inserted into the GIS server log file.
public void InitLogging(ILog log)
{
m_log = log;
}
A mandatory interface that must be supported by all server object extensions, and includes two methods: Init and Shutdown. This interface is used by the server object to manage the lifetime of the server object extension. The server object cocreates the server object extension and calls the Init method handing it a back reference to the server object via the server object helper argument. The server object helper implements a weak reference on the server object. The extension can keep a strong reference on the server object helper (for example, in a member variable) but should not keep a strong reference on the server object. Extensions should get the server object from the server object helper in order to make any method calls on the server object and release the reference after making the method calls. Init is called once, when the instance of the server object extension is created. The Shutdown method is called once and informs the server object extension that the server object's context is being shut down and is about to go away. In response the server object extension should release its reference on the server object helper. The log entries are merely informative and completely optional.
public void Init(IServerObjectHelper pSOH)
{
m_SOH = pSOH;
m_log.AddMessage(3,8000,"VegUtilsSOE custom message. Init called");
}
public void Shutdown()
{
m_log.AddMessage(3,8000,"VegUtilsSOE custom message. Shutdown called");
m_SOH = null;
m_layer = null;
m_log = null;
}
If your server object extension includes configuration properties or requires
any additional intitialization logic, you need to implement IObjectConstruct.
IObjectConstruct is an optional interface for server object extensions. The
interface includes a single method called Construct. Construct is called only
once, when the server object extension is created, after
IServerObjectExtension::Init is called. You should include any expensive
initialization logic within your implementation of Construct.
Construct hands back the configuration properties for the server object
extension as a property set. The configuration properties are stored in the
server object configuration file. Configuration files are named <service
name>.<server object type>.cfg and stored on the SOM machine in the
<ArcGIS Install>\server\user\cfg directory. For example, a map service
named Yellowstone has a configuration file named Yellowstone.MapServer.cfg.
Properties of server object extensions configured for use with a server object
are also stored in the cfg file. In this scenario, a set of properties are
being read from the service cfg file. The property values, a feature layer name
and field name, are validated to confirm they exist in the default map frame
associated with the map server object. If an error occurs, the appropriate log
entries are added.
public void Construct(IPropertySet props)
{
try
{
m_layerName = props.GetProperty("LayerName") as string;
m_fieldName = props.GetProperty("FieldName") as string;
}
catch (Exception ex)
{
m_log.AddMessage(1, 8000, "VegUtilsSOE custom error. Error reading properties: " + ex.Message + " " + props.Count.ToString());
return;
}
try
{
IMapServer ms = (IMapServer) m_SOH.ServerObject;
IMapServerObjects mso = (IMapServerObjects) ms;
IMap map = mso.get_Map(ms.DefaultMapName);
UID ltype = new UIDClass();
ltype.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}";
IEnumLayer el = map.get_Layers(ltype, true);
el.Reset();
ILayer l = null;
while ((l = el.Next()) != null)
{
if (l.Name == m_layerName)
{
m_layer = l;
break;
}
}
if (m_layer == null)
{
m_log.AddMessage(1, 8000, "VegUtilsSOE custom error: Layer " + m_layerName + " not found.");
return;
}
IFeatureLayer fl = (IFeatureLayer)m_layer;
IFeatureClass fc = fl.FeatureClass;
if (fc.FindField(m_fieldName) == -1)
m_log.AddMessage(1, 8000, "VegUtilsSOE custom error: Field " + m_fieldName + " not found in layer " + m_layerName);
else
m_log.AddMessage(3, 8000, "VegUtilsSOE sucessfully initialized.");
}
catch (Exception ex)
{
m_log.AddMessage(1, 8000, "VegUtilsSOE custom error: Failed to initialize extension: " + ex.Message + "::" + ex.StackTrace.Length.ToString());
}
}
While IServerObjectExtension::Init and IObjectConstruct::Construct are called once when the instance of the server object extension is created, if your server object extension requires logic to run each time its server context is acquired and released (each time a client calls CreateServerContext and ReleaseContext), you need to implement IObjectActivate. IObjectActivate is an optional interface for server object extensions that includes two methods: Activate and Deactivate. Activate is called each time a client calls CreateServerContext on the server object extension's server object's context, and Deactivate is called each time a client releases the context (via ReleaseContext). Because Activate and Deactivate are called each time a client gets and releases the server object's context, any logic you implement in these methods should not be expensive. In this scenario, we merely add some instructive information to the log file to indicate the method was called.
public void Deactivate()
{
m_log.AddMessage(3,8000,"VegUtilsSOE custom message. Deactivate called");
}
public void Activate()
{
m_log.AddMessage(3,8000,"VegUtilsSOE custom message. Activate called");
}
IVegUtilsSOE is a custom interface implemented by the custom SOE to expose a method called by a client application. Implementing the sumVegetationType method defined by the IVegUtilsSOE interface is the last step to complete the server object extension. The sumVegetationType method takes as arguments a point and a distance. In the method, you'll add code that buffers the point to the specified distance, then queries the layer's feature class for all the polygons that intersect that buffer. For each polygon, it clips it to the buffer, creates a graphic of the clipped polygon, and adds its area to a Dictionary object based on the value of the specified field.
public VegSOEInterfacesCSharp.IVegResultsSOE sumVegetationType(IPoint pPoint, double dDistance)
{
if (m_layer == null)
{
m_log.AddMessage(1, 8000, "VegUtilsSOE custom error: layer not found");
return null;
}
IFeatureLayer fl = (IFeatureLayer) m_layer;
IFeatureClass pVegClass = fl.FeatureClass;
ITopologicalOperator pTopoOp = (ITopologicalOperator) pPoint;
IGeometry pGeom = pTopoOp.Buffer(dDistance);
ISpatialFilter pSFilter = new SpatialFilter();
pSFilter.Geometry = pGeom;
pSFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
pSFilter.GeometryField = pVegClass.ShapeFieldName;
IFeatureCursor pFCursor = pVegClass.Search(pSFilter, true);
Before looping through the features, you need to create a GraphicElements collection to hold the graphics, a simple fill symbol to apply to each graphic element, a dictionary object that you will use to categorize the different vegetation types, and some other needed variables. Note that the fill symbol is created using a helper method called newFillS . You will create this method later.
pTopoOp = (ITopologicalOperator) pGeom;
int lPrim = pVegClass.FindField(m_fieldName);
System.Collections.Specialized.ListDictionary dict = new System.Collections.Specialized.ListDictionary();
ISimpleFillSymbol pSFS = newFillS();
IGraphicElements pGraphics = new GraphicElements();
The next step is to loop through the features in the pVegClass feature class that intersect the buffer geometry and clip each vegetation polygon to the buffer. The resulting clipped geometry is then used to create a graphic that is added to the graphics collection. The area of the clipped geometry is added to the total area of the feature's type (as defined by the field specified in the server object extension properties) in the dictionary object
IFeature pFeature;
while ((pFeature = pFCursor.NextFeature()) != null)
{
IFillShapeElement pFE = (IFillShapeElement) new PolygonElement();
IElement pElement = pFE as IElement;
IGeometry pNewGeom = pTopoOp.Intersect(pFeature.Shape, esriGeometryDimension.esriGeometry2Dimension);
pElement.Geometry = pNewGeom;
pFE.Symbol = pSFS;
IGraphicElement ge = (IGraphicElement) pFE;
pGraphics.Add(ge);
IArea pArea = pNewGeom as IArea;
string sType = pFeature.get_Value(lPrim) as string;
if (dict.Contains(sType))
dict[sType] = (double)dict[sType] + pArea.Area;
else
dict[sType] = pArea.Area;
}
At this point, the dictionary object will have a key for each unique value of the field whose item is the total area for that unique value within the buffer. The next step is to create a record set object and copy the keys and items from the dictionary into rows and fields in the record set. This is accomplished using the sumRS helper method, which will be implemented later.
IRecordSet psumRS = sumRS(dict);
Finally, since the sumVegetationType function returns a VegResultsSOE object, the last part of the function creates a new VegResultsSOE object, sets the graphics collection and summary record set in the object, and returns the object to the caller. Note that the new VegResultsSOE object is referenced using the VegSOEInterfacesCSharp.IVegResultsSOE interface. The client application will work with the interface to process the results.
VegSOEInterfacesCSharp.IVegResultsSOE pRes = new VegResultsSOE();
pRes.ResGraphics = pGraphics;
pRes.Stats = psumRS;
return pRes;
}
As described in the previous step, the sumVegetationType method makes use of two
helper methods to create a fill symbol (newFillS) and to copy the contents of a
dictionary object to a record set (sumRS). You will now implement these helper
methods in your server object extension class.
The sumRS method takes a dictionary object as an argument, and returns a record
set. The function creates a new record set with a field for the type of
vegetation (key) and a field for the total area (value). It then loops through
the keys and values in the dictionary and creates a row in the record set for
each key/value pair.
private IRecordSet sumRS(System.Collections.Specialized.ListDictionary dict)
{
IRecordSet pNewRs = new RecordSet();
IRecordSetInit prsInit = pNewRs as IRecordSetInit;
IFields pFields = new Fields();
IFieldsEdit pFieldsEdit = pFields as IFieldsEdit;
pFieldsEdit.FieldCount_2 = 2;
IField pField = new Field();
IFieldEdit pFieldEdit = pField as IFieldEdit;
pFieldEdit.Name_2 = "Type";
pFieldEdit.Type_2 = esriFieldType.esriFieldTypeString;
pFieldEdit.Length_2 = 50;
pFieldsEdit.set_Field(0, pField);
pField = new Field();
pFieldEdit = pField as IFieldEdit;
pFieldEdit.Name_2 = "Area";
pFieldEdit.Type_2 = esriFieldType.esriFieldTypeDouble;
pFieldsEdit.set_Field(1, pField);
prsInit.CreateTable(pFields);
ICursor pIC = prsInit.Insert();
IRowBuffer pRowBuf = prsInit.CreateRowBuffer();
System.Collections.IDictionaryEnumerator myEnumerator = dict.GetEnumerator();
while (myEnumerator.MoveNext())
{
pRowBuf.set_Value(0, myEnumerator.Key);
pRowBuf.set_Value(1, myEnumerator.Value);
pIC.InsertRow(pRowBuf);
}
return pNewRs;
}
The newFillS method creates and returns a new SimpleFillSymbol object. This fill symbol is a hollow fill symbol with a green outline. The client application can choose to use this rendering scheme when working with the server object extension results.
private ISimpleFillSymbol newFillS()
{
ISimpleLineSymbol pSLS = new SimpleLineSymbol();
IRgbColor pcolor = new RgbColor();
pcolor.Red = 0;
pcolor.Green = 255;
pcolor.Blue = 0;
pSLS.Color = pcolor;
pSLS.Style = esriSimpleLineStyle.esriSLSSolid;
pSLS.Width = 2;
ISimpleFillSymbol pSFS = new SimpleFillSymbol();
pSFS.Outline = pSLS;
pSFS.Style = esriSimpleFillStyle.esriSFSHollow;
return pSFS;
}
}
}
In the VegSOECSharp project, open the AssemblyInfo.cs file and change it to reflect the following settings. It is important that the ComVisibleAttribute be set to true (or removed since the default is true). Setting this on the assembly will make all public types visible to COM clients. The ComVisibleAttribute can also be applied to individual types in the assembly.
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("VegSOECSharp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("ESRI")]
[assembly: AssemblyProduct("VegSOECSharp")]
[assembly: AssemblyCopyright("Copyright 2006")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisibleAttribute(true)]
Build the VegSOEInterfacesCSharp and VegSOECSharp projects. Each project will create an assembly, VegSOEInterfacesCSharp.dll and VegSOECSharp.dll. The deployment section below will discuss the distribution and registration of these assemblies.
In this part of the implementation, you'll create the necessary objects to implement a property page in ArcCatalog that allows you to configure your server object extension's properties. The properties for this server object extension are:
Both of these properties will be settable using this property page. The property page will display all of the feature layers in the default map, and the fields for the selected feature layer. The property page will only be used by the GIS server administrator to specify the properties for the custom server object extension when the extension is enabled on a map service (MapServer).
The first step is to create a new project that will contain the implementation of an ArcCatalog property page.
This project will contain a class file and a Windows form. The Windows form will provide the visual interface of the property page in ArcCatalog. The class file will provide the logic for registering the property page, showing\hiding the property page, and retrieving and setting server object extension properties in the server object configuration file.
You'll need to add references to assemblies that contain the ArcObjects required for implementation code in both the property page class and form.

using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry;
namespace VegSOEPropsCSharp
{
public partial class FormVegProps : Form
{
public FormVegProps()
{
InitializeComponent();
}
public int getHWnd()
{
return this.Handle.ToInt32();
}
private IMapDocument m_map;
private string m_layer;
private string m_field;
public void setMap(string sName)
{
ComboLayers.Items.Clear();
ComboFields.Items.Clear();
m_map = new MapDocumentClass();
m_map.Open(sName, null);
IMap map = m_map.get_Map(0);
UID id = new UIDClass();
id.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}";
IEnumLayer el = map.get_Layers(id, true);
ILayer l = null;
while ((l = el.Next()) != null)
{
if (l is IFeatureLayer)
{
IFeatureLayer fl = (IFeatureLayer) l;
IFeatureClass fc = fl.FeatureClass;
if (fc.ShapeType == esriGeometryType.esriGeometryPolygon && fc.FeatureType == esriFeatureType.esriFTSimple)
ComboLayers.Items.Add(l.Name);
}
}
if (m_layer == null)
ComboLayers.SelectedIndex = 0;
else
{
IEnumerator itemenum = ComboLayers.Items.GetEnumerator();
while (itemenum.MoveNext())
{
string lname = itemenum.Current.ToString();
if (lname == m_layer)
ComboLayers.SelectedIndex = ComboLayers.Items.IndexOf(itemenum.Current);
}
}
}
public string theLayer
{
get { return m_layer; }
set { ComboLayers.Text = value;
m_layer = ComboLayers.Text; }
}
public string theField
{
get { return m_field; }
set { ComboFields.Text = value;
m_field = ComboFields.Text; }
}
private void ComboLayers_SelectedIndexChanged(object sender, EventArgs e)
{
ComboFields.Items.Clear();
IMap map = m_map.get_Map(0);
UID id = new UIDClass();
id.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}";
IEnumLayer el = map.get_Layers(id, true);
ILayer l = null;
while ((l = el.Next()) != null)
{
if (l is IFeatureLayer && l.Name == ComboLayers.Text)
{
IFeatureLayer fl = (IFeatureLayer) l;
IFeatureClass fc = fl.FeatureClass;
IFields flds = fc.Fields;
for (int i = 0; i < flds.FieldCount; i++)
{
IField fld = flds.get_Field(i);
ComboFields.Items.Add(fld.Name);
}
}
}
if (m_field == null)
ComboFields.SelectedIndex = 0;
else
{
IEnumerator itemenum = ComboFields.Items.GetEnumerator();
while (itemenum.MoveNext())
{
string fname = itemenum.Current.ToString();
if (fname == m_field)
ComboFields.SelectedIndex = ComboFields.Items.IndexOf(itemenum.Current);
}
}
m_layer = ComboLayers.SelectedItem.ToString();
}
private void ComboFields_SelectedIndexChanged(object sender, EventArgs e)
{
m_field = ComboFields.Text;
}
private void FormVegProps_FormClosed(object sender, FormClosedEventArgs e)
{
m_map.Close();
m_map = null;
m_layer = null;
m_field = null;
}
}
using System; using System.Runtime.InteropServices; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.Catalog; using ESRI.ArcGIS.CatalogUI; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Server; using ESRI.ArcGIS.Framework;
namespace VegSOEPropsCSharp
{
[GuidAttribute("EB085B89-D01F-448c-98F0-448F0BECB0BF")]
public class VegSOEProps: IComPropertyPage, IAGSSOEParameterPage
{
private IPropertySet m_props;
private IPropertySet m_soprops;
private string m_exttype;
private string m_sotype;
private FormVegProps propertyPage;
The constructor of the Property page class will be called by ArcCatalog when the
property page needs to be created and displayed. The VegSOEProps constructor
will populate a few member variables. A new instance of the FormVegProps
Windows form will be created and assigned to the propertyPage variable. The
server object extension will be registered to be used with server objects of
type "MapServer", stored using the m_sotype variable. The name of the
extension, "VegUtilitiesSOE_CSharp", will to stored using the m_exttype
variable. This value must match the name of the server object extension when it
is registered with ArcGIS Server (discussed in the next section).
The destructor is explicitly defined to dispose of the property page.
public VegSOEProps()
{
propertyPage = new FormVegProps();
m_sotype = "MapServer";
m_exttype = "VegUtilitiesSOE_CSharp";
}
~VegSOEProps()
{
propertyPage.Dispose();
propertyPage = null;
}
Two interfaces need to be implemented: IComPropertyPage and IAGSSOEParameterPage.
The IComPropertyPage interfaces defines the framework for ArcCatalog to work with property pages in general, such as show and hide. The following code defines the minimum implementation requirements for this scenario.
public bool IsPageDirty
{
get { return false; }
}
public void Cancel()
{}
public int get_HelpContextID(int controlID)
{ return 0; }
public string Title
{
get { return null; }
set { }
}
public void SetObjects(ISet objects)
{}
public int Width
{
get { return 0;}
}
public int Priority
{
get { return 0; }
set {}
}
public void Apply()
{}
public IComPropertyPageSite PageSite
{
set { }
}
public void Deactivate()
{ }
public int Height
{
get { return 0; }
}
public void Show()
{
propertyPage.Show();
}
public string HelpFile
{
get { return null; }
}
public int Activate()
{
return propertyPage.getHWnd();
}
public bool Applies(ISet objects)
{
return false;
}
public void Hide()
{
propertyPage.Hide();
}
<Extension>
<TypeName>VegUtilitiesSOE_CSharp</TypeName>
<Enabled>true</Enabled>
<Properties>
<LayerName>Vegetation</LayerName>
<FieldName>PRIMARY_</FieldName>
</Properties>
<Info>
<WebEnabled>true</WebEnabled>
<WebCapabilities></WebCapabilities>
</Info>
</Extension>
The LayerName and FieldName properties will differ depending on changes made
via the property page. When the custom server object extension is checked in
ArcCatalog, these properties are added to the map server object configuration
file. The properties are added to the configuration file in the getter portion
of the ExtensionProperties property definition. To store a custom property in
the configuration file, add it in the getter. If the properties have already
been added to the configuration file, the setter portion of the
ExtensionProperties property will return a propertyset with current properties
and values.
public IPropertySet ServerObjectProperties
{
get { return m_soprops; }
set {
m_soprops = value;
try
{
propertyPage.theLayer = m_props.GetProperty("LayerName").ToString();
propertyPage.theField= m_props.GetProperty("FieldName").ToString();
propertyPage.setMap(m_soprops.GetProperty("FilePath").ToString());
}
catch
{
propertyPage.setMap(m_soprops.GetProperty("FilePath").ToString());
}
}
}
public IPropertySet ExtensionProperties
{
get
{
m_props.SetProperty("LayerName",propertyPage.theLayer);
m_props.SetProperty("FieldName",propertyPage.theField);
return m_props;
}
set
{
m_props = value;
}
}
public string ServerObjectExtensionType
{
get
{
return m_exttype;
}
}
public string ServerObjectType
{
get
{
return m_sotype;
}
}
[ComRegisterFunction()]
static void RegisterFunction(String regKey)
{
Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(regKey.Substring(18) + "\\Implemented Categories\\" + "{A585A585-B58B-4560-80E3-87A411859379}");
}
[ComUnregisterFunction()]
static void UnregisterFunction(String regKey)
{
Microsoft.Win32.Registry.ClassesRoot.DeleteSubKeyTree(regKey.Substring(18));
}
}
}

In the VegSOEPropsCSharp project, open the AssemblyInfo.cs file and change it to reflect the following settings.
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("VegSOEPropsCSharp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("ESRI")]
[assembly: AssemblyProduct("VegSOEPropsCSharp")]
[assembly: AssemblyCopyright("Copyright 2006")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Build the VegSOEPropsCSharp project and create the assembly
VegSOEPropsCSharp.dll. The deployment section below will discuss the
distribution and registration of this assembly.
Components of a custom server object extension must be registered in two ways. One, the assemblies must be registered with .NET\COM on the machine they will be used. And two, the server object extension must be registered with ArcGIS Server.
Thusfar, three .NET assemblies have been created:

The VegSOECSharp assembly must be registered on all Server Object Container machines (ArcSOC.exe) where a server object may enable and use the server object extension. As a COM client, the ArcSOC.exe process can work with other COM objects and types. To expose the .NET types in the VegSOECSharp assembly to COM, the .NET Assembly Registration tool (regasm.exe) will be used. To register the VegSOECSharp.dll, do the following:
regasm VegSOECSharp.dll /codebase
The regasm tool reads the metadata within an assembly and adds the necessary entries to the registry. The /codebase option registers the explicit location of the assembly. The codebase option will return a warning indicating that the assembly should be signed. If you choose to sign the assembly, you can also place it in the Global Assembly Cache (GAC) and remove the "/codebase" option when using regasm. To sign the assembly and add it to the GAC, do the following:
sn -k MyKeyPair.snk
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("C:/temp/MyKeyPair.snk")]
regasm VegSOECSharp.dll
gacutil -i VegSOECSharp.dll
regasm /unregister VegSOECSharp.dll
The VegSOEInterfacesCSharp assembly stores interface definitions implemented by
the server object extension, therefore it must be registered on all Server
Object Container machines (ArcSOC.exe) where a server object may enable and use
the server object extension. As a COM client, the ArcSOC.exe process can
work with the server object extension classes and interfaces as COM
objects and types.
In addition, the VegSOEInterfacesCSharp.dll must be registered on any
application client machine that will consume the server object extension.
As a result, the Microsoft .NET Framework used to build the assembly must be
installed on the client machine. The VegSOEInterfacesCSharp.dll contains
the interfaces a client application will use to access server object extension
objects remotely (just like working with ArcObjects remotely via ArcGIS
Server). No business logic is distributed with the
VegSOEInterfacesCSharp.dll.
To expose the .NET interface types in the VegSOEInterfacesCSharp assembly to
COM, the .NET Assembly Registration tool (regasm.exe) will be used. To register
the VegSOEInterfacesCSharp.dll, do the following:
regasm VegSOEInterfacesCSharp.dll /tlb:VegSOEInterfacesCSharp.tlb
The regasm tool reads the metadata within an assembly. Since the assembly only contains interface types, a type library must be generated using the /tlb option. The location of the tlb is stored in the registry.
regasm /unregister VegSOEInterfacesCSharp.dll /tlb:VegSOEInterfacesCSharp.tlb
The VegSOEPropsCSharp assembly must be registered on all machines where ArcCatalog will be used to administer GIS Servers where the server object extension can be enabled. Since the assembly is a .NET component, the Microsoft .NET Framework used to build the assembly must also be installed. As a COM client, ArcCatalog can work with other COM objects and types. To expose the .NET types and Windows form in the VegSOEPropsCSharp assembly to COM so they can be used by ArcCatalog, the .NET Assembly Registration tool (regasm.exe) will be used. To register the VegSOEPropsCSharp.dll, do the following:
regasm VegSOEPropsCSharp.dll /codebase
The regasm tool reads the metadata within an assembly and adds the necessary entries to the registry. The /codebase option registers the explicit location of the assembly. The codebase option will return a warning indicating that the assembly should be signed. If you choose to sign the assembly, you can also place it in the Global Assembly Cache (GAC) and remove the "/codebase" option when using regasm. To sign the assembly and add it to the GAC, do the following:
sn -k MyKeyPair.snk
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("C:/temp/MyKeyPair.snk")]
regasm VegSOEPropsCSharp.dll
gacutil -i VegSOEPropsCSharp.dll
regasm /unregister VegSOEPropsCSharp.dll
using ESRI.ArcGIS.ADF.Connection.AGS;
using ESRI.ArcGIS.Server;
Create a new instance of AGSServerConnection to initiate a connection to the SOM. Specify the correct name or ip address of the machine on which the SOM is running. Cast to IServerObjectAdmin2 to create a new extension type.
AGSServerConnection gisconnection = new AGSServerConnection(); gisconnection.Host = "localhost";
gisconnection.Connect(); IServerObjectAdmin2 soa = (IServerObjectAdmin2) gisconnection.ServerObjectAdmin; IServerObjectExtensionType soet = soa.CreateExtensionType();
Set properties on the new extension type. All properties
listed are defined using a string.
The CLSID property is really the ProgID. This is the fully qualified name
of the class that implements IServerObjectExtension. In our
scenario, the CLSID value is "VegSOECSharp.VegUtilsSOE".
The Description can be populated with basic information on the purpose of
the server object extension. It will be displayed in ArcCatalog and
accessible programmatically.
The Name property should be a intuitive title of the server object
extension and unique among all other extensions. The Name is used by
ArcCatalog to display the server object extension on the capabilities
tab. It is also used to uniquely identify the server
object extension and display the correct property page.
Important: The Name property must match the
ServerObjectExtensionType property defined in the property page class to
display the correct property page when the server object extension is selected
in ArcCatalog. In this scenario, the Name property must match the
m_exttype variable value in the VegSOEProps.cs class file.
soet.CLSID = "VegSOECSharp.VegUtilsSOE"; soet.Description = "Veg Utilities Server Object Extension"; soet.Name = "VegUtilitiesSOE_CSharp";
Once the server object extension properties are set, it can be added to a
server object type using the AddExtensionType method off the
IServerObjectAdmin2 interface. To add the extension to multiple server
object types, call this method for each type. To remove the extension,
call DeleteExtensionType with the same parameters.
soa.AddExtensionType("MapServer", soet);
Console.WriteLine("Registered SOE with ArcGIS Server");
Console.ReadLine();
<types> <ServerObjectType> <Name>MapServer</Name> <ExtensionTypes> <ExtensionType> <Name>VegUtilitiesSOE_CSharp</Name> <DisplayName></DisplayName> <CLSID>VegSOECSharp.VegUtilsSOE</CLSID> <Description>Veg Utilities Server Object Extension</Description> </ExtensionType> </ExtensionTypes> </ServerObjectType> </types>
The easiest way to enable and configure a server object extension is to use
ArcCatalog. A set of sample data is provided for this scenario in
<ArcGIS Install\DeveloperKit\SamplesNET\Server\data\Yellowstone.
The Yellowstone.mxd map document contains two layers: a polygon feature
class and a grid. The polygon feature class is stored in the
personal geodatabase veg.mdb. It contains polygons referencing the
geographic areas of different vegetation types in Yellowstone National
Park. The grid is an DEM in Arc\Info GRID format and merely provides
a topographic reference for the area. In this section, we will
add the Yellowstone map document as an ArcGIS Server map service and enable the
VegUtilitiesSOE_CSharp extension using a field in the vegetation
layer.
Now that you have developed your server object extension and deployed it with an
ArcGIS Server map service, you can build your Web application to make use of
it. In this example, we will create a Web application from scratch using
the Web ADF components. The Web ADF and ASP.NET will provide the basic
framework for mapping, capturing user events, and displaying results.
We will create a custom tool to interact with the server object extension.
Register the COM types in the VegSOEInterfacesCSharp assembly
As mentioned in part 3, the VegSOEInterfacesCSharp.dll must be registered on
any client machine that will consume the server object extension. The
assembly contains COM interface types that will be used by the Web application
to work with custom server object extension COM objects remotely. The
VegSOEInterfacesCSharp.dll was built at the conclusion of part 1 of this
scenario.
regasm VegSOEInterfacesCSharp.dll /tlb:VegSOEInterfacesCSharp.tlb
The regasm tool reads the metadata within an assembly and adds the necessary entries to the registry. Since the assembly only contains interface types, a type library must be generated using the /tlb option. The location of the tlb is stored in the registry.







Add content to capture user input in the browser
The custom server object extension will be utilized via a custom
tool. The Web application user interface contains a textbox and
checkbox to store information that the custom tool will use to calculate buffer
distance and update a GridView. In this section, Web content will be
added to capture textbox and checkbox values.
<script language="javascript" type="text/javascript">
function SetCheckBox(id)
{
if (id == 'CheckBox1'){
document.getElementById('CheckBox1').value = "";
document.getElementById('CheckBox1').value = document.getElementById('CheckBox1').checked;
}
}
function CustomLoad(){
var customform = document.forms[0];
var original_value = customform.elements["ESRIWebADFHiddenFields"].value;
var new_value = original_value + ",TextBox1,CheckBox1";
customform.elements["ESRIWebADFHiddenFields"].value = new_value;
}
</script>
The SetCheckBox function will be called when CheckBox1 is
checked. It will set the value of CheckBox1 to "true" or "false",
checked or unchecked respectively.
<input type="hidden" name="ESRIWebADFHiddenFields" id="ESRIWebADFHiddenFields" value="maxx,maxy,minx,miny,coords,Map1_mode,control,Toolbar1_Group_current_tool," />When the Web ADF generates a postback (callback or full page postback) the createClientPostBackQueryString function in display_dotnetadf.js iterates through the comma-delimited array and adds an argument and value for each field. To take advantage of this capability which is already included in the Web ADF, we will append the textbox and checkbox ids onto the list. The values will be parsed in the custom tool implementation code.
<body onload="CustomLoad()">
Add a custom tool to work with the server object extension
In Solution Explorer, right-click the Web project and select "Add New Item...". In the Add New Item dialog, under the Visual Studio Installed Templates section, select the "Class" item. Set the class file name to "VegTool.cs" and make sure the language is "Visual C#". Visual Studio will prompt you to create an "App_Code" folder and place the new class file inside. Click Yes. The VegTool.cs file should open for you to start adding content. This file will contain the executable code associated with the custom tool.
The server object extension exposes two interfaces: IVegUtilsSOE and
IVegResultsSOE. As a client, the Web application needs a
reference to the type definition for both interfaces to remotely utilize the
COM objects they represent. The COM interface types are provided in the
VegSOEInterfacesCSharp.dll.
In Solution Explorer, right-click the Web project and select
"Add Reference...". In the dialog, browse to the location of the
VegSOEInterfacesCSharp.dll and add it to the project.
Addition components are also required to work with ArcGIS Server and the Web ADF. In Solution Explorer, right-click the Web project and select "Add ArcGIS Reference...". In the dialog, select the following components, click Add, then Finish:
ESRI.ArcGIS.ADF.ArcGISServer ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer ESRI.ArcGIS.Server ESRI.ArcGIS.Carto ESRI.ArcGIS.Geometry ESRI.ArcGIS.System ESRI.ArcGIS.Display
At the top of the VegTool.cs file, add the following using statements:
[C#]
using ESRI.ArcGIS.ADF.Web.UI.WebControls; using ESRI.ArcGIS.ADF.Web.UI.WebControls.Tools; using ESRI.ArcGIS.ADF.ArcGISServer; using ESRI.ArcGIS.ADF.Web.DataSources; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Server; using ESRI.ArcGIS.Geometry; using VegSOEInterfacesCSharp;
public class VegTool : IMapServerToolAction
{
public void ServerAction(ToolEventArgs args)
{
ESRI.ArcGIS.ADF.Web.UI.WebControls.Map mapctrl = (ESRI.ArcGIS.ADF.Web.UI.WebControls.Map) args.Control;
System.Web.UI.Page page = mapctrl.Page;
string cbxvalue = String.Empty;
string tbxvalue = String.Empty;
string callbackArgs = String.Empty;
System.Collections.Specialized.NameValueCollection keyValColl = null;
if (page.IsCallback)
{
callbackArgs = page.Request.Params["__CALLBACKPARAM"];
keyValColl = CallbackUtility.ParseStringIntoNameValueCollection(callbackArgs);
}
else
{
keyValColl = page.Request.Params;
}
tbxvalue = keyValColl["TextBox1"];
cbxvalue = keyValColl["CheckBox1"];
ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality mapfunc = (ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality) mapctrl.GetFunctionality(mapctrl.MapResourceManagerInstance.ResourceItems.Count - 1);
ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal mapres = (ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal) mapfunc.MapResource;
IServerContext sc = mapres.ServerContextInfo.ServerContext;
IMapServer mapserver = mapres.MapServer;
PointEventArgs pargs = (PointEventArgs) args;
ESRI.ArcGIS.ADF.Web.Geometry.Point inpt = ESRI.ArcGIS.ADF.Web.Geometry.Point.ToMapPoint(pargs.ScreenPoint, mapctrl.Extent, mapfunc.DisplaySettings.ImageDescriptor.Width, mapfunc.DisplaySettings.ImageDescriptor.Height);
IPoint pt = (IPoint)sc.CreateObject("esriGeometry.Point");
pt.X = inpt.X;
pt.Y = inpt.Y;
double distance = 0;
if (!Double.TryParse(tbxvalue, out distance))
{
distance = 10000;
}
IServerObjectExtensionManager soext_manager = (IServerObjectExtensionManager) mapserver;
IServerObjectExtension soext = soext_manager.FindExtensionByTypeName("VegUtilitiesSOE_CSharp");
IVegUtilsSOE vegutils = (IVegUtilsSOE) soext;
IVegResultsSOE vegresults = vegutils.sumVegetationType(pt, distance);
IGraphicElements comGraphics = vegresults.ResGraphics;
GraphicElement[] proxyGraphics = (GraphicElement[])
ESRI.ArcGIS.ADF.ArcGISServer.Converter.ComObjectToValueObject(comGraphics, sc, typeof(GraphicElement[]));
RgbColor rgb = new RgbColor();
rgb.Red = 155;
rgb.Green = 0;
rgb.Blue = 0;
rgb.AlphaValue = 255;
SimpleLineSymbol sls = new SimpleLineSymbol();
sls.Style = ESRI.ArcGIS.ADF.ArcGISServer.esriSimpleLineStyle.esriSLSSolid;
sls.Color = rgb;
sls.Width = 0.2;
foreach (ESRI.ArcGIS.ADF.ArcGISServer.PolygonElement pe in proxyGraphics)
{
SimpleFillSymbol sfs = (SimpleFillSymbol) pe.Symbol;
sfs.Outline = sls;
}
mapfunc.MapDescription.CustomGraphics = proxyGraphics;
GridView gdview = (GridView) mapctrl.Page.FindControl("GridView1");
string showtable = "'hidden'";
Boolean displaydiv = false;
if (!Boolean.TryParse(cbxvalue, out displaydiv))
{
displaydiv = false;
}
if (displaydiv)
{
IRecordSet rs = vegresults.Stats;
ESRI.ArcGIS.ADF.ArcGISServer.RecordSet value_rs = (ESRI.ArcGIS.ADF.ArcGISServer.RecordSet)ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.ComObjectToValueObject(rs, sc, typeof(ESRI.ArcGIS.ADF.ArcGISServer.RecordSet));
System.Data.DataTable datatable = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.ToDataTable(value_rs);
gdview.DataSource = datatable;
gdview.DataBind();
string returnstring = null;
using (System.IO.StringWriter sw = new System.IO.StringWriter())
{
HtmlTextWriter htw = new HtmlTextWriter(sw);
gdview.RenderControl(htw);
htw.Flush();
returnstring = sw.ToString();
}
CallbackResult cr = new CallbackResult("div", "griddiv", "innercontent", returnstring);
mapctrl.CallbackResults.Add(cr);
if (datatable.Rows.Count > 1)
showtable = "'visible'";
}
object[] oa = new object[1];
string sa = "var griddiv = document.getElementById('griddiv');";
sa += "griddiv.style.visibility = " + showtable + ";";
oa[0] = sa;
CallbackResult cr1 = new CallbackResult(null, null, "javascript", oa);
mapctrl.CallbackResults.Add(cr1);
if (mapctrl.ImageBlendingMode == ImageBlendingMode.WebTier)
{
mapctrl.Refresh();
}
else if (mapctrl.ImageBlendingMode == ImageBlendingMode.Browser)
{
mapctrl.RefreshResource(mapres.Name);
}
}
}
| Property | Value | Description |
|---|---|---|
| Text |
Vegetation Summary Proximity Search Tool |
Label for the tool in the Toolbar |
| ClientAction |
Point |
Client event passed to the server |
| Name | VegetationSummary | Object name of the tool, if used in code |
| ServerActionAssembly |
App_Code |
Class libraries associated with a Web site are compiled into an assembly named App_Code |
| ServerActionClass |
VegTool |
The name of the custom class which implements IMapServerToolAction and will be executed when this tool is used in the map |
This scenario includes functionality and programming techniques covering a number of different aspects of ArcObjects, the ArcGIS Server ArcObjects API, ArcGIS Server SOAP API, Web ADF Common API and Web controls.
You are encouraged to read The ArcGIS Server ArcObjects API , to get a better understanding of core ArcGIS Server programming concepts such as stateful versus stateless server application development. This section also covers concepts and programming guidelines for working with server contexts and ArcObjects running within those contexts, as well as discussion on extending server objects as demonstrated in this scenario.
This scenario makes use of the Web ADF to provide the majority of the user interface for this Web application. To learn more about the Web ADF read Developing Web Application using the Web ADF - which includes detailed descriptions and examples of using the Web controls, including the Map and Toolbar Web controls that you made use of while programming this Web application. If you are unfamiliar with ASP.NET Web development, it's also recommended that you 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 application is no exception. It includes the use of ArcObjects to work with the components of a MapServer, buffer and clip geometries, query a geodatabase, and create graphics. To learn more about these aspects of ArcObjects, refer to the online developer documentation on the Carto, Display, GeoDatabase, and Geometry object libraries.