How to use the Geolocation API

Geolocation is a new standard that allow us to identify where a user is located by the use of scripts within the browser.

Geolocation-example

This is particularly useful when we want to offer in our web app local information to the users. Some examples of it could be a web app that tells you where is the nearest bus station, a restaurant or just to know where you are when you get lost in the city. Something that by the way, happens to me a lot.

The first thing you need to know is that the Geolocation API just defines a high-level interface to access the geolocation information, but it is completely agnostic on the system used to obtain that geolocation information.

 

 

There are several ways to get the information like:

  • GPS
  • GSM/CDMA cell IDs
  • IP address

As you can already imagine the accuracy of the results will depend a lot on how the geolocation information is retrieved.

Now that we have a little bit of background, let’s see more in detail what the Geolocation API offer us. The API defines a new interface that is called, not surprisingly, Geolocation. This interface defines three methods:

//Used to retrieve the current geolocation, just one time.
void getCurrentPosition(
     //Callback to receive geolocation info
     in PositionCallback successCallback,
     //Callback to receive errors produced during the call
     in optional PositionErrorCallback errorCallback,
     //Options about the geolocation information to retrieve
     in optional PositionOptions options);

//Used to track the geolocation of the user.
long watchPosition(
     //Callback to receive geolocation info
     in PositionCallback successCallback,
     //Callback to receive errors produced during the call
     in optional PositionErrorCallback errorCallback,
     //Options about the geolocation information to retrieve
     in optional PositionOptions options);

//Used to stop tracking the geolocation of the user.
void clearWatch(
     //id of the watch retrieved with watchPosition
     in long watchId);

At this point you will have already noticed that the method getCurrentPosition does not return any value, this is because the API is asynchronous, so in order to obtain the results we need to implement a callback function (PositionCallback) that will be called once the method has the results.

The other two parameters are optional, they let us to define another callback function to receive any error produced while calling the method and the different options to configure the geolocation information that will be retrieved.

Before we proceed to implement a real example let’s examine the rest of classes used in the API:

// used to receive successful notifications about position requests
interface PositionCallback {
    void handleEvent(in Position position);
}

// used to receive error notifications about position requests
interface PositionErrorCallback {
    void handleEvent(in PositionError error);
} 

// used to specify the options about the geolocation information to retrieve
interface PositionOptions {
    // by default false. Indicates you want to obtain the information with
    // the best accuracy available, even if that consumes more battery or
    // means slower response time.
    attribute boolean enableHighAccuracy;
    
    // expressed in milliseconds
    long timeout;

    // expressed in milliseconds. Indicates you accept a cached value
    // no greater than the specified time. If 0, acquires a new position
    long maximumAge
} 

// used to obtain geolocation position information
interface Position {
    readonly Coordinates coords;
    readonly DOMTimeStamp timestamp;
} 

// used to obtain errors while requesting geolocation information
interface PositionError {
    const unsigned short PERMISSION_DENIED = 1;
    const unsigned short POSITION_UNAVAILABLE = 2;
    const unsigned short TIMEOUT = 3;
    readonly attribute unsigned short code;
    readonly attribute DOMString message;
}

// used to obtain the geolocation position details
interface Coordinates {
    //specified in decimal degrees
    readonly attribute double latitude;
    //specified in decimal degrees
    readonly attribute double longitude;
    // specified in meters
    readonly attribute double? altitude;
    // specified in meters and corresponding to 95% confidence level
    readonly attribute double accuracy;
    // specified in meters and corresponding to 95% confidence level
    readonly attribute double? altitudeAccuracy;
    // specified in degrees, denotes the direction of travel counting
    // clockwise relative to the true north (0º to 360º)
    readonly attribute double? heading;
    // specified in meters per second
    readonly attribute double? speed;
}

Now that we have all info we can start creating a real example, to test them you will need any of the modern browsers that supports this feature like Internet Explorer 9.

