Obtaining the Visitor's Location

Warning

The content in this article may still be applicable to the current version of the Bing Maps AJAX Control, but it uses a previous version of the Bing Maps AJAX Control which is no longer supported. More information about the current version of the Bing Maps AJAX Control is found in the Bing Map Control SDK.

The "Locate Me" function on the official Bing Maps website detects the IP address provided by your browser and performs a geographic lookup. Based on where the IP is registered, the Bing Maps map will automatically re-center on your estimated location.

Although this functionality is not officially part of the Bing Maps AJAX Control SDK, you can still leverage the system from your own applications. In this tutorial I'll explain how you can obtain the location of the visitor viewing your page.

Normally, you'd need to have a huge database to map IP addresses to their corresponding coordinates. However, obtaining such a database and keeping it updated can be quite hard. That's why we'll want to use another method such as the AJAX endpoint from bing.com.

The locate me function gets its information from this URL: https://www.bing.com/WiFiIPService/locate.ashx.

This request outputs a JavaScript response similar to:

SetAutoLocateViewport(39.7393, -104.9844, 7, false, "%1 has determined your location by using your computer's IP address.");

Listing 1 Result of requesting user location.

This is very useful information. We can leverage it in our applications by following a two step process:

  1. Create an AJAX xmlHttp object to send the request

  2. Create the appropriate JavaScript callback function (SetAutoLocateViewport).

Once we are aware of the user's latitude and longitude, we can do all sorts of things ranging from automatically re-centering the map to customizing the page with specialized advertising.

Making an AJAX call

In order to make the call to the site happen behind the covers of our web page, we need to set up an xml http handler to make the call and retrieve the result for us. We also need a handler function to process the result when it comes back. These functions are standard methods that you will often see in Bing Maps sample code:

<script>
  var xmlhttp;
  function InitXmlHttp() {
    // Attempt to initialize xmlhttp object
    try
    {
      xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e)
    {
      // Try to use different activex object
      try
      {
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch (E)
      {
        xmlhttp = false;
      }
    }
    // If not initialized, create XMLHttpRequest object
    if (!xmlhttp && typeof XMLHttpRequest!='undefined')
    {
      xmlhttp = new XMLHttpRequest();
    }
    // Define function call for when Request obj state has changed
    xmlhttp.onreadystatechange=searchHandler;
  }

//Handler function to process results from xml http object
  function searchHandler()
  {
    if (xmlhttp.readyState==4)
    {
      eval( xmlhttp.responseText );
    }
  }

//Add remaining listings here

</script>

Listing 2 Setting up for AJAX calls

Once we are able to send HTTP calls and receive responses, we still need to make our call to the locate me function and set up the proper callback method.

Locate Me JavaScript

Before making the call, we need to add a handler to process our request. We can display the latitude and longitude in an alert, or use it to re-center a VE map, or add a pushpin that says "you are here". In this example, we add a pushpin:

function SetAutoLocateViewport(latitude, longitude, lvl, bl, msg)
{
  var message = msg.replace('%1', 'This Demo');
  var position = new VELatLong(latitude, longitude);
  var pin = new VEPushpin(
    pinID,
    position,
    null,'You Are Here', message);
  map.AddPushpin(pin);
  pinID++;
  map.SetCenterAndZoom(position, 14);
}

Listing 3 Our Locate Me callback function

Note that we actually perform four different actions in this method:

  1. Format our message by replacing the %1 place holder with the name of our demo.

  2. Create a new pushpin based on the returned latitude and longitude

  3. Add the pushpin to our existing VE map control (cleverly named map)

  4. Center the map on our new position and zoom to a low level map view.

The last thing we need to do is make sure the locate me method gets called properly. We can do this by adding the call to our "on load" function after we create our map:

function FindMe()
{
  InitXmlHttp();
  xmlhttp.open("GET","https://www.bing.com/WiFiIPService/locate.ashx",true);
  xmlhttp.send(null);
}

function GetMap()
{
  map = new VEMap('myMap');
  map.LoadMap();
  FindMe();
}

Listing 4 Making the Locate Me Call and Creating a Map

Finally, to make the demo complete, we have to have an HTML body that goes with our JavaScript:

<body onload="GetMap();">
  <div id='myMap' style="position:relative; width:400px; height:400px;"></div>
</body>

Listing 5 An HTML page with a basic map control

If you put it all together, you should see a VE map centered on the location mapped to your IP address. You should also see a pushpin indicating "You are here".

Error Handling

If, for some reason, the Locate Me function can't determine a location, you will receive a different message similar to:

ShowMessage("Bing Maps cannot determine your current location. Try again later.");

Listing 6 Error message

You can trap this error message by adding another callback handler to your application:

function ShowMessage(message)
{
  //do something in response to the error
}

Listing 7 Error handling

Fortunately, errors like this rarely occur.

Conclusion

You can modify this basic concept in many ways. For example, with the addition of the Bing Maps What/Where find controls, you have a very simple auto-centering search page. Add a few geoRSS layers and you can instantly show your users where all the nearby restaurants and movie theaters are based on their current location.

However, you should be aware that this functionality is not officially supported as part of the Bing Maps AJAX Control SDK and is subject to change at any time. Use it at your own risk. You should also note that the resolution of location based on IP address is somewhat problematic. You will almost always end up in the correct city area (for larger cities and towns), but you will rarely get anything resembling street level accuracy.

Good luck with your application!

This article is an update of an article originally contributed by Yousef El-Dardiry of MapStats fame. The update was performed by Robert McGovern MVP (Bing Maps/MapPoint).

The final source code for the Locate Me application is as follows:

<html>
<head>
<script src="https://dev.virtualearth.net/mapcontrol/v4/mapcontrol.js"></script>
<script>

  var xmlhttp=false;
  var pinID =1;
  var map = null;

  function FindMe()
  {
    InitXmlHttp();
       xmlhttp.open("GET","https://www.bing.com/WiFiIPService/locate.ashx",true);
      xmlhttp.send(null);
  }

  function GetMap()
  {
    map = new VEMap('myMap');
    map.LoadMap();
    FindMe();
  }

  function SetAutoLocateViewport(latitude, longitude, lvl, bl, msg)
  {

    var message = msg.replace('%1', 'This Demo');
    var pin = new VEPushpin(
      pinID,
      new VELatLong(latitude, longitude),
      null,'You Are Here', message);
    map.AddPushpin(pin);
    pinID++;
    map.SetCenterAndZoom(new VELatLong(latitude, longitude), 14);
  }

  /* This function is used to initialize the xmlHttp object */

  function InitXmlHttp() {

        // Attempt to initialize xmlhttp object
        try
        {
              xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch (e)
        {
              // Try to use different activex object
              try
              {
                    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
              }
              catch (E)
              {
                    xmlhttp = false;
              }
        }
    // If not initialized, create XMLHttpRequest object
        if (!xmlhttp && typeof XMLHttpRequest!='undefined')
        {
              xmlhttp = new XMLHttpRequest();
        }

        // Define function call for when Request obj state has changed
        xmlhttp.onreadystatechange=searchHandler;
  }

  function searchHandler()
  {

      if (xmlhttp.readyState==4)
        {
              eval( xmlhttp.responseText );
        }
  }

</script>
</head>

<body onload="GetMap();">
  <div id='myMap' style="position:relative; width:400px; height:400px;"></div>

</body>
</html>

Listing 8 Final Source Code