Common Geocoding
Common_Geocoding_VBNet\Default.aspx.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 Partial Class [Default]
  Inherits System.Web.UI.Page
  #Region "ASP.NET Page Event Handlers"
    Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs)
        Try
            If Not IsPostBack Then
                Dim bloomingField As ESRI.ArcGIS.ADF.Web.Geometry.Envelope = New ESRI.ArcGIS.ADF.Web.Geometry.Envelope( _
                       -83.3337801950437, 42.5314716926848, -83.2476061838133, 42.6123242012297)
                Map1.Extent = bloomingField
            End If
            Dim customScriptKey As String = "customDataItemScript"
            ' Check whether a script block with the name stored in customScriptKey has already
            ' been registered on the client, and whether the page is in an asynchronous postback.
            ' If neither of these is true, create and register the script block.  Note that replacing
            ' ScriptManager1.IsInAsyncPostBack with Page.IsPostback will work initially, but if a
            ' full page postback occurs, the script may be lost.
            If (Not Me.Page.ClientScript.IsClientScriptBlockRegistered(Me.GetType(), customScriptKey)) AndAlso (Not ScriptManager1.IsInAsyncPostBack) Then
                ' Construct the JavaScript block that will be responsible for processing data items.
                ' 
                ' onLoadFunction specifies AsyncResponseHandler as a handler for the pageLoading AJAX
                ' client-side event.  This event fires during asynchronous postbacks after the response 
                ' has been received from the server, but before any content on the page is updated.  
                ' 
                ' AsyncResponseHandler retrieves the data items registered server-side during the 
                ' asynchronous postback by accessing the dataItems property on the second argument 
                ' passed to the handler.  It then gets the particular data item corresponding to the 
                ' page by passing the page's client ID to the dataItems array as an array index.  This 
                ' data item, assumed to be formatted as a Web ADF callback result, is then passed to 
                ' ESRI.ADF.System.processCallbackResult - the client-side Web ADF function responsible 
                ' for parsing callback results and updating Web ADF controls accordingly.
                '
                ' Below the function declarations, onLoadFunction is added as a handler for the AJAX 
                ' client-side event init, which is raised once when the page is first rendered. This 
                ' is therefore the appropriate place for onLoadFunction to be called, since the 
                ' asynchronous pageLoading handler in this case can remain unchanged for the life
                ' of the application.
                '
                ' The functions are enclosed in an extra pair of curly braces to allow the subsequent
                ' call to String.Format.  String.Format is designed to replace the contents of curly
                ' braces with the parameters passed to the function call.  These extra braces "escape" 
                ' the braces that must enclose a JavaScript function's logic, essentially telling 
                ' String.Format to not replace the contents of these particular braces.
                Dim scriptBlock As String = "" & ControlChars.CrLf & "                " & ControlChars.CrLf & "                function onLoadFunction(){{" & ControlChars.CrLf & "                  Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(AsyncResponseHandler);" & ControlChars.CrLf & "                }}" & ControlChars.CrLf & ControlChars.CrLf & "                function AsyncResponseHandler(sender, args) {{" & ControlChars.CrLf & "                  var dataItems = args.get_dataItems();" & ControlChars.CrLf & "                  if (dataItems['{0}'] != null)" & ControlChars.CrLf & "                    ESRI.ADF.System.processCallbackResult(dataItems['{0}']);" & ControlChars.CrLf & "                }}" & ControlChars.CrLf & ControlChars.CrLf & "                Sys.Application.add_init(onLoadFunction);"

                ' Insert the client ID of the page into the script block.  
                scriptBlock = String.Format(scriptBlock, Page.ClientID)

                ' Register the script on the client.  This will make the script block available client-side
                ' and execute statements that are not function or object declarations, in this case adding
                ' onLoadFunction as a handler for the init event.
                Me.Page.ClientScript.RegisterStartupScript(Me.GetType(), customScriptKey, scriptBlock, True)
            End If
        Catch exception As System.Exception
            Dim jsErrorAlert As String = String.Format("<script>{0}</script>", GetJavaScriptErrorString(exception))
            Response.Write(jsErrorAlert)
        End Try
    End Sub

  #End Region

  #Region "ASP.NET Web Control Event Handlers"

  ' Geocodes the input address, adds spatial results to the map, and adds text/tabular results to the page.
  ' Called when the user clicks the Geocode button.
  Protected Sub btnGeocode_Click(ByVal sender As Object, ByVal e As System.EventArgs)
    Try
      ' Get the resource item for the first item in the GeocodeResourceManager and make sure it's initialized
      Dim geocodeResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.GeocodeResourceItem = GeocodeResourceManager1.ResourceItems.Find("Geocode Resource")
      If (Not geocodeResourceItem.Resource.Initialized) Then
        geocodeResourceItem.InitializeResource()
      End If

      ' Create a Web ADF Common API geocode functionality
      Dim commonGeocodeFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IGeocodeFunctionality = CType(geocodeResourceItem.Resource.CreateFunctionality(GetType(ESRI.ArcGIS.ADF.Web.DataSources.IGeocodeFunctionality), Nothing), ESRI.ArcGIS.ADF.Web.DataSources.IGeocodeFunctionality)

      Dim addressFieldList As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.Geocode.Field) = commonGeocodeFunctionality.GetAddressFields()

      Dim addressValueList As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.Geocode.AddressValue) = New System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.Geocode.AddressValue)()

      ' Retrieve the input street from txtAddress and store in an AddressValue object
      Dim streetAddressValue As ESRI.ArcGIS.ADF.Web.Geocode.AddressValue = New ESRI.ArcGIS.ADF.Web.Geocode.AddressValue("STREET", txtAddress.Text)

      ' Retrieve the input zip from txtZipCode and store in an AddressValue object
            Dim zoneAddressValue As ESRI.ArcGIS.ADF.Web.Geocode.AddressValue = New ESRI.ArcGIS.ADF.Web.Geocode.AddressValue("Zip", txtZipCode.Text)

      ' Add the input street and zip to the list of address values
      addressValueList.Add(streetAddressValue)
      addressValueList.Add(zoneAddressValue)

      ' Get graphics layer from Map and clear it of all graphic elements
      Dim elementGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer = GetGraphicsLayer("Graphics Resource", "Geocode Results")
      elementGraphicsLayer.Clear()

      ' Declare a Web ADF Point object to store the result geometry
      Dim adfPoint As ESRI.ArcGIS.ADF.Web.Geometry.Point = Nothing

      ' Hide the controls that may display textual/tabular results to false.  If one is used to
      ' display results, it will be set to visible when necessary.
      lblResults.Visible = False
      grdMatchInfo.Visible = False

      ' Check whether all the match candidates are being returned or not
      If chkAllCandidates.Checked Then
        ' Get all available match candidates

        ' Get the minimum match score from the minimum score drop-down
        commonGeocodeFunctionality.MinCandidateScore = Integer.Parse(ddlMinScore.SelectedValue)

        ' Execute the geocode operation.  Here we call FindAddressCandidates because we wish
        ' to find all the match candidates that meet or exceed the minimum score
        Dim matchDataTable As System.Data.DataTable = commonGeocodeFunctionality.FindAddressCandidates(addressValueList, True, True)

        ' Find the index of the shape column from the results data table
        Dim shapeColumnIndex As Integer = -1
        Dim j As Integer = 0
        Do While j < matchDataTable.Columns.Count
          If matchDataTable.Columns(j).DataType Is GetType(ESRI.ArcGIS.ADF.Web.Geometry.Geometry) Then
            shapeColumnIndex = j
            Exit Do
          End If
          j += 1
        Loop

        ' Check that results were found
        If matchDataTable.Rows.Count > 0 Then
          ' Bind the results to the GridView control
          grdMatchInfo.DataSource = matchDataTable
          grdMatchInfo.DataBind()
          grdMatchInfo.Visible = True

          ' Get the geometry column from the results table
          Dim shapeColumn As System.Data.DataColumn = matchDataTable.Columns(shapeColumnIndex)

          ' Add a point on the map for each candidate
          For Each dataRow As System.Data.DataRow In matchDataTable.Rows
            Dim adfPointAsObject As Object = dataRow(shapeColumnIndex)
            If TypeOf adfPointAsObject Is ESRI.ArcGIS.ADF.Web.Geometry.Point Then
              adfPoint = CType(adfPointAsObject, ESRI.ArcGIS.ADF.Web.Geometry.Point)
              AddPointToMap(adfPoint, elementGraphicsLayer)
            End If
          Next dataRow

          ' Update the match count label
          lblMatchCount.Text = String.Format("Number of Matches: {0}", matchDataTable.Rows.Count)
        End If
      Else
        ' Just get the one best-matching address

        ' Get the minimum match score from the minimum score drop-down
        commonGeocodeFunctionality.MinMatchScore = Integer.Parse(ddlMinScore.SelectedValue)

        ' Execute the geocode operation.  In this case, we call GeocodeAddress, which returns
        ' only the best match as a Web ADF Point
        adfPoint = commonGeocodeFunctionality.GeocodeAddress(addressValueList)

        ' If a match was found, put it on the map
        If (Not adfPoint Is Nothing) AndAlso ((Not adfPoint.X.Equals(Double.NaN))) Then
          AddPointToMap(adfPoint, elementGraphicsLayer)

          ' Update the results and match count label controls.  Use string.Format with N as the 
          ' format provider to specify that the coordinates be presented in general numeric format.
          lblResults.Text = String.Format("Address found at {0:N}, {1:N}", adfPoint.X.ToString(), adfPoint.Y.ToString())
          lblResults.Visible = True
          lblMatchCount.Text = "Number of Matches: 1"
        End If
      End If

      ' Check whether the match point is null or is populated with coordinates of NaN
      If (Not adfPoint Is Nothing) AndAlso ((Not adfPoint.X.Equals(Double.NaN))) Then
        Map1.RefreshResource("Graphics Resource")
        ScriptManager1.RegisterDataItem(Page, Map1.CallbackResults.ToString(), False)
      Else
        ' No results were found, so inform the user by setting the Label control
        ' accordingly
        lblResults.Text = "No matches found"
        lblResults.Visible = True
      End If
    Catch exception As System.Exception
      Dim errorCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = GetErrorCallback(exception)
      Map1.CallbackResults.Add(errorCallbackResult)
      ScriptManager1.RegisterDataItem(Page, Map1.CallbackResults.ToString(), False)
    End Try
  End Sub

  ' Clears spatial geocode results from the map and textual/tabular geocode results from the page.
  ' Called when the user clicks the Clear button
  Protected Sub btnClearResults_Click(ByVal sender As Object, ByVal e As System.EventArgs)
    Try
      ' Get the graphics layer where spatial geocode results are stored from the Map
      Dim elementGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer = GetGraphicsLayer("Graphics Resource", "Geocode Results")
      ' Remove all graphics from the layer
      elementGraphicsLayer.Clear()

      ' Hide textual/tabular results elements
      grdMatchInfo.Visible = False
      lblResults.Visible = False

      ' Update the number of matches label
      lblMatchCount.Text = "Number of Matches:"

      ' Refresh the graphics resource
      Map1.RefreshResource("Graphics Resource")

      ' Register the Map's callback results as a data item.  This sends the results back
      ' to the client, where they can be accessed by handlers of the pageLoading, pageLoaded, or
      ' endRequest events on the PageRequestManager object.  More specifically, the callback
      ' results will be inserted into the dataItems array, which is passed to the client as a 
      ' property of the second argument passed to any of the three aforementioned event 
      ' handlers.  The response can be retrieved from the array by specifying the client ID of 
      ' the control passed into the RegisterDataItem call (in this case Page) as the array
      ' index.  In this case, a pageLoading handler was declared in the script block 
      ' registered in the Page's PreRender event, and this handler retrieves the dataItem and 
      ' passes it to ESRI.ADF.System.processCallbackResult.
      ScriptManager1.RegisterDataItem(Page, Map1.CallbackResults.ToString(), False)
    Catch exception As System.Exception
      Dim errorCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = GetErrorCallback(exception)
      Map1.CallbackResults.Add(errorCallbackResult)
      ScriptManager1.RegisterDataItem(Page, Map1.CallbackResults.ToString(), False)
    End Try
  End Sub

  #End Region

  #Region "Instance Methods"

  ' Adds the passed-in point to the passed-in graphics layer
  Private Sub AddPointToMap(ByVal adfPoint As ESRI.ArcGIS.ADF.Web.Geometry.Point, ByVal elementGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer)
    Try
      ' Create a default symbol for the point
      Dim defaultAdfSimpleMarkerSymbol As ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol = New ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol()
      defaultAdfSimpleMarkerSymbol.Color = System.Drawing.Color.GreenYellow
      defaultAdfSimpleMarkerSymbol.Width = 20
      defaultAdfSimpleMarkerSymbol.Type = ESRI.ArcGIS.ADF.Web.Display.Symbol.MarkerSymbolType.Star

      ' Create a selected symbol for the point
      Dim selectedAdfSimpleMarkerSymbol As ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol = New ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol()
      selectedAdfSimpleMarkerSymbol.Color = System.Drawing.Color.Yellow
      selectedAdfSimpleMarkerSymbol.Width = 20
      selectedAdfSimpleMarkerSymbol.Type = ESRI.ArcGIS.ADF.Web.Display.Symbol.MarkerSymbolType.Star

      ' Create a graphic based on the passed-in point, the default symbol, and the selected symbol
      Dim graphicElement As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement = New ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement(adfPoint, defaultAdfSimpleMarkerSymbol, selectedAdfSimpleMarkerSymbol)

      ' Add the graphic to the passed-in graphics layer
      elementGraphicsLayer.Add(graphicElement)
    Catch exception As System.Exception
      Dim errorCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = GetErrorCallback(exception)
      Map1.CallbackResults.Add(errorCallbackResult)
    End Try
  End Sub

  ' Retrieves the graphics layer of the passed-in name from the graphics resource of the passed-in name.
  ' If the graphics layer does not exist, it is created.
  Private Function GetGraphicsLayer(ByVal graphicsResourceName As String, ByVal graphicsLayerName As String) As ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer
    Try
      ' Retrieve the graphics resource of the passed-in name
      Dim graphicsMapResource As ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapResource = CType(MapResourceManager1.GetResource(graphicsResourceName), ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapResource)

      ' Attempt to retrieve the graphics layer having the passed-in name from the resource
      Dim elementGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer = Nothing
      If graphicsMapResource.Graphics.Tables.Contains(graphicsLayerName) Then
        elementGraphicsLayer = TryCast(graphicsMapResource.Graphics.Tables(graphicsLayerName), ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer)
      End If

      ' If no graphics layer with the passed-in name was found, create one
      If elementGraphicsLayer Is Nothing Then
        elementGraphicsLayer = New ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer()
        elementGraphicsLayer.TableName = graphicsLayerName
        graphicsMapResource.Graphics.Tables.Add(elementGraphicsLayer)
        ' Refresh the Toc so the layer is displayed in it
        Toc1.Refresh()
      End If

      Return elementGraphicsLayer
    Catch exception As System.Exception
      Dim errorCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = GetErrorCallback(exception)
      Map1.CallbackResults.Add(errorCallbackResult)
      Return Nothing
    End Try
  End Function

  ' Constructs a callback result that will display an error message based on the passed-in
  ' exception
  Private Function GetErrorCallback(ByVal exception As System.Exception) As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult
    ' Create a callback result to display an error message
    Dim jsAlertErrorMessage As String = GetJavaScriptErrorString(exception)
    Dim alertCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsAlertErrorMessage)
    Return alertCallbackResult
  End Function

  ' Constructs JavaScript necessary to display an error message based on the passed-in exception.
  Private Function GetJavaScriptErrorString(ByVal exception As System.Exception) As String
    ' Get the website's configuration file
    Dim webConfig As System.Configuration.Configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(System.Web.HttpContext.Current.Request.ApplicationPath)

    ' Get the "compilation" section of the config file
    Dim compilationSection As System.Web.Configuration.CompilationSection = TryCast(webConfig.GetSection("system.web/compilation"), System.Web.Configuration.CompilationSection)

    ' If the config file's compilation section specifies debug mode, include 
    ' stack trace information in the error message.  Otherwise, just return 
    ' the exception message.
    Dim errorMessage As String = Nothing
    If (Not compilationSection Is Nothing) AndAlso (compilationSection.Debug) Then
      Dim stackTrace As String = exception.StackTrace.Replace("\", "\\")
      errorMessage = exception.Message & "\n\n" & stackTrace.Trim()
    Else
      errorMessage = exception.Message
    End If

    ' Create a callback result to display an error message
    Dim jsAlertException As String = "alert('" & errorMessage & "')"
    Return jsAlertException
  End Function
  #End Region
End Class