<!DOCTYPE html>
<html>
    <head>
        <title>Geolocation example</title>
        <script 
            type="text/javascript" 
            src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0">
        </script>
        <script type="text/javascript">
            var map = null;

            function successCallback(position) {
                // we create the location for the Bing Map
                var userLocation = new Microsoft.Maps.Location(
                    position.coords.latitude,
                    position.coords.longitude);

                // we add the pushpin to the map
                var pin = new Microsoft.Maps.Pushpin(userLocation);
                map.entities.push(pin);

                // refresh the map centering the view on current location
                map.setView({ center: userLocation });

                showMessage("Your location is: " + userLocation.toString());
            }

            function errorCallback(error) {
                var code = error.code;
                var message = "unknown error";
                switch (code) {
                    case 1:
                        message = "permission to geolocate you has been denied";
                        break;
                    case 2:
                        message = "position is not available";
                        break;
                    case 3:
                        message = "geolocation request timed out";
                        break;
                }
                showMessage(message);
            }

            function showMessage(message) {
                var div = document.getElementById("message");
                div.innerHTML = message;
            }

            function getMap() {
                // replace with your own Bing credentials
                var mapOptions = {
                    credentials: "YOUR BING CREDENTIALS",
                    center: new Microsoft.Maps.Location(40.42, -3.8),
                    mapTypeId: Microsoft.Maps.MapTypeId.road,
                    zoom: 16
                }

                // we create the Bing map with a default position
                map = new Microsoft.Maps.Map(document.getElementById("map"), mapOptions);

                // if the browser supports Geolocation we do the request
                if (navigator.geolocation) {
                    var options = {
                        enableHighAccuracy: true,
                        timeout: 2000,
                        maximumAge: 0
                    };

                    navigator.geolocation.getCurrentPosition(
                        successCallback,
                        errorCallback,
                        options);
                }
                else {
                    showError("Your browser does not support geolocation");
                }
            }
        </script>
    </head>
    <body onload="getMap();">
        <div id="map" style="position:relative; width:400px; height:400px;">
        </div>
        <div id="message"></div>
    </body>
</html>

In the above example we have seen:

  • how to request the position of the visitor who is accessing our web page
  • how to load a Bing map using the Bing Maps API and set a pushpin in the map indicating our current location
  • how to handle the errors in the case the geolocation information could not be retrieved.

Widget Twitter not found. Root element is missing

I don’t know when the issue started, but today I noticed the Widget Twitter in BlogEngine 2.0 has stopped working.

Instead of displaying the latest tweets, it shows the error “Widget Twitter not found. Root element is missing” as you can see in the image below.

widget twitter not found

 

The error looks a bit weird because I haven’t done any modification lately in my blog that could crash it, in addition if you do a quick research online you will see I’m not the only one suffering the issue. It is like all twitter widgets going mad at the same time.

Fortunately it has a very easy solution, the problem is related to the data feed used to display the latest tweets. So to fix it you only need to go to your folder App_Data, delete the file “twitter_feeds.xml” and force the refresh of your site. Once it loads again it will work as normally.

I hope it helps.

Internet Explorer and Web Standards

Many times I see people complaining about Internet Explorer 9 not rendering properly a “standard” web page, after digging into what the problem can be it normally resumes into two options:

  • The web page is not using the document mode “IE9 standards”.
  • The web page is using conditional comments making use of browser detection instead of feature detection and treats IE9 as IE7 or IE6, very old versions of IE that do not support today standards.

Let’s start with what the IE9 standards mode is.

Internet Explorer is the only browser that offers backward compatibility with older versions of the browser. This is done incorporating the new, plus the older versions of the rendering engine with each release of Internet Explorer.

As a user of IE you only need to press F12 to open the Developer Tools and select what document mode use.

InternetExplorer_document_mode_ie9_standards

This is very powerful because as developers we can decide what version of the IE rendering engine will be used to render the markup of our pages. In the case of Internet Explorer 9, as you have seen in the figure above, we can chose between the next document modes:

  • IE 9 standards: This is the default and latest standards-compliant behavior used to render webs that have a strict or unknown document type.
  • IE 8 standards: This behavior acts as it does IE8.
  • IE 7 standards: This behavior acts as it does IE7.
  • Quirks: This is the oldest behavior that can be used with IE, and applies when rendering a document with no doctype or a quirks doctype. It is similar to the behavior of IE5.

Selecting a different mode will reload the page to render it in the document mode selected, but will not change the user-agent string sent to the website. To do that you can also change the Browser Mode using the developer tools.

Regardless the best practice and my suggestion is to target always the standards and keep our sites upgraded to support them, in the case you have an old site you can’t migrate yet, you don’t need to continue using old versions of IE. You can upgrade to IE9 and use as quick fix the X-UA-Compatible meta tag to set what render engine IE has to use to display properly the page.

