Server spatial query server object extension
SpatialQuerySOE_VBNet\Extension.vb
' Copyright 2010 ESRI
' 
' All rights reserved under the copyright laws of the United States
' and applicable international laws, treaties, and conventions.
' 
' You may freely redistribute and use this sample code, with or
' without modification, provided you include the original copyright
' notice and use restrictions.
' 
' See the use restrictions.
' 

Imports Microsoft.VisualBasic
Imports System
Imports System.Runtime.InteropServices
Imports System.EnterpriseServices

Namespace SpatialQuerySOE_VBNet
  ''' <summary>
  ''' Implementation of the Spatial Query SOE.
  ''' </summary>
  <AutomationProxy(True), ClassInterface(ClassInterfaceType.None), Guid("3896A0F5-1028-489c-81CD-F7757D117C9E")> _
  Public Class Extension
    Inherits ServicedComponent
    Implements SpatialQuerySOE.Interfaces_VBNet.IExtension, ESRI.ArcGIS.Server.IServerObjectExtension, ESRI.ArcGIS.esriSystem.IObjectConstruct, ESRI.ArcGIS.esriSystem.ILogSupport, ESRI.ArcGIS.esriSystem.IObjectActivate


#Region "Member Variables"

    Private m_ServerObjectHelper As ESRI.ArcGIS.Server.IServerObjectHelper
    Private m_featureLayer As ESRI.ArcGIS.Carto.IFeatureLayer
    Private m_layerName As String
    Private m_fieldName As String
    Private m_log As ESRI.ArcGIS.esriSystem.ILog

#End Region

#Region "IServerObjectExtension Members"

    Public Sub Init(ByVal pSOH As ESRI.ArcGIS.Server.IServerObjectHelper) Implements ESRI.ArcGIS.Server.IServerObjectExtension.Init
      m_ServerObjectHelper = pSOH
      m_log.AddMessage(3, 8000, "SpatialQuerySOE custom message. Init called")
    End Sub

    Public Sub Shutdown() Implements ESRI.ArcGIS.Server.IServerObjectExtension.Shutdown
      m_log.AddMessage(3, 8000, "SpatialQuerySOE custom message. Shutdown called")
      m_ServerObjectHelper = Nothing
      m_featureLayer = Nothing
      m_log = Nothing
    End Sub

#End Region

#Region "SpatialQuerySOE.Interfaces_VBNet.IExtension Members"

    Public Function QueryPoint(ByVal point As ESRI.ArcGIS.Geometry.IPoint, ByVal distance As Double) As SpatialQuerySOE.Interfaces_VBNet.IResults Implements SpatialQuerySOE.Interfaces_VBNet.IExtension.QueryPoint
      If m_featureLayer Is Nothing Then
        m_log.AddMessage(1, 8000, "SpatialQuerySOE custom error: layer not found")
        Return Nothing
      End If

      Dim featureClass As ESRI.ArcGIS.Geodatabase.IFeatureClass = m_featureLayer.FeatureClass

      ' buffer the point
      Dim topologicalOperator As ESRI.ArcGIS.Geometry.ITopologicalOperator = CType(point, ESRI.ArcGIS.Geometry.ITopologicalOperator)
      Dim queryGeometry As ESRI.ArcGIS.Geometry.IGeometry = topologicalOperator.Buffer(distance)

      ' query the feature class
      Dim spatialFilter As ESRI.ArcGIS.Geodatabase.ISpatialFilter = New ESRI.ArcGIS.Geodatabase.SpatialFilter()
      spatialFilter.Geometry = queryGeometry
      spatialFilter.SpatialRel = ESRI.ArcGIS.Geodatabase.esriSpatialRelEnum.esriSpatialRelIntersects
      spatialFilter.GeometryField = featureClass.ShapeFieldName

      Dim resultsFeatureCursor As ESRI.ArcGIS.Geodatabase.IFeatureCursor = featureClass.Search(spatialFilter, True)

      ' loop thourgh the features, clip each geometry to the buffer
      ' and total areas by attribute value
      topologicalOperator = CType(queryGeometry, ESRI.ArcGIS.Geometry.ITopologicalOperator)
      Dim classFieldIndex As Integer = featureClass.FindField(m_fieldName)

      Dim summaryStatsDictionary As New System.Collections.Specialized.ListDictionary()

      ' create the symbol and graphic elements collection for the graphics
      Dim simpleFillSymbol As ESRI.ArcGIS.Display.ISimpleFillSymbol = createFillSymbol()
      Dim resultsGraphics As ESRI.ArcGIS.Carto.IGraphicElements = New ESRI.ArcGIS.Carto.GraphicElements()

      Dim resultsFeature As ESRI.ArcGIS.Geodatabase.IFeature
      resultsFeature = resultsFeatureCursor.NextFeature()
      Do While resultsFeature IsNot Nothing
        ' create the graphic
        Dim fillShapeElement As ESRI.ArcGIS.Carto.IFillShapeElement = TryCast(New ESRI.ArcGIS.Carto.PolygonElement, ESRI.ArcGIS.Carto.IFillShapeElement)
        Dim element As ESRI.ArcGIS.Carto.IElement = TryCast(fillShapeElement, ESRI.ArcGIS.Carto.IElement)

        ' clip the geometry
        Dim clippedResultsGeometry As ESRI.ArcGIS.Geometry.IGeometry = topologicalOperator.Intersect(resultsFeature.Shape, ESRI.ArcGIS.Geometry.esriGeometryDimension.esriGeometry2Dimension)
        element.Geometry = clippedResultsGeometry
        fillShapeElement.Symbol = simpleFillSymbol
        Dim resultsGraphicElement As ESRI.ArcGIS.Carto.IGraphicElement = TryCast(fillShapeElement, ESRI.ArcGIS.Carto.IGraphicElement)
        resultsGraphics.Add(resultsGraphicElement)

        ' get statistics and add to dictionary
        Dim area As ESRI.ArcGIS.Geometry.IArea = TryCast(clippedResultsGeometry, ESRI.ArcGIS.Geometry.IArea)
        Dim resultsClass As String = TryCast(resultsFeature.Value(classFieldIndex), String)

        ' If the class is already in the dictionary, add the current feature's area to the existing entry
        If summaryStatsDictionary.Contains(resultsClass) Then
          summaryStatsDictionary(resultsClass) = CDbl(summaryStatsDictionary(resultsClass)) + area.Area
        Else
          summaryStatsDictionary(resultsClass) = area.Area
        End If
        resultsFeature = resultsFeatureCursor.NextFeature()
      Loop

      ' create the summary statistics recordset
      Dim summaryStatsRecordSet As ESRI.ArcGIS.Geodatabase.IRecordSet = createSummaryRecordSet(summaryStatsDictionary)

      ' create the results object
      Dim results As SpatialQuerySOE.Interfaces_VBNet.IResults = New Results()
      results.ResultsGraphics = resultsGraphics
      results.SummaryStatistics = summaryStatsRecordSet

      Return results
    End Function

    Private Function createSummaryRecordSet(ByVal summaryStatsDictionary As System.Collections.Specialized.ListDictionary) As ESRI.ArcGIS.Geodatabase.IRecordSet
      ' initialize the summary statistics record set
      Dim summaryStatsRecordSet As ESRI.ArcGIS.Geodatabase.IRecordSet = New ESRI.ArcGIS.Geodatabase.RecordSet()
      Dim recordSetInit As ESRI.ArcGIS.Geodatabase.IRecordSetInit = TryCast(summaryStatsRecordSet, ESRI.ArcGIS.Geodatabase.IRecordSetInit)

      Dim summaryFields As ESRI.ArcGIS.Geodatabase.IFields = New ESRI.ArcGIS.Geodatabase.Fields()
      Dim summaryFieldsEdit As ESRI.ArcGIS.Geodatabase.IFieldsEdit = TryCast(summaryFields, ESRI.ArcGIS.Geodatabase.IFieldsEdit)
      summaryFieldsEdit.FieldCount_2 = 2

      Dim field As ESRI.ArcGIS.Geodatabase.IField = New ESRI.ArcGIS.Geodatabase.Field()
      Dim fieldEdit As ESRI.ArcGIS.Geodatabase.IFieldEdit = TryCast(field, ESRI.ArcGIS.Geodatabase.IFieldEdit)
      fieldEdit.Name_2 = "Type"
      fieldEdit.Type_2 = ESRI.ArcGIS.Geodatabase.esriFieldType.esriFieldTypeString
      fieldEdit.Length_2 = 50
      summaryFieldsEdit.Field_2(0) = field

      field = New ESRI.ArcGIS.Geodatabase.Field()
      fieldEdit = TryCast(field, ESRI.ArcGIS.Geodatabase.IFieldEdit)

      fieldEdit.Name_2 = "Area"
      fieldEdit.Type_2 = ESRI.ArcGIS.Geodatabase.esriFieldType.esriFieldTypeDouble
      summaryFieldsEdit.Field_2(1) = field

      recordSetInit.CreateTable(summaryFields)

      Dim cursor As ESRI.ArcGIS.Geodatabase.ICursor = recordSetInit.Insert()
      Dim rowBuffer As ESRI.ArcGIS.Geodatabase.IRowBuffer = recordSetInit.CreateRowBuffer()

      ' Copy the summary stats to the record set
      Dim summaryStatsEnumerator As System.Collections.IDictionaryEnumerator = summaryStatsDictionary.GetEnumerator()
      Do While summaryStatsEnumerator.MoveNext()
        rowBuffer.Value(0) = summaryStatsEnumerator.Key
        rowBuffer.Value(1) = summaryStatsEnumerator.Value
        cursor.InsertRow(rowBuffer)
      Loop

      Return summaryStatsRecordSet
    End Function

    Private Function createFillSymbol() As ESRI.ArcGIS.Display.ISimpleFillSymbol
      Dim simpleLineSymbol As ESRI.ArcGIS.Display.ISimpleLineSymbol = New ESRI.ArcGIS.Display.SimpleLineSymbol()
      Dim rgbColor As ESRI.ArcGIS.Display.IRgbColor = New ESRI.ArcGIS.Display.RgbColor()
      rgbColor.Red = 0
      rgbColor.Green = 255
      rgbColor.Blue = 0
      simpleLineSymbol.Color = rgbColor
      simpleLineSymbol.Style = ESRI.ArcGIS.Display.esriSimpleLineStyle.esriSLSSolid
      simpleLineSymbol.Width = 2

      Dim simpleFillSymbol As ESRI.ArcGIS.Display.ISimpleFillSymbol = New ESRI.ArcGIS.Display.SimpleFillSymbol()
      simpleFillSymbol.Outline = simpleLineSymbol
      simpleFillSymbol.Style = ESRI.ArcGIS.Display.esriSimpleFillStyle.esriSFSHollow

      Return simpleFillSymbol
    End Function
#End Region

#Region "IObjectConstruct Members"
    Public Overloads Sub Construct(ByVal props As ESRI.ArcGIS.esriSystem.IPropertySet) Implements ESRI.ArcGIS.esriSystem.IObjectConstruct.Construct
      Try
        m_layerName = TryCast(props.GetProperty("LayerName"), String)
        m_fieldName = TryCast(props.GetProperty("FieldName"), String)
      Catch ex As Exception
        m_log.AddMessage(1, 8000, "SpatialQuerySOE custom error. Error reading properties: " & ex.Message & " " & props.Count.ToString())
        Return
      End Try

      Try
        ' Get the map underlying the map service and the IGeoFeatureLayers contained in the map
        Dim mapServer As ESRI.ArcGIS.Carto.IMapServer = CType(m_ServerObjectHelper.ServerObject, ESRI.ArcGIS.Carto.IMapServer)
        Dim mapServerObjects As ESRI.ArcGIS.Carto.IMapServerObjects = CType(mapServer, ESRI.ArcGIS.Carto.IMapServerObjects)
        Dim map As ESRI.ArcGIS.Carto.IMap = mapServerObjects.Map(mapServer.DefaultMapName)
        Dim layerTypeID As ESRI.ArcGIS.esriSystem.UID = New ESRI.ArcGIS.esriSystem.UIDClass()
        layerTypeID.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}"
        Dim enumLayer As ESRI.ArcGIS.Carto.IEnumLayer = map.Layers(layerTypeID, True)
        enumLayer.Reset()

        ' Get the layer specified as the SOE layer
        m_featureLayer = TryCast(enumLayer.Next(), ESRI.ArcGIS.Carto.IFeatureLayer)
        Do While m_featureLayer IsNot Nothing
          If m_featureLayer.Name = m_layerName Then
            Exit Do
          End If
          m_featureLayer = TryCast(enumLayer.Next(), ESRI.ArcGIS.Carto.IFeatureLayer)
        Loop

        If m_featureLayer Is Nothing Then
          m_log.AddMessage(1, 8000, "SpatialQuerySOE custom error: Layer " & m_layerName & " not found.")
          Return
        End If

        ' Make sure the layer contains the field specified by the SOE's configuration
        If m_featureLayer.FeatureClass.FindField(m_fieldName) = -1 Then
          m_log.AddMessage(1, 8000, "SpatialQuerySOE custom error: Field " & m_fieldName & " not found in layer " & m_layerName)
        Else
          m_log.AddMessage(3, 8000, "SpatialQuerySOE successfully initialized.")
        End If

      Catch ex As Exception
        m_log.AddMessage(1, 8000, "SpatialQuerySOE custom error: Failed to initialize extension: " & ex.Message & "::" & ex.StackTrace.Length.ToString())
      End Try
    End Sub
#End Region

#Region "ILogSupport Members"
    Public Sub InitLogging(ByVal log As ESRI.ArcGIS.esriSystem.ILog) Implements ESRI.ArcGIS.esriSystem.ILogSupport.InitLogging
      m_log = log
    End Sub
#End Region

#Region "IObjectActivate Members"

    Private Sub IObjectActivate_Activate() Implements ESRI.ArcGIS.esriSystem.IObjectActivate.Activate
      m_log.AddMessage(3, 8000, "SpatialQuerySOE custom message. Activate called")
    End Sub

    Private Sub Deactivate() Implements ESRI.ArcGIS.esriSystem.IObjectActivate.Deactivate
      m_log.AddMessage(3, 8000, "SpatialQuerySOE custom message. Deactivate called")
    End Sub

#End Region




  End Class
End Namespace