Thursday, 23 April 2009

British MP’s Expenses Data Visualisation via Virtual Earth CTP

 

Following on from my previous blog on US Politicians data visualisation using a listbox, this time I’ve moved across the pond to my homeland and created one for British Politician’s Expenses (a bit of a hot topic in the UK at present) based on the CTP of the Virtual Earth for Silverlight control.

The application enables you to quickly view Members of Parliament (MP) expenses for 2007/08 based on:

  • Location / Political Party (colour of the pin)
  • Travel expenses (opacity of the pin)
  • Second home allowance (size of the pin)

Note: An MP represents ~100k people, so map gives a good indication of population density across Britain.

Using the Search tool you can filter the results  based on MP’s name/constituency Travel expenses and second home allowance. Any changes to the map are reflected in the pages URL making it easy to share custom reports via the share button on Twitter, Digg, Facebook and Email.

E.g. the link blow shows MPs in the South East of England:

http://home.btconnect.com/martibiz/mps.htm#?s=&t=0&h=0&y=51.5417&x=-0.1491&z=12

Sample screen shot 

Tech notes:

The visualisation makes use of Client Technical Preview (CTP). The data is sourced from the Guardian MP expense claims with MP geo data coming from TheyWorkForYou, though some data cleansing was required to link the two sets of data. The data is pulled in as  JSON, to keep things as fast as possible I embedded the JSON in the XAP assembly and loaded it using the following code:

void LoadMPdetails()
   {
       Stream file = Assembly.GetExecutingAssembly().GetManifestResourceStream("mmMPs.Assets.mpDetails.js"); 
       TextReader tr = new StreamReader(file);

       JsonArray jsonArray = (JsonArray)JsonArray.Load(tr);

       mps = new List<MP>();

       foreach (JsonObject jsonItem in jsonArray)
       {
           MP item = new MP();
           item.name = jsonItem["name"];
           item.constituency =  jsonItem["constituency"];
           item.party =  jsonItem["party"];
           mps.Add(item);
       }
   }

Even though this is a Silverlight 2.0 application we support deep linking (added to Silverlight 3.0 beta) so you can create custom views and share the links with your friends. Each time the viewport is changed or a search preformed the pages URL is updated and added to the browsers page history using  JQuery and jquery.history and some C# code provided by nerdplusart.com

The code below shows the logic to display the push pin with the tooltips:

private void AddPin2(Loc con)
        {
            // pushpin image
            Image image = new Image();

            if (con.mp == null)
                image.Source = new BitmapImage(new Uri("/assets/pinMagenta.png", UriKind.Relative));
            else        
            switch (con.mp.party)
            {
                case "Liberal Democrat":
                    image.Source = new BitmapImage(new Uri("/assets/pinYellow.png", UriKind.Relative));
                    break;

                case "Conservative":
                    image.Source = new BitmapImage(new Uri("/assets/pinBlue.png", UriKind.Relative));
                    break;
  
                case "Labour":
                    image.Source = new BitmapImage(new Uri("/assets/pinRed.png", UriKind.Relative));
                    break;

                default:
                    image.Source = new BitmapImage(new Uri("/assets/pinMagenta.png", UriKind.Relative));
                    break;
            }

            int size = 35;
            if (con.expense.Cost_Of_Staying_Away_From_Main_Home > 5000)
                size = (int) Math.Min(( (con.expense.Cost_Of_Staying_Away_From_Main_Home - 5000) / 5000) * 10 + 35,80);
            image.Width = size; image.Height = size;

            if (con.expense.Cost_Of_Staying_Away_From_Main_Home < 500)
                image.Opacity = 0.6;
            else
                image.Opacity = 0.6 + (con.expense.Cost_Of_Staying_Away_From_Main_Home / 23000);
            

            image.MouseEnter += new System.Windows.Input.MouseEventHandler(i_MouseEnter);
            image.MouseLeave += new System.Windows.Input.MouseEventHandler(i_MouseLeave);
            image.MouseLeftButtonDown +=new System.Windows.Input.MouseButtonEventHandler(image_MouseLeftButtonDown); // += new System.Windows.Input.MouseButtonEventHandler(image_MouseLeftButtonUp);


            // Add Tooltip
            var tooltipObject = new StackPanel();

            var title = new TextBlock();
            title.FontWeight = FontWeights.Bold;
            title.Text = con.name;
            tooltipObject.Children.Add(title);
            
            var description = new TextBlock();
            if (con.mp != null)
                description.Text = con.mp.party + " - " + con.mp.name + "\n\n" +getExpenseReport(con.expense); // .Total_Allowances_Claimed_Inc_Travel ; // "Info goes here....."; // "This is an arbitrary description of the \"Huge Square\" to be displayed within the Tooltip.";
            tooltipObject.Children.Add(description);

            image.Tag = con.name;

            ToolTipService.SetToolTip(image, tooltipObject);
            
            //Add the pushpin to the Map 
            myMap.Children.Add(image);

            //Position the pushpin using the attached properties 
            MapLayer.SetMapPosition(image, new Location(con.centre_lat, con.centre_lon));
            MapLayer.SetMapPositionMethod(image, PositionMethod.BottomLeft);

        }

Things I would have liked to do if I had the time:

Better control of the map pins when zooming out, would like some form of clustering and pins to be a bit smaller, 600+ large pins is too much for a place the size of Britain.

Restrict zoom level and panning just to the UK, as things get a bit confusing for the user seeing a small island with los of dots.

Host via Silverlight Streaming, but couldn’t figure out how I could get the deep linking working from an IFRAME hosted on a different domain.

Make the tooltip timeouts longer, but this isn’t a trivial task, though you can find a ToolTipService on codeplex that supports such a feature.

2 comments:

SoulSolutions said...

Checkout the SDK help file that ships with the CTP control, under "Developing with the Silverlight Map Control" -> "Creating a Custom Map Mode". Here you can nicely inherit from the default base map but restict the zoom level and bounds.
Regarding hosting in SLS you can always serve the XAP from there but host the page yourself, just remember to set crossdomain policy on your server.

Martin Duffy said...

I did create a SLS Version.

The problem I had was getting the XAP to update the browser history that is the issue. As if you click on share it will return the IFRAME URI and not the host page URI. Not sure if browser would allow such cross domain access as bit of a security risk.