It is obvious but worthy to mention that if you use an older document mode as “Quirks mode”, you will use it with its full consequences. This means means that your modern IE9 browser will behave as the old IE5 browser and will not be able to use all the new standards and performance improvements that IE9 includes. You can easily see this by running this code and visualizing it in “Internet Explorer 9 standards” and any other document mode:

   1: <html>
   2:   <head>
   3:     <title>HTML5 Canvas example</title>
   4:     <script>
   5:       function drawCanvas(){
   6:  
   7:         var canvas = document.getElementById('myCanvas');
   8:  
   9:         var context = canvas.getContext('2d');
  10:  
  11:         context.fillRect (128, 25, 100, 100);
  12:       }
  13:     </script>
  14:     <style type="text/css">
  15:       canvas { border: 2px solid black; }
  16:     </style>
  17:   </head>
  18:   <body onload="drawCanvas();">
  19:  
  20:     <canvas id="myCanvas" width="260" height="200">
  21:     The document mode does not support canvas
  22:     </canvas>
  23:  
  24:   </body>
  25: </html>

 

So, if you find your website not rendering properly in IE9, first of all check that the site is being rendered in IE9 standards mode and secondly that you are not using any conditional comments that serve custom code for older versions of IE.

In this article we will not cover best practices on how to use feature detection instead of browser detection bad practices to avoid conditional comments, but I recommend reading the article Same Markup: Writing Cross-Browser Code.

You can read additional information on how internet explorer determines the document mode.

Using 301 Redirects

Using 301 redirects are a useful way to indicate visitors and search crawlers that a page has been moved permanently to another location.

This is especially important, for instance, when you move your site to another domain. Redirecting all the pages on the old domain to your new site will help Google or Bing crawlers to indicate your site has permanently moved avoiding SEO issues.

There is another case where you want to make use of 301redirects to avoid hurting your SEO, this is when you have more than one domain pointing to the same content.

In my case I have two domains: www.josefcobonnin.com and www.josebonnin.com pointing to this blog. I don’t want to eliminate the first domain, but if I don’t do it the search engines will consider the content duplicated and will impact my positioning on the search results. To avoid it we can make use again of the 301 redirects.

The way to implement 301 redirects with ASP.NET is very easy, what we do is to capture the requests we receive and add to the response the HTTP header Location with the new URL. 

The next code should be added in the Global.asax file of our ASP.NET projects.

   1: void Application_BeginRequest(object sender, EventArgs e)
   2: {
   3:         string url = HttpContext.Current.Request.Url.ToString().ToLower();
   4:         if (url.Contains("http://www.josefcobonnin.com"))
   5:         {
   6:             HttpContext.Current.Response.Status = "301 Moved Permanently";
   7:  
   8:             HttpContext.Current.Response.AddHeader("Location", url.Replace("http://www.josefcobonnin.com", "http://www.josebonnin.com"));
   9:         }
  10: }

After implementing this short code, all the requests done to www.josefcobonnin will redirect with a 301 to www.josebonnin.com. You can easily test it using the IE9 developer tools and checking the headers returned.

301 redirects

TF Command Tips

When you work with TFS there are certain situations that will happen first or later like somebody going on holidays without doing checkin, a project that was created wrong, etc. those situations require you might need delete a project, delete a workspace, undo other changes…most of them can be accomplished using TF.exe. That’s the reason why I always say to TFS users that “TF.exe /?” is your friend.

I post some of them here as a kind of reminder for myself. As I will not be using TFS very often from now, I’m afraid my brain will deallocate the syntax to allocate other knowledge pretty soon.

Delete a project:

  • TFSDeleteProject /server:SERVER_NAME “PROJECT_NAME”

View checked out files for a specific project/user:

  • tf status “SERVER_PATH” /user:USER_NAME /recursive

View checked out files for all users:

  • tf status “SERVER_PATH” /user:* /recursive

View workspaces in a computer:

  • tf workspaces /computer:COMPUTER_NAME /owner:*

Undo others code:

  • tf undo /workspace:WORKSPACE_NAME “ITEM_PATH” /s:URL

Baseless merge:

  • tf merge /baseless “FROM” “TO” /version:T /recursive