ArcGIS_AddDynamicData_VBNet\App_Code\ServerObjectStateModifier.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 Public Class ServerObjectStateModifier ' Custom enumeration for specifying the behavior of ApplySimpleRenderers Public Enum RendererAction ApplyNew ApplyOriginal ApplyLast End Enum ' Adds the canada shapefile to the passed-in resource and assigns it the passed-in name Public Sub AddLayer(ByVal mapResourceLocal As ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal, ByVal layerName As String) ' Get the map underlying the passed-in resource Dim mapServerObjects As ESRI.ArcGIS.Carto.IMapServerObjects = CType(mapResourceLocal.MapServer, ESRI.ArcGIS.Carto.IMapServerObjects) Dim aoMap As ESRI.ArcGIS.Carto.IMap = mapServerObjects.Map(mapResourceLocal.DataFrame) ' Check whether the map already contains a layer with the passed-in name Dim layerAdded As Boolean = False Dim i As Integer = 0 Do While i < aoMap.LayerCount If aoMap.Layer(i).Name = layerName Then layerAdded = True Exit Do End If i += 1 Loop ' Only execute the logic to add the layer if a layer of the same name has not already been added If (Not layerAdded) Then ' Create a reference to the canada shapefile as an ArcObjects feature layer Dim serverContext As ESRI.ArcGIS.Server.IServerContext = mapResourceLocal.ServerContextInfo.ServerContext Dim workspaceFactory As ESRI.ArcGIS.Geodatabase.IWorkspaceFactory = CType(serverContext.CreateObject("esriDataSourcesFile.ShapefileWorkspaceFactory"), ESRI.ArcGIS.Geodatabase.IWorkspaceFactory) Dim featureWorkspace As ESRI.ArcGIS.Geodatabase.IFeatureWorkspace = TryCast(workspaceFactory.OpenFromFile("C:\temp\data\", 0), ESRI.ArcGIS.Geodatabase.IFeatureWorkspace) Dim aoFeatureLayer As ESRI.ArcGIS.Carto.IFeatureLayer = CType(serverContext.CreateObject("esriCarto.FeatureLayer"), ESRI.ArcGIS.Carto.IFeatureLayer) aoFeatureLayer.FeatureClass = featureWorkspace.OpenFeatureClass(layerName) aoFeatureLayer.Name = layerName ' Apply a renderer to the feature layer Dim aoGeoFeatureLayer As ESRI.ArcGIS.Carto.IGeoFeatureLayer = TryCast(aoFeatureLayer, ESRI.ArcGIS.Carto.IGeoFeatureLayer) ApplySimpleRenderer(aoGeoFeatureLayer, serverContext, 0, 0, 210) ' Set the dynamic layer's visibility based on the session variable. Default to visible if the session variable ' does not yet exist. If (Not System.Web.HttpContext.Current.Session("dynamicLayerVisible") Is Nothing) Then aoFeatureLayer.Visible = CBool(System.Web.HttpContext.Current.Session("dynamicLayerVisible")) Else aoFeatureLayer.Visible = True End If ' Add the layer to the map and move it below any other layers aoMap.AddLayer(aoFeatureLayer) aoMap.MoveLayer(aoFeatureLayer, aoMap.LayerCount - 1) ' Before pushing the changes to the server, create a dictionary containing the current visibility of the map layers. ' We do this because updating the objects on the server (via RefreshServerObjects) also updates the map description ' referenced by the map resource with the service's updated default description, which includes default layer ' visibility. Dim visibleLayers As System.Collections.Generic.Dictionary(Of String, Boolean) = New System.Collections.Generic.Dictionary(Of String, Boolean)() For Each layerDescription As ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription In mapResourceLocal.MapDescription.LayerDescriptions visibleLayers.Add(layerDescription.LayerID.ToString(), layerDescription.Visible) Next layerDescription ' Register changes to server object - dynamic layer added. mapResourceLocal.RefreshServerObjects() ' Reset the visibility of the layers in the resource's map description to what it was before updating the map service. For Each layerVisiblePair As System.Collections.Generic.KeyValuePair(Of String, Boolean) In visibleLayers ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality.UpdateVisibleLayer(mapResourceLocal.MapDescription, layerVisiblePair.Key, layerVisiblePair.Value) Next layerVisiblePair End If End Sub ' Adds the passed-in GeoFeatureLayer to the passed-in local map resource and sets its position to that specified by the ' passed-in argument Public Sub AddLayer(ByVal mapResourceLocal As ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal, ByVal geoFeatureLayer As ESRI.ArcGIS.Carto.IGeoFeatureLayer, ByVal layerIndex As Integer) ' Get the map underlying the passed-in resource Dim mapServerObjects As ESRI.ArcGIS.Carto.IMapServerObjects = CType(mapResourceLocal.MapServer, ESRI.ArcGIS.Carto.IMapServerObjects) Dim aoMap As ESRI.ArcGIS.Carto.IMap = mapServerObjects.Map(mapResourceLocal.DataFrame) ' Get the server context of the passed-in map service Dim serverContext As ESRI.ArcGIS.Server.IServerContext = mapResourceLocal.ServerContextInfo.ServerContext ' Add the passed-in layer to the map and move it to the passed-in index aoMap.AddLayer(geoFeatureLayer) aoMap.MoveLayer(geoFeatureLayer, layerIndex) ' Before pushing the changes to the server, create a dictionary containing the current visibility of the map layers. ' We do this because updating the objects on the server (via RefreshServerObjects) also updates the map description ' referenced by the map resource with the service's updated default description, which includes default layer ' visibility. So when RefreshServerObjects is called, the visibility of the map layers will be reset to default Dim visibleLayers As System.Collections.Generic.Dictionary(Of String, Boolean) = New System.Collections.Generic.Dictionary(Of String, Boolean)() For Each layerDescription As ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription In mapResourceLocal.MapDescription.LayerDescriptions visibleLayers.Add(layerDescription.LayerID.ToString(), layerDescription.Visible) Next layerDescription ' Register changes to server object, adding the passed-in layer at the map service level. mapResourceLocal.RefreshServerObjects() ' Reset the visibility of the layers in the resource's map description to what it was before updating the map service. For Each layerVisiblePair As System.Collections.Generic.KeyValuePair(Of String, Boolean) In visibleLayers ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality.UpdateVisibleLayer(mapResourceLocal.MapDescription, layerVisiblePair.Key, layerVisiblePair.Value) Next layerVisiblePair End Sub ' Removes the layer with the passed-in name from the passed-in resource Public Function RemoveLayer(ByVal mapResourceLocal As ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal, ByVal layerName As String) As System.Collections.Generic.Dictionary(Of Integer, ESRI.ArcGIS.Carto.ILayer) ' Get the map underlying the passed-in map resource Dim mapServerObjects As ESRI.ArcGIS.Carto.IMapServerObjects = CType(mapResourceLocal.MapServer, ESRI.ArcGIS.Carto.IMapServerObjects) Dim aoMap As ESRI.ArcGIS.Carto.IMap = mapServerObjects.Map(mapResourceLocal.DataFrame) ' Get a reference to the layer with the passed-in name from the map Dim enumLayer As ESRI.ArcGIS.Carto.IEnumLayer = aoMap.Layers(Nothing, True) Dim currentLayer As ESRI.ArcGIS.Carto.ILayer = enumLayer.Next() Dim removedLayerDictionary As System.Collections.Generic.Dictionary(Of Integer, ESRI.ArcGIS.Carto.ILayer) = New System.Collections.Generic.Dictionary(Of Integer, ESRI.ArcGIS.Carto.ILayer)() Dim layerIndex As Integer = 0 Do While currentLayer IsNot Nothing If currentLayer.Name = layerName Then removedLayerDictionary.Add(layerIndex, currentLayer) Exit Do End If currentLayer = enumLayer.Next() layerIndex += 1 Loop ' Make sure the layer was found before executing logic to remove it If Not currentLayer Is Nothing Then ' Delete the layer from the map aoMap.DeleteLayer(currentLayer) ' Before committing the change to the map service, create a dictionary containing the current visibility of the map layers. ' We do this because updating the objects on the server (via RefreshServerObjects) also updates the map description ' referenced by the map resource with the service's updated default description, which includes default layer visibility. Dim visibleLayers As System.Collections.Generic.Dictionary(Of String, Boolean) = New System.Collections.Generic.Dictionary(Of String, Boolean)() For Each layerDescription As ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription In mapResourceLocal.MapDescription.LayerDescriptions visibleLayers.Add(layerDescription.LayerID.ToString(), layerDescription.Visible) Next layerDescription ' Register the layer removal with the server object mapResourceLocal.RefreshServerObjects() ' Reset the visibility of the remaining layers back to what it was before the update For Each layerVisiblePair As System.Collections.Generic.KeyValuePair(Of String, Boolean) In visibleLayers ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality.UpdateVisibleLayer(mapResourceLocal.MapDescription, layerVisiblePair.Key, layerVisiblePair.Value) Next layerVisiblePair End If Return removedLayerDictionary End Function ' Moves the layer with the passed-in name to the passed-in index within the passed-in resource Public Function MoveLayer(ByVal mapResourceLocal As ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal, ByVal layerName As String, ByVal layerIndex As Integer) As Integer ' Get the map underlying the passed-in map resource Dim mapServerObjects As ESRI.ArcGIS.Carto.IMapServerObjects = CType(mapResourceLocal.MapServer, ESRI.ArcGIS.Carto.IMapServerObjects) Dim aoMap As ESRI.ArcGIS.Carto.IMap = mapServerObjects.Map(mapResourceLocal.DataFrame) If (layerIndex < 0) OrElse (layerIndex > aoMap.LayerCount - 1) Then Return -1 End If ' Get a reference to the layer with the passed-in name from the map Dim enumLayer As ESRI.ArcGIS.Carto.IEnumLayer = aoMap.Layers(Nothing, True) Dim currentLayer As ESRI.ArcGIS.Carto.ILayer = enumLayer.Next() Do While currentLayer IsNot Nothing If currentLayer.Name = layerName Then Exit Do End If currentLayer = enumLayer.Next() Loop ' Make sure the layer was found before executing logic to move it If Not currentLayer Is Nothing Then ' Move the layer to the passed-in layer index aoMap.MoveLayer(currentLayer, layerIndex) ' Before committing the change to the map service, create a dictionary containing the current visibility of the map layers. ' We do this because updating the objects on the server (via RefreshServerObjects) also updates the map description ' referenced by the map resource with the service's updated default description, which includes default layer visibility. Dim visibleLayers As System.Collections.Generic.Dictionary(Of String, Boolean) = New System.Collections.Generic.Dictionary(Of String, Boolean)() For Each layerDescription As ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription In mapResourceLocal.MapDescription.LayerDescriptions visibleLayers.Add(layerDescription.LayerID.ToString(), layerDescription.Visible) Next layerDescription ' Register the layer repositioning with the server object mapResourceLocal.RefreshServerObjects() ' Reset the visibility of the remaining layers back to what it was before the update For Each layerVisiblePair As System.Collections.Generic.KeyValuePair(Of String, Boolean) In visibleLayers ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality.UpdateVisibleLayer(mapResourceLocal.MapDescription, layerVisiblePair.Key, layerVisiblePair.Value) Next layerVisiblePair End If Return layerIndex End Function ' Changes the renderers for all layers belonging to the passed-in local map resource. If rendererAction is ApplyNew, ' the renderers are set to simple renderers with randomly generated colors. If ApplyOriginal, the renderers are set ' to what they were when the page first loaded. If ApplyLast, then the renderers are set to the last new renderer ' applied. Public Sub ApplySimpleRenderers(ByVal mapResourceLocal As ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal, ByVal rendererAction As RendererAction) ' Get a reference to the ArcObjects map and layers via server context Dim aoServerContext As ESRI.ArcGIS.Server.IServerContext = mapResourceLocal.ServerContextInfo.ServerContext Dim aoMapServerObjects As ESRI.ArcGIS.Carto.IMapServerObjects = CType(mapResourceLocal.MapServer, ESRI.ArcGIS.Carto.IMapServerObjects) Dim aoMap As ESRI.ArcGIS.Carto.IMap = aoMapServerObjects.Map(mapResourceLocal.DataFrame) Dim enumLayer As ESRI.ArcGIS.Carto.IEnumLayer = aoMap.Layers(Nothing, True) ' For each layer, we will store a dictionary containing its initial renderers (i.e. on initial load) in session. ' Check whether the session variable for the current layer has already been created. If so, retrieve it. If not, ' create a new dictionary. This dictionary will be used to reset renderers to their initial load state. Dim originalRendererDictionary As System.Collections.Generic.Dictionary(Of Integer, ESRI.ArcGIS.Carto.IFeatureRenderer) = Nothing Dim originalRendererKey As String = String.Format("{0}_originalRendererDictionary", mapResourceLocal.Name) If System.Web.HttpContext.Current.Session(originalRendererKey) Is Nothing Then originalRendererDictionary = New System.Collections.Generic.Dictionary(Of Integer, ESRI.ArcGIS.Carto.IFeatureRenderer)() Else originalRendererDictionary = CType(System.Web.HttpContext.Current.Session(originalRendererKey), System.Collections.Generic.Dictionary(Of Integer, ESRI.ArcGIS.Carto.IFeatureRenderer)) End If ' For each layer, we will also store a dictionary containing its current renderers. This will be used to re-apply ' the current renderer on each page request. Dim currentRendererDictionary As System.Collections.Generic.Dictionary(Of Integer, ESRI.ArcGIS.Carto.IFeatureRenderer) = Nothing Dim currentRendererKey As String = String.Format("{0}_currentRendererDictionary", mapResourceLocal.Name) If System.Web.HttpContext.Current.Session(currentRendererKey) Is Nothing Then currentRendererDictionary = New System.Collections.Generic.Dictionary(Of Integer, ESRI.ArcGIS.Carto.IFeatureRenderer)() Else currentRendererDictionary = CType(System.Web.HttpContext.Current.Session(currentRendererKey), System.Collections.Generic.Dictionary(Of Integer, ESRI.ArcGIS.Carto.IFeatureRenderer)) End If ' Loop through each layer in the current map, either applying a new random simple renderer, reverting to the random simple ' renderer that was last applied, or reverting the renderer to its initial load state. Dim layerIndex As Integer = 0 Dim aoGeoFeatureLayer As ESRI.ArcGIS.Carto.IGeoFeatureLayer = CType(enumLayer.Next(), ESRI.ArcGIS.Carto.IGeoFeatureLayer) Do While aoGeoFeatureLayer IsNot Nothing Select Case rendererAction Case rendererAction.ApplyNew ' If the current layer's renderer is not in the dictionary of initial renderers, then this is the ' first time this code has been executed during the session. The layer's current renderer is ' therefore the initial load state renderer, so we add it to the original renderer dictionary. If (Not originalRendererDictionary.ContainsKey(layerIndex)) Then originalRendererDictionary.Add(layerIndex, aoGeoFeatureLayer.Renderer) System.Web.HttpContext.Current.Session(originalRendererKey) = originalRendererDictionary End If ' Apply a randomly colored simple renderer to the current layer ApplySimpleRenderer(aoGeoFeatureLayer, aoServerContext, -1, -1, -1) ' If the current renderer dictionary contains the current layer's renderer, remove it so it can ' be replaced with the new renderer. If currentRendererDictionary.ContainsKey(layerIndex) Then currentRendererDictionary.Remove(layerIndex) End If ' Update the current renderer dictionary with the current renderer currentRendererDictionary.Add(layerIndex, aoGeoFeatureLayer.Renderer) System.Web.HttpContext.Current.Session(currentRendererKey) = currentRendererDictionary Case rendererAction.ApplyLast ' Set the layer's renderer to the last random simple renderer applied aoGeoFeatureLayer.Renderer = CType(currentRendererDictionary(layerIndex), ESRI.ArcGIS.Carto.IFeatureRenderer) Case rendererAction.ApplyOriginal ' Set the layer's renderer to the one it had when the session started If originalRendererDictionary.ContainsKey(layerIndex) Then aoGeoFeatureLayer.Renderer = CType(originalRendererDictionary(layerIndex), ESRI.ArcGIS.Carto.IFeatureRenderer) End If End Select aoGeoFeatureLayer = CType(enumLayer.Next(), ESRI.ArcGIS.Carto.IGeoFeatureLayer) layerIndex += 1 Loop ' Get the current resource's map description before the map and layers are updated on the server. We do this ' because when the server objects are updated, the layer visibility reverts to that specified by the map ' document underlying the map service, and the map description contains information about current layer ' visibility Dim adfMapDescription As ESRI.ArcGIS.ADF.ArcGISServer.MapDescription = mapResourceLocal.MapDescription ' Register the layer changes with the server. This applies the new renderer. mapResourceLocal.RefreshServerObjects() ' Set the resource's map description to that retrieved before server objects were updated, so layer visibility ' is the same as before the update. mapResourceLocal.MapDescription = adfMapDescription End Sub ' Generates a simple renderer and applies it to the passed-in layer. For any passed-in color value that is not within the ' valid range (0-255), a random value within the valid range is applied. So, for example, a randomly colored renderer can ' be generated by passing in RGB parameters of -1, -1, and -1. Private Sub ApplySimpleRenderer(ByVal aoGeoFeatureLayer As ESRI.ArcGIS.Carto.IGeoFeatureLayer, ByVal aoServerContext As ESRI.ArcGIS.Server.IServerContext, ByVal red As Integer, ByVal green As Integer, ByVal blue As Integer) ' Generate an ArcObjects color for the renderer. Use the specified RGB value if valid, otherwise use a random integer within ' the valid range. Dim aoRgbColor As ESRI.ArcGIS.Display.IRgbColor = CType(aoServerContext.CreateObject("esriDisplay.RgbColor"), ESRI.ArcGIS.Display.IRgbColor) Dim randomizer As System.Random = New System.Random() If (red < 0) OrElse (red > 255) Then aoRgbColor.Red = randomizer.Next(0, 256) Else aoRgbColor.Red = red End If If (green < 0) OrElse (green > 255) Then aoRgbColor.Green = randomizer.Next(0, 256) Else aoRgbColor.Green = green End If If (blue < 0) OrElse (blue > 255) Then aoRgbColor.Blue = randomizer.Next(0, 256) Else aoRgbColor.Blue = blue End If ' Instantiate an ArcObjects simple renderer to use in specifying the layer's renderer Dim aoSimpleRenderer As ESRI.ArcGIS.Carto.ISimpleRenderer = CType(aoServerContext.CreateObject("esriCarto.SimpleRenderer"), ESRI.ArcGIS.Carto.ISimpleRenderer) ' Check the layer's geometry type and create a symbol for the renderer accordingly Dim geometryType As ESRI.ArcGIS.Geometry.esriGeometryType = aoGeoFeatureLayer.FeatureClass.ShapeType Select Case aoGeoFeatureLayer.FeatureClass.ShapeType Case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPoint Dim aoSimpleMarkerSymbol As ESRI.ArcGIS.Display.ISimpleMarkerSymbol = CType(aoServerContext.CreateObject("esriDisplay.SimpleMarkerSymbol"), ESRI.ArcGIS.Display.ISimpleMarkerSymbol) aoSimpleMarkerSymbol.Color = CType(aoRgbColor, ESRI.ArcGIS.Display.IColor) aoSimpleRenderer.Symbol = CType(aoSimpleMarkerSymbol, ESRI.ArcGIS.Display.ISymbol) Case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline Dim aoSimpleLineSymbol As ESRI.ArcGIS.Display.ISimpleLineSymbol = CType(aoServerContext.CreateObject("esriDisplay.SimpleLineSymbol"), ESRI.ArcGIS.Display.ISimpleLineSymbol) aoSimpleLineSymbol.Color = CType(aoRgbColor, ESRI.ArcGIS.Display.IColor) aoSimpleRenderer.Symbol = CType(aoSimpleLineSymbol, ESRI.ArcGIS.Display.ISymbol) Case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon Dim aoSimpleFillSymbol As ESRI.ArcGIS.Display.ISimpleFillSymbol = CType(aoServerContext.CreateObject("esriDisplay.SimpleFillSymbol"), ESRI.ArcGIS.Display.ISimpleFillSymbol) aoSimpleFillSymbol.Color = CType(aoRgbColor, ESRI.ArcGIS.Display.IColor) aoSimpleRenderer.Symbol = CType(aoSimpleFillSymbol, ESRI.ArcGIS.Display.ISymbol) Case Else Throw New System.Exception("No renderer or symbol selected. Shape type undetermined.") End Select ' Assign the new renderer to the passed-in layer aoGeoFeatureLayer.Renderer = CType(aoSimpleRenderer, ESRI.ArcGIS.Carto.IFeatureRenderer) End Sub End Class