Common_TaskResults_VBNet\ZoomToResults\ZoomToResults.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 Namespace ZoomToResults_VBNet ' To override ITaskResultsContainer methods implemented in TaskResults but not marked virtual, implement ' ITaskResultsContainer methods directly. For example, the DisplayResults and StartTaskActivityIndicator ' methods are implemented within this class. <System.Web.UI.ToolboxData("<{0}:ZoomToResults runat=""server"" Width=""200px"" Height=""200px"" " & ControlChars.CrLf & " BackColor=""#ffffff"" Font-Names=""Verdana"" Font-Size=""8pt"" ForeColor=""#000000""> </{0}:ZoomToResults>")> _ Public Class ZoomToResults Inherits ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults Implements ESRI.ArcGIS.ADF.Web.UI.WebControls.ITaskResultsContainer #Region "Public Properties" ''' <summary> ''' The maximum number of features the result set can contain for zooming to results to occur ''' </summary> <System.ComponentModel.Category("TaskResults"), System.ComponentModel.DefaultValue(10), System.ComponentModel.Description("The maximum number of features the result set can contain for zooming to results to occur"), System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute), System.ComponentModel.NotifyParentProperty(True)> _ Public Property MaxResultsForMapZoom() As Integer Get Dim o As Object = StateManager.GetProperty("MaxResultsForMapZoom") If (o Is Nothing) Then Return 10 Else Return System.Convert.ToInt32(o) End If End Get Set If Value >= 0 Then StateManager.SetProperty("MaxResultsForMapZoom", Value) Else Throw New System.Exception() End If End Set End Property ''' <summary> ''' The maximum number of features the result set can contain for automatic result selection to occur ''' </summary> <System.ComponentModel.Category("TaskResults"), System.ComponentModel.DefaultValue(10), System.ComponentModel.Description("The maximum number of features the result set can contain for automatic result selection to occur"), System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute), System.ComponentModel.NotifyParentProperty(True)> _ Public Property MaxResultsForAutoSelect() As Integer Get Dim o As Object = StateManager.GetProperty("MaxResultsForAutoSelect") If (o Is Nothing) Then Return 10 Else Return System.Convert.ToInt32(o) End If End Get Set If Value >= 0 Then StateManager.SetProperty("MaxResultsForAutoSelect", Value) Else Throw New System.Exception() End If End Set End Property ''' <summary> ''' Minimum width in map units that will be zoomed to ''' </summary> <System.ComponentModel.Category("TaskResults"), System.ComponentModel.DefaultValue(10.0), System.ComponentModel.Description("Minimum width in map units that will be zoomed to"), System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute), System.ComponentModel.NotifyParentProperty(True)> _ Public Property MinWidthOfZoom() As Double Get Dim o As Object = StateManager.GetProperty("MinWidthOfZoom") If (o Is Nothing) Then Return 10.0 Else Return System.Convert.ToDouble(o) End If End Get Set If Value >= 0 Then StateManager.SetProperty("MinWidthOfZoom", Value) Else Throw New System.Exception() End If End Set End Property ''' <summary> ''' The percentage to expand the result set's extent by when zooming to results ''' </summary> <System.ComponentModel.Category("TaskResults"), System.ComponentModel.DefaultValue(10.0), System.ComponentModel.Description("The percentage to expand the result set's extent by when zooming to results"), System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute), System.ComponentModel.NotifyParentProperty(True)> _ Public Property ZoomExtentExpansionPercent() As Double Get Dim o As Object = StateManager.GetProperty("ZoomExtentExpansionPercent") If (o Is Nothing) Then Return 10.0 Else Return System.Convert.ToDouble(o) End If End Get Set If Value >= 0 Then StateManager.SetProperty("ZoomExtentExpansionPercent", Value) Else Throw New System.Exception() End If End Set End Property ''' <summary> ''' Whether to show an activity indicator during task execution ''' </summary> <System.ComponentModel.Category("TaskResults"), System.ComponentModel.DefaultValue(False), System.ComponentModel.Description("Whether to show an activity indicator during task execution"), System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute), System.ComponentModel.NotifyParentProperty(True)> _ Public Property ShowTaskActivityIndicator() As Boolean Get Dim o As Object = StateManager.GetProperty("ShowTaskActivityIndicator") If (o Is Nothing) Then Return False Else Return System.Convert.ToBoolean(o) End If End Get Set StateManager.SetProperty("ShowTaskActivityIndicator", Value) End Set End Property ''' <summary> ''' Whether to display task results in the control. Results can be zoomed to whether or not they are displayed. ''' </summary> <System.ComponentModel.Category("TaskResults"), System.ComponentModel.DefaultValue(False), System.ComponentModel.Description("Whether to display task results in the control. Results can be zoomed to whether or not they are displayed."), System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute), System.ComponentModel.NotifyParentProperty(True)> _ Public Property DisplayTaskResults() As Boolean Get Dim o As Object = StateManager.GetProperty("DisplayTaskResults") If (o Is Nothing) Then Return False Else Return System.Convert.ToBoolean(o) End If End Get Set StateManager.SetProperty("DisplayTaskResults", Value) End Set End Property #End Region #Region "ASP.NET WebControl Overrides - RenderContents" ' Overriden to add a design-time warning if the control has not been buddied ' with a map Protected Overrides Overloads Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter) If Me.DesignMode Then Me.RenderDesignTimeHtml(writer) Else MyBase.RenderContents(writer) End If End Sub #End Region #Region "ITaskResultsContainer Members - StartTaskActivityIndicator, DisplayResults" ' Overriding the StartTaskActivityIndicator method in TaskResults is not possible since the method is ' not marked virtual. Instead implement the method via ITaskResultsContainer and if necessary call the ' method in the base class (TaskResults). Private Overloads Sub StartTaskActivityIndicator(ByVal task As ESRI.ArcGIS.ADF.Web.UI.WebControls.ITask, ByVal taskJobID As String) Implements ESRI.ArcGIS.ADF.Web.UI.WebControls.ITaskResultsContainer.StartTaskActivityIndicator If ShowTaskActivityIndicator Then MyBase.StartTaskActivityIndicator(task, taskJobID) End If End Sub ' Overriding the DisplayResults method in TaskResults is not possible since the method is ' not marked virtual. Instead implement the method via ITaskResultsContainer to zoom to ' and/or select task results. Then call the base class's DisplayResults method if results ' are to be displayed in this control. Private Overloads Sub DisplayResults(ByVal task As ESRI.ArcGIS.ADF.Web.UI.WebControls.ITask, ByVal taskJobID As String, ByVal taskInputs As Object, ByVal taskResults As Object) Implements ESRI.ArcGIS.ADF.Web.UI.WebControls.ITaskResultsContainer.DisplayResults ' Throw an exception if the Map property is not set to a valid map. If Me.MapInstance Is Nothing Then Throw New System.Exception("You must set the Map property to a valid map") End If ' Return if the auto select and map zoom capabilities are disabled If Me.MaxResultsForMapZoom = 0 AndAlso Me.MaxResultsForAutoSelect = 0 Then Return End If ' Create the envlope to hold the extent that the map will be set to Dim adfEnvelope As ESRI.ArcGIS.ADF.Web.Geometry.Envelope = Nothing ' Declare / initialize variables to use below Dim intTotalResultsCount As Integer = 0 Dim doZoom As Boolean = False Dim doSelect As Boolean = False Dim resultsDataSet As System.Data.DataSet = Nothing ' Test to see if the results are contained in a TreeViePlusNode Dim treeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = TryCast(taskResults, ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode) If Not treeViewPlusNode Is Nothing Then ' If it is a task results node count all the data ' table rows in each GraphicsLayerNode intTotalResultsCount = Me.CountAllResultsInNode(treeViewPlusNode) ' Set flags indicating whether we should zoom or select based ' on the size of the result set doZoom = (intTotalResultsCount <= Me.MaxResultsForMapZoom) doSelect = (intTotalResultsCount <= Me.MaxResultsForAutoSelect) ' If we are going to zoom or select then process the results If doZoom OrElse doSelect Then Me.ProcessNode(treeViewPlusNode, doSelect, doZoom, adfEnvelope) End If Else ' Cast the results set to a DataSet resultsDataSet = TryCast(taskResults, System.Data.DataSet) ' Make sure we have a valid dataset with tables before iterating If Not resultsDataSet Is Nothing AndAlso resultsDataSet.Tables.Count > 0 Then ' Count the number of results For Each dataTable As System.Data.DataTable In resultsDataSet.Tables intTotalResultsCount += dataTable.Rows.Count Next dataTable If intTotalResultsCount > 0 Then ' Set flags indicating whether we should zoom or select based ' on the size of the result set doZoom = (intTotalResultsCount <= Me.MaxResultsForMapZoom) doSelect = (intTotalResultsCount <= Me.MaxResultsForAutoSelect) ' If we are going to zoom or select then process the results If doZoom OrElse doSelect Then For Each dataTable As System.Data.DataTable In resultsDataSet.Tables ' Create a GraphicsLayer to pass to ProcessResultsLayer. This will be used to ' get the extent of its features. Note that we create the layer from a copy ' of the current data table because ToGraphicsLayer alters the passed-in table. Dim graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = ESRI.ArcGIS.ADF.Web.Converter.ToGraphicsLayer(dataTable.Copy()) If Not graphicsLayer Is Nothing Then Me.ProcessResultsLayer(doSelect, doZoom, graphicsLayer, adfEnvelope) End If Next dataTable End If End If End If End If ' Zoom the map if the results set is equal to or less then the MaxResultsForZoom If doZoom AndAlso Not adfEnvelope Is Nothing Then ' Resize the envelope if it is smaller than the minimum extent size If adfEnvelope.Width < Me.MinWidthOfZoom AndAlso adfEnvelope.Height < Me.MinWidthOfZoom Then Dim dblExpandX As Double = (Me.MinWidthOfZoom - adfEnvelope.Width) / 2 Dim dblExpandY As Double = (Me.MinWidthOfZoom - adfEnvelope.Height) / 2 adfEnvelope = New ESRI.ArcGIS.ADF.Web.Geometry.Envelope(adfEnvelope.XMin - dblExpandX, adfEnvelope.YMin - dblExpandY, adfEnvelope.XMax + dblExpandX, adfEnvelope.YMax + dblExpandY) ElseIf adfEnvelope.Width < Me.MinWidthOfZoom Then Dim dblExpandX As Double = (Me.MinWidthOfZoom - adfEnvelope.Width) / 2 adfEnvelope = New ESRI.ArcGIS.ADF.Web.Geometry.Envelope(adfEnvelope.XMin - dblExpandX, adfEnvelope.YMin, adfEnvelope.XMax + dblExpandX, adfEnvelope.YMax) ElseIf adfEnvelope.Height < Me.MinWidthOfZoom Then Dim dblExpandY As Double = (Me.MinWidthOfZoom - adfEnvelope.Height) / 2 adfEnvelope = New ESRI.ArcGIS.ADF.Web.Geometry.Envelope(adfEnvelope.XMin, adfEnvelope.YMin - dblExpandY, adfEnvelope.XMax, adfEnvelope.YMax + dblExpandY) End If ' Zoom out a little when you set the extent so you can see some area around the features If Me.ZoomExtentExpansionPercent > 0 Then adfEnvelope = adfEnvelope.Expand(ZoomExtentExpansionPercent) End If ' Hold onto the old extent to test with later Dim adfOriginalEnvelope As ESRI.ArcGIS.ADF.Web.Geometry.Envelope = TryCast(MapInstance.Extent.Clone(), ESRI.ArcGIS.ADF.Web.Geometry.Envelope) ' Find the scale that the map will display at if the new envelope is applied Dim dblEnvelopeScale As Double = 0 If MapInstance.Extent.Width / adfEnvelope.Width < MapInstance.Extent.Height / adfEnvelope.Height Then dblEnvelopeScale = MapInstance.Scale * (adfEnvelope.Width / MapInstance.Extent.Width) Else dblEnvelopeScale = MapInstance.Scale * (adfEnvelope.Height / MapInstance.Extent.Height) End If ' Remove callbacks from the map to ensure the extent change is processed Me.MapInstance.CallbackResults.Clear() 'RemoveAllMapCallbacks(MapInstance); ' If the data source is cached, zoom to the lowest cache level (largest map scale) that ' encompasses the target extent Dim tileCacheInfo As ESRI.ArcGIS.ADF.Web.DataSources.TileCacheInfo = Me.MapInstance.PrimaryMapResourceInstance.MapInformation.TileCacheInfo If Not tileCacheInfo Is Nothing Then ' Iterate through the levels of the cache, starting with the lowest (largest ' map scale) Dim lodInfo As ESRI.ArcGIS.ADF.Web.DataSources.LodInfo() = tileCacheInfo.LodInfos For i As Integer = tileCacheInfo.LodInfos.GetUpperBound(0) To 0 Step -1 ' Check whether the current level displays at a scale greater than or equal ' to that required for the target extent If lodInfo(i).Scale >= dblEnvelopeScale Then ' Center the map at the center of the target envelope Me.MapInstance.CenterAt(New ESRI.ArcGIS.ADF.Web.Geometry.Point(adfEnvelope.XMin + (adfEnvelope.Width / 2), adfEnvelope.YMin + (adfEnvelope.Height / 2))) ' Set the map level to the current one if it isn't already If Me.MapInstance.Level <> i Then Me.MapInstance.Level = i End If Exit For End If Next i Else ' This data is not cached so we don't need to worry about tile level. ' Check whether the current map scale matches the target scale. If so, we just need to pan, ' so we use the CenterAt method. If we set the extent when the scale / map dimensions are ' the same we can get artifacts in the graphics layer in IE6 If System.Math.Round(MapInstance.Scale, 0) = System.Math.Round(dblEnvelopeScale, 0) Then Me.MapInstance.CenterAt(New ESRI.ArcGIS.ADF.Web.Geometry.Point(adfEnvelope.XMin + (adfEnvelope.Width / 2), adfEnvelope.YMin + (adfEnvelope.Height / 2))) Else Me.MapInstance.Extent = adfEnvelope End If End If End If ' If results are to be displayed in this control, invoke the base class's DisplayResults method. ' This will handle adding the results to the control and the map. If Me.DisplayTaskResults Then MyBase.DisplayResults(task, taskJobID, taskInputs, taskResults) End If End Sub #End Region #Region "Private Instance Methods - CountAllResultsInNode, ProcessNode, ProcessResultsLayer, RenderDesignTimeHtml" ' Gets the number of descendant result features contained by the passed-in node Private Function CountAllResultsInNode(ByVal node As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode) As Integer Dim resultsCount As Integer = 0 ' If the passed-in node is a graphics layer node, add the number of rows in its graphics layer ' to the number of results Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = TryCast(node, ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode) If Not graphicsLayerNode Is Nothing Then resultsCount += graphicsLayerNode.Layer.Rows.Count End If ' Get the number of results contained by child nodes For Each childNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode In node.Nodes resultsCount += Me.CountAllResultsInNode(childNode) Next childNode Return resultsCount End Function ' Calculates the containing envelope for and selects any results contained by the passed-in node Private Sub ProcessNode(ByVal node As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode, ByVal doSelect As Boolean, ByVal doZoom As Boolean, ByRef adfEnvelope As ESRI.ArcGIS.ADF.Web.Geometry.Envelope) ' If the node is a graphics layer node, pass its graphics layer to ProcessDataTable Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = TryCast(node, ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode) If Not graphicsLayerNode Is Nothing AndAlso graphicsLayerNode.Value <> "Input Features" Then Me.ProcessResultsLayer(doSelect, doZoom, graphicsLayerNode.Layer, adfEnvelope) End If ' Process any child nodes For Each childNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode In node.Nodes Me.ProcessNode(childNode, doSelect, doZoom, adfEnvelope) Next childNode End Sub ' Selects all of the passed-in layer's features and unions the layer's extent with the ' passed-in envelope Private Sub ProcessResultsLayer(ByVal doSelect As Boolean, ByVal doZoom As Boolean, ByVal graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer, ByRef adfEnvelope As ESRI.ArcGIS.ADF.Web.Geometry.Envelope) If graphicsLayer.Rows.Count > 0 Then ' Expand the new extent to include the extent of the graphics layer If doZoom Then ' Create a new envelope if the one passed-in isn't initialized If adfEnvelope Is Nothing Then adfEnvelope = New ESRI.ArcGIS.ADF.Web.Geometry.Envelope() End If ' Union the layer's extent with the envelope adfEnvelope.Union(graphicsLayer.FullExtent) End If ' Select the layer's features If doSelect Then For Each dataRow As System.Data.DataRow In graphicsLayer.Rows dataRow(graphicsLayer.IsSelectedColumn) = True Next dataRow End If End If End Sub ' Renders the control at design-tiem. Displays a warning if the control has ' not been buddied with a map Private Sub RenderDesignTimeHtml(ByVal writer As System.Web.UI.HtmlTextWriter) ' Create a table to format the design-time display Dim table As System.Web.UI.WebControls.Table = New System.Web.UI.WebControls.Table() Dim row As System.Web.UI.WebControls.TableRow = New System.Web.UI.WebControls.TableRow() table.CellPadding = 4 table.Rows.Add(row) ' Add the control's ID Dim cell As System.Web.UI.WebControls.TableCell = New System.Web.UI.WebControls.TableCell() row.Cells.Add(cell) cell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap" cell.Text = String.Format("{0}<br>", Me.ClientID) cell.ColumnSpan = 2 row = New System.Web.UI.WebControls.TableRow() table.Rows.Add(row) ' Add the control type cell = New System.Web.UI.WebControls.TableCell() row.Cells.Add(cell) cell.Text = "ZoomToResults WebControl" cell.ColumnSpan = 2 ' If necessary, add a warning that the control needs to be buddied to a map If Me.Map.Equals("(none)") OrElse String.IsNullOrEmpty(Me.Map) Then row = New System.Web.UI.WebControls.TableRow() table.Rows.Add(row) cell = New System.Web.UI.WebControls.TableCell() row.Cells.Add(cell) cell.ForeColor = System.Drawing.Color.Red cell.Text = "Warning:" cell = New System.Web.UI.WebControls.TableCell() row.Cells.Add(cell) cell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap" cell.Text = "You must set the Map property." End If table.RenderControl(writer) End Sub #End Region End Class End Namespace