<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-19519354</id><updated>2012-02-14T21:31:51.574Z</updated><category term='GIS'/><category term='Personal'/><category term='Google Maps'/><category term='Wireless'/><category term='Vista'/><category term='IE9'/><category term='OS OpenData'/><category term='SQL Server'/><category term='Google Docs'/><category term='Virtual Earth'/><category term='AJAX'/><category term='Metastorm'/><category term='JScript.NET'/><category term='VB.NET'/><category term='Windows Live Writer'/><category term='London'/><category term='Oracle'/><category term='Skype'/><category term='Programming'/><category term='ASP.NET'/><category term='Finance'/><category term='Web'/><category term='Environment'/><category term='FreeFlow'/><category term='Windows Workflow'/><category term='OS OpenSpace'/><category term='FxCop'/><category term='Delphi'/><category term='Dell'/><category term='Work'/><category term='Friend Connect'/><category term='Spam'/><category term='JScript'/><category term='FireFox'/><category term='Windows 7'/><category term='IE7'/><category term='TV'/><category term='jQuery'/><category term='XSLT'/><category term='IE6'/><category term='CSS'/><category term='Music'/><category term='Films'/><category term='Visio'/><category term='Jobs'/><category term='BPM'/><category term='Postcodes'/><category term='IIS'/><category term='MapPoint'/><category term='Google'/><category term='Random Pub Finder'/><category term='Blogging'/><category term='C#'/><category term='PHP'/><category term='Maths'/><category term='AdSense'/><category term='Google Analytics'/><category term='iTunes'/><category term='SEO'/><category term='WCF'/><category term='Trains'/><category term='flickr'/><category term='IE8'/><category term='Sharepoint'/><category term='HTML'/><category term='MySql'/><category term='SonicWall'/><category term='JavaScript'/><category term='WPF'/><category term='Football'/><category term='ZX Spectrum'/><category term='House prices'/><category term='.NET'/><category term='Books'/><title type='text'>Doogal Bell's bloggy thing</title><subtitle type='html'>"quite amusing and drastically geeky at the same time"</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default?start-index=101&amp;max-results=100'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>512</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-19519354.post-3716941516063497462</id><published>2012-02-14T21:31:00.001Z</published><updated>2012-02-14T21:31:51.604Z</updated><title type='text'>The scale of the universe</title><content type='html'>&lt;p&gt;If like me you find it hard to grasp how the tiny the tiniest things are and how massively huge the biggest things are, then have a look at this&lt;/p&gt;  &lt;p&gt;&lt;a title="http://htwins.net/scale2/" href="http://htwins.net/scale2/"&gt;http://htwins.net/scale2/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I’m not sure our minds can ever fully comprehend these different distances and sizes, but maybe it’ll help…&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3716941516063497462?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3716941516063497462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3716941516063497462' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3716941516063497462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3716941516063497462'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2012/02/scale-of-universe.html' title='The scale of the universe'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4037988371917083746</id><published>2012-01-30T22:20:00.001Z</published><updated>2012-01-30T22:21:23.395Z</updated><title type='text'>Let’s outsource the bankers</title><content type='html'>&lt;p&gt;The main argument for bankers earning vast amounts of money seems to be that in a global market, they’ll just bugger off somewhere else if we can’t match the money available elsewhere. But the odd thing is, for the rest of us, globalization has led to stagnating wages as jobs have been outsourced to countries with cheaper labour. Odd that the free market doesn’t work in the same way for the rich as it does for everyone else. &lt;/p&gt;  &lt;p&gt;So if we assume that maybe this argument is a bit of a fib, and assuming we can find decent replacements in some far off land who are willing to work for a much smaller pay packet, can’t we just outsource all our bankers along with some of our well paid CEOs? That’s some offshoring I wouldn’t mind seeing.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4037988371917083746?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4037988371917083746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4037988371917083746' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4037988371917083746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4037988371917083746'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2012/01/lets-outsource-bankers.html' title='Let’s outsource the bankers'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6347841830336282973</id><published>2012-01-28T21:20:00.001Z</published><updated>2012-01-28T21:20:03.606Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Jobs'/><title type='text'>Developer interview questions</title><content type='html'>&lt;p&gt;A while back I had to interview some people for a developer role at work so came up with a few questions, combining a few from the web with some of my own. This is essentially a note to myself for next time I’m interviewing.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;jQuery&lt;/strong&gt;    &lt;br /&gt;What's a jQuery selector? How would you select an item by its ID? By its class?    &lt;br /&gt;Give some examples of JQuery UI effects and widgets and what they could be used for&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;.NET     &lt;br /&gt;&lt;/strong&gt;What's an interface? Compare and contrast with an abstract class    &lt;br /&gt;How does memory management differ between .NET and a non-managed language? How can we make .NET behave more like a non-managed language?    &lt;br /&gt;What are generics? Why use List&amp;lt;&amp;gt; instead of ArrayList?    &lt;br /&gt;What's a virtual function? How does it relate to OOP?&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Web&lt;/strong&gt;    &lt;br /&gt;What is a RESTful web service? Why are they preferred to SOAP web services?    &lt;br /&gt;What are the common data formats returned by an AJAX web service call? Is one better than the other? What about if you wanted to call it from a fat client?    &lt;br /&gt;Name and describe several HTTP status codes    &lt;br /&gt;Name the various HTTP verbs and when they are used&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;General&lt;/strong&gt;    &lt;br /&gt;Your application has a performance problem, how would you investigate the issue?    &lt;br /&gt;An error occurs in your web application but only in production and only happens occasionally, how do you go about tracking down the problem?    &lt;br /&gt;What are some of the issues around multi-threaded applications?    &lt;br /&gt;Discuss some ways of ensuring code quality remains high in a project    &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6347841830336282973?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6347841830336282973/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6347841830336282973' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6347841830336282973'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6347841830336282973'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2012/01/developer-interview-questions.html' title='Developer interview questions'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2568775615814102999</id><published>2012-01-21T21:33:00.001Z</published><updated>2012-01-21T21:33:11.250Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Analytics'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Do it yourself inbound link alerts</title><content type='html'>&lt;p&gt;&lt;a href="http://www.embeddedanalytics.com/"&gt;Embedded Analytics&lt;/a&gt; provide a nice service that will email you whenever somebody clicks on a new link to your site. I’ve been signed up for a while and it’s interesting to see who’s linked to my site. But I received an email last week informing me that my site had so many inbound links that I would have to start paying for the service. To be fair the amount they were going to charge me wasn’t a lot, but I couldn’t really justify spending money on something that is essentially just a way to waste a bit of time for me. And I also figured I could probably do the same thing myself through the Google Analytics API, since this is what Embedded Analytics uses.&lt;/p&gt;  &lt;p&gt;I’m assuming that Embedded Analytics uses the source for visitors to your site to spot new links. There is a downside to this since it won’t spot links that have been added but have not been clicked on, but generally these won’t be that interesting, since they presumably are links on low traffic sites. &lt;/p&gt;  &lt;p&gt;So to implement this requires a few steps. Pull out the data from Google Analytics and store this data somewhere (DB, XML file, whatever). Then next time we pull the data out of Google, check for new URLs in the returned data and send a notification of these new URLs. Embedded Analytics also goes a step further and validates that the links are valid and that the pages containing them are available from the web. I was only really interested in the first part of this solution so have written a piece of code to pull out the URLs using the Google Data API for .NET. The rest of the work is left as an exercise for the reader!&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System;
&lt;span style="color: blue"&gt;using &lt;/span&gt;Google.GData.Analytics;

&lt;span style="color: blue"&gt;namespace &lt;/span&gt;GoogleAnalytics
{
  &lt;span style="color: blue"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Program
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
    {
      &lt;span style="color: #2b91af"&gt;AccountQuery &lt;/span&gt;feedQuery = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AccountQuery&lt;/span&gt;();
      &lt;span style="color: #2b91af"&gt;AnalyticsService &lt;/span&gt;service = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AnalyticsService&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;DoogalAnalytics&amp;quot;&lt;/span&gt;);
      service.setUserCredentials(&lt;span style="color: #a31515"&gt;&amp;quot;email&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;);

      &lt;span style="color: #2b91af"&gt;DataQuery &lt;/span&gt;pageViewQuery = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataQuery&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;https://www.google.com/analytics/feeds/data&amp;quot;&lt;/span&gt;);
      pageViewQuery.Ids = &lt;span style="color: #a31515"&gt;&amp;quot;ga:202885&amp;quot;&lt;/span&gt;;
      pageViewQuery.Metrics = &lt;span style="color: #a31515"&gt;&amp;quot;ga:visits&amp;quot;&lt;/span&gt;;
      pageViewQuery.Dimensions = &lt;span style="color: #a31515"&gt;&amp;quot;ga:source,ga:referralPath&amp;quot;&lt;/span&gt;;
      pageViewQuery.Sort = &lt;span style="color: #a31515"&gt;&amp;quot;ga:source,ga:referralPath&amp;quot;&lt;/span&gt;;
      pageViewQuery.GAStartDate = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now.AddMonths(-1).ToString(&lt;span style="color: #a31515"&gt;&amp;quot;yyyy-MM-dd&amp;quot;&lt;/span&gt;);
      pageViewQuery.GAEndDate = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now.ToString(&lt;span style="color: #a31515"&gt;&amp;quot;yyyy-MM-dd&amp;quot;&lt;/span&gt;);

      &lt;span style="color: #2b91af"&gt;DataFeed &lt;/span&gt;feed = service.Query(pageViewQuery);
      &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;i = 0; i &amp;lt; feed.Entries.Count; i++)
      {
        &lt;span style="color: #2b91af"&gt;DataEntry &lt;/span&gt;pvEntry = (&lt;span style="color: #2b91af"&gt;DataEntry&lt;/span&gt;)feed.Entries[i];
        &lt;span style="color: blue"&gt;string &lt;/span&gt;host = pvEntry.Dimensions[0].Value;
        &lt;span style="color: blue"&gt;string &lt;/span&gt;path = pvEntry.Dimensions[1].Value;
        &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;&amp;quot;http://&amp;quot; &lt;/span&gt;+ host + path);
      }

      &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.ReadLine();
    }
  }
}&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2568775615814102999?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2568775615814102999/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2568775615814102999' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2568775615814102999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2568775615814102999'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2012/01/do-it-yourself-inbound-link-alerts.html' title='Do it yourself inbound link alerts'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-5833016473415389696</id><published>2012-01-06T21:44:00.001Z</published><updated>2012-01-06T21:44:09.352Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><title type='text'>Another take on peer to peer lending</title><content type='html'>&lt;p&gt;It seems a little strange to me that at a time when banks are meant to be desperately trying to increase their balance sheets, they are offering such meagre interest rates. I dunnow but maybe offer some decent rates and people will shove their money in your bank? But it seems they have decided it’s better to offer crap rates and hope we are too stupid to realise we can get better returns elsewhere.&lt;/p&gt;  &lt;p&gt;So for a few years I’ve been stashing cash in &lt;a href="http://www.zopa.co.uk"&gt;Zopa&lt;/a&gt; and getting a return that manages to beat inflation by lending money directly to people, with the caveat that there is a higher level of risk than having money in the bank. &lt;/p&gt;  &lt;p&gt;But I’m obviously not the only person to choose this option and rates have been falling of late. And although I’m happy to finance personal loans, i did have a hankering to lend money to businesses, especially since this seems to be something else banks are failing to do properly*&lt;/p&gt;  &lt;p&gt;Which is where &lt;a href="http://www.fundingcircle.com/"&gt;Funding Circle&lt;/a&gt; comes in. This is peer to peer lending but the other party is a business. Interest rates are currently some what better than Zopa. Time will tell how bad the default rates are but I’m going to drip feed some money in there and see how it pans out.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;*I always thought banking was pretty straightforward, take in money from deposits, then lend it out to other people and skim a bit of profit from the transaction. Simple and a bit boring. Maybe they should have stuck to this slightly dull job, rather than inventing, buying and selling insane derivatives that nobody understands and nobody can quantify the associated risk. Get back to doing what you’re meant to do and people might get off your back a bit…&lt;/em&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-5833016473415389696?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/5833016473415389696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=5833016473415389696' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5833016473415389696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5833016473415389696'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2012/01/another-take-on-peer-to-peer-lending.html' title='Another take on peer to peer lending'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3831517116345444263</id><published>2011-12-30T11:16:00.001Z</published><updated>2011-12-30T11:16:44.489Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><title type='text'>Why all the free ads for Facebook?</title><content type='html'>&lt;p&gt;A while back I picked up a copy of the Evening Standard and was surprised to see Facebook and Twitter logos at the top of every page, suggested readers follow ES on these two websites. And this is just an extreme example of what I’m seeing more and more. Ads on the telly and in newspapers no longer show the URL of the company’s website, but the URL of their Facebook page instead.&lt;/p&gt;  &lt;p&gt;But the thing that confuses me is why big companies would choose Facebook as their main point of contact with their customers? Sure, social networks are the big thing at the moment and getting users to follow you or like your product might have some benefit, but there seem to be a number of downsides.&lt;/p&gt;  &lt;p&gt;First, who owns all the data being collected about your customers? My guess is Facebook. Can you extract that data if Facebook decide they don’t want you anymore or you decide to move? I guess it’s probably possible via the Facebook API but it seems somewhat risky. And even if you do extract it, Facebook will no doubt keep hold of it as well.&lt;/p&gt;  &lt;p&gt;Then there’s the question of ads. The company pages I’ve seen on Facebook seem to have the same ads as any other page. I’ve found no indication that companies get any of the income from those ads, so why drive traffic to Facebook so they can make money from your brand? And what if Facebook decide to show ads for one of your competitors?&lt;/p&gt;  &lt;p&gt;Frankly it all seems a bit odd. Big companies have big IT departments and generally have their own websites, fully under their control. It is pretty simple to add some Facebook widgets to your own site and get integration that way which seems a more sane approach if you want to get hooked into Facebook.&lt;/p&gt;  &lt;p&gt;For a one man band kind of company, I can see the sense in putting you web presence on Facebook, it’s a lot simpler and cheaper than building your own website, but for multi-nationals my prediction for 2012 is that this is something they do much less of.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3831517116345444263?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3831517116345444263/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3831517116345444263' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3831517116345444263'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3831517116345444263'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/12/why-all-free-ads-for-facebook.html' title='Why all the free ads for Facebook?'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1434592245753397421</id><published>2011-12-26T19:46:00.001Z</published><updated>2011-12-26T19:51:38.168Z</updated><title type='text'>I am Sheldon…</title><content type='html'>&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-QKrwoytfh38/TvjPGAxPihI/AAAAAAAAAKI/6UijdOVaUC4/s1600-h/111225-101223%25255B2%25255D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px 10px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Sheldon / Doogal" border="0" alt="Sheldon / Doogal" align="left" src="http://lh4.ggpht.com/-zvjaHcSGNj4/TvjPHnm1xkI/AAAAAAAAAKQ/xudJgi0Dve0/111225-101223_thumb.jpg?imgmax=800" width="244" height="184" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;…with a somewhat larger waste line, somewhat smaller IQ and hopefully with less OCD tendencies.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1434592245753397421?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1434592245753397421/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1434592245753397421' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1434592245753397421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1434592245753397421'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/12/i-am-sheldon.html' title='I am Sheldon…'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-zvjaHcSGNj4/TvjPHnm1xkI/AAAAAAAAAKQ/xudJgi0Dve0/s72-c/111225-101223_thumb.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4921838916919836038</id><published>2011-12-02T20:49:00.001Z</published><updated>2011-12-02T20:49:43.032Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Friend Connect'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><title type='text'>Goodbye Google Friend Connect</title><content type='html'>&lt;p&gt;I’ve been disenchanted with &lt;a href="http://www.google.com/friendconnect/"&gt;Google Friend Connect&lt;/a&gt; for a while. I only used it for commenting on my website but it has a number of weaknesses&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;A crappy user experience&lt;/li&gt;    &lt;li&gt;No notifications of new comments, which is a pain if you only receive a handful of comments&lt;/li&gt;    &lt;li&gt;Ignoring query strings in URLs, so comments don’t stick to the right page&lt;/li&gt;    &lt;li&gt;Weird date formatting and no control over how they appear&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;For a long while I assumed Google would actually update the widgets but I’m not sure anything ever got changed after the initial release. It was released and then, nothing. Even a &lt;a href="http://www.google.com/friendconnect/"&gt;visit to the site&lt;/a&gt; shows a copyright notice from 2009, which suggests they haven’t done much with it for some time. &lt;/p&gt;  &lt;p&gt;So I had been meaning to convert to some other system but just hadn’t got round to it. Then I noticed &lt;a href="http://googleblog.blogspot.com/2011/11/more-spring-cleaning-out-of-season.html"&gt;Google have decided to can it&lt;/a&gt; (which of course hasn’t been mentioned anywhere on the Friend Connect site itself), so I decided it was time to finally do something. Google’s suggested solution is to hook into Google+, but that seems like a pretty useless way to add commenting to a website. So my suggested solution is to sign up to &lt;a href="http://disqus.com/"&gt;Disqus&lt;/a&gt;, it takes about 10 minutes to plug it in to your website and looks pretty good straight out of the box.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4921838916919836038?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4921838916919836038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4921838916919836038' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4921838916919836038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4921838916919836038'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/12/goodbye-google-friend-connect.html' title='Goodbye Google Friend Connect'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-145779950024567502</id><published>2011-10-11T21:51:00.001+01:00</published><updated>2011-10-11T21:51:42.942+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Google Maps in a desktop app</title><content type='html'>&lt;p&gt;I’ve seen one or two examples of using Google Maps in a WinForms desktop app, but the ones I’ve seen seem to involve loading image tiles from the Google server directly. There’s nothing wrong with that approach but I thought it would be a lot simpler to simply host a local web page using the Google Maps API in a WebBrowser control in an application. &lt;a href="http://www.doogal.co.uk/files/GoogleMaps.zip"&gt;Here is a very simple example of this idea&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;If you want to extend this example, it’s possible to call scripts in the page via the &lt;font face="Courier New"&gt;WebBrowser.Document.InvokeScript&lt;/font&gt; method and the application can respond to events in the page via the &lt;font face="Courier New"&gt;WebBrowser.ObjectForScripting&lt;/font&gt; property.&lt;/p&gt;  &lt;p&gt;As an aside, similar ideas can be applied to hosting other Javascript web components, such as an HTML editor like &lt;a href="http://ckeditor.com/"&gt;CKEditor&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-145779950024567502?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/145779950024567502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=145779950024567502' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/145779950024567502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/145779950024567502'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/10/google-maps-in-desktop-app.html' title='Google Maps in a desktop app'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6335824669845399338</id><published>2011-07-14T22:08:00.001+01:00</published><updated>2011-07-14T22:10:25.174+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Friend Connect'/><category scheme='http://www.blogger.com/atom/ns#' term='IE8'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Google Maps and Friend Connect weirdness in IE8</title><content type='html'>&lt;p&gt;I received a bug report about my website. The maps that appear on my &lt;a href="http://www.doogal.co.uk/UKPostcodes.php?Search=BB3 3"&gt;UK postcodes pages&lt;/a&gt; weren’t working in IE8. I hadn’t noticed since they were working fine in IE9 but when I switched to compatibility view in IE9 I started to see the same problem. This helped because I was then able to debug my JavaScript (much as I love &lt;a href="http://www.my-debugbar.com/wiki/IETester/HomePage"&gt;IETester&lt;/a&gt;, I’d love it even more if I had access to the developer tools for each version of IE). And debugging the script revealed &lt;font face="Courier New"&gt;google.maps&lt;/font&gt; was null.&lt;/p&gt;  &lt;p&gt;At this point I assumed this was a problem with my Google Maps script, so tried loading &lt;a href="http://code.google.com/apis/maps/documentation/javascript/basics.html#Async"&gt;Google Maps asynchronously&lt;/a&gt; then tried &lt;a href="http://code.google.com/apis/maps/documentation/javascript/basics.html#Versioning"&gt;specifying an older version of the API&lt;/a&gt;. Neither helped. I tried inserting the script in my HTML head but with no luck.&lt;/p&gt;  &lt;p&gt;Finally I had a look at the &lt;font face="Courier New"&gt;google&lt;/font&gt; object and saw the only thing defined in there was &lt;font face="Courier New"&gt;friendconnect&lt;/font&gt;. Now my suspicions moved away from Google Maps to Google Friend Connect. my Friend Connect stuff appears at the bottom of each page and the script reference for it was also down the bottom of the page. So I thought I’d tried moving the Friend Connect script reference to the html header. And voila, the maps started working again.&lt;/p&gt;  &lt;p&gt;So the conclusion? I’m not really sure, although I suspect Friend Connect is removing the &lt;font face="Courier New"&gt;google.maps&lt;/font&gt; object, although it seems odd that it only happens on IE8.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6335824669845399338?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6335824669845399338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6335824669845399338' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6335824669845399338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6335824669845399338'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/07/google-maps-and-friend-connect.html' title='Google Maps and Friend Connect weirdness in IE8'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6585809573303000396</id><published>2011-06-04T22:43:00.001+01:00</published><updated>2011-06-04T22:43:19.034+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Analytics'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Retrieving the most popular pages using Google Analytics API</title><content type='html'>&lt;p&gt;For a long time I’ve shown the most popular pages on &lt;a href="http://www.doogal.co.uk/"&gt;the home page of my website&lt;/a&gt;. I did this by logging every page that was viewed to the MySql database on the back end. This kind of worked but had a few problems. First, it wasn’t very clever since it couldn’t tell the difference between a real visitor and a search engine bot. Second, since I’ve started to get quite a few visitors (no, really), it was writing a large amount of data to the database. &lt;/p&gt;  &lt;p&gt;So I thought there must be a better solution. Figuring that all the information I needed was already being collected by Google Analytics, I thought I could grab this data and dump it into a much smaller with just the page URL and the number of visits (rather than adding a row for every visit). So I coded up a solution using the .NET wrapper around the &lt;a href="http://code.google.com/apis/analytics/docs/gdata/home.html"&gt;Google Analytics API&lt;/a&gt;. And this is what it looks like (with the database access code removed for clarity). You’ll need to provide your own email address, password and Google Analytics account table ID to get this to work, for obvious reasons. &lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System;
&lt;span style="color: blue"&gt;using &lt;/span&gt;Google.GData.Analytics;

&lt;span style="color: blue"&gt;namespace &lt;/span&gt;GoogleAnalytics
{
  &lt;span style="color: blue"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Program
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
    {
      &lt;span style="color: #2b91af"&gt;AccountQuery &lt;/span&gt;feedQuery = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AccountQuery&lt;/span&gt;();
      &lt;span style="color: #2b91af"&gt;AnalyticsService &lt;/span&gt;service = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AnalyticsService&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;DoogalAnalytics&amp;quot;&lt;/span&gt;);
      service.setUserCredentials(&lt;span style="color: #a31515"&gt;&amp;quot;email address&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;);

      &lt;span style="color: #2b91af"&gt;DataQuery &lt;/span&gt;pageViewQuery = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataQuery&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;https://www.google.com/analytics/feeds/data&amp;quot;&lt;/span&gt;);
      pageViewQuery.Ids = &lt;span style="color: #a31515"&gt;&amp;quot;Google Analytics account table ID&amp;quot;&lt;/span&gt;;
      pageViewQuery.Metrics = &lt;span style="color: #a31515"&gt;&amp;quot;ga:visits&amp;quot;&lt;/span&gt;;
      pageViewQuery.Dimensions = &lt;span style="color: #a31515"&gt;&amp;quot;ga:pagePath&amp;quot;&lt;/span&gt;;
      pageViewQuery.Sort = &lt;span style="color: #a31515"&gt;&amp;quot;-ga:visits&amp;quot;&lt;/span&gt;;
      pageViewQuery.GAStartDate = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now.AddMonths(-1).ToString(&lt;span style="color: #a31515"&gt;&amp;quot;yyyy-MM-dd&amp;quot;&lt;/span&gt;);
      pageViewQuery.GAEndDate = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now.ToString(&lt;span style="color: #a31515"&gt;&amp;quot;yyyy-MM-dd&amp;quot;&lt;/span&gt;);

      &lt;span style="color: #2b91af"&gt;DataFeed &lt;/span&gt;feed = service.Query(pageViewQuery);
      &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;i = 0; i &amp;lt; 20; i++)
      {
        &lt;span style="color: #2b91af"&gt;DataEntry &lt;/span&gt;pvEntry = (&lt;span style="color: #2b91af"&gt;DataEntry&lt;/span&gt;)feed.Entries[i];
        &lt;span style="color: blue"&gt;string &lt;/span&gt;page = pvEntry.Dimensions[0].Value.Substring(1);
        &lt;span style="color: blue"&gt;string &lt;/span&gt;visits = pvEntry.Metrics[0].Value;

        &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(page + &lt;span style="color: #a31515"&gt;&amp;quot;: &amp;quot; &lt;/span&gt;+ visits);
      }

      &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.ReadLine();
    }
  }
}&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6585809573303000396?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6585809573303000396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6585809573303000396' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6585809573303000396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6585809573303000396'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/06/retrieving-most-popular-pages-using.html' title='Retrieving the most popular pages using Google Analytics API'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8775037387006517100</id><published>2011-05-30T21:08:00.001+01:00</published><updated>2011-05-30T21:08:05.803+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='XSLT'/><title type='text'>Poor man’s XSLT profiling for .NET</title><content type='html'>&lt;p&gt;If you’ve ever looked round for a profiler for XSL transformations then chances are you’ve found the &lt;a href="http://msdn.microsoft.com/en-us/library/ee473446.aspx"&gt;Microsoft add-on for Visual Studio&lt;/a&gt;, which looks like it’s just the ticket, if you happen to have Visual Studio Team System. But if you don’t happen to own that version, then it might look like you have to upgrade your VS license or buy some other XSLT profiler. &lt;/p&gt;  &lt;p&gt;But if you happen to own a .NET profiler (I highly recommend &lt;a href="http://smartbear.com/products/development-tools/performance-profiling/"&gt;AQTime&lt;/a&gt;) then there may be another solution. Visual Studio comes with the &lt;a href="http://msdn.microsoft.com/en-us/library/bb399405.aspx"&gt;XSLTC tool&lt;/a&gt; that can be used to generate an assembly from an XSL transformation. Once we’ve got an assembly, then we can build a small wrapper application that loads up the assembly, passes it to an instance of the &lt;font face="Courier New"&gt;XslCompiledTransform&lt;/font&gt; class and calls the transform. And once we’ve got that, we can use a standard .NET profiler to find bottlenecks. &lt;/p&gt;  &lt;p&gt;And as I understand it, the XSLT profiler add-on for Visual Studio works in just this way so profiling using this technique should be just as effective as the Microsoft version.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8775037387006517100?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8775037387006517100/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8775037387006517100' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8775037387006517100'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8775037387006517100'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/05/poor-mans-xslt-profiling-for-net.html' title='Poor man’s XSLT profiling for .NET'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2130026248918770014</id><published>2011-04-14T22:05:00.001+01:00</published><updated>2011-04-14T22:05:13.083+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Music'/><title type='text'>Spotify not too good to be true anymore</title><content type='html'>&lt;p&gt;&lt;a href="http://doogalbellend.blogspot.com/2009/02/spotify-future-of-music.html"&gt;Apparently I’ve been using Spotfiy for over two years&lt;/a&gt;. Funny, it seems longer than that. It was the perfect music service for me, unlimited music of my choosing on my PC, which is where I listen to music most of the time, with the only minor downside being some adverts that play occasionally between tracks. &lt;a href="http://www.spotify.com/int/blog/archives/2011/04/14/upcoming-changes-to-spotify-free-open/"&gt;But it looks like it won’t be quite so perfect anymore.&lt;/a&gt; Free users can only listen to a track a total of five times and total listening time will be limited to 10 hours a month.&lt;/p&gt;  &lt;p&gt;As a frequent user of Spotify I can see why they are doing this. First, it’s obvious that advertising revenue is not what they were hoping for, most of the ads are still for Spotify itself. Second, using it has had a perhaps not unexpected effect on my music buying behaviour. First example, U2 put their last album up on Spotify before its official release. I had a listen and realised it was rubbish, so as a marketing exercise I doubt it was a huge success. Second example/s, quite a few new releases are put onto Spotify Premium upon release. On a couple of occasions I’ve then purchased the album before it’s become available on the free version (Elbow, Arcade Fire if you’re wondering). I’m guessing this isn’t what Spotify wanted me to do, they were presumably hoping I’d pony up the Premium version. And then when those albums did become available on the free version (generally only a few weeks after release) I was hit with a mild feeling of regret for spending money that I didn’t really need to and deciding to think twice before making another purchase. Again, probably not what the music industry sponsors of Spotify were hoping for.&lt;/p&gt;  &lt;p&gt;So now I’ve got a choice, sign up for a tenner a month and continue on as I am at the moment or spend that tenner on a CD every month. I guess the music industry don’t care too much which way that ten quid gets to them, so it’s purely a personal dilemma. But the people who can’t or won’t spend a tenner (teenagers, students mostly I guess) will probably rediscover the skill of searching for pirated albums on Google. The music industry is still caught between a rock and a hard place.&amp;#160;&amp;#160; &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2130026248918770014?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2130026248918770014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2130026248918770014' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2130026248918770014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2130026248918770014'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/04/spotify-not-too-good-to-be-true-anymore.html' title='Spotify not too good to be true anymore'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2554177186181307188</id><published>2011-03-12T10:49:00.001Z</published><updated>2011-03-12T10:51:00.124Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySql'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='OS OpenData'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Updating the Code-Point postcode datataset in MySql</title><content type='html'>&lt;p&gt;Some time ago &lt;a href="http://doogalbellend.blogspot.com/2010/06/importing-code-point-dataset-into-mysql.html"&gt;I imported the Ordnance Survey Code-Point postcode dataset into MySql&lt;/a&gt;. It looks like there’s a new version of that dataset available which includes new postcodes so I wanted to update my database. I guess I could just empty the table and re-import the data, but since it takes some time import and &lt;a href="http://www.doogal.co.uk/UKPostcodes.php"&gt;the data is live on the web&lt;/a&gt;, this wasn’t the ideal solution. Fortunately, MySql has a useful IGNORE keyword which will ignore failed inserts so any old postcodes will be ignored (since the postcode is used as the primary key on the table) whilst new ones are inserted. Of course, this assumes that the latitude and longitude of old postcodes doesn’t change, which I’m hoping is a reasonable assumption. So my new code looks like this.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System.IO;
&lt;span style="color: blue"&gt;using &lt;/span&gt;DotNetCoords;
&lt;span style="color: blue"&gt;using &lt;/span&gt;LumenWorks.Framework.IO.Csv;
&lt;span style="color: blue"&gt;using &lt;/span&gt;MySql.Data.MySqlClient;

&lt;span style="color: blue"&gt;namespace &lt;/span&gt;ImportCodepoint
{
  &lt;span style="color: blue"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Program
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
    {
      &lt;span style="color: blue"&gt;string&lt;/span&gt;[] files = &lt;span style="color: #2b91af"&gt;Directory&lt;/span&gt;.GetFiles(&lt;span style="color: #a31515"&gt;@&amp;quot;C:\Users\Doogal\Downloads\codepo_gb\Code-Point Open\Data&amp;quot;&lt;/span&gt;);
      &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;string &lt;/span&gt;file &lt;span style="color: blue"&gt;in &lt;/span&gt;files)
      {
        ReadFile(file);
      }

    }

    &lt;span style="color: blue"&gt;private static void &lt;/span&gt;ReadFile(&lt;span style="color: blue"&gt;string &lt;/span&gt;file)
    {
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;StreamReader &lt;/span&gt;reader = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StreamReader&lt;/span&gt;(file))
      {
        &lt;span style="color: #2b91af"&gt;CsvReader &lt;/span&gt;csvReader = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;CsvReader&lt;/span&gt;(reader, &lt;span style="color: blue"&gt;false&lt;/span&gt;);
        &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;MySqlConnection &lt;/span&gt;conn = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MySqlConnection&lt;/span&gt;(
          &lt;span style="color: #a31515"&gt;&amp;quot;server=server;uid=username;pwd=password;database=database;&amp;quot;&lt;/span&gt;))
        {
          conn.Open();
          &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] data &lt;span style="color: blue"&gt;in &lt;/span&gt;csvReader)
          {
            &lt;span style="color: blue"&gt;string &lt;/span&gt;postcode = data[0];
            &lt;span style="color: green"&gt;// some postcodes have spaces, some don't
            &lt;/span&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(postcode.IndexOf(&lt;span style="color: #a31515"&gt;' '&lt;/span&gt;) &amp;lt; 0)
              postcode = data[0].Substring(0, data[0].Length - 3) + &lt;span style="color: #a31515"&gt;&amp;quot; &amp;quot; &lt;/span&gt;+ data[0].Substring(data[0].Length - 3);
            &lt;span style="color: green"&gt;// some have two spaces...
            &lt;/span&gt;postcode = postcode.Replace(&lt;span style="color: #a31515"&gt;&amp;quot;  &amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot; &amp;quot;&lt;/span&gt;);
            
            &lt;span style="color: blue"&gt;double &lt;/span&gt;easting = &lt;span style="color: blue"&gt;double&lt;/span&gt;.Parse(data[10]);
            &lt;span style="color: blue"&gt;double &lt;/span&gt;northing = &lt;span style="color: blue"&gt;double&lt;/span&gt;.Parse(data[11]);

            &lt;span style="color: green"&gt;// there are some postcodes with no location
            &lt;/span&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;((easting != 0) &amp;amp;&amp;amp; (northing != 0))
            {
              &lt;span style="color: green"&gt;// convert easting/northing to lat/long
              &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OSRef &lt;/span&gt;osRef = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OSRef&lt;/span&gt;(easting, northing);
              &lt;span style="color: #2b91af"&gt;LatLng &lt;/span&gt;latLng = osRef.ToLatLng();
              latLng.ToWGS84();

              &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;MySqlCommand &lt;/span&gt;command = conn.CreateCommand())
              {
                &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(postcode);
                command.CommandTimeout = 60;
                command.CommandText = &lt;span style="color: blue"&gt;string&lt;/span&gt;.Format(
                  &lt;span style="color: #a31515"&gt;&amp;quot;INSERT IGNORE INTO Postcodes (Postcode, Latitude, Longitude) &amp;quot; &lt;/span&gt;+
                  &lt;span style="color: #a31515"&gt;&amp;quot;VALUES ('{0}', {1}, {2})&amp;quot;&lt;/span&gt;,
                  postcode, latLng.Latitude, latLng.Longitude);
                &lt;span style="color: blue"&gt;int &lt;/span&gt;count = command.ExecuteNonQuery();
                &lt;span style="color: blue"&gt;if &lt;/span&gt;(count &amp;gt; 0)
                  &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;&amp;quot;Added&amp;quot;&lt;/span&gt;);
              }
            }
          }
        }
      }
    }
  }
}&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2554177186181307188?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2554177186181307188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2554177186181307188' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2554177186181307188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2554177186181307188'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/03/updating-code-point-postcode-datataset.html' title='Updating the Code-Point postcode datataset in MySql'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-508271662911501821</id><published>2011-02-22T08:13:00.001Z</published><updated>2011-04-14T22:08:39.149+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>GZipping all content served up by ASP.NET</title><content type='html'>&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; – I now realise this post is kind of pointless, there &lt;em&gt;is&lt;/em&gt; a module for compression of dynamic content, called unsurprisingly DynamicCompressionModule… But the approach described may be useful for someone somewhere…&lt;/p&gt;  &lt;p&gt;I couldn’t find anything that will GZip all the content returned by ASP.NET. There’s a module for compression of static files but nothing for dynamic content. There may be a good reason for this, perhaps the overhead of GZipping content on the fly can kill your server, but since my current project has no static content I thought it would be useful to give it a go. The solution is pretty simple, register the following module in web.config and you’re good to go.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System.IO.Compression;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Web;

&lt;span style="color: blue"&gt;namespace &lt;/span&gt;MyNamespace
{
&lt;span style="color: gray"&gt;  &lt;/span&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;GzipModule &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;IHttpModule
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;public void &lt;/span&gt;Dispose()
    {
      
    }

    &lt;span style="color: blue"&gt;public void &lt;/span&gt;Init(&lt;span style="color: #2b91af"&gt;HttpApplication &lt;/span&gt;context)
    {
      context.BeginRequest += &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;(context_BeginRequest);
    }

    &lt;span style="color: blue"&gt;void &lt;/span&gt;context_BeginRequest(&lt;span style="color: blue"&gt;object &lt;/span&gt;sender, &lt;span style="color: #2b91af"&gt;EventArgs &lt;/span&gt;e)
    {
      &lt;span style="color: #2b91af"&gt;HttpApplication &lt;/span&gt;app = (&lt;span style="color: #2b91af"&gt;HttpApplication&lt;/span&gt;)sender;
      &lt;span style="color: blue"&gt;if &lt;/span&gt;((app.Request.Headers[&lt;span style="color: #a31515"&gt;&amp;quot;Accept-Encoding&amp;quot;&lt;/span&gt;] != &lt;span style="color: blue"&gt;null&lt;/span&gt;) &amp;amp;&amp;amp;
            (app.Request.Headers[&lt;span style="color: #a31515"&gt;&amp;quot;Accept-Encoding&amp;quot;&lt;/span&gt;].Contains(&lt;span style="color: #a31515"&gt;&amp;quot;gzip&amp;quot;&lt;/span&gt;)))
      {
        app.Response.Filter = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;GZipStream&lt;/span&gt;(app.Response.Filter, &lt;span style="color: #2b91af"&gt;CompressionMode&lt;/span&gt;.Compress);
        app.Response.AppendHeader(&lt;span style="color: #a31515"&gt;&amp;quot;Content-encoding&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;gzip&amp;quot;&lt;/span&gt;);
        app.Response.Cache.VaryByHeaders[&lt;span style="color: #a31515"&gt;&amp;quot;Accept-encoding&amp;quot;&lt;/span&gt;] = &lt;span style="color: blue"&gt;true&lt;/span&gt;;
      }
    }
  }
}&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;font face="Verdana"&gt;Registration is as follows&lt;/font&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;modules&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
      &lt;/span&gt;&lt;span style="color: blue"&gt;      &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;add &lt;/span&gt;&lt;span style="color: red"&gt;name&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;GzipModule&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;MyNamespace.GzipModule&lt;/span&gt;&amp;quot; &lt;span style="color: blue"&gt;/&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;modules&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-508271662911501821?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/508271662911501821/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=508271662911501821' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/508271662911501821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/508271662911501821'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/02/gzipping-all-content-served-up-by.html' title='GZipping all content served up by ASP.NET'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-280874657557414070</id><published>2011-02-05T21:34:00.001Z</published><updated>2011-02-05T21:34:10.136Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>Fixing 404 errors when using ASP.NET 4 routing</title><content type='html'>&lt;p&gt;It took me a while to figure this out. Routing is meant to be baked into ASP.NET 4 but when i tried to set it up, all I got was 404 errors. I did a lot of Googling but couldn’t find anything. It turned out all I was missing was this in web.config&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;system.webServer&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;modules &lt;/span&gt;&lt;span style="color: red"&gt;runAllManagedModulesForAllRequests&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;true&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;modules&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-280874657557414070?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/280874657557414070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=280874657557414070' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/280874657557414070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/280874657557414070'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/02/fixing-404-errors-when-using-aspnet-4.html' title='Fixing 404 errors when using ASP.NET 4 routing'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6591957051103817097</id><published>2011-02-03T20:43:00.001Z</published><updated>2011-02-03T20:43:12.325Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySql'/><category scheme='http://www.blogger.com/atom/ns#' term='OS OpenData'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Northern Ireland postcode data</title><content type='html'>&lt;p&gt;The &lt;a href="http://data.gov.uk/dataset/os-code-point-open"&gt;OS Code-Point Open dataset&lt;/a&gt; is great, except for a few omissions. It doesn’t include data for Northern Ireland, the Isle of Man or the Channel Islands. It turns out that the &lt;a href="http://www.nisra.gov.uk/geography/default.asp6.htm"&gt;Northern Irish postcode data can be found here&lt;/a&gt;. Unfortunately that data is in ESRI and MapInfo formats, which I’m not sure how to read. Fortunately &lt;a href="http://jamiethompson.co.uk/web/2010/05/30/code-point-open-northern-ireland-addendum/"&gt;Jamie Thompson has converted it to CSV&lt;/a&gt;, which is a little easier to deal with. &lt;/p&gt;  &lt;p&gt;From that CSV file, it’s quite simple to import the data into SQL Server or MySql using code slightly modified from my Code-Point examples (&lt;a href="http://doogalbellend.blogspot.com/2010/06/importing-code-point-dataset-into-sql.html"&gt;SQL Server here&lt;/a&gt; and &lt;a href="http://doogalbellend.blogspot.com/2010/06/importing-code-point-dataset-into-mysql.html"&gt;MySql here&lt;/a&gt;). The only thing to note is that the CSV file uses Irish grid references rather than OS grid references.&lt;/p&gt;  &lt;p&gt;Now to figure out where to get hold of the Isle of Man and Channel Islands data…&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6591957051103817097?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6591957051103817097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6591957051103817097' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6591957051103817097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6591957051103817097'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/02/northern-ireland-postcode-data.html' title='Northern Ireland postcode data'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8654225258912611000</id><published>2011-01-31T22:12:00.001Z</published><updated>2011-02-01T09:40:49.477Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Don’t believe everything that Reflector tells you</title><content type='html'>&lt;p&gt;Every .NET developer loves Reflector, since it gives us a chance to see inside assemblies that we don’t have the source for. And I’ve even read bloggers showing off the code that has been reverse engineered by it as evidence of poor coding practices at some organisation or another &lt;em&gt;(“look, these guys use gotos!”&lt;/em&gt;). But though Reflector is a brilliant tool, its reverse engineering skills are not perfect. See this fairly innocent looking switch statement from some code I’m working on&amp;#160; &lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;        switch&lt;/span&gt;(type)
        {
          &lt;span style="color: blue"&gt;case &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;gateway&amp;quot;&lt;/span&gt;:
            SetValue(component, &lt;span style="color: #a31515"&gt;&amp;quot;@type&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;decision&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;);
            &lt;span style="color: blue"&gt;string &lt;/span&gt;xml = component.InnerXml;
            xml = xml.Replace(&lt;span style="color: #a31515"&gt;&amp;quot;gateway&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;decision&amp;quot;&lt;/span&gt;);
            component.InnerXml = xml;
            &lt;span style="color: blue"&gt;break&lt;/span&gt;;

          &lt;span style="color: blue"&gt;case &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;deliverable&amp;quot;&lt;/span&gt;:
          &lt;span style="color: blue"&gt;case &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;dataObject&amp;quot;&lt;/span&gt;:
            SetValue(component, &lt;span style="color: #a31515"&gt;&amp;quot;@type&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;document&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;);
            &lt;span style="color: blue"&gt;break&lt;/span&gt;;

          &lt;span style="color: blue"&gt;case &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;annotation&amp;quot;&lt;/span&gt;:
            SetValue(component, &lt;span style="color: #a31515"&gt;&amp;quot;@type&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;note&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;);
            &lt;span style="color: blue"&gt;break&lt;/span&gt;;
        } &lt;/pre&gt;

&lt;p&gt;And this is what Reflector shows from the compiled assembly&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;        if &lt;/span&gt;(CS$4$0001 != &lt;span style="color: blue"&gt;null&lt;/span&gt;)
        {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(!(CS$4$0001 == &lt;span style="color: #a31515"&gt;&amp;quot;gateway&amp;quot;&lt;/span&gt;))
            {
                &lt;span style="color: blue"&gt;if &lt;/span&gt;((CS$4$0001 == &lt;span style="color: #a31515"&gt;&amp;quot;deliverable&amp;quot;&lt;/span&gt;) || (CS$4$0001 == &lt;span style="color: #a31515"&gt;&amp;quot;dataObject&amp;quot;&lt;/span&gt;))
                {
                    &lt;span style="color: blue"&gt;goto &lt;/span&gt;Label_00B0;
                }
                &lt;span style="color: blue"&gt;if &lt;/span&gt;(CS$4$0001 == &lt;span style="color: #a31515"&gt;&amp;quot;annotation&amp;quot;&lt;/span&gt;)
                {
                    &lt;span style="color: blue"&gt;goto &lt;/span&gt;Label_00C5;
                }
            }
            &lt;span style="color: blue"&gt;else
            &lt;/span&gt;{
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.SetValue(component, &lt;span style="color: #a31515"&gt;&amp;quot;@type&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;decision&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;);
                &lt;span style="color: blue"&gt;string &lt;/span&gt;xml = component.InnerXml.Replace(&lt;span style="color: #a31515"&gt;&amp;quot;gateway&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;decision&amp;quot;&lt;/span&gt;);
                component.InnerXml = xml;
            }
        }
        &lt;span style="color: blue"&gt;goto &lt;/span&gt;Label_00DA;
    Label_00B0:
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.SetValue(component, &lt;span style="color: #a31515"&gt;&amp;quot;@type&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;document&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;);
        &lt;span style="color: blue"&gt;goto &lt;/span&gt;Label_00DA;
    Label_00C5:
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.SetValue(component, &lt;span style="color: #a31515"&gt;&amp;quot;@type&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;note&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;);
    Label_00DA:;&lt;/pre&gt;

&lt;p&gt;Which I think proves my point…&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8654225258912611000?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8654225258912611000/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8654225258912611000' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8654225258912611000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8654225258912611000'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/01/dont-believe-everything-that-reflector.html' title='Don’t believe everything that Reflector tells you'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-5230016866805262351</id><published>2011-01-13T21:59:00.001Z</published><updated>2011-01-13T21:59:04.768Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Better debugging of .NET services</title><content type='html'>&lt;p&gt;For a long while I’ve been &lt;a href="http://msdn.microsoft.com/en-us/library/aa984342(v=vs.71).aspx"&gt;debugging a .NET service using the recommended approach&lt;/a&gt;. Whilst this works, it’s kind of painful. the steps are something like this&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Build the service&lt;/li&gt;    &lt;li&gt;Realise the service was already running. Stop it from the Services Control Panel applet.&lt;/li&gt;    &lt;li&gt;Build the service again&lt;/li&gt;    &lt;li&gt;Start the service from the Services Control Panel applet.&lt;/li&gt;    &lt;li&gt;Attach to the process from Visual Studio&lt;/li&gt;    &lt;li&gt;Realise the service has already executed the piece of code I wanted to debug.&lt;/li&gt;    &lt;li&gt;Goto step 1.&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;There had to be a better way. &lt;a href="http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx"&gt;And a bit of Googling brought up this approach&lt;/a&gt;. But I didn’t really understand how it worked. I guess I’d assumed the error shown in Visual Studio when you try to debug a service was actually coming from Visual Studio, but I now realise the error is coming from .NET. So by having a different piece of code run when in debug mode, the service is treated like any old application. &lt;/p&gt;  &lt;p&gt;My solution is slightly different so I can also test the service starting and stopping. My service implementation has &lt;font face="Courier New"&gt;StartService&lt;/font&gt; and &lt;font face="Courier New"&gt;StopService&lt;/font&gt; public methods, which are called from &lt;font face="Courier New"&gt;OnStart&lt;/font&gt; and &lt;font face="Courier New"&gt;OnStop&lt;/font&gt;. And my &lt;font face="Courier New"&gt;Main&lt;/font&gt; method looks like this.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;    static void &lt;/span&gt;Main()
    {
      &lt;span style="color: blue"&gt;#if &lt;/span&gt;(!DEBUG)
      &lt;span style="color: #2b91af"&gt;ServiceBase&lt;/span&gt;[] ServicesToRun;
      ServicesToRun = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ServiceBase&lt;/span&gt;[] 
            { 
                &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyService&lt;/span&gt;() 
            };
      &lt;span style="color: #2b91af"&gt;ServiceBase&lt;/span&gt;.Run(ServicesToRun);
      &lt;span style="color: blue"&gt;#else
      &lt;/span&gt;&lt;span style="color: gray"&gt;WorkflowService service = new MyService();
      service.StartService();
      service.DoStuff();
      service.StopService();
      &lt;/span&gt;&lt;span style="color: blue"&gt;#endif               
    &lt;/span&gt;}&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-5230016866805262351?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/5230016866805262351/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=5230016866805262351' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5230016866805262351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5230016866805262351'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2011/01/better-debugging-of-net-services.html' title='Better debugging of .NET services'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-322417149545997938</id><published>2010-12-10T22:28:00.001Z</published><updated>2010-12-10T22:28:35.317Z</updated><title type='text'>Onshoring, the new offshoring</title><content type='html'>&lt;p&gt;Us software developers have had to live with offshoring for years. And I can understand why companies do it. If software isn’t your raison d'être then why not outsource it, and if you’re going to outsource it, why not send it offshore where you can get probably the work done cheaper?&lt;/p&gt;  &lt;p&gt;But there are problems with offshoring. There are the time differences and cultural differences that may make it not go as smoothly as hoped. But I’m not one to think there’s any reason why software developed in another country should be any worse than software produced at home. I’ve dealt with many developers, at home and overseas and there are good and bad developers everywhere.&lt;/p&gt;  &lt;p&gt;But now things have changed again. The UK government introduced a scheme, the intra company transfer visa, which is intended to bring in people to the UK to fill skills gaps that can’t be filled by the UK population. &lt;a href="http://www.techeye.net/business/uk-big-business-exploits-visa-loophole-to-decimate-it-jobs"&gt;But big business have found some sweet loopholes in this scheme that means they can pay pretty much minimum wage to their developers whilst claiming back their expenses and not paying NI for them&lt;/a&gt;. I can understand why this is attractive to companies, it saves them money after all, and why should they care about the greater good? I doubt &lt;a href="http://www.americanheritage.com/articles/web/20060105-henry-ford-five-dollar-day-model-t-ford-motor-company-assembly-line-james-couzens-highland-park-detroit-automobiles.shtml"&gt;Henry Ford&lt;/a&gt; would survive very long in the modern world. But I can’t see it’s particularly good for the country as a whole, with lower tax revenue and higher unemployment being the obvious outcome. So what the hell was the government thinking of? I’m quite happy to compete with anybody on a level playing field, but when the playing field has been slanted like this, I’m pretty baffled about what’s going on.&lt;/p&gt;  &lt;p&gt;Yesterday we had students smashing up the streets because they think it’s unfair that they are going to be in massive debt after they complete their degrees. In the past, graduates would be the people going into these relatively low paid jobs. How are they going to react when they get out of university to find there are no jobs available for them, due to this wonderful scheme? &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-322417149545997938?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/322417149545997938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=322417149545997938' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/322417149545997938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/322417149545997938'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/12/onshoring-new-offshoring.html' title='Onshoring, the new offshoring'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6519341482246796402</id><published>2010-11-22T21:29:00.001Z</published><updated>2010-11-22T21:29:58.048Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Analytics'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>Adding a simple website hit counter using the Google Analytics API</title><content type='html'>&lt;p&gt;Last time, I built &lt;a href="http://doogalbellend.blogspot.com/2010/11/simple-google-analytics-net-api-usage.html"&gt;a simple app to get the number of visitors to a site using the Google Analytics API&lt;/a&gt;. So the obvious next step is to get that data displayed on a web page. The simplest way to do this would be to add some server-side code to an ASP.NET page. But if you’ve run the code in the previous blog entry, you’ll notice it’s pretty slow, generally taking over a second to get the data back from Google. Adding a second to every page load isn’t such a good idea, so this is my slightly more complicated solution, which loads up the hit count via AJAX.&lt;/p&gt;  &lt;p&gt;First we need a generic handler, looking something like this&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="background: yellow"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;@ &lt;/span&gt;&lt;span style="color: maroon"&gt;WebHandler &lt;/span&gt;&lt;span style="color: red"&gt;Language&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;C#&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;Class&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;visitorCounter&amp;quot; &lt;/span&gt;&lt;span style="background: yellow"&gt;%&amp;gt;

&lt;/span&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Web;
&lt;span style="color: blue"&gt;using &lt;/span&gt;Google.GData.Analytics;

&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;visitorCounter &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;IHttpHandler 
&lt;/span&gt;{
  &lt;span style="color: blue"&gt;public void &lt;/span&gt;ProcessRequest (&lt;span style="color: #2b91af"&gt;HttpContext &lt;/span&gt;context) 
  {
    &lt;span style="color: #2b91af"&gt;AccountQuery &lt;/span&gt;feedQuery = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AccountQuery&lt;/span&gt;();
    &lt;span style="color: #2b91af"&gt;AnalyticsService &lt;/span&gt;service = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AnalyticsService&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;DoogalAnalytics&amp;quot;&lt;/span&gt;);
    service.setUserCredentials(&lt;span style="color: #a31515"&gt;&amp;quot;email&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;);
    &lt;span style="color: #2b91af"&gt;DataQuery &lt;/span&gt;pageViewQuery = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataQuery&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;https://www.google.com/analytics/feeds/data&amp;quot;&lt;/span&gt;);
    pageViewQuery.Ids = &lt;span style="color: #a31515"&gt;&amp;quot;ga:103173&amp;quot;&lt;/span&gt;;
    pageViewQuery.Metrics = &lt;span style="color: #a31515"&gt;&amp;quot;ga:visits&amp;quot;&lt;/span&gt;;
    pageViewQuery.GAStartDate = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now.AddMonths(-1).ToString(&lt;span style="color: #a31515"&gt;&amp;quot;yyyy-MM-dd&amp;quot;&lt;/span&gt;);
    pageViewQuery.GAEndDate = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now.ToString(&lt;span style="color: #a31515"&gt;&amp;quot;yyyy-MM-dd&amp;quot;&lt;/span&gt;);
    &lt;span style="color: #2b91af"&gt;DataEntry &lt;/span&gt;pvEntry = service.Query(pageViewQuery).Entries[0] &lt;span style="color: blue"&gt;as &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataEntry&lt;/span&gt;;

    context.Response.ContentType = &lt;span style="color: #a31515"&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;;
    context.Response.Write(pvEntry.Metrics[0].Value);
  }
 
  &lt;span style="color: blue"&gt;public bool &lt;/span&gt;IsReusable {
    &lt;span style="color: blue"&gt;get &lt;/span&gt;{
       &lt;span style="color: blue"&gt;return false&lt;/span&gt;;
    }
  }
}&lt;/pre&gt;

&lt;p&gt;Then we just need a bit of jQuery magic to show it on the page.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;p&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
      &lt;/span&gt;Visitors this month: &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;visitorCount&amp;quot;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
      &amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text/javascript&amp;quot;&amp;gt;
        &lt;/span&gt;$(document).ready(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
          $.get(&lt;span style="color: maroon"&gt;'visitorCounter.ashx'&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(data) {
            $(&lt;span style="color: maroon"&gt;'#visitorCount'&lt;/span&gt;).html(data);
          });
        });

      &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;script&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;p&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;
&lt;font color="#333333" face="Verdana"&gt;With a little work this could probably be wrapped up as an ASP.NET control, but I’ll leave that as an exercise for the reader.&lt;/font&gt;&lt;/span&gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6519341482246796402?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6519341482246796402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6519341482246796402' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6519341482246796402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6519341482246796402'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/11/adding-simple-website-hit-counter-using.html' title='Adding a simple website hit counter using the Google Analytics API'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2292973536234027998</id><published>2010-11-15T21:47:00.001Z</published><updated>2010-11-15T21:48:05.590Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Analytics'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Simple Google Analytics .NET API usage</title><content type='html'>&lt;p&gt;So if you want to create your own version of &lt;a href="http://www.embeddedanalytics.com/"&gt;Embedded analytics&lt;/a&gt;, the first thing to do is figure out how to use the Google Analytics API. If you’re wanting to use the .NET API, then this little snippet might help you get started. It loops through all the registered sites and shows the number of visits to each site over the last month.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Program
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
    {
      &lt;span style="color: #2b91af"&gt;AccountQuery &lt;/span&gt;feedQuery = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AccountQuery&lt;/span&gt;();
      &lt;span style="color: #2b91af"&gt;AnalyticsService &lt;/span&gt;service = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AnalyticsService&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;DoogalAnalytics&amp;quot;&lt;/span&gt;);
      service.setUserCredentials(&lt;span style="color: #a31515"&gt;&amp;quot;email&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;);
      &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;AccountEntry &lt;/span&gt;entry &lt;span style="color: blue"&gt;in &lt;/span&gt;service.Query(feedQuery).Entries)
      {
        &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(entry.Title.Text);

        &lt;span style="color: #2b91af"&gt;DataQuery &lt;/span&gt;pageViewQuery = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataQuery&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;https://www.google.com/analytics/feeds/data&amp;quot;&lt;/span&gt;);
        pageViewQuery.Ids = entry.ProfileId.Value;
        pageViewQuery.Metrics = &lt;span style="color: #a31515"&gt;&amp;quot;ga:visits&amp;quot;&lt;/span&gt;;
        pageViewQuery.GAStartDate = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now.AddMonths(-1).ToString(&lt;span style="color: #a31515"&gt;&amp;quot;yyyy-MM-dd&amp;quot;&lt;/span&gt;);
        pageViewQuery.GAEndDate = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now.ToString(&lt;span style="color: #a31515"&gt;&amp;quot;yyyy-MM-dd&amp;quot;&lt;/span&gt;);
        &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;DataEntry &lt;/span&gt;pvEntry &lt;span style="color: blue"&gt;in &lt;/span&gt;service.Query(pageViewQuery).Entries)
        {
          &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(pvEntry.Metrics[0].Value);
        }

      }
      &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.ReadLine();
    }
  }&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2292973536234027998?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2292973536234027998/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2292973536234027998' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2292973536234027998'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2292973536234027998'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/11/simple-google-analytics-net-api-usage.html' title='Simple Google Analytics .NET API usage'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4292117882246106765</id><published>2010-11-14T21:21:00.001Z</published><updated>2010-11-14T21:21:42.259Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Google Analytics'/><title type='text'>Embedded Analytics</title><content type='html'>&lt;p&gt;I’ve been working on a website for somebody and they wanted to show some information about the number of visitors to the site on the site itself. Since we already had Google Analytics installed, the obvious solution was something that hooked into that. I knew there was a &lt;a href="http://code.google.com/apis/gdata/"&gt;Google Analytics API&lt;/a&gt; which I could hook into, but being lazy I hoped somebody else had already implemented something for me. &lt;/p&gt;  &lt;p&gt;Luckily for me, there is a &lt;a href="http://www.embeddedanalytics.com/"&gt;company called Embedded Analytics&lt;/a&gt; that do provide this kind of service. There are various free tools available although it does cost money if you want to use it for multiple sites. But the free service was good enough for my current needs. &lt;/p&gt;  &lt;p&gt;Setup was simple, although you do obviously need to let them have access to your Google Analytics data. If you don’t feel comfortable with that then this isn’t the service for you. If you are OK with that, then you can quickly knock together a chart showing the number of visitors or page views in the recent past. Another nice little tool is the ability to get email alerts when somebody has linked to your site.&lt;/p&gt;  &lt;p&gt;And now I think I might want to add this to other sites, but it’s going to start costing money to use on more than one site. When my laziness is confronted by my tight fistedness, my tight fistedness always wins out. So now I have to go back to that API and figure out how to do all this and more myself.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4292117882246106765?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4292117882246106765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4292117882246106765' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4292117882246106765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4292117882246106765'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/11/embedded-analytics.html' title='Embedded Analytics'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6738910176879345192</id><published>2010-11-11T21:43:00.001Z</published><updated>2010-11-11T21:43:25.384Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Creating a valid file name in C#</title><content type='html'>&lt;p&gt;This was new to me so I thought I’d share. If you want to generate a file name based on some text, but that text may contain characters that aren’t allowed in file names, what to do? In the past, I’ve fumbled around, replacing invalid characters as bugs have presented themselves. But there is a much more robust way, once I noticed the &lt;font face="Courier New"&gt;Path.GetInvalidFileNameChars&lt;/font&gt; method. That led to this very simple method.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;    private string &lt;/span&gt;ReplaceInvalidFileNameChars(&lt;span style="color: blue"&gt;string &lt;/span&gt;fileName)
    {
      &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;char &lt;/span&gt;invalidChar &lt;span style="color: blue"&gt;in &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Path&lt;/span&gt;.GetInvalidFileNameChars())
      {
        fileName = fileName.Replace(invalidChar, &lt;span style="color: #a31515"&gt;'_'&lt;/span&gt;);
      }

      &lt;span style="color: blue"&gt;return &lt;/span&gt;fileName;
    }&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6738910176879345192?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6738910176879345192/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6738910176879345192' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6738910176879345192'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6738910176879345192'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/11/creating-valid-file-name-in-c.html' title='Creating a valid file name in C#'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-192410155168425445</id><published>2010-11-07T20:45:00.001Z</published><updated>2010-11-07T20:47:30.342Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Styled maps–Showing urban areas</title><content type='html'>&lt;p&gt;Styled Google Maps but I haven’t found a use for them up until now. But I had a desire to view urban areas in the UK and realised I could do that with styled maps, so here it is.&lt;/p&gt; &lt;div style="width: 600px; height: 600px; text-align:center;" id="map"&gt;   &lt;span style="color:Gray;"&gt;Loading map...&lt;/span&gt; &lt;/div&gt; &lt;script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"&gt;&lt;/script&gt; &lt;script type="text/javascript"&gt;	
	var urbanStyle = [
  	{
    		featureType: "landscape.man_made",
    		elementType: "all",
    		stylers: [
      		{ hue: "#ff1a00" },
      		{ saturation: 80 },
      		{ lightness: -41 }
    		]
  		},{
    		featureType: "road",
    		elementType: "all",
    		stylers: [
      		{ visibility: "off" }
    		]
  		}
	];
        var latlng = new google.maps.LatLng(54.559322587438636, -4.1748046875);
        var options = {
          zoom: 6,
          center: latlng,
          mapTypeControlOptions: { mapTypeIds: [] }
        };
 
        var map = new google.maps.Map(document.getElementById("map"), options);
	var styledMapOptions = { name: "NoRoads" };
      	var noRoadsMapType = new google.maps.StyledMapType(urbanStyle, styledMapOptions);
      	map.mapTypes.set('NoRoads', noRoadsMapType);
      	map.setMapTypeId('NoRoads');
&lt;/script&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-192410155168425445?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/192410155168425445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=192410155168425445' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/192410155168425445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/192410155168425445'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/11/styled-mapsshowing-urban-areas.html' title='Styled maps–Showing urban areas'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-276981752178901702</id><published>2010-10-12T20:17:00.001+01:00</published><updated>2010-10-12T20:17:42.951+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Docs'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Uploading files and folders to a specific Google Docs folder using the .NET API</title><content type='html'>&lt;p&gt;The .NET API for Google Docs is a strange beast. First, it has a &lt;font face="Courier New"&gt;DocumentsRequest&lt;/font&gt; class and a &lt;font face="Courier New"&gt;DocumentsService&lt;/font&gt; class. Sometimes you’ll need to use one class, sometimes the other and it’s not clear what the distinction is between the two. And I’d assumed the .NET API supported everything you could do with the raw HTTP API and hence I thought uploading a file or folder to a specific folder wasn’t possible. This led to further confusion when I tried to move a folder and it did move but left the original folder behind and I couldn’t figure out how to delete that folder. And as with most things from Google, the documentation is somewhat lacking.&lt;/p&gt;  &lt;p&gt;So I eventually figured out I’d have to do a little work myself and came up with a couple of extension methods, one to create a folder and one to upload a file, but both accepting a parent folder.&amp;#160; When I say I needed to do a little work, what I really mean is I used Reflector to see how the current methods worked and employed some copy and paste…&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  public static class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;GoogleDocsExtensions
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Document &lt;/span&gt;CreateFolder(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DocumentsRequest &lt;/span&gt;docsRequest, &lt;span style="color: blue"&gt;string &lt;/span&gt;folderName, &lt;span style="color: #2b91af"&gt;Document &lt;/span&gt;parentFolder)
    {
      &lt;span style="color: #2b91af"&gt;Document &lt;/span&gt;doc = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Document&lt;/span&gt;();
      doc.Type = Google.Documents.&lt;span style="color: #2b91af"&gt;Document&lt;/span&gt;.&lt;span style="color: #2b91af"&gt;DocumentType&lt;/span&gt;.Folder;
      doc.Title = folderName;
      &lt;span style="color: blue"&gt;if &lt;/span&gt;(parentFolder == &lt;span style="color: blue"&gt;null&lt;/span&gt;)
        &lt;span style="color: blue"&gt;return &lt;/span&gt;docsRequest.Insert&amp;lt;&lt;span style="color: #2b91af"&gt;Document&lt;/span&gt;&amp;gt;(&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;DocumentsListQuery&lt;/span&gt;.documentsBaseUri), doc);
      &lt;span style="color: blue"&gt;else
      &lt;/span&gt;{
        &lt;span style="color: blue"&gt;return &lt;/span&gt;docsRequest.Insert&amp;lt;&lt;span style="color: #2b91af"&gt;Document&lt;/span&gt;&amp;gt;(&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;(parentFolder.AtomEntry.Content.AbsoluteUri), doc);
      }
    }

    &lt;span style="color: blue"&gt;public static void &lt;/span&gt;UploadFile(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DocumentsService &lt;/span&gt;docsService, &lt;span style="color: blue"&gt;string &lt;/span&gt;file, &lt;span style="color: #2b91af"&gt;Document &lt;/span&gt;folder)
    {
      &lt;span style="color: #2b91af"&gt;FileInfo &lt;/span&gt;info = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;FileInfo&lt;/span&gt;(file);
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;FileStream &lt;/span&gt;input = info.Open(&lt;span style="color: #2b91af"&gt;FileMode&lt;/span&gt;.Open, &lt;span style="color: #2b91af"&gt;FileAccess&lt;/span&gt;.Read, &lt;span style="color: #2b91af"&gt;FileShare&lt;/span&gt;.ReadWrite))
      {
        &lt;span style="color: #2b91af"&gt;Uri &lt;/span&gt;uri = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;(folder.AtomEntry.Content.AbsoluteUri);

        &lt;span style="color: blue"&gt;string &lt;/span&gt;str = info.Extension.ToUpper().Substring(1);
        &lt;span style="color: blue"&gt;string &lt;/span&gt;contentType = (&lt;span style="color: blue"&gt;string&lt;/span&gt;)&lt;span style="color: #2b91af"&gt;DocumentsService&lt;/span&gt;.DocumentTypes[str];
        docsService.Insert(uri, input, contentType, info.Name);
      }
    }
  }&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-276981752178901702?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/276981752178901702/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=276981752178901702' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/276981752178901702'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/276981752178901702'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/10/uploading-files-and-folders-to-specific.html' title='Uploading files and folders to a specific Google Docs folder using the .NET API'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3493116803333870608</id><published>2010-10-10T12:18:00.001+01:00</published><updated>2010-10-10T16:50:00.725+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows 7'/><title type='text'>Installing PHP 5.2 on Windows 7 IIS 7.5</title><content type='html'>&lt;p&gt;I had a bit of fun upgrading my Windows 7 machine to PHP 5.2 the other day. There’s &lt;a href="http://php.iis.net/"&gt;an installer available&lt;/a&gt; that goes most of the way but didn’t quite work for me (there’s also an installer available on &lt;a href="http://www.php.net"&gt;www.php.net&lt;/a&gt;, I’m not sure how they differ).&lt;/p&gt;  &lt;p&gt;The first problem was that my installation of IIS didn’t have the FastCGI module installed. This was pretty easy to rectify, in the IIS administrator go to Modules and then click the ‘Configure Native Modules’ link and then click the ‘Register’ button. The FastCGI module will live somewhere like ‘C:\Windows\System32\inetsrv\iisfcgi.dll’. Then I manually added a handler for PHP files, although if you add the module with the correct ‘FastCgiModule’ name, a re-install should correctly add the handler.&lt;/p&gt;  &lt;p&gt;After this was set up, I was able to load up PHP pages but they weren’t being rendered properly, because it looked like the PHP code wasn’t being evaluated and was just getting outputted as plain text. This confused me for a while and I thought the installer hadn’t done its job. But after firing up Fiddler, I saw this header in the response from the server – &lt;font face="Courier New"&gt;X-Powered-By: PHP/5.2.14&lt;/font&gt;, which suggested the file was getting handed to PHP which was doing &lt;em&gt;something&lt;/em&gt; with it. So I guessed there must be a problem somewhere in the PHP config file, php.ini. After searching around in there, I discovered this setting - &lt;font face="Courier New"&gt;short_open_tag = Off&lt;/font&gt;. This means any PHP files that use &lt;font face="Courier New"&gt;&amp;lt;?&lt;/font&gt; as their opening tag instead of &lt;font face="Courier New"&gt;&amp;lt;?php&lt;/font&gt; will not get evaluated, and turning that option on fixed my problems. One other thing to note is that an IIS reset seems to be required for any changes to that config file, since when using FastCGI the PHP executable stays resident in memory rather than restarting for every request.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3493116803333870608?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3493116803333870608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3493116803333870608' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3493116803333870608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3493116803333870608'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/10/installing-php-52-on-windows-7-iis-75.html' title='Installing PHP 5.2 on Windows 7 IIS 7.5'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3082651675864473357</id><published>2010-10-05T08:37:00.001+01:00</published><updated>2011-11-17T20:23:09.225Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>WinForms ProgressBar with text</title><content type='html'>&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_I7johDbDcO8/TKt3Hhv2ABI/AAAAAAAAAHI/TynYNgLaG1w/s1600-h/ProgressBar%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px 10px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Progress bar with text" border="0" alt="Progress bar with text" align="left" src="http://lh6.ggpht.com/_I7johDbDcO8/TKt3IX6hopI/AAAAAAAAAHM/6-n2MfjwytQ/ProgressBar_thumb.png?imgmax=800" width="244" height="48" /&gt;&lt;/a&gt;Somebody asked how to use my &lt;a href="http://www.doogal.co.uk/transparent.php"&gt;transparent label&lt;/a&gt; on top of a &lt;font face="Courier New"&gt;ProgressBar&lt;/font&gt; control, since it disappeared when the progress bar changed its state. I had a look at it, but the transparent label wasn’t getting any notification of changes, even when I hooked into the &lt;font face="Courier New"&gt;WndProc&lt;/font&gt; method. So I wondered if it would be possible to sub-class the &lt;font face="Courier New"&gt;ProgressBar&lt;/font&gt; control and write some text on top of it that way. And that seems to work OK, as shown below.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; – I’ve fixed the flickering&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Update 2&lt;/strong&gt; – It now picks up the text colour from the &lt;font face="Courier New"&gt;ForeColor&lt;/font&gt; property &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Update 3&lt;/strong&gt; – Should now work on Windows XP&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.ComponentModel;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Drawing;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Windows.Forms;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System;

&lt;span style="color: blue"&gt;namespace &lt;/span&gt;WinFormControls
{
 
  &lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TextProgressBar &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;ProgressBar
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;protected override &lt;/span&gt;&lt;span style="color: #2b91af"&gt;CreateParams &lt;/span&gt;CreateParams
    {
      &lt;span style="color: blue"&gt;get
      &lt;/span&gt;{
        &lt;span style="color: #2b91af"&gt;CreateParams &lt;/span&gt;result = &lt;span style="color: blue"&gt;base&lt;/span&gt;.CreateParams;
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Environment&lt;/span&gt;.OSVersion.Platform == &lt;span style="color: #2b91af"&gt;PlatformID&lt;/span&gt;.Win32NT
            &amp;amp;&amp;amp; &lt;span style="color: #2b91af"&gt;Environment&lt;/span&gt;.OSVersion.Version.Major &amp;gt;= 6)
        {
          result.ExStyle |= 0x02000000; &lt;span style="color: green"&gt;// WS_EX_COMPOSITED 
        &lt;/span&gt;}

        &lt;span style="color: blue"&gt;return &lt;/span&gt;result;
      }
    }

    &lt;span style="color: blue"&gt;protected override void &lt;/span&gt;WndProc(&lt;span style="color: blue"&gt;ref &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Message &lt;/span&gt;m)
    {
      &lt;span style="color: blue"&gt;base&lt;/span&gt;.WndProc(&lt;span style="color: blue"&gt;ref &lt;/span&gt;m);
      &lt;span style="color: blue"&gt;if &lt;/span&gt;(m.Msg == 0x000F)
      {
        &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Graphics &lt;/span&gt;graphics = CreateGraphics())
        &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SolidBrush &lt;/span&gt;brush = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SolidBrush&lt;/span&gt;(ForeColor))
        {
          &lt;span style="color: #2b91af"&gt;SizeF &lt;/span&gt;textSize = graphics.MeasureString(Text, &lt;span style="color: #2b91af"&gt;SystemFonts&lt;/span&gt;.DefaultFont);
          graphics.DrawString(Text, &lt;span style="color: #2b91af"&gt;SystemFonts&lt;/span&gt;.DefaultFont, brush, (Width - textSize.Width) / 2, (Height - textSize.Height) / 2);
        }
      }
    }

    [&lt;span style="color: #2b91af"&gt;EditorBrowsable&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;EditorBrowsableState&lt;/span&gt;.Always)]
    [&lt;span style="color: #2b91af"&gt;Browsable&lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;)]
    &lt;span style="color: blue"&gt;public override string &lt;/span&gt;Text
    {
      &lt;span style="color: blue"&gt;get
      &lt;/span&gt;{
        &lt;span style="color: blue"&gt;return base&lt;/span&gt;.Text;
      }
      &lt;span style="color: blue"&gt;set
      &lt;/span&gt;{
        &lt;span style="color: blue"&gt;base&lt;/span&gt;.Text = &lt;span style="color: blue"&gt;value&lt;/span&gt;;
        Refresh();
      }
    }
  }
}&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3082651675864473357?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3082651675864473357/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3082651675864473357' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3082651675864473357'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3082651675864473357'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/10/winforms-progressbar-with-text.html' title='WinForms ProgressBar with text'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_I7johDbDcO8/TKt3IX6hopI/AAAAAAAAAHM/6-n2MfjwytQ/s72-c/ProgressBar_thumb.png?imgmax=800' height='72' width='72'/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2298939091678911258</id><published>2010-10-03T17:12:00.001+01:00</published><updated>2010-10-03T17:12:27.635+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><title type='text'>The problems with financial theories</title><content type='html'>&lt;p&gt;We’re all looking for people to blame for the current mess we are in and apparently there are &lt;a href="http://www.bbc.co.uk/blogs/thereporters/robertpeston/2010/09/cause_39_of_the_banking_crisis.html"&gt;38 causes of the current financial crisis&lt;/a&gt;. I’m not sure where to get hold of the original report so I’m not sure of what’s on the list but it made me think again about what I learned during my brief time working in the world of high finance a few years ago. I was there for a couple of years, so I'm clearly not hugely knowledgeable but the assumptions made when concocting the financial models used by many investors in the city always seemed a little fishy to me. Here are some of the main assumptions made and the problems I think are associated with them. Almost every financial theory will use one or more of these assumptions.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;The market is efficient&lt;/strong&gt; – &lt;em&gt;All information about an asset is known and priced into the cost of that asset.&lt;/em&gt; Peston says this is one of the 38 causes. There are several issues with this theory, my favourite being this paradox – if the market is efficient then there is very little point in trying to play the market, since the market will always be priced accurately so there will be no opportunity to beat it by buying low, selling high. But if there are no players in the market, then it won’t be efficient. Which suggests it can never be perfectly efficient. &lt;/p&gt;  &lt;p&gt;Another problem with the efficient market theory can be seen with something like the dot com bubble, where prices bore no relation to their underlying value, how is that sensible? The fact is share prices often bear no relation to the true worth of the company, because people think their value will continue to grow so they will be able to sell them on to a greater fool. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Investors are rational&lt;/strong&gt; - &lt;em&gt;Given two different assets with the same risk, investors will choose the one with the highest return&lt;/em&gt;. Clearly this isn't the case, otherwise bubbles would never happen. Another problem is that even a rational investor may not know the true risk/reward relationship. Shares may fall in value, making their dividend yield look very attractive, but there may be a good reason for this, like the economy is hitting trouble. Finally, some investors are just taking a punt, with no rational thought behind their trading. &lt;/p&gt;  &lt;p&gt;Finally, if we decide that not all investors are rational, then we have another reason why the market will not be efficient.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;There are no transaction charges&lt;/strong&gt; – This may be nearly true for large institutional investors but for the man on the street this is very much not true, meaning any kind of investment strategy that involves a lot of trading will incur huge transaction costs, which will eat away at any profits made.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;The market is liquid&lt;/strong&gt; - This assumption basically says if you want to buy or sell, then you can. Whilst this holds true for most shares most of the time, it falls over at the most vital point, when markets are crashing, because everybody is trying to sell and there is nobody willing to buy. It is also not true for quite a few asset classes, as anyone who's tried to sell a house will know.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Normal distribution – &lt;/strong&gt;A lot of financial theories use the normal distribution for the distribution of volatility/risk. Unfortunately this doesn’t seem to be the case. The black swans seem to occur a lot more frequently than a normal distribution would suggest. For the banks out there, this doesn’t seem to be such a problem, since they take the profits during the good times and get bailed out when those once in a lifetime occurrences strike a little too often.&lt;/p&gt;  &lt;p&gt;And it’s the combination of all these flawed assumptions that caused at least some of the problems. They were used to build mathematical models that seemingly abolished risk or certainly incorrectly measured it. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2298939091678911258?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2298939091678911258/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2298939091678911258' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2298939091678911258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2298939091678911258'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/10/problems-with-financial-theories.html' title='The problems with financial theories'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7713453041903876970</id><published>2010-09-29T20:50:00.001+01:00</published><updated>2010-09-29T20:50:43.557+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IE9'/><title type='text'>IE9 hardware acceleration not working</title><content type='html'>&lt;p&gt;I downloaded the IE9 beta a week or so ago. Though it looked pretty good, the performance was less than stellar. When I looked at the options dialog, I saw that ‘Use software rendering instead of GPU rendering’ was checked and also disabled, which made unchecking it quite difficult. Apparently it will be disabled if Internet Explorer decides your graphics card isn’t good enough. I’m not exactly sure what the minimum requirements are, although I have heard rumours that the card must support DirectX 11. &lt;/p&gt;  &lt;p&gt;Anyway it seems my old Radeon X600 wasn’t up to the job so I purchased a Radeon HD5450. I have no idea if it’s a good graphics card, but it’s pretty cheap and it does support DirectX 11. After installing that, IE9 was still pretty slow. Until I remembered to uncheck that checkbox…&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7713453041903876970?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7713453041903876970/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7713453041903876970' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7713453041903876970'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7713453041903876970'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/09/ie9-hardware-acceleration-not-working.html' title='IE9 hardware acceleration not working'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3706567049990999248</id><published>2010-09-09T21:08:00.001+01:00</published><updated>2010-09-12T20:34:00.102+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Structured storage viewer</title><content type='html'>&lt;p&gt;Before the advent of .NET, Windows development involved a lot of fiddling around with COM interfaces and trying to figure out HRESULT error codes. One prime example of this is the interfaces used to access structure storage. Structured storage is a strange beast itself, providing a way to stitch together composite files, sort of like a seriously complicated version of ZIP files. Of course this was a solution in search of a problem, it turns out ZIP files are pretty much adequate for most situations, which is probably why Office now uses them.&lt;/p&gt;  &lt;p&gt;But these technologies have a habit of sticking around (I guess Windows will continue supporting structured storage for the rest of time or until Windows no longer exists, whichever comes sooner). So I was faced with writing some code that had to interact with structured storage. .NET wisely doesn’t have any support for it, so I was faced with dealing with the really horrible COM interfaces. And I was even thinking I’d have to write a little application to let me see inside these composite files, so I’d have a clue what was going on. But fortunately I discovered somebody had already done the job for me, and I finally get to the point of this post. If you need a structured storage viewer, download it from here. &lt;a title="http://www.mitec.cz/ssv.html" href="http://www.mitec.cz/ssv.html"&gt;http://www.mitec.cz/ssv.html&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3706567049990999248?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3706567049990999248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3706567049990999248' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3706567049990999248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3706567049990999248'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/09/structure-storage-viewer.html' title='Structured storage viewer'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4576400907985284669</id><published>2010-08-22T17:57:00.001+01:00</published><updated>2010-08-22T17:57:27.921+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Converting a Cursor to a Bitmap in .NET</title><content type='html'>&lt;p&gt;I’ve been playing around with a little tool I’ve been developing and needed to display a &lt;font face="Courier New"&gt;Cursor&lt;/font&gt; in a &lt;font face="Courier New"&gt;PictureBox&lt;/font&gt;. There doesn’t seem to be built-in way to convert a &lt;font face="Courier New"&gt;Cursor&lt;/font&gt; to a &lt;font face="Courier New"&gt;Bitmap&lt;/font&gt;, which kind of makes sense since it might be an animated cursor so it wouldn’t be sensible to convert it to a Bitmap. I couldn’t find any examples on the web, so thought I’d post this code that does the trick.&lt;/p&gt;  &lt;pre class="code"&gt;          System.Windows.Forms.&lt;span style="color: #2b91af"&gt;Cursor &lt;/span&gt;cursor = &lt;span style="color: blue"&gt;new &lt;/span&gt;System.Windows.Forms.&lt;span style="color: #2b91af"&gt;Cursor&lt;/span&gt;(stream);
          &lt;span style="color: #2b91af"&gt;Bitmap &lt;/span&gt;bitmap = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Bitmap&lt;/span&gt;(cursor.Size.Width, cursor.Size.Height);
          &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Graphics &lt;/span&gt;gBmp = &lt;span style="color: #2b91af"&gt;Graphics&lt;/span&gt;.FromImage(bitmap))
          {
            cursor.Draw(gBmp, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Rectangle&lt;/span&gt;(0, 0, cursor.Size.Width, cursor.Size.Height));
            pictureBox.Image = bitmap;
          }&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4576400907985284669?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4576400907985284669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4576400907985284669' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4576400907985284669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4576400907985284669'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/08/converting-cursor-to-bitmap-in-net.html' title='Converting a Cursor to a Bitmap in .NET'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-5399523068211803801</id><published>2010-08-12T20:13:00.001+01:00</published><updated>2010-08-12T20:13:45.302+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>Storing and restoring the clipboard contents</title><content type='html'>&lt;p&gt;&lt;/p&gt; &lt;p&gt;My &lt;a href="http://doogalbellend.blogspot.com/2010/07/quick-hack-to-convert-html-to-word.html"&gt;hacky method to get HTML into Word programmatically&lt;/a&gt; had a slight flaw in that it splatted anything the user had placed on the clipboard. I consider it to be bad manners to mess with the clipboard because it’s the user’s clipboard, not my application’s. Many years ago I was forced to use Lotus Notes, which used the clipboard whenever I replied to an email, so I can’t really write code that does something that has annoyed me in the past.&lt;/p&gt; &lt;p&gt;So I had to figure out how to fix it. The below did the trick, although I haven’t tested it with all clipboard formats so it may not work with all of them. And if you’re wondering why this is in VB.NET and the previous example was in C#, I was forced to convert all the code to VB.NET, much to my chagrin. &lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: green"&gt;      ' store current contents of clipboard
      &lt;/span&gt;&lt;span style="color: blue"&gt;Dim &lt;/span&gt;currentData &lt;span style="color: blue"&gt;As &lt;/span&gt;IDataObject = Clipboard.GetDataObject()
      &lt;span style="color: blue"&gt;Dim &lt;/span&gt;formats &lt;span style="color: blue"&gt;As String&lt;/span&gt;() = currentData.GetFormats()
      &lt;span style="color: blue"&gt;Dim &lt;/span&gt;currentClipboard &lt;span style="color: blue"&gt;As &lt;/span&gt;Dictionary(&lt;span style="color: blue"&gt;Of String&lt;/span&gt;, &lt;span style="color: blue"&gt;Object&lt;/span&gt;) = &lt;span style="color: blue"&gt;New &lt;/span&gt;Dictionary(&lt;span style="color: blue"&gt;Of String&lt;/span&gt;, &lt;span style="color: blue"&gt;Object&lt;/span&gt;)
      &lt;span style="color: blue"&gt;For Each &lt;/span&gt;Format &lt;span style="color: blue"&gt;As String In &lt;/span&gt;formats
        currentClipboard.Add(Format, currentData.GetData(Format))
      &lt;span style="color: blue"&gt;Next

      &lt;/span&gt;&lt;span style="color: green"&gt;' do stuff...

      ' put back old clipboard contents
      &lt;/span&gt;&lt;span style="color: blue"&gt;Dim &lt;/span&gt;newData &lt;span style="color: blue"&gt;As &lt;/span&gt;DataObject = &lt;span style="color: blue"&gt;New &lt;/span&gt;DataObject()
      &lt;span style="color: blue"&gt;For Each &lt;/span&gt;savedFormat &lt;span style="color: blue"&gt;As &lt;/span&gt;KeyValuePair(&lt;span style="color: blue"&gt;Of String&lt;/span&gt;, &lt;span style="color: blue"&gt;Object&lt;/span&gt;) &lt;span style="color: blue"&gt;In &lt;/span&gt;currentClipboard
        newData.SetData(savedFormat.Key, savedFormat.Value)
      &lt;span style="color: blue"&gt;Next
      &lt;/span&gt;Clipboard.SetDataObject(newData)&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;pre class="code"&gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-5399523068211803801?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/5399523068211803801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=5399523068211803801' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5399523068211803801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5399523068211803801'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/08/storing-and-restoring-clipboard.html' title='Storing and restoring the clipboard contents'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8636732741252059566</id><published>2010-08-11T20:39:00.001+01:00</published><updated>2010-08-11T20:39:41.302+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FxCop'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>FxCop 10</title><content type='html'>&lt;p&gt;When I fired up FxCop 1.36 today, the update check, for probably the first time ever, told me there was a new version available. So I went off to get the update. The odd thing was that I wasn’t pointed towards an installer but a text file. That pointed out that FxCop was now part of the Windows SDK, so installing it is now something of a bigger job than it used to be. In fact, installing the SDK doesn’t install FxCop, it just copies the installer to your machine which then has to be run separately.&lt;/p&gt;  &lt;p&gt;But the fact that the version number had increased so much got me excited, thinking there must be lots of improvements… After loading it up and running it against a project, I was a little disappointed. I didn’t spot any new rules and the only difference I noticed was that &lt;a href="http://doogalbellend.blogspot.com/2010/06/fxcop-custom-rules.html"&gt;my custom rules assembly&lt;/a&gt; no longer worked.&lt;/p&gt;  &lt;p&gt;Updating my rules assembly was pretty simple. First remove the references to the 1.36 assemblies (FxcopSdk.dll and Microsoft.Cci.dll), then update the project to target the .NET Framework 4 (of course this means it has to be built in VS 2010), add references to the version 10 assemblies and rebuild. And it all worked first time. My rules are pretty simple so I don’t know if it will be as easy to upgrade more complex rules assemblies but it all looks pretty good for backwards compatibility.&lt;/p&gt;  &lt;p&gt;That said, there doesn’t seem to be any compelling reason to upgrade, unless I’ve missed something.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8636732741252059566?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8636732741252059566/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8636732741252059566' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8636732741252059566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8636732741252059566'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/08/fxcop-10.html' title='FxCop 10'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3532158461271478936</id><published>2010-07-31T22:26:00.001+01:00</published><updated>2010-07-31T22:26:51.800+01:00</updated><title type='text'>Things are a bit quiet round here</title><content type='html'>&lt;p&gt;If you are one of the many subscribers to my blog (where many is a value is slightly larger than zero) you may have noticed things have been a bit quiet round here. There is a reason for this. &lt;a href="http://www.doogal.co.uk/"&gt;My proper website&lt;/a&gt; has suddenly become relatively popular for some reason, so I’ve made the decision to spend my spare time updating that more regularly, since it is actually producing income for me, which in the current climate is definitely a good thing. I’ll still post here, but it may not be so frequent. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3532158461271478936?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3532158461271478936/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3532158461271478936' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3532158461271478936'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3532158461271478936'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/07/things-are-bit-quiet-round-here.html' title='Things are a bit quiet round here'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7856587256154773212</id><published>2010-07-16T22:16:00.001+01:00</published><updated>2010-07-16T22:16:10.367+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Quick hack to convert HTML to Word documents</title><content type='html'>&lt;p&gt;There are lots of products that will convert HTML files to Word format or RTF, but they mostly cost money. And there are so many of them, it’s hard to know which are good without testing them all. Word of course can open HTML files directly but one issue I found with this is if you then save the file as a DOC file any images are not embedded in the file (although they are in DOCX files). &lt;/p&gt;  &lt;p&gt;But being the cheapskate I am, I thought there must be a way to implement this myself, so came up with this quick hack, which loads the HTML into IE, copies it to the clipboard, pastes it into Word and then saves it as a Word document. It does require that Word is installed on the user’s machine and it does splat any contents of the clipboard but other than that it seems reasonably robust. My only other thought is how horrible using the Word COM API is from .NET, bring on optional parameters!&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;    private bool &lt;/span&gt;loadComplete;
    &lt;span style="color: blue"&gt;public void &lt;/span&gt;ConvertToWord(&lt;span style="color: blue"&gt;string &lt;/span&gt;htmlFile, &lt;span style="color: blue"&gt;string &lt;/span&gt;filename)
    {
      &lt;span style="color: green"&gt;// open in IE
      &lt;/span&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;WebBrowser &lt;/span&gt;browser = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;WebBrowser&lt;/span&gt;())
      {
        loadComplete = &lt;span style="color: blue"&gt;false&lt;/span&gt;;
        browser.DocumentCompleted += &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;WebBrowserDocumentCompletedEventHandler&lt;/span&gt;(
          browser_DocumentCompleted);
        browser.Navigate(htmlFile);
        &lt;span style="color: blue"&gt;while &lt;/span&gt;(!loadComplete)
        {
          &lt;span style="color: #2b91af"&gt;Thread&lt;/span&gt;.Sleep(50);
          System.Windows.Forms.&lt;span style="color: #2b91af"&gt;Application&lt;/span&gt;.DoEvents();
        }

        &lt;span style="color: green"&gt;// copy to clipboard
        &lt;/span&gt;browser.Document.ExecCommand(&lt;span style="color: #a31515"&gt;&amp;quot;SelectAll&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;, &lt;span style="color: blue"&gt;null&lt;/span&gt;);
        browser.Document.ExecCommand(&lt;span style="color: #a31515"&gt;&amp;quot;Copy&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;, &lt;span style="color: blue"&gt;null&lt;/span&gt;);
        
        &lt;span style="color: green"&gt;// open Word, paste and save...
        &lt;/span&gt;&lt;span style="color: blue"&gt;object &lt;/span&gt;dummy = &lt;span style="color: #2b91af"&gt;Type&lt;/span&gt;.Missing;
        &lt;span style="color: #2b91af"&gt;ApplicationClass &lt;/span&gt;wordApp = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ApplicationClass&lt;/span&gt;();
        &lt;span style="color: blue"&gt;try
        &lt;/span&gt;{
          &lt;span style="color: #2b91af"&gt;Document &lt;/span&gt;newDoc = wordApp.Documents.Add(&lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy);

          wordApp.Selection.Paste();

          &lt;span style="color: blue"&gt;object &lt;/span&gt;fileName = filename;
          newDoc.SaveAs(&lt;span style="color: blue"&gt;ref &lt;/span&gt;fileName, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, 
            &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, 
            &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy);
          ((&lt;span style="color: #2b91af"&gt;_Document&lt;/span&gt;)newDoc).Close(&lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy);
        }
        &lt;span style="color: blue"&gt;finally
        &lt;/span&gt;{
          wordApp.Quit(&lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy, &lt;span style="color: blue"&gt;ref &lt;/span&gt;dummy);
        }
      }
    }

    &lt;span style="color: blue"&gt;void &lt;/span&gt;browser_DocumentCompleted(&lt;span style="color: blue"&gt;object &lt;/span&gt;sender, &lt;span style="color: #2b91af"&gt;WebBrowserDocumentCompletedEventArgs &lt;/span&gt;e)
    {
      loadComplete = &lt;span style="color: blue"&gt;true&lt;/span&gt;;
    }&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7856587256154773212?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7856587256154773212/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7856587256154773212' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7856587256154773212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7856587256154773212'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/07/quick-hack-to-convert-html-to-word.html' title='Quick hack to convert HTML to Word documents'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7068872071125173669</id><published>2010-06-29T21:39:00.001+01:00</published><updated>2010-06-29T21:39:11.913+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Visio'/><title type='text'>Visio API ‘Cell is guarded’ exception</title><content type='html'>&lt;p&gt;If you’re using the Visio COM API to generate or modify Visio diagrams, you may have come across the exception ‘Cell is guarded’ when trying to set a cell’s &lt;font face="Courier New"&gt;FormulaU&lt;/font&gt; property (and probably when setting the &lt;font face="Courier New"&gt;Formula&lt;/font&gt; property). For me it only happened on a machine with Visio 2007 installed, Visio 2003 was fine. The solution was pretty simple, use the &lt;font face="Courier New"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ms367595(v=office.12).aspx"&gt;FormulaForceU&lt;/a&gt;&lt;/font&gt; property instead (although potentially this may lead to unexpected behaviour apparently, worked fine for me though).&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7068872071125173669?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7068872071125173669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7068872071125173669' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7068872071125173669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7068872071125173669'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/06/visio-api-cell-is-guarded-exception.html' title='Visio API ‘Cell is guarded’ exception'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3828611139139591157</id><published>2010-06-24T19:33:00.001+01:00</published><updated>2010-06-24T19:33:55.234+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySql'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='OS OpenData'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Importing the Code-Point dataset into MySql</title><content type='html'>&lt;p&gt;I recently realised that capturing the geocoding results from Google wasn’t such a good idea. It seems that postcodes that are only partially correct will still be geocoded, but will be returned with a default latitude and longitude which are kind of in the right area for that postcode area but obviously aren’t right, since the postcode doesn’t exist.&lt;/p&gt;  &lt;p&gt;So I decided it was time to grasp the nettle and use the Code-Point dataset for my &lt;a href="http://www.doogal.co.uk/UKPostcodes.php"&gt;UK postcodes&lt;/a&gt; page, so the data should be much more accurate. The only issue with the Code-Point dataset is that it doesn’t contain postcodes in Northern Ireland, Guernsey, Jersey and the Isle of Man for some reason.&lt;/p&gt;  &lt;p&gt;So the first thing to do was to download the &lt;a href="http://www.mysql.com/downloads/connector/net/"&gt;.NET connector for MySql&lt;/a&gt;. Then I modified &lt;a href="http://doogalbellend.blogspot.com/2010/06/importing-code-point-dataset-into-sql.html"&gt;my SQL Server import code&lt;/a&gt; to use MySql. As mentioned in that post, the code has dependencies on &lt;a href="http://www.codeproject.com/KB/database/CsvReader.aspx"&gt;this CSV parser&lt;/a&gt; and my &lt;a href="http://www.doogal.co.uk/dotnetcoords.php"&gt;.NET coordinates library&lt;/a&gt;. I made some modifications to add spaces to postcodes as necessary and also to not bother inserting postcodes with no geographical data (I’m not sure if this is missing data or if these are simply non-geographical postcodes).&lt;/p&gt;  &lt;p&gt;The code is shown below. Although it works, this is a simple implementation and is not particularly fast. It will probably take a few hours running against a local MySql installation but running against a remote installation will probably take longer. I only plan to run it once, so I’m not particularly concerned with performance, but if performance is an issue, you’ll probably want to take a look at the MySqlBulkLoader class or peruse the &lt;a href="http://dev.mysql.com/doc/refman/5.1/en/insert-speed.html"&gt;MySql documentation on improving the performance of inserts&lt;/a&gt;.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System.IO;
&lt;span style="color: blue"&gt;using &lt;/span&gt;DotNetCoords;
&lt;span style="color: blue"&gt;using &lt;/span&gt;LumenWorks.Framework.IO.Csv;
&lt;span style="color: blue"&gt;using &lt;/span&gt;MySql.Data.MySqlClient;

&lt;span style="color: blue"&gt;namespace &lt;/span&gt;ImportCodepoint
{
  &lt;span style="color: blue"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Program
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
    {
      &lt;span style="color: blue"&gt;string&lt;/span&gt;[] files = &lt;span style="color: #2b91af"&gt;Directory&lt;/span&gt;.GetFiles(&lt;span style="color: #a31515"&gt;@&amp;quot;C:\Users\Doogal\Downloads\codepo_gb\Code-Point Open\data\CSV&amp;quot;&lt;/span&gt;);
      &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;string &lt;/span&gt;file &lt;span style="color: blue"&gt;in &lt;/span&gt;files)
      {
        ReadFile(file);
      }

    }

    &lt;span style="color: blue"&gt;private static void &lt;/span&gt;ReadFile(&lt;span style="color: blue"&gt;string &lt;/span&gt;file)
    {
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;StreamReader &lt;/span&gt;reader = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StreamReader&lt;/span&gt;(file))
      {
        &lt;span style="color: #2b91af"&gt;CsvReader &lt;/span&gt;csvReader = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;CsvReader&lt;/span&gt;(reader, &lt;span style="color: blue"&gt;false&lt;/span&gt;);
        &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;MySqlConnection &lt;/span&gt;conn = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MySqlConnection&lt;/span&gt;(
          &lt;span style="color: #a31515"&gt;&amp;quot;server=127.0.0.1;uid=root;pwd=password;database=test;&amp;quot;&lt;/span&gt;))
        {
          conn.Open();
          &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] data &lt;span style="color: blue"&gt;in &lt;/span&gt;csvReader)
          {
            &lt;span style="color: blue"&gt;string &lt;/span&gt;postcode = data[0];
            &lt;span style="color: green"&gt;// some postcodes have spaces, some don't
            &lt;/span&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(postcode.IndexOf(&lt;span style="color: #a31515"&gt;' '&lt;/span&gt;) &amp;lt; 0)
              postcode = data[0].Substring(0, data[0].Length - 3) + &lt;span style="color: #a31515"&gt;&amp;quot; &amp;quot; &lt;/span&gt;+ data[0].Substring(data[0].Length - 3);
            &lt;span style="color: blue"&gt;double &lt;/span&gt;easting = &lt;span style="color: blue"&gt;double&lt;/span&gt;.Parse(data[10]);
            &lt;span style="color: blue"&gt;double &lt;/span&gt;northing = &lt;span style="color: blue"&gt;double&lt;/span&gt;.Parse(data[11]);

            &lt;span style="color: green"&gt;// there are some postcodes with no location
            &lt;/span&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;((easting != 0) &amp;amp;&amp;amp; (northing != 0))
            {
              &lt;span style="color: green"&gt;// convert easting/northing to lat/long
              &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OSRef &lt;/span&gt;osRef = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OSRef&lt;/span&gt;(easting, northing);
              &lt;span style="color: #2b91af"&gt;LatLng &lt;/span&gt;latLng = osRef.ToLatLng();
              latLng.ToWGS84();

              &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;MySqlCommand &lt;/span&gt;command = conn.CreateCommand())
              {
                &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(postcode);
                command.CommandTimeout = 60;
                command.CommandText = &lt;span style="color: blue"&gt;string&lt;/span&gt;.Format(&lt;span style="color: #a31515"&gt;&amp;quot;INSERT INTO Postcodes (Postcode, Latitude, Longitude) &amp;quot; &lt;/span&gt;+
                  &lt;span style="color: #a31515"&gt;&amp;quot;VALUES ('{0}', {1}, {2})&amp;quot;&lt;/span&gt;,
                  postcode, latLng.Latitude, latLng.Longitude);
                command.ExecuteNonQuery();
              }
            }
          }
        }
      }
    }
  }
}&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3828611139139591157?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3828611139139591157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3828611139139591157' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3828611139139591157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3828611139139591157'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/06/importing-code-point-dataset-into-mysql.html' title='Importing the Code-Point dataset into MySql'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-5323171103003964961</id><published>2010-06-22T21:22:00.001+01:00</published><updated>2010-06-22T21:22:54.731+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySql'/><title type='text'>MySql Workbench</title><content type='html'>&lt;p&gt;I’ve used &lt;a href="http://www.phpmyadmin.net"&gt;phpMyAdmin&lt;/a&gt; for a long time to administer MySql databases on the web. I’ve been pretty happy with it, although it can occasionally behave a bit strangely if it times out. But I was at the MySql website the other day to download MySql for my laptop when I spotted &lt;a href="http://www.mysql.com/products/workbench/"&gt;MySql Workbench&lt;/a&gt;, so I thought I’d give it a go. And it’s a very nice piece of free software. Call me an old timer but I still prefer a desktop application and it delivers. It looks good and offers all the features I need. I’ve been spoiled by SQL Server Management Studio (and not spoiled by the Oracle admin tools…) but MySql Workbench seems comparable in terms of functionality and ease of use. The pre-release 5.2 seems quite a lot better than the official 5.1 release, so go for that if you feel brave.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-5323171103003964961?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/5323171103003964961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=5323171103003964961' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5323171103003964961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5323171103003964961'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/06/mysql-workbench.html' title='MySql Workbench'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7748914385136590127</id><published>2010-06-18T21:30:00.001+01:00</published><updated>2010-06-18T21:41:12.921+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FxCop'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>FxCop custom rules</title><content type='html'>&lt;p&gt;There are quite a few examples of custom rules for FxCop (&lt;a href="http://www.binarycoder.net/fxcop/index.html"&gt;this was the best I found&lt;/a&gt;) but I was unable to find an example Visual Studio project to download. So after I got my simple rules assembly up and running, I thought I’d upload it to the web, &lt;a href="http://www.doogal.co.uk/files/FxCopRules.zip"&gt;you can grab it from here&lt;/a&gt;. It just contains one rule, taken from the site mentioned before, which checks for the usage of the ArrayList class.&lt;/p&gt;  &lt;p&gt;My only advice is if you are having trouble getting your rules loaded up into FxCop then debug your assembly through Visual Studio and make sure you have set up Visual Studio to break when CLR exceptions are thrown. If any exceptions are thrown when trying to load your assembly, FxCop will catch the exception and not display the rules.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7748914385136590127?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7748914385136590127/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7748914385136590127' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7748914385136590127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7748914385136590127'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/06/fxcop-custom-rules.html' title='FxCop custom rules'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1577417375272107726</id><published>2010-06-17T21:09:00.001+01:00</published><updated>2010-06-17T21:09:23.749+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>Getting the height of a background image</title><content type='html'>&lt;p&gt;Getting hold of the physical height of an image would initially appear to be a pretty straightforward thing to do. This may well be the case with an image held in an &lt;font face="Courier New"&gt;&amp;lt;img&amp;gt;&lt;/font&gt; tag (at least if the height of the &lt;font face="Courier New"&gt;&amp;lt;img&amp;gt;&lt;/font&gt; tag hasn’t been set) since it will resize to fit the image so you just need to get the height of the &lt;font face="Courier New"&gt;&amp;lt;img&amp;gt;&lt;/font&gt; element. But with a background image it’s not quite so simple. I wanted to get the height of an image used as a background image so I could resize the header of some HTML output when users supplied their own custom background images. &lt;/p&gt;  &lt;p&gt;Even so, this should be pretty easy I thought. Load up the image using the &lt;font face="Courier New"&gt;Image&lt;/font&gt; JavaScript object and get the height from there. There were a couple of issues with this. I decided to pick up the image URL from the background-image CSS, rather than hard coding it. But this was complicated by the fact that different browsers store the URL in different ways, some with quotes, some without. &lt;/p&gt;  &lt;p&gt;The other crazy thing I encountered was Internet Explorer seemingly loading up an old version of the image and returning the dimensions of that even though a new different sized image had replaced it. Emptying my cache and fiddling with the options dialog made no difference, so I was forced to add a query string to stop this insane caching.&lt;/p&gt;  &lt;p&gt;Another thing which seems to have tripped up other people when I was trawling the web is the need to set up the &lt;font face="Courier New"&gt;onload&lt;/font&gt; handler before setting the &lt;font face="Courier New"&gt;src&lt;/font&gt; property. &lt;/p&gt;  &lt;p&gt;Here’s the code, hope it’s useful.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: green"&gt;      // get the background image
      &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;backgroundImg = &lt;span style="color: blue"&gt;new &lt;/span&gt;Image();
      backgroundImg.onload = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        headerHeight = backgroundImg.height;
        &lt;span style="color: green"&gt;// do other stuff
      &lt;/span&gt;};
      &lt;span style="color: blue"&gt;var &lt;/span&gt;imgSrc = $(&lt;span style="color: #a31515"&gt;'.header'&lt;/span&gt;).css(&lt;span style="color: #a31515"&gt;'background-image'&lt;/span&gt;);
      &lt;span style="color: green"&gt;// IE and FireFox have quotes, Safari and Chrome don't
      &lt;/span&gt;imgSrc = imgSrc.replace(/[&lt;span style="color: #a31515"&gt;&amp;quot;']/g, '');
      &lt;/span&gt;imgSrc = imgSrc.substring(4);
      imgSrc = imgSrc.substring(0, imgSrc.length - 1);
      &lt;span style="color: green"&gt;// IE seems to be too aggressive in caching
      &lt;/span&gt;backgroundImg.src = imgSrc + &lt;span style="color: #a31515"&gt;'?' &lt;/span&gt;+ Math.random();&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1577417375272107726?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1577417375272107726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1577417375272107726' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1577417375272107726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1577417375272107726'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/06/getting-height-of-background-image.html' title='Getting the height of a background image'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6314978168865040813</id><published>2010-06-13T21:53:00.001+01:00</published><updated>2010-06-13T22:06:20.014+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='OS OpenData'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Importing the Code-Point dataset into SQL Server</title><content type='html'>&lt;p&gt;Many moons ago, I &lt;a href="http://doogalbellend.blogspot.com/2009/09/using-ogc-functions-in-sql-server-2008.html"&gt;imported my own postcode dataset into SQL Server&lt;/a&gt; but ever since the &lt;a href="https://www.ordnancesurvey.co.uk/opendatadownload/products.html"&gt;Ordnance Survey have made their Code-Point dataset available for free&lt;/a&gt;, I’ve been meaning to import that, since it’s likely their data will be more correct than my own.&lt;/p&gt;  &lt;p&gt;It comes in a series of CSV files and contains the postcode, easting, northing and various other bits of data. Unfortunately the documentation is somewhat lacking so it’s not entirely clear what all the other data is. But those three bits of data are all I was after. &lt;/p&gt;  &lt;p&gt;So I wrote a little console application to import the data, that looks like this&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Data.SqlClient;
&lt;span style="color: blue"&gt;using &lt;/span&gt;System.IO;
&lt;span style="color: blue"&gt;using &lt;/span&gt;DotNetCoords;
&lt;span style="color: blue"&gt;using &lt;/span&gt;LumenWorks.Framework.IO.Csv;

&lt;span style="color: blue"&gt;namespace &lt;/span&gt;ImportCodepoint
{
  &lt;span style="color: blue"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Program
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
    {
      &lt;span style="color: blue"&gt;string&lt;/span&gt;[] files = &lt;span style="color: #2b91af"&gt;Directory&lt;/span&gt;.GetFiles(&lt;span style="color: #a31515"&gt;@&amp;quot;C:\Users\Doogal\Downloads\codepo_gb\Code-Point Open\data\CSV&amp;quot;&lt;/span&gt;);
      &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;string &lt;/span&gt;file &lt;span style="color: blue"&gt;in &lt;/span&gt;files)
      {
        &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;&amp;quot;Importing file &amp;quot; &lt;/span&gt;+ &lt;span style="color: #2b91af"&gt;Path&lt;/span&gt;.GetFileName(file));
        ReadFile(file);
      }

    }

    &lt;span style="color: blue"&gt;private static void &lt;/span&gt;ReadFile(&lt;span style="color: blue"&gt;string &lt;/span&gt;file)
    {
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;StreamReader &lt;/span&gt;reader = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StreamReader&lt;/span&gt;(file))
      {
        &lt;span style="color: #2b91af"&gt;CsvReader &lt;/span&gt;csvReader = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;CsvReader&lt;/span&gt;(reader, &lt;span style="color: blue"&gt;false&lt;/span&gt;);
        &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlConnection &lt;/span&gt;conn = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlConnection&lt;/span&gt;(
          &lt;span style="color: #a31515"&gt;&amp;quot;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Postcodes;Data Source=DOOGAL-LAPTOP\\SQLEXPRESS&amp;quot;&lt;/span&gt;))
        {
          conn.Open();
          &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] data &lt;span style="color: blue"&gt;in &lt;/span&gt;csvReader)
          {
            &lt;span style="color: blue"&gt;string &lt;/span&gt;postcode = data[0];
            &lt;span style="color: blue"&gt;double &lt;/span&gt;easting = &lt;span style="color: blue"&gt;double&lt;/span&gt;.Parse(data[10]);
            &lt;span style="color: blue"&gt;double &lt;/span&gt;northing = &lt;span style="color: blue"&gt;double&lt;/span&gt;.Parse(data[11]);

            &lt;span style="color: green"&gt;// convert easting/northing to lat/long
            &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OSRef &lt;/span&gt;osRef = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OSRef&lt;/span&gt;(easting, northing);
            &lt;span style="color: #2b91af"&gt;LatLng &lt;/span&gt;latLng = osRef.ToLatLng();
            latLng.ToWGS84();

            &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlCommand &lt;/span&gt;command = conn.CreateCommand())
            {
              command.CommandText = &lt;span style="color: blue"&gt;string&lt;/span&gt;.Format(&lt;span style="color: #a31515"&gt;&amp;quot;INSERT INTO Postcodes (Postcode, Coordinates) &amp;quot; &lt;/span&gt;+
                &lt;span style="color: #a31515"&gt;&amp;quot;VALUES ('{0}', geography::STPointFromText('POINT({2} {1})', 4326))&amp;quot;&lt;/span&gt;,
                data[0], latLng.Latitude, latLng.Longitude);
              command.ExecuteNonQuery();
            }
          }
        }
      }
    }
  }
}&lt;/pre&gt;

&lt;p&gt;A couple of points, &lt;a href="http://www.codeproject.com/KB/database/CsvReader.aspx"&gt;I’ve used this CSV parser&lt;/a&gt;, rather than re-inventing the wheel. It would be pretty easy to do this parsing myself, since only a few fields are quoted and no fields contain any special characters, but I’ve been pretty happy with this CSV parser in the past so saw no point re-inventing the wheel.&lt;/p&gt;

&lt;p&gt;I’ve also used my &lt;a href="http://www.doogal.co.uk/dotnetcoords.php"&gt;.Net coordinates library&lt;/a&gt; to convert GB OS references to latitude/longitude, but obviously you can store the postcodes using eastings and northings if you prefer.&lt;/p&gt;

&lt;p&gt;The final thing to do is to check that the imported data looks sensible. The easiest way to do this is to query the table in SQL Server Management Studio and look at the spatial results. Unfortunately this is limited to 5000 results, so I used this little bit of SQL (based on &lt;a href="http://www.iknowkungfoo.com/blog/index.cfm/2009/2/20/SQL-approach-to-Showing-Every-Nth-Record"&gt;this blog post&lt;/a&gt;) to select a subset of the data to make sure the general shape looked correct. Obviously this doesn’t check all the data but did build some confidence that what I’d done was correct.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;SELECT 
   &lt;/span&gt;EXPR2&lt;span style="color: gray"&gt;.*
&lt;/span&gt;&lt;span style="color: blue"&gt;FROM 
   &lt;/span&gt;&lt;span style="color: gray"&gt;(
   &lt;/span&gt;&lt;span style="color: blue"&gt;SELECT 
      &lt;/span&gt;Postcode&lt;span style="color: gray"&gt;, &lt;/span&gt;Coordinates&lt;span style="color: gray"&gt;,
      &lt;/span&gt;RowNumber&lt;span style="color: gray"&gt;,
      (&lt;/span&gt;EXPR1&lt;span style="color: gray"&gt;.&lt;/span&gt;RowNumber &lt;span style="color: gray"&gt;% &lt;/span&gt;400&lt;span style="color: gray"&gt;) &lt;/span&gt;&lt;span style="color: blue"&gt;AS &lt;/span&gt;ROW_MOD
   &lt;span style="color: blue"&gt;FROM
      &lt;/span&gt;&lt;span style="color: gray"&gt;(
      &lt;/span&gt;&lt;span style="color: blue"&gt;SELECT 
         &lt;/span&gt;Postcode&lt;span style="color: gray"&gt;, &lt;/span&gt;Coordinates&lt;span style="color: gray"&gt;, 
         &lt;/span&gt;&lt;span style="color: magenta"&gt;ROW_NUMBER&lt;/span&gt;&lt;span style="color: gray"&gt;() &lt;/span&gt;&lt;span style="color: blue"&gt;OVER &lt;/span&gt;&lt;span style="color: gray"&gt;( &lt;/span&gt;&lt;span style="color: blue"&gt;ORDER BY &lt;/span&gt;Postcode &lt;span style="color: gray"&gt;) &lt;/span&gt;&lt;span style="color: blue"&gt;AS &lt;/span&gt;&lt;span style="color: red"&gt;'RowNumber'
      &lt;/span&gt;&lt;span style="color: blue"&gt;FROM 
         &lt;/span&gt;Postcodes 
      &lt;span style="color: gray"&gt;) &lt;/span&gt;EXPR1
   &lt;span style="color: gray"&gt;) &lt;/span&gt;EXPR2
&lt;span style="color: blue"&gt;WHERE 
   &lt;/span&gt;EXPR2&lt;span style="color: gray"&gt;.&lt;/span&gt;ROW_MOD &lt;span style="color: gray"&gt;= &lt;/span&gt;0&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6314978168865040813?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6314978168865040813/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6314978168865040813' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6314978168865040813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6314978168865040813'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/06/importing-code-point-dataset-into-sql.html' title='Importing the Code-Point dataset into SQL Server'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7921052171283273438</id><published>2010-06-11T17:13:00.001+01:00</published><updated>2010-08-12T20:15:23.941+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>Image file locking in the .NET Bitmap constructor</title><content type='html'>&lt;p&gt;Consider the following code&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;      Dim &lt;/span&gt;bitmap &lt;span style="color: blue"&gt;As New &lt;/span&gt;Drawing.Bitmap(fileName)
      &lt;span style="color: blue"&gt;Return &lt;/span&gt;bitmap&lt;/pre&gt;

&lt;p&gt;It certainly looks fairly innocuous but it had a problem, or rather it caused a problem further down the track. We wanted to replace some of the image files that we had previously loaded into the application. But trying to do that threw an exception telling us &lt;em&gt;“The process cannot access the file '…’ because it is being used by another process.”&lt;/em&gt;. Which wasn’t entirely accurate, actually we couldn’t access the file because it was in use by &lt;em&gt;this&lt;/em&gt; process. But nitpicking aside, the reason for this was the &lt;font face="Courier New"&gt;Bitmap&lt;/font&gt; constructor above which loads the file but doesn’t unlock it, only a call to &lt;font size="2" face="Courier New"&gt;Bitmap.Dispose()&lt;/font&gt; unlocks it again.&lt;/p&gt;

&lt;p&gt;So my first attempt to fix this looked something like this&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;      Dim &lt;/span&gt;fs &lt;span style="color: blue"&gt;As New &lt;/span&gt;FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
      &lt;span style="color: blue"&gt;Dim &lt;/span&gt;bitmap &lt;span style="color: blue"&gt;As New &lt;/span&gt;Drawing.Bitmap(fs)
      fs.Close()
      &lt;span style="color: blue"&gt;Return &lt;/span&gt;bitmap&lt;/pre&gt;

&lt;p&gt;Since this opened the image via a &lt;font face="Courier New"&gt;FileStream&lt;/font&gt; and closed it afterwards, this should solve the problem. And it did on my Windows 7 machine, but it caused even bigger problems on a Windows XP machine. We started getting an out of memory exception for no apparent reason. &lt;/p&gt;

&lt;p&gt;I eventually came to the conclusion that this was down to the &lt;font face="Courier New"&gt;Bitmap&lt;/font&gt; constructor that took a &lt;font face="Courier New"&gt;FileStream&lt;/font&gt; instance. When it’s passed a file name, it can make a pretty good guess at what kind of file it’s loading based on the extension, but when it is just given a bunch of bytes, it has to make a guess as to what type of image it’s looking at and I presume the heuristics in XP weren’t as good as they are in Windows 7. I’m guessing here, since all this happens in the guts of the GDI+ API. &lt;/p&gt;

&lt;p&gt;So my final attempt at fixing this looks like this&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;      Dim &lt;/span&gt;fileBitmap &lt;span style="color: blue"&gt;As New &lt;/span&gt;Drawing.Bitmap(fileName)
      &lt;span style="color: blue"&gt;Dim &lt;/span&gt;bitmap &lt;span style="color: blue"&gt;As New &lt;/span&gt;Drawing.Bitmap(fileBitmap)
      fileBitmap.Dispose()
      &lt;span style="color: blue"&gt;Return &lt;/span&gt;bitmap&lt;/pre&gt;

&lt;p&gt;Which so far hasn’t caused any more issues…&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7921052171283273438?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7921052171283273438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7921052171283273438' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7921052171283273438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7921052171283273438'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/06/image-file-locking-in-net-bitmap.html' title='Image file locking in the .NET Bitmap constructor'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4900345774432388579</id><published>2010-06-05T11:39:00.001+01:00</published><updated>2010-06-05T11:47:44.328+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Debugging KML loading problems in Google Maps</title><content type='html'>&lt;p&gt;I was attempting to load some KML into Google Maps via the KmlLayer class and not having much luck. Nothing was appearing. Unfortunately the &lt;a href="http://code.google.com/apis/maps/documentation/javascript/reference.html#KmlLayer"&gt;KmlLayer class doesn’t provide an event to indicate an error has occurred&lt;/a&gt;. So what is available to debug these problems and what are some of the issues that can cause load errors?&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://www.fiddler2.com/fiddler2/"&gt;Fiddler&lt;/a&gt; may help, although it didn’t give any indication to me about what was going on. But I have seen some HTTP responses with 400 bad request status codes in the past. Unfortunately they didn’t say why the request was bad. &lt;/li&gt;    &lt;li&gt;Check that the KML is valid using &lt;a href="http://www.feedvalidator.org/"&gt;Feed Validator&lt;/a&gt;. I’ve found this doesn’t work too well with large KML files in IE, so use some other browser instead. &lt;/li&gt;    &lt;li&gt;When Google Maps does server side rendering of KML it appears to do some caching of the data, so if you change your KML, changes may not appear immediately. &lt;/li&gt;    &lt;li&gt;There are limits to the size of KML file supported by Google Maps, &lt;a href="http://code.google.com/apis/kml/documentation/mapsSupport.html"&gt;have a look at this page to see if this is your problem&lt;/a&gt;. I think this is what caused my issues.&lt;/li&gt;    &lt;li&gt;Try loading the KML in Google Earth to see if it’s an issue with the KML or Google Maps’ handling of it.&lt;/li&gt; &lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4900345774432388579?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4900345774432388579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4900345774432388579' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4900345774432388579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4900345774432388579'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/06/debugging-kml-loading-problems-in.html' title='Debugging KML loading problems in Google Maps'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3505934026429672850</id><published>2010-06-01T22:10:00.001+01:00</published><updated>2010-08-15T11:21:28.306+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Moving from Google Maps 2 to Google Maps 3</title><content type='html'>&lt;p&gt;After playing with version 3 of the Google Maps API and trying to convert my &lt;a href="http://www.doogal.co.uk/ScratchPad.htm"&gt;ScratchPad application&lt;/a&gt;, I’m pretty impressed. It’s a much more consistent API than version 2 and provides quite a few new features. But one thing is for sure, converting an application from version 2 to version 3 will be a big chunk of work. Pretty much every line will need rewriting. &lt;/p&gt;  &lt;p&gt;So I thought I’d provide a little crib sheet to help people who are trying to upgrade. I don’t claim that this list is in any way complete. I’ll update it as I find more things out, but even so I only intend it to be a pointer to the right class/method to use. In most cases the way methods or classes are used has also changed. If you have any other information to add, please add a comment.&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="416"&gt;&lt;strong&gt;Google Maps version 2&lt;/strong&gt;&lt;/td&gt;        &lt;td valign="top" width="609"&gt;&lt;strong&gt;Google Maps version 3&lt;/strong&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GMap2 class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.Map class&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GMap2.disableDoubleClickZoom()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;MapOptions.disableDoubleClickZoom:true passed to Map constructor or Map.setOptions()&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GMap2.getDragObject().setDraggableCursor()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;MapOptions.draggableCursor passed to Map constructor or Map.setOptions()&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GMap2.addOverlay()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;Call setMap() on the layer to add&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GMap2.clearOverlays()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;???&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GMap2.removeOverlay()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;Call setMap(null) on the layer to remove&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GLatLng class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.LatLng class&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GMarker class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.Marker class&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GMarker.bindInfoWindowHtml()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.InfoWindow class, but you need to manually open the info window using          &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font face="Courier New"&gt;google.maps.event.addListener(marker, 'click', function () {              &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; infoWindow.open(map, marker);               &lt;br /&gt;&amp;#160; });&lt;/font&gt;&lt;/p&gt;       &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GEvent.bind(), GEvent.addListener&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.event.addListener(), although it doesn’t seem possible to bind the event to particular object instance (where the keyword this can be used in the event handler)&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GEvent.removeListener()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.event.removeListener()&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GNavLabelControl class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;???&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GAdsManager class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;???&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GGeoXml class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.KmlLayer class&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GGeoXml.load event&lt;/td&gt;        &lt;td valign="top" width="609"&gt;???&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GGeoXml.getDefaultCenter()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;KmlLayer.getDefaultViewport().getCenter()&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GStreetviewOverlay class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;This is now integrated into the map, pass streetViewControl: true into MapOptions to make it appear&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GClientGeocoder class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.Geocoder class&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GClientGeocoder.getLatLng()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;Geocoder.geocode()&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GPolyline class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.Polyline class&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GPolyline.getLength()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;There is no direct replacement, but &lt;a href="http://groups.google.com/group/google-maps-js-api-v3/browse_thread/thread/3a9f3b83b941a2d7/7695b14e1dbc4f1c"&gt;this forum post discusses how to implement something similar yourself&lt;/a&gt;.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GPolyline.getVertexCount()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;Polyline.getPath().length()&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GDirections class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.DirectionsService class to query for directions, google.maps.DirectionsRenderer class to render them on a map&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GDirections.load()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;DirectionService.route()&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GDirections.loadFromWaypoints()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;DirectionsRequest.waypoints property&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GDirections.clear()&lt;/td&gt;        &lt;td valign="top" width="609"&gt;No equivalent&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GTrafficOverlay class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;google.maps.TrafficLayer class&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GLayer class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;??? – &lt;a href="http://code.google.com/p/gmaps-api-issues/issues/detail?id=1502"&gt;This is in the API issues database&lt;/a&gt;, star it if you want it implemented&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="416"&gt;GTileLayerOverlay class&lt;/td&gt;        &lt;td valign="top" width="609"&gt;???&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3505934026429672850?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3505934026429672850/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3505934026429672850' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3505934026429672850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3505934026429672850'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/06/moving-from-google-maps-2-to-google.html' title='Moving from Google Maps 2 to Google Maps 3'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-5555015959246413099</id><published>2010-05-31T12:40:00.001+01:00</published><updated>2010-05-31T12:40:00.936+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySql'/><title type='text'>Improving the performance of REGEXP queries in MySql</title><content type='html'>&lt;p&gt;Say we’re trying to query a table of UK postcode and just want to return the postcodes in a particular area(the Birmingham area B for instance). A naive implementation of this may be something like this&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT * FROM Postcodes WHERE Postcode LIKE 'B%'&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;This works for some postcodes but doesn’t work for this particular example because it also returns any postcodes in the BA, BB, BT etc areas. So it looks like a regular expression is required, to ensure the area code is followed by a number, so we try the following&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT * FROM Postcodes WHERE Postcode REGEXP'^B[0-9]'&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;This works and returns what we expect but it is much slower than the LIKE query. So is there any way to speed it up? Actually, it’s pretty straightforward, just combine the LIKE and the REGEXP queries, like so&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT * FROM Postcodes WHERE Postcode LIKE 'B%' AND Postcode REGEXP'^B[0-9]'&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;This give MySql the chance to first filter the data based on the LIKE clause then only use the regular expression on the filtered data. It’s not quite as fast as the original LIKE implementation but it’s much quicker than REGEXP on its own and unlike the original LIKE implementation it actually works.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-5555015959246413099?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/5555015959246413099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=5555015959246413099' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5555015959246413099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5555015959246413099'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/05/improving-performance-of-regexp-queries.html' title='Improving the performance of REGEXP queries in MySql'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8405087317770600561</id><published>2010-05-30T18:58:00.001+01:00</published><updated>2010-05-30T19:01:45.122+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Maths'/><title type='text'>The Fibonacci Sequence</title><content type='html'>&lt;p&gt;For Christmas 1987, my brother gave me ‘yet another interesting book’ (his words), “The Penguin Dictionary of Curious and Interesting Numbers”. Clearly it must be pretty interesting, since I was surprised to see &lt;a href="http://www.amazon.co.uk/Penguin-Dictionary-Curious-Interesting-Numbers/dp/0140261494"&gt;it is still available&lt;/a&gt;. Being something of a maths geek, I did enjoy reading it, but it has been languishing in my loft for a few years now.&lt;/p&gt;  &lt;p&gt;Jump forward a couple of decades and I was reading &lt;a href="http://www.amazon.co.uk/Rabbit-Problem-Emily-Gravett/dp/0230704239"&gt;“The Rabbit Problem”&lt;/a&gt; as a bedtime story to my daughter and was wondering why it was set in Fibonacci’s Field. Clearly this was related to the Fibonacci Sequence (and reading the back cover gave the game away) but I was unsure how the Fibonacci Sequence was related to rabbits. So time to climb up into the loft to find out. Turns out the Fibonacci Sequence was the solution to a problem about rabbits breeding. And it also appears a lot in nature, specifically in the number of petals etc in plants.&lt;/p&gt;  &lt;p&gt;As if that wasn’t enough, the ratios of successive terms of the Fibonacci Sequence tend towards the Golden Ratio, another interesting number that you can find out about in the dictionary. And here’s a little Javascript example to show that convergence. &lt;/p&gt; &lt;script type="text/javascript"&gt;
      var i = 1;
      var j = 1;
      var sum = 0;

      function fibonacci() {
        if (sum &lt; Number.MAX_VALUE) {
          sum = i + j;
          document.getElementById('fibonacciVals').value += sum + ' ' + j/i + '\r\n';
          i = j;
          j = sum;
          setTimeout("fibonacci()", 100);
        }
      }

    &lt;/script&gt; &lt;input onclick="javascript:fibonacci();" type="button" value="Generate" /&gt;  &lt;br /&gt;&lt;textarea id="fibonacciVals" rows="40" cols="60"&gt;&lt;/textarea&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8405087317770600561?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8405087317770600561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8405087317770600561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8405087317770600561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8405087317770600561'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/05/fibonacci-sequence.html' title='The Fibonacci Sequence'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6971812305411838238</id><published>2010-05-30T11:19:00.001+01:00</published><updated>2010-05-30T11:19:38.630+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='SEO'/><title type='text'>Free XML sitemap generator with unlimited pages</title><content type='html'>&lt;p&gt;I’ve been happily using &lt;a href="http://www.xml-sitemaps.com/"&gt;this free XML sitemap generator&lt;/a&gt; for a while, but as my sites have got bigger I keep hitting the 500 page limit. Since I’m too tight to actually pay for this kind of service I’ve been searching around for a while for another free alternative. I was even thinking of writing my own, but before I was forced down that road, I found &lt;a href="http://www.auditmypc.com/xml-sitemap.asp"&gt;this alternative generator&lt;/a&gt;. I’m not too keen on Java applets generally, but this one seems to work very well. If it fails to download any pages, you can go back and retry downloading the failed pages. It also gives details of how long pages take to download, so it can also be a useful performance checker of a website.&lt;/p&gt;  &lt;p&gt;Even so, I’m a little ambivalent towards sitemaps, especially if they are generated using one of these tools that just crawl your website to find all the pages. What information does this provide to search engines that they can’t find for themselves by crawling the site themselves? And If I do want to provide any more useful information, I have to edit the thing by hand, which I really can’t be bothered doing.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6971812305411838238?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6971812305411838238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6971812305411838238' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6971812305411838238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6971812305411838238'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/05/free-xml-sitemap-generator-with.html' title='Free XML sitemap generator with unlimited pages'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1319025411163601863</id><published>2010-05-28T21:21:00.001+01:00</published><updated>2010-05-28T21:21:15.430+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Google Maps elevation API</title><content type='html'>&lt;p&gt;Version 3 of the Google Maps API adds a new dimension to maps, the elevation of locations. I did plan to knock together an example of how this works, but &lt;a href="http://gmaps-samples-v3.googlecode.com/svn/trunk/elevation/elevation-profile.html"&gt;the example provided by Google&lt;/a&gt; is pretty good itself. &lt;/p&gt;  &lt;p&gt;I think this new API could be very useful. The obvious use would be when planning a walk or a bike ride and wanting to get an idea for the terrain, but I’m sure there are many more uses.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1319025411163601863?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1319025411163601863/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1319025411163601863' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1319025411163601863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1319025411163601863'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/05/google-maps-elevation-api.html' title='Google Maps elevation API'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-5759757875416988386</id><published>2010-05-28T20:17:00.001+01:00</published><updated>2010-05-28T20:31:56.087+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Google Maps styled maps</title><content type='html'>&lt;p&gt;Version 3 of the Google Maps API adds support for &lt;a href="http://code.google.com/apis/maps/documentation/javascript/overlays.html#StyledMaps"&gt;styled maps&lt;/a&gt;, which means it is now possible to hide features or display them differently on the base map. This could lead to some interesting customisations, and also leads to some questions about how exactly Google have implemented this? Are they creating map tiles on the fly? Or have they stored every possible permutation of styling? Surely the former, but how come it doesn’t seem to make any difference to the performance of rendering?&lt;/p&gt;  &lt;p&gt;Anyway I had a play with the &lt;a href="http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html"&gt;styled map wizard&lt;/a&gt; (which doesn’t work in IE for some reason) and thought I’d experiment with a map displaying just rail lines. And I kind of got this working, but there was a fundamental problem. Rail lines are only displayed when the map is zoomed in below a certain threshold so the map wasn’t any use to get a broad overview of rail lines in the UK for instance. And the styled maps API doesn’t seem to provide any control over this thresholding.&lt;/p&gt;  &lt;p&gt;So it looks good as an initial implementation, but I think more control is required to be a completely useful API.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-5759757875416988386?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/5759757875416988386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=5759757875416988386' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5759757875416988386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5759757875416988386'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/05/google-maps-styled-maps.html' title='Google Maps styled maps'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-9047243241232630697</id><published>2010-05-25T20:58:00.001+01:00</published><updated>2010-05-25T20:58:43.067+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Google Maps geolocation API</title><content type='html'>&lt;blockquote&gt;   &lt;p&gt;Google have had an API to get the user’s location for a while (via the Google loader), but &lt;a href="http://www.doogal.co.uk/WhereAmI.php"&gt;I didn’t think it was very good&lt;/a&gt;. But now &lt;a href="http://code.google.com/apis/maps/documentation/javascript/basics.html#DetectingUserLocation"&gt;there are two new methods of figuring out the user’s location&lt;/a&gt;, one using the new W3C standard and the other using Google Gears. The good news is they seem to do a very good job of locating the user (scarily so in fact), the bad news is that neither are supported by Internet Explorer or Safari.&lt;/p&gt;    &lt;p&gt;Google Chrome uses Google Gears to do the job, FireFox uses the W3C method and frankly I’m not too bothered what Opera supports, since so few people use it.&lt;/p&gt;    &lt;p&gt;One annoyance is the prompting you get in the browser, although this is fairly understandable since this information could potentially be used for evil. And it won’t be an especially useful feature until it’s adopted by more browsers. But a combination of all three methods may produce a reasonable compromise.&amp;#160; &lt;/p&gt;&lt;/blockquote&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-9047243241232630697?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/9047243241232630697/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=9047243241232630697' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/9047243241232630697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/9047243241232630697'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/05/google-maps-geolocation-api.html' title='Google Maps geolocation API'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1227205307053791400</id><published>2010-05-21T20:36:00.001+01:00</published><updated>2010-05-21T20:36:33.896+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Postcodes'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Google Maps geocoding still sucks</title><content type='html'>&lt;p&gt;I spent a little time today starting to convert some of my pages to the Google Maps API version 3. At the same time I thought I’d convert my geocoding to use the latest version since I was sure it would have improved since &lt;a href="http://www.tomanthony.co.uk/blog/geocoding-uk-postcodes-with-google-map-api/"&gt;it was added to the API and failed to work too well&lt;/a&gt; three years ago. But no, it still sucks big time for postcode geocoding, &lt;a href="http://www.doogal.co.uk/GeocodingTest.php"&gt;as this test page demonstrates&lt;/a&gt;. But what is strange is that the GlocalSearch class still does a fine job of geocoding. Same company, different APIs and different results. This can’t be down to the Royal Mail throwing their toys out of the pram since the &lt;a href="https://www.ordnancesurvey.co.uk/opendatadownload/products.html"&gt;Ordnance Survey now provides geocoded postcode data for free&lt;/a&gt; so I assume it’s down to Google stuffing up.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1227205307053791400?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1227205307053791400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1227205307053791400' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1227205307053791400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1227205307053791400'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/05/google-maps-geocoding-still-sucks.html' title='Google Maps geocoding still sucks'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4699837524861319415</id><published>2010-05-21T14:35:00.001+01:00</published><updated>2010-05-21T14:35:31.976+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Converting to Google Maps version 3</title><content type='html'>&lt;p&gt;Google Maps API version 3 has just moved from the Google Labs to live so I thought I’d try converting a simple version 2 map to version 3. This is what the version 2 code looks like&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  &amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;src&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;http://maps.google.com/maps?file=api&amp;amp;amp;v=2&amp;amp;amp;key=ABQIAAAAjtZCgAx5i04BiZDO6HlxhRQUdBDpWCOMRMbgTcqadX0jQ8HOERSxXxhk24TIBUpivovAKLrnpSio9w&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;script&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
  &amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text/javascript&amp;quot;&amp;gt;
    &lt;/span&gt;window.onload = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
      &lt;span style="color: blue"&gt;if &lt;/span&gt;(GBrowserIsCompatible()) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;map = &lt;span style="color: blue"&gt;new &lt;/span&gt;GMap2(document.getElementById(&lt;span style="color: maroon"&gt;&amp;quot;map&amp;quot;&lt;/span&gt;));
        map.setCenter(&lt;span style="color: blue"&gt;new &lt;/span&gt;GLatLng(51.49506473014368, -0.130462646484375), 10);
        map.addControl(&lt;span style="color: blue"&gt;new &lt;/span&gt;GSmallMapControl());
        map.addControl(&lt;span style="color: blue"&gt;new &lt;/span&gt;GMapTypeControl());
        map.enableDoubleClickZoom();
        map.enableScrollWheelZoom();
        &lt;span style="color: blue"&gt;var &lt;/span&gt;layer = &lt;span style="color: blue"&gt;new &lt;/span&gt;GGeoXml(&lt;span style="color: maroon"&gt;&amp;quot;http://www.doogal.co.uk/LondonStationsKML.php&amp;quot;&lt;/span&gt;);
        map.addOverlay(layer);
      }
    }
  &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;script&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;And this is what the version 3 code looks like&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  &amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;src&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;http://maps.google.com/maps/api/js?sensor=false&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;script&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
  &amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text/javascript&amp;quot;&amp;gt;
    &lt;/span&gt;window.onload = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
      &lt;span style="color: blue"&gt;var &lt;/span&gt;latlng = &lt;span style="color: blue"&gt;new &lt;/span&gt;google.maps.LatLng(51.49506473014368, -0.130462646484375);
      &lt;span style="color: blue"&gt;var &lt;/span&gt;options = {
        zoom: 10,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      };

      &lt;span style="color: blue"&gt;var &lt;/span&gt;map = &lt;span style="color: blue"&gt;new &lt;/span&gt;google.maps.Map(document.getElementById(&lt;span style="color: maroon"&gt;&amp;quot;map&amp;quot;&lt;/span&gt;), options);

      &lt;span style="color: blue"&gt;var &lt;/span&gt;georssLayer = &lt;span style="color: blue"&gt;new &lt;/span&gt;google.maps.KmlLayer(&lt;span style="color: maroon"&gt;'http://www.doogal.co.uk/LondonStationsKML.php'&lt;/span&gt;);
      georssLayer.setMap(map);
    }
  &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;script&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;The first thing I noticed is that the API key is no longer required which is very welcome, since it makes everything simpler (no more hassle when moving scripts between domains or trying to debug somewhere other than the domain or localhost). I guess the namespaces are a good thing and the class names seem more memorable. At this point I’m not sure if the API is any better than the old one, I’ll have to do some more digging before I can make a decision. But it does look like there’ll be quite a bit of work to convert old sites to the new API. Not that we’re forced to upgrade of course, at least for the moment.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4699837524861319415?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4699837524861319415/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4699837524861319415' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4699837524861319415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4699837524861319415'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/05/converting-to-google-maps-version-3.html' title='Converting to Google Maps version 3'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-443690609961140696</id><published>2010-04-23T10:48:00.001+01:00</published><updated>2010-04-23T11:50:15.517+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='IE8'/><title type='text'>JavaScript not executing in Internet Explorer 8</title><content type='html'>&lt;p&gt;I’ve been caught out by this on numerous occasions and still haven’t learnt my lesson. So in an attempt to remember in the future and perhaps help somebody else out, here’s the situation. Take the following script declaration&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text/javascript&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;src&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;Scripts/jquery-1.4.1.min.js&amp;quot; /&amp;gt;
&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;This may look valid and probably should be, but Internet Explorer thinks the script tag hasn’t been closed, which means any script blocks after it will be ignored. The declaration needs to be&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text/javascript&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;src&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;Scripts/jquery-1.4.1.min.js&amp;quot;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;script&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-443690609961140696?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/443690609961140696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=443690609961140696' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/443690609961140696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/443690609961140696'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/04/javascript-not-executing-in-internet.html' title='JavaScript not executing in Internet Explorer 8'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4667851433155680070</id><published>2010-04-11T11:27:00.001+01:00</published><updated>2010-04-11T11:27:59.432+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='Postcodes'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Building a random postcode generator</title><content type='html'>&lt;p&gt;Some time a go I noticed that the keywords ‘random postcode generator’ were landing quite a few people at &lt;a href="http://www.doogal.co.uk/"&gt;my website&lt;/a&gt;, even though I didn’t have a random postcode generator. Which got me thinking there must be a need for such a tool, if people were ending up at a site that didn’t have one desperately looking for it. &lt;a href="http://www.doogal.co.uk/PostcodeGenerator.php"&gt;So I built one&lt;/a&gt; and it’s now the most visited page on my site. &lt;/p&gt;  &lt;p&gt;So that’s my little success story. But then I started wondering why anybody would want a random postcode anyway. I guess some people are just after a fake postcode they can plug into a website registration form, for whatever reason, and that page will do the trick nicely. But I imagine there are other people who need to generate random postcodes programmatically for some reason, so I’m going to explain how my postcode generator works. It may not be the best approach, but it seems to work.&lt;/p&gt;  &lt;p&gt;I guess one approach would be to have a complete list of UK postcodes and just select one at random. I didn’t have a complete list of postcodes and you probably won’t either, unless you’ve bought the PAF database from the Royal Mail. So I went for a more long winded approach that requires less initial data to get working. First I got together a table of postcode districts (&lt;a href="http://en.wikipedia.org/wiki/List_of_postcode_districts_in_the_United_Kingdom"&gt;original list&lt;/a&gt; and my &lt;a href="http://www.doogal.co.uk/PostcodeDistricts.php"&gt;more programmer friendly version&lt;/a&gt;), which is the first half of a postcode. Potentially you could try to create this part of the postcode randomly but you’d end up creating invalid postcodes a lot of the time. Also, not all postcode districts are of the same format.&lt;/p&gt;  &lt;p&gt;So with this table in place, I get a random postcode district and then append a random second half of the postcode (which is always of the form digit-letter-letter). I did this in PHP using the following (this contains some code which is specific to my website but you should be able to modify for your own needs)&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160; $result = DbQuery(&amp;quot;SELECT Postcode FROM PostcodeDistricts ORDER BY rand( ) LIMIT 1&amp;quot;);      &lt;br /&gt;&amp;#160; $line = mysql_fetch_array($result, MYSQL_ASSOC);       &lt;br /&gt;&amp;#160; $num = rand(0,9);       &lt;br /&gt;&amp;#160; $firstChar = rand(1, 26);       &lt;br /&gt;&amp;#160; $secondChar = rand(1, 26);       &lt;br /&gt;&amp;#160; print($line[&amp;quot;Postcode&amp;quot;] . &amp;quot; &amp;quot; . $num . chr(64+$firstChar) . chr(64+$secondChar));       &lt;br /&gt;&amp;#160; mysql_free_result($result);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;The only problem with this is that it will sometimes generate invalid postcodes, so the next step is to check the postcode is valid. I did this using Google’s Local Search API from JavaScript, as so.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text/javascript&amp;quot;&amp;gt;
    &lt;/span&gt;&lt;span style="color: green"&gt;/* &amp;lt;![CDATA[ */
    &lt;/span&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;CreateRandomPostcode()
    {
      document.getElementById(&lt;span style="color: #a31515"&gt;&amp;quot;postcode&amp;quot;&lt;/span&gt;).innerHTML = &lt;span style="color: #a31515"&gt;&amp;quot;Thinking&amp;quot;&lt;/span&gt;;
      TryCreateRandomPostcode();
    }
 
    &lt;span style="color: blue"&gt;function &lt;/span&gt;TryCreateRandomPostcode()
    {
      document.getElementById(&lt;span style="color: #a31515"&gt;&amp;quot;postcode&amp;quot;&lt;/span&gt;).innerHTML += &lt;span style="color: #a31515"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;;
      &lt;span style="color: blue"&gt;var &lt;/span&gt;xmlhttp;
      &lt;span style="color: blue"&gt;if &lt;/span&gt;(window.XMLHttpRequest) {
        xmlhttp = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();     &lt;span style="color: green"&gt;// Firefox, Safari, ...
      &lt;/span&gt;}
      &lt;span style="color: blue"&gt;else if &lt;/span&gt;(window.ActiveXObject)   &lt;span style="color: green"&gt;// ActiveX version
      &lt;/span&gt;{
        xmlhttp = &lt;span style="color: blue"&gt;new &lt;/span&gt;ActiveXObject(&lt;span style="color: #a31515"&gt;&amp;quot;Microsoft.XMLHTTP&amp;quot;&lt;/span&gt;);  &lt;span style="color: green"&gt;// Internet Explorer
      &lt;/span&gt;}
 
      xmlhttp.onreadystatechange = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;if&lt;/span&gt;(xmlhttp.readyState == 4 &amp;amp;&amp;amp; xmlhttp.status == 200) {
          Geocode(xmlhttp.responseText);
        }
      }
      xmlhttp.open(&lt;span style="color: #a31515"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;CreateRandomPostcode.php&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;);
      xmlhttp.send();
    }
 
    &lt;span style="color: blue"&gt;function &lt;/span&gt;Geocode(postcode)
    {
      &lt;span style="color: blue"&gt;var &lt;/span&gt;localSearch = &lt;span style="color: blue"&gt;new &lt;/span&gt;GlocalSearch();
      localSearch.setSearchCompleteCallback(&lt;span style="color: blue"&gt;null&lt;/span&gt;,
        &lt;span style="color: blue"&gt;function&lt;/span&gt;()
        {
          &lt;span style="color: blue"&gt;if &lt;/span&gt;(localSearch.results[0])
          {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;results = localSearch.results[0];
 
            &lt;span style="color: blue"&gt;if &lt;/span&gt;((results.lat &amp;gt; 49) &amp;amp;&amp;amp; (results.lat &amp;lt; 61) &amp;amp;&amp;amp; (results.lng &amp;gt; -12) &amp;amp;&amp;amp; (results.lng &amp;lt; 3)) {
              document.getElementById(&lt;span style="color: #a31515"&gt;&amp;quot;postcode&amp;quot;&lt;/span&gt;).innerHTML = &lt;span style="color: #a31515"&gt;&amp;quot;Your random postcode is &amp;quot; &lt;/span&gt;+ postcode;
              document.getElementById(&lt;span style="color: #a31515"&gt;&amp;quot;lat&amp;quot;&lt;/span&gt;).value = results.lat;
              document.getElementById(&lt;span style="color: #a31515"&gt;&amp;quot;long&amp;quot;&lt;/span&gt;).value = results.lng;
            }
            &lt;span style="color: blue"&gt;else &lt;/span&gt;{
              TryCreateRandomPostcode();
            }
          }
          &lt;span style="color: blue"&gt;else
          &lt;/span&gt;{
            TryCreateRandomPostcode();
          }
        });
 
      localSearch.execute(postcode + &lt;span style="color: #a31515"&gt;&amp;quot;, UK&amp;quot;&lt;/span&gt;);
    }
 
    &lt;span style="color: green"&gt;/* ]]&amp;gt; */
  &lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;script&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Basically it tries to geocode the postcode and if it fails (or it lies outside the UK), assumes the postcode is not valid and tries a different postcode until a valid one is produced. This could all be done server-side by &lt;a href="http://doogalbellend.blogspot.com/2009/06/improving-local-search-net-call.html"&gt;hacking the Local Search API&lt;/a&gt;, but this met my simple needs.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4667851433155680070?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4667851433155680070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4667851433155680070' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4667851433155680070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4667851433155680070'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/04/building-random-postcode-generator.html' title='Building a random postcode generator'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4061427048081980535</id><published>2010-04-02T11:40:00.001+01:00</published><updated>2010-04-02T11:40:29.200+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Temporary file class for C#</title><content type='html'>&lt;p&gt;Quite frequently I need to create a temporary file, do some processing on it, then delete it. In order to ensure the file gets deleted, I put the file deletion code in a &lt;font face="Courier New"&gt;try…finally&lt;/font&gt;, which got me thinking about writing a simple class that implements &lt;font face="Courier New"&gt;IDisposable&lt;/font&gt; to handle this scenario, allowing me to use &lt;font face="Courier New"&gt;using&lt;/font&gt;. It’s very simple, but here it is anyway.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TemporaryFile &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;IDisposable
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;public &lt;/span&gt;TemporaryFile(&lt;span style="color: blue"&gt;string &lt;/span&gt;fileName)
    {
      &lt;span style="color: blue"&gt;this&lt;/span&gt;.fileName = fileName;
    }

    ~TemporaryFile()
    {
      DeleteFile();
    }

    &lt;span style="color: blue"&gt;public void &lt;/span&gt;Dispose()
    {
      DeleteFile();
      &lt;span style="color: #2b91af"&gt;GC&lt;/span&gt;.SuppressFinalize(&lt;span style="color: blue"&gt;this&lt;/span&gt;);
    }

    &lt;span style="color: blue"&gt;private string &lt;/span&gt;fileName;
    &lt;span style="color: blue"&gt;public string &lt;/span&gt;FileName
    {
      &lt;span style="color: blue"&gt;get &lt;/span&gt;{ &lt;span style="color: blue"&gt;return &lt;/span&gt;fileName;  }
    }

    &lt;span style="color: blue"&gt;private void &lt;/span&gt;DeleteFile()
    {
      &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.Exists(fileName))
        &lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.Delete(fileName);
    }
  }&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4061427048081980535?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4061427048081980535/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4061427048081980535' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4061427048081980535'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4061427048081980535'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/04/temporary-file-class-for-c.html' title='Temporary file class for C#'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2184235142689481391</id><published>2010-03-27T11:23:00.001Z</published><updated>2011-04-25T13:22:31.653+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jQuery zoom addin with image map support</title><content type='html'>&lt;p&gt;&lt;a href="http://doogalbellend.blogspot.com/2009/07/moozoom-with-image-maps.html"&gt;In the past I fiddled around with&lt;/a&gt; the &lt;a href="http://www.rborn.info/products.php"&gt;moozoom&lt;/a&gt; plugin to add support for image maps and on a new project I needed the same thing. But this new project uses a lot of jQuery stuff and jQuery and mootools don’t play together particularly well. Although there are a lot of image zooming plugins available for jQuery, none of them did exactly what I needed. I just wanted image zooming, panning and support for image maps. Anyway, this is what I came up with. This is my first adventure in the world of jQuery plugins, so it may not be a perfect implementation but it works for me…&lt;/p&gt;  &lt;p&gt;Usage is pretty simple&lt;/p&gt;  &lt;pre class="code"&gt;$(&lt;span style="color: #a31515"&gt;'.selector'&lt;/span&gt;).zoomable();&lt;/pre&gt;

&lt;p&gt;where .selector is the selector for your image. &lt;a href="http://www.doogal.co.uk/zoomable.php"&gt;View an example here&lt;/a&gt;. You can now also programmatically zoom in and out using &lt;font face="Courier New"&gt;$(&lt;span style="color: #a31515"&gt;'.selector'&lt;/span&gt;).zoomable(&lt;span style="color: #a31515"&gt;'zoomIn'&lt;/span&gt;);&lt;/font&gt; and &lt;font face="Courier New"&gt;$(&lt;span style="color: #a31515"&gt;'.selector'&lt;/span&gt;).zoomable(&lt;span style="color: #a31515"&gt;'zoomOut'&lt;/span&gt;);&lt;/font&gt; which you can hook up to buttons for users without a mouse wheel&lt;/p&gt;

&lt;p&gt;Here’s the code (requires jQuery and jQuery UI)&lt;/p&gt;

&lt;pre class="code"&gt;(&lt;span style="color: blue"&gt;function &lt;/span&gt;($) {
  $.fn.zoomable = &lt;span style="color: blue"&gt;function &lt;/span&gt;(method) {
 
    &lt;span style="color: blue"&gt;return this&lt;/span&gt;.each(&lt;span style="color: blue"&gt;function &lt;/span&gt;(index, value) {
      &lt;span style="color: #006400"&gt;// restore data, if there is any for this element
      &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;zoomData;
      &lt;span style="color: blue"&gt;if &lt;/span&gt;($(&lt;span style="color: blue"&gt;this&lt;/span&gt;).data(&lt;span style="color: maroon"&gt;'zoomData'&lt;/span&gt;) == &lt;span style="color: blue"&gt;null&lt;/span&gt;) {
        zoomData = {
          busy: &lt;span style="color: blue"&gt;false&lt;/span&gt;,
          x_fact: 1.2,
          currentZoom: 1,
          originalMap: &lt;span style="color: blue"&gt;null&lt;/span&gt;,
          currentX: 0,
          currentY: 0
        };
        $(&lt;span style="color: blue"&gt;this&lt;/span&gt;).data(&lt;span style="color: maroon"&gt;'zoomData'&lt;/span&gt;, zoomData);
      }
      &lt;span style="color: blue"&gt;else
        &lt;/span&gt;zoomData = $(&lt;span style="color: blue"&gt;this&lt;/span&gt;).data(&lt;span style="color: maroon"&gt;'zoomData'&lt;/span&gt;);
      
      &lt;span style="color: blue"&gt;var &lt;/span&gt;init = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(value.useMap != &lt;span style="color: maroon"&gt;&amp;quot;&amp;quot;&lt;/span&gt;) {
          &lt;span style="color: blue"&gt;var &lt;/span&gt;tempOriginalMap = document.getElementById(value.useMap.substring(1));
          zoomData.originalMap = tempOriginalMap.cloneNode(&lt;span style="color: blue"&gt;true&lt;/span&gt;);
          &lt;span style="color: #006400"&gt;// for IE6, we need to manually copy the areas' coords
          &lt;/span&gt;&lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; zoomData.originalMap.areas.length; i++)
            zoomData.originalMap.areas[i].coords = tempOriginalMap.areas[i].coords;
        }

        $(value).css(&lt;span style="color: maroon"&gt;'position'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'relative'&lt;/span&gt;).css(&lt;span style="color: maroon"&gt;'left'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'0'&lt;/span&gt;).css(&lt;span style="color: maroon"&gt;'top'&lt;/span&gt;, 0).css(&lt;span style="color: maroon"&gt;'margin'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'0'&lt;/span&gt;);

        $(value).draggable();

        &lt;span style="color: #006400"&gt;// jquery mousewheel not working in FireFox for some reason
        &lt;/span&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;($.browser.mozilla) {
          value.addEventListener(&lt;span style="color: maroon"&gt;'DOMMouseScroll'&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e) {
            e.preventDefault();
            zoomMouse(-e.detail);
          }, &lt;span style="color: blue"&gt;false&lt;/span&gt;);
          &lt;span style="color: blue"&gt;if &lt;/span&gt;(value.useMap != &lt;span style="color: maroon"&gt;&amp;quot;&amp;quot;&lt;/span&gt;) {
            $(value.useMap)[0].addEventListener(&lt;span style="color: maroon"&gt;'DOMMouseScroll'&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e) {
              e.preventDefault();
              zoomMouse(-e.detail);
            }, &lt;span style="color: blue"&gt;false&lt;/span&gt;);
          }
        }
        &lt;span style="color: blue"&gt;else &lt;/span&gt;{
          $(value).bind(&lt;span style="color: maroon"&gt;'mousewheel'&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e) {
            e.preventDefault();
            zoomMouse(e.wheelDelta);
          });
          &lt;span style="color: blue"&gt;if &lt;/span&gt;(value.useMap != &lt;span style="color: maroon"&gt;&amp;quot;&amp;quot;&lt;/span&gt;) {
            $(value.useMap).bind(&lt;span style="color: maroon"&gt;'mousewheel'&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e) {
              e.preventDefault();
              zoomMouse(e.wheelDelta);
            });
          }
        }

        $(value).bind(&lt;span style="color: maroon"&gt;'mousemove'&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e) {
          zoomData.currentX = e.pageX;
          zoomData.currentY = e.pageY;
        });
      };

      &lt;span style="color: blue"&gt;var &lt;/span&gt;left = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;parseInt($(value).css(&lt;span style="color: maroon"&gt;'left'&lt;/span&gt;));
      };
      
      &lt;span style="color: blue"&gt;var &lt;/span&gt;top = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;parseInt($(value).css(&lt;span style="color: maroon"&gt;'top'&lt;/span&gt;));
      }
      
      &lt;span style="color: blue"&gt;var &lt;/span&gt;zoomIn = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: #006400"&gt;// zoom as if mouse is in centre of image
        &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;parent = $(value).parent()[0];
        zoom(zoomData.x_fact, left()+parent.offsetLeft+(value.width/2), top()+parent.offsetTop+(value.height/2));
      };
      
      &lt;span style="color: blue"&gt;var &lt;/span&gt;zoomOut = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: #006400"&gt;// zoom as if mouse is in centre of image
        &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;yi = parseInt($(value).css(&lt;span style="color: maroon"&gt;'top'&lt;/span&gt;));
        &lt;span style="color: blue"&gt;var &lt;/span&gt;parent = $(value).parent()[0];
        zoom(1 / zoomData.x_fact, left()+parent.offsetLeft+(value.width/2), top()+parent.offsetTop+(value.height/2));
      };
      
      &lt;span style="color: blue"&gt;var &lt;/span&gt;zoomMouse = &lt;span style="color: blue"&gt;function &lt;/span&gt;(delta) {

        &lt;span style="color: #006400"&gt;// zoom out ---------------
        &lt;/span&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(delta &amp;lt; 0) {
          zoom(1 / zoomData.x_fact, zoomData.currentX, zoomData.currentY);
        }

        &lt;span style="color: #006400"&gt;// zoom in -----------
        &lt;/span&gt;&lt;span style="color: blue"&gt;else if &lt;/span&gt;(delta &amp;gt; 0) {
          zoom(zoomData.x_fact, zoomData.currentX, zoomData.currentY);
        }
      };

      &lt;span style="color: blue"&gt;var &lt;/span&gt;zoomMap = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: #006400"&gt;// resize image map
        &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;map = document.getElementById(value.useMap.substring(1));
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(map != &lt;span style="color: blue"&gt;null&lt;/span&gt;) {
          &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; map.areas.length; i++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;area = map.areas[i];
            &lt;span style="color: blue"&gt;var &lt;/span&gt;originalArea = zoomData.originalMap.areas[i];
            &lt;span style="color: blue"&gt;var &lt;/span&gt;coords = originalArea.coords.split(&lt;span style="color: maroon"&gt;','&lt;/span&gt;);
            &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;j = 0; j &amp;lt; coords.length; j++) {
              coords[j] = Math.round(coords[j] * zoomData.currentZoom);
            }
            &lt;span style="color: blue"&gt;var &lt;/span&gt;coordsString = &lt;span style="color: maroon"&gt;&amp;quot;&amp;quot;&lt;/span&gt;;
            &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;k = 0; k &amp;lt; coords.length; k++) {
              &lt;span style="color: blue"&gt;if &lt;/span&gt;(k &amp;gt; 0)
                coordsString += &lt;span style="color: maroon"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;;
              coordsString += coords[k];
            }
            area.coords = coordsString;
          }
        }
      };

      &lt;span style="color: blue"&gt;var &lt;/span&gt;zoom = &lt;span style="color: blue"&gt;function &lt;/span&gt;(fact, mouseX, mouseY) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(!zoomData.busy) {
          zoomData.busy = &lt;span style="color: blue"&gt;true&lt;/span&gt;;

          &lt;span style="color: blue"&gt;var &lt;/span&gt;xi = left();
          &lt;span style="color: blue"&gt;var &lt;/span&gt;yi = top();

          &lt;span style="color: blue"&gt;var &lt;/span&gt;new_h = (value.height * fact);
          &lt;span style="color: blue"&gt;var &lt;/span&gt;new_w = (value.width * fact);
          zoomData.currentZoom = zoomData.currentZoom * fact;

          &lt;span style="color: #006400"&gt;// calculate new X and y based on mouse position
          &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;parent = $(value).parent()[0];
          mouseX = mouseX - parent.offsetLeft
          &lt;span style="color: blue"&gt;var &lt;/span&gt;newImageX = (mouseX - xi) * fact;
          xi = mouseX - newImageX;

          mouseY = mouseY - parent.offsetTop
          &lt;span style="color: blue"&gt;var &lt;/span&gt;newImageY = (mouseY - yi) * fact;
          yi = mouseY - newImageY;

          $(value).animate({
            left: xi,
            top: yi,
            height: new_h,
            width: new_w
          }, 100, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            zoomData.busy = &lt;span style="color: blue"&gt;false&lt;/span&gt;;
          });

          zoomMap();
        }
      };
      
      &lt;span style="color: blue"&gt;if &lt;/span&gt;(method == &lt;span style="color: maroon"&gt;&amp;quot;zoomIn&amp;quot;&lt;/span&gt;)
        zoomIn();
      &lt;span style="color: blue"&gt;else if &lt;/span&gt;(method == &lt;span style="color: maroon"&gt;&amp;quot;zoomOut&amp;quot;&lt;/span&gt;)
        zoomOut();
      &lt;span style="color: blue"&gt;else
        &lt;/span&gt;init();
    });
  };
})(jQuery);&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2184235142689481391?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2184235142689481391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2184235142689481391' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2184235142689481391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2184235142689481391'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/03/jquery-zoom-addin-with-image-map.html' title='jQuery zoom addin with image map support'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4908791230871870943</id><published>2010-03-23T21:32:00.001Z</published><updated>2010-03-27T11:12:57.581Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jqGrid hints and tips</title><content type='html'>&lt;p&gt;I’ve been spending a lot of time with &lt;a href="http://www.trirand.com/blog/"&gt;jqGrid&lt;/a&gt; recently. It’s a marvellous piece of work but the documentation doesn’t always match up to the quality of the code. I’m not complaining, I realise it’s free, it’s flipping great and as a developer myself I know documentation is always the last thing I want to tackle. But here are a few things I’ve discovered along the way where it wasn’t immediately apparent what was causing the problem. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Multiple rows being selected&lt;/strong&gt; – This can be caused by a couple of things. The first thing to check is that the row IDs being used are sensible. My problem was caused by having some rows with the same ID. Another time the problem cropped up when I used email addresses as my row IDs. It seems the grid has problems with certain characters being used in IDs. The @ symbol did it for me but there are others, like spaces, that can cause problems.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Arbitrary XML&lt;/strong&gt; – The docs say that jqGrid can accept any arbitrary XML document, so long as you provide a mapping to describe how to map it to the standard XML format. This is almost true, but falls over if your XML document contains attributes that you want to pull into the grid. Attributes aren’t supported at all. I managed to get round this in a couple of steps. First I had to hack jqGrid so the &lt;font face="Courier New"&gt;loadComplete&lt;/font&gt; event was triggered before the grid was actually populated, rather than after. Then I could fiddle with my returned XML before jqGrid tried to read it. Then I wrote some code to convert attributes to elements with he same name (this could probably be improved with some jQuery magic but it did the job for me) and called it for the relevant attributes.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;setNodeText(newElem, text) {
  &lt;span style="color: blue"&gt;if &lt;/span&gt;(text != &lt;span style="color: blue"&gt;null&lt;/span&gt;) {
    &lt;span style="color: blue"&gt;try &lt;/span&gt;{
      newElem.textContent = text;
    }
    &lt;span style="color: blue"&gt;catch &lt;/span&gt;(e) {
      newElem.text = text;
    }
  }
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;convertAttributeToElement(xml, node, attributeName) {
  &lt;span style="color: blue"&gt;var &lt;/span&gt;name = node.getAttribute(attributeName);
  addChildNode(xml, node, attributeName, name);
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;addChildNode(xml, parent, nodeName, value) {
  &lt;span style="color: blue"&gt;var &lt;/span&gt;newElem = xml.createElement(nodeName);
  setNodeText(newElem, value);
  parent.appendChild(newElem);
}&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Word wrap&lt;/strong&gt; – I wish this was on by default, since it is now the first thing I do when I’m setting up a grid for the first time. Just modify the ui.jqgrid.css file so the definition for &lt;font face="Courier New"&gt;.ui-jqgrid tr.jqgrow td&lt;/font&gt; includes the following &lt;font face="Courier New"&gt;white-space: normal;&lt;/font&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4908791230871870943?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4908791230871870943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4908791230871870943' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4908791230871870943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4908791230871870943'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/03/jqgrid-hints-and-tips.html' title='jqGrid hints and tips'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7744263448930197524</id><published>2010-03-14T21:39:00.001Z</published><updated>2010-03-14T21:39:36.304Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>Forcing a .NET application to run as 32-bit under 64-bit Windows</title><content type='html'>&lt;p&gt;I was having problems with running a .NET application under 64-bit Windows 7. This was because by default, .NET applications are built to target any platform. This app clearly hadn’t been tested on 64-bit Windows so blew up when it tried to instantiate a 32-bit COM object. The following link explains some of the options on how to fix this problem.&amp;#160;&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;a title="http://www.lostechies.com/blogs/gabrielschenker/archive/2009/10/21/force-net-application-to-run-in-32bit-process-on-64bit-os.aspx" href="http://www.lostechies.com/blogs/gabrielschenker/archive/2009/10/21/force-net-application-to-run-in-32bit-process-on-64bit-os.aspx"&gt;http://www.lostechies.com/blogs/gabrielschenker/archive/2009/10/21/force-net-application-to-run-in-32bit-process-on-64bit-os.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Obviously setting the platform target wasn’t an option for me since I didn’t have access to the source code. Using corflags.exe also wasn’t possible because the application was strong named so setting that flag would break the strong naming. So I was forced to create a wrapper application that was compiled for 32-bits. This initially also didn’t work but the only thing I needed to do was add the &lt;font face="Courier New"&gt;STAThread&lt;/font&gt; attribute to my wrapper’s Main method since the application I was calling also had this attribute on its Main method. Then it all worked, hurrah!&lt;/p&gt;  &lt;p&gt;Now I just need to figure out how to do the same thing for a .NET service… And for all you .NET developers out there, if you don’t have the time or resources to test on 64-bit Windows, just flip that platform target switch to x86. Even the cheapest new PCs come with 64-bit versions of Windows (my laptop cost £400) and flipping that switch pretty much guarantees your app will work on 64-bit platforms.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7744263448930197524?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7744263448930197524/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7744263448930197524' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7744263448930197524'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7744263448930197524'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/03/forcing-net-application-to-run-as-32.html' title='Forcing a .NET application to run as 32-bit under 64-bit Windows'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6806258481459539685</id><published>2010-03-12T10:06:00.001Z</published><updated>2010-03-17T21:45:55.513Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><title type='text'>XML encoding in PHP</title><content type='html'>&lt;p&gt;I’ve needed it in &lt;a href="http://doogalbellend.blogspot.com/2009/09/xml-encoding-in-javascript.html"&gt;JavaScript in the past&lt;/a&gt; and now I need it in PHP. Escape all those pesky special XML characters like so&lt;/p&gt;  &lt;pre&gt;  function XmlEncode($content)
  {
    $trans = array(&amp;quot;&amp;amp;&amp;quot; =&amp;gt; &amp;quot;&amp;amp;amp;&amp;quot;, &amp;quot;&amp;lt;&amp;quot; =&amp;gt; &amp;quot;&amp;amp;lt;&amp;quot;, &amp;quot;&amp;gt;&amp;quot; =&amp;gt; &amp;quot;&amp;amp;gt;&amp;quot;, &amp;quot;'&amp;quot; =&amp;gt; &amp;quot;&amp;amp;apos;&amp;quot;,
      &amp;quot;\&amp;quot;&amp;quot; =&amp;gt; &amp;quot;&amp;amp;quot;&amp;quot;, &amp;quot;’&amp;quot; =&amp;gt; &amp;quot;&amp;amp;apos;&amp;quot;);
  	return strtr($content, $trans);
  }&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6806258481459539685?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6806258481459539685/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6806258481459539685' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6806258481459539685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6806258481459539685'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/03/xml-encoding-in-php.html' title='XML encoding in PHP'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6574245277682471939</id><published>2010-03-05T21:27:00.006Z</published><updated>2010-03-05T21:36:39.053Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Maths'/><title type='text'>Collatz conjecture implementation</title><content type='html'>I only learnt about the &lt;a href="http://en.wikipedia.org/wiki/Collatz_conjecture"&gt;Collatz conjecture&lt;/a&gt; today so thought I'd implement it in JavaScript. Type in a starting number and watch as the process converges to 1, probably
&lt;br/&gt;

&lt;script type="text/javascript"&gt;
        function StartCollatz() {
            document.getElementById('results').value = '';
            CollatzConjecture(parseInt(document.getElementById('startNumber').value), 1);
        }     
  
        function CollatzConjecture(n, currentCount) {
            document.getElementById('results').value += currentCount + ' ' + n + '\r\n';
            if (n &gt; 1) {
                if (n % 2 == 1)
                    CollatzConjecture(3 * n + 1, currentCount+1)
                else
                    CollatzConjecture(n / 2, currentCount+1);
            }
        }
    &lt;/script&gt;
&lt;input id="startNumber"&gt;
&lt;input onclick="javascript:StartCollatz()" value="Collatz!" type="button"&gt;
&lt;br/&gt;
&lt;textarea id="results" rows="30"&gt;&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6574245277682471939?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6574245277682471939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6574245277682471939' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6574245277682471939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6574245277682471939'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/03/collatz-conjecture-implementation.html' title='Collatz conjecture implementation'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8187368974200781957</id><published>2010-02-15T21:17:00.001Z</published><updated>2010-02-15T21:17:22.048Z</updated><title type='text'>Hollands Pies reviewed</title><content type='html'>&lt;p&gt;So I’m slowly working my way through &lt;a href="http://doogalbellend.blogspot.com/2010/01/buying-steak-and-kidney-puddings-on-web.html"&gt;the pies I purchased the other day&lt;/a&gt; and he’s what I think of them so far. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Meat pie&lt;/strong&gt; – this is a little pie, about the size of a standard pork pie but heated up. It contains pork and beef and tastes pretty yummy. My only complaint is the scalding hot liquid contained within which burnt my mouth first time, although this was easily rectified second time around by slicing the pie in half. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Cheese and onion&lt;/strong&gt; – many years ago when I was a vegetarian, this was one of the few pies I was able to partake of. That and the &lt;a href="http://en.wikipedia.org/wiki/Butter_pie"&gt;butter pie&lt;/a&gt;, which seems to have disappeared from the face of the earth, probably due to its unfortunate name. It did contain more than just butter, which is why I didn’t collapse from heart failure. But back to the cheese and onion pie. Unfortunately this was disappointing, there was very little evidence of onion, just some bright yellow gloop that may well have originated at the Sellafield reprocessing plant (I joke of course, before anyone tries to sue me). &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Potato and meat&lt;/strong&gt; – before some government organisation got involved, this was known as a meat and potato pie. Now it’s just a perfectly decent pie with a silly name.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Steak and kidney pudding&lt;/strong&gt; – this is where things get interesting. This is not like any other pie you’ll ever experience. With most pies the pastry is essentially just some hard packaging to keep the contents together, with little merit of its own. But with the steak and kidney pudding, the suet case is an essential part of the whole experience. Not only are the contents tasty, but so is the pastry. Even the cooking instructions are unique. No oven for this pie, but a pan of boiling water. There is one downside to this. Whereas pies are generally a pretty portable food stuff, the steak and kidney pudding needs a plate and fork.&lt;/p&gt;  &lt;p&gt;So in conclusion, I would recommend all of these pies from Hollands, except the cheese and onion. But for ultimate pleasure, order ten steak and kidney puddings…&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8187368974200781957?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8187368974200781957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8187368974200781957' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8187368974200781957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8187368974200781957'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/02/hollands-pies-reviewed.html' title='Hollands Pies reviewed'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8357355805410422267</id><published>2010-02-13T16:01:00.001Z</published><updated>2010-02-13T16:01:24.189Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Metastorm'/><category scheme='http://www.blogger.com/atom/ns#' term='FreeFlow'/><title type='text'>FreeFlow now open source</title><content type='html'>&lt;p&gt;I don’t really have time to maintain it anymore, so I’ve open sourced FreeFlow, my little toolkit and administration tool for Metastorm BPM. Go grab it from &lt;a href="http://freeflow.codeplex.com/"&gt;CodePlex&lt;/a&gt; and make it better! &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8357355805410422267?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8357355805410422267/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8357355805410422267' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8357355805410422267'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8357355805410422267'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/02/freeflow-now-open-source.html' title='FreeFlow now open source'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7400091533063331027</id><published>2010-02-10T10:17:00.001Z</published><updated>2010-02-10T12:31:20.191Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='WCF'/><title type='text'>Using PUT and DELETE in WCF web services</title><content type='html'>&lt;p&gt;&lt;a href="http://www.doogal.co.uk/blogimages/UsingPUTandDELETEinWCFwebservices_99E9/image.png"&gt;&lt;img style="margin: 0px 10px 0px 0px; display: inline" title="IIS7 handler mappings" alt="IIS7 handler mappings" align="left" src="http://www.doogal.co.uk/blogimages/UsingPUTandDELETEinWCFwebservices_99E9/image_thumb.png" width="531" height="372" /&gt;&lt;/a&gt;I’ve been doing a little work on a web page that needs to grab some data from a web service and then update that data via PUT and DELETE requests. I haven’t got access to the web service currently so decided to write my own dummy implementation using WCF. &lt;/p&gt;  &lt;p&gt;The first problem I encountered was that by default PUT and DELETE calls won’t be allowed at all by IIS. This can be resolved by fiddling with the Handler Mappings options in IIS7 to allow the required verbs, as shown on the left.&lt;/p&gt;  &lt;p&gt;My next problem was that I wanted to use the same URL for both PUT and DELETE requests. The &lt;font face="Courier New"&gt;WebInvoke&lt;/font&gt; attribute that is applied to your WCF method doesn’t support multiple HTTP verbs, so I was a little stumped at this point. The solution is actually pretty straightforward, have multiple methods pointing to the same URL, like so&lt;/p&gt;  &lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;WebInvoke&lt;/span&gt;(Method = &lt;span style="color: #a31515"&gt;&amp;quot;DELETE&amp;quot;&lt;/span&gt;, UriTemplate = &lt;span style="color: #a31515"&gt;&amp;quot;EditFolder&amp;quot;&lt;/span&gt;)]
&lt;span style="color: blue"&gt;public void &lt;/span&gt;DeleteFolder(&lt;span style="color: #2b91af"&gt;Stream &lt;/span&gt;input)&lt;/pre&gt;
This is actually probably a good restriction by WCF. If you want different verb types on the same URL, the chances are that you have two different operations anyway. 

  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7400091533063331027?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7400091533063331027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7400091533063331027' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7400091533063331027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7400091533063331027'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/02/using-put-and-delete-in-wcf-web.html' title='Using PUT and DELETE in WCF web services'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7976478633709472858</id><published>2010-02-03T21:37:00.001Z</published><updated>2010-02-03T21:37:48.606Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Unminifying Javascript files</title><content type='html'>&lt;p&gt;Am I a web developer? I dunnow, I’ve spent a lot of time building web sites but I don’t really feel like a web developer. The web just plain scares me. Debug any web application and you’re faced with shedloads of anonymous functions, eval code and general weirdness. Look at the poster boys of Web 2.0, JQuery and its children JQuery UI and JQGrid. Yes they are fecking marvellous bits of technology but try stepping through the code and tell me you have the faintest idea what on earth is going on. It feels like the web is held together with a pile of JavaScript sellotape.&lt;/p&gt;  &lt;p&gt;But in some kind of sado-masochistic twist, the web is now even more convoluted because the JavaScript is even less readable because it’s all been minified in an attempt to save bandwidth. So now if I find myself in a debugger trying to figure out what some JavaScript is doing, I see one long line of code with useless function names and I go off to bang my head against a wall because it’s less painful. But I have found one tool that improves the situation, &lt;a href="http://jsbeautifier.org/"&gt;JS Beautifier&lt;/a&gt;, which will attempt to make your minified code more readable and understandable by a human. Beyond the initial rant, this is primarily a reminder to myself.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7976478633709472858?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7976478633709472858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7976478633709472858' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7976478633709472858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7976478633709472858'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/02/unminifying-javascript-files.html' title='Unminifying Javascript files'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1685511772594960170</id><published>2010-01-31T22:03:00.001Z</published><updated>2010-01-31T22:19:25.536Z</updated><title type='text'>David Cameron defaced</title><content type='html'>&lt;p&gt;&lt;a href="http://www.doogal.co.uk/blogimages/DavidCamerondefaced_13649/poster.jpg"&gt;&lt;img style="margin: 0px 10px 0px 0px; display: inline" title="David Cameron defaced" alt="David Cameron defaced" align="left" src="http://www.doogal.co.uk/blogimages/DavidCamerondefaced_13649/poster_thumb.jpg" width="240" height="120" /&gt;&lt;/a&gt; When the smug face of David Cameron started popping up on billboards all over the place, I had a very strong urge to deface it. It would appear I wasn’t the only one, although these are generally more amusing than what I had in mind.&lt;/p&gt;  &lt;p&gt;&lt;a title="http://www.mydavidcameron.com/" href="http://www.mydavidcameron.com/"&gt;http://www.mydavidcameron.com/&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1685511772594960170?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1685511772594960170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1685511772594960170' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1685511772594960170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1685511772594960170'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/01/david-cameron-defaced.html' title='David Cameron defaced'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1059580501991668769</id><published>2010-01-30T21:35:00.001Z</published><updated>2010-01-30T21:35:23.847Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Metastorm'/><title type='text'>Metastorm release BPM 9 and don’t tell anyone</title><content type='html'>&lt;p&gt;Here’s an odd thing. When most software companies release a new version of their software, they shout about it until they are hoarse. But Metastorm have released version 9 of their BPM software and as far as I can see they haven’t even produced a press release to announce it to the world. Searching on &lt;a href="http://news.google.com/news?hl=en&amp;amp;ned=uk&amp;amp;q=metastorm+bpm+9"&gt;Google News&lt;/a&gt; brings back no results and I can’t see any mention of it on the Metastorm website.&lt;/p&gt;  &lt;p&gt;Initially I’d assumed this was because version 9 was released in the quiet period before Christmas and the PR onslaught would start in the new year, but here we are well into 2010 and there is still silence.&lt;/p&gt;  &lt;p&gt;Now generally if it’s a choice between cock-up and conspiracy, I’ll plump for cock-up every time but I really can’t believe that any software company can forget to produce a press release to announce their new baby to the world so I have to go for the conspiracy option but what is the conspiracy? Answers on a postcode or in the comments…&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1059580501991668769?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1059580501991668769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1059580501991668769' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1059580501991668769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1059580501991668769'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/01/metastorm-release-bpm-9-and-dont-tell.html' title='Metastorm release BPM 9 and don’t tell anyone'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6191276442264527988</id><published>2010-01-28T20:57:00.001Z</published><updated>2010-01-28T20:57:31.526Z</updated><title type='text'>Buying steak and kidney puddings on the web</title><content type='html'>&lt;p&gt;I was reminded of steak and kidney puddings some time ago when I read Stuart Maconie’s &lt;em&gt;“Pies and Prejudice”&lt;/em&gt; and suddenly had a strong desire to once again experience the long forgotten taste of them. But my searches on the web to find somewhere to purchase them in this culinary wasteland called London led me nowhere. Then the other day I received an email from Holland's Pies and ventured back to their website. I then discovered they have recently opened their &lt;a href="http://www.hollandspies.co.uk/shop/online-shop"&gt;online shop&lt;/a&gt; with a selection of their pies available. So £18 and a couple of days later I am now in possession of ten pies (including several steak and kidney pudds)and am looking forward to sampling one for my lunch tomorrow… &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6191276442264527988?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6191276442264527988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6191276442264527988' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6191276442264527988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6191276442264527988'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/01/buying-steak-and-kidney-puddings-on-web.html' title='Buying steak and kidney puddings on the web'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7607812453532329197</id><published>2010-01-25T21:25:00.001Z</published><updated>2010-01-25T21:25:50.351Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Environment'/><title type='text'>Cost benefit analysis of energy efficient bulbs</title><content type='html'>&lt;p&gt;We’ve had six GU10 bulbs in our kitchen for a while now and I’ve never really been very happy with them. They swallow a lot of power, don’t seem to last very long and are pretty expensive to replace. I got hold of an LED replacement some time ago and was less than impressed. It didn’t produce enough light, was really really expensive and then died after a couple of months. So when I popped into Maplin at the weekend, I thought I’d try out &lt;a href="http://www.maplin.co.uk/Module.aspx?ModuleNo=220698"&gt;their low-energy GU10 replacement&lt;/a&gt;. Admittedly these are even more expensive than the full powered version, but they claim to last for 8000 hours and use a lot less power. I’m happy with the amount of light they throw out, although they suffer from the usual problem of taking a while to warm up. Anyway, I thought I’d do a quick calculation of how long it would take me to earn back the money I shelled out for them. &lt;/p&gt;  &lt;p&gt;Cost per KWH – 0.15&lt;/p&gt;  &lt;p&gt;Old 6 bulbs @ 50W = 300W&lt;/p&gt;  &lt;p&gt;New 6 bulbs @ 11W = 66W&lt;/p&gt;  &lt;p&gt;KWHs saved = 0.3-0.066 = 0.234 KWHs&lt;/p&gt;  &lt;p&gt;Cost of bulbs = £7.49*6 = £45&lt;/p&gt;  &lt;p&gt;Number of KWHs required to cover cost = 45/0.15 = 300KWHs&lt;/p&gt;  &lt;p&gt;Number of hours required to cover cost = 300/0.234 = 1282 hours&lt;/p&gt;  &lt;p&gt;Assuming 5 hours use a day = 1282/5 days = 256 days = 8.5 months&lt;/p&gt;  &lt;p&gt;So based on this very rough calculation and assuming I’ve calculated it correctly, this would appear to be a good deal. Fingers crossed they actually last for as long as claimed, which I’m reasonably confident about based on my experiences with other energy efficient bulbs.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7607812453532329197?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7607812453532329197/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7607812453532329197' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7607812453532329197'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7607812453532329197'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/01/cost-benefit-analysis-of-energy.html' title='Cost benefit analysis of energy efficient bulbs'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6862010863324156859</id><published>2010-01-04T21:28:00.001Z</published><updated>2010-01-04T21:28:21.972Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Getting all the points in a SqlGeometry</title><content type='html'>&lt;p&gt;It’s quite simple to get hold of the points in a SQL Server geometry polygon using the &lt;font face="Courier New"&gt;STNumPoints&lt;/font&gt; and &lt;font face="Courier New"&gt;STPointN&lt;/font&gt; SQL functions, but this requires quite a few queries (or some kind of stored procedure). I tend not to like running lots of queries, even if that does smell like premature optimisation, and for my little project I don’t want to be adding stored procedures but then I realised the SqlGeometry type is implemented as a .NET type, so all its properties and methods are available from .NET code, so here’s a little extension method to get all the points for a polygon&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public static class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;GeometryHelper
&lt;/span&gt;{
  &lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlGeometry&lt;/span&gt;[] Points(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlGeometry &lt;/span&gt;geometry)
  {
    &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;SqlGeometry&lt;/span&gt;&amp;gt; points = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;SqlGeometry&lt;/span&gt;&amp;gt;();
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;i = 1; i &amp;lt;= geometry.STNumPoints(); i++)
    {
      points.Add(geometry.STPointN(i));
    }
    &lt;span style="color: blue"&gt;return &lt;/span&gt;points.ToArray();
  }
}&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6862010863324156859?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6862010863324156859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6862010863324156859' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6862010863324156859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6862010863324156859'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/01/getting-all-points-in-sqlgeometry.html' title='Getting all the points in a SqlGeometry'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4278751150852852229</id><published>2010-01-03T22:00:00.001Z</published><updated>2010-01-03T22:00:33.583Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Converting a SqlDouble to a .NET double</title><content type='html'>&lt;p&gt;Note to self - this is simple, just typecast like so&lt;/p&gt;  &lt;pre class="code"&gt;(&lt;span style="color: blue"&gt;double&lt;/span&gt;)points[i].STX&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4278751150852852229?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4278751150852852229/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4278751150852852229' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4278751150852852229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4278751150852852229'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/01/converting-sqldouble-to-net-double.html' title='Converting a SqlDouble to a .NET double'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3930507381741707094</id><published>2010-01-03T13:14:00.001Z</published><updated>2010-01-03T13:14:31.294Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='SEO'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><title type='text'>Improving performance with Gzip compression in PHP</title><content type='html'>&lt;p&gt;I was perusing through some of the data provided by &lt;a href="https://www.google.com/webmasters/tools"&gt;Google Webmaster Tools&lt;/a&gt; when I came upon the Site Performance section. this told me that &lt;a href="http://www.doogal.co.uk/"&gt;doogal.co.uk&lt;/a&gt; took longer to load than 96% of sites on the web, which was a little embarrassing. I knew what was causing at least some of this slowdown, the incomplete &lt;a href="http://www.doogal.co.uk/UKPostcodes.php"&gt;list of UK postcodes&lt;/a&gt; which has been getting gradually slower as the number of postcodes has got longer and longer. I’d already started to address that by paging the data, but &lt;a href="http://www.doogal.co.uk/UKPostcodesCSV.php"&gt;the CSV data&lt;/a&gt; couldn’t really be paged, without reducing the value of it. So I had a look at the suggestion provided by Google, which was to use Gzip compression on the page. I’ve not really thought much about GZip compression before, assuming if it was so useful it would be on by default but I thought I’d give it a go anyway. So I fired up Fiddler and tried downloading my big CSV page and it took approximately 9.5 seconds to fully download. I arrived at this figure as an average after several reloads.&lt;/p&gt;  &lt;p&gt;Next I added this line to my PHP source file, &lt;font face="Courier New"&gt;ob_start(&amp;quot;ob_gzhandler&amp;quot;);&lt;/font&gt;, and measured the difference. It now took 3.7 seconds to download, wow! A lot of gain for very little effort.&lt;/p&gt;  &lt;p&gt;At this point, I wondered if I could add Gzip compression to all my pages. It turns out this is straightforward, just add the following to the .htaccess file&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;php_flag zlib.output_compression on&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;After adding this, the CSV page now arrived in 2.7 seconds. I’m not sure why this is even faster, but I’m not complaining. Now the rest of the site feels much snappier as well. So what am I missing? Is there a reason GZip compression isn’t on by default? Am I going to get bitten by this at some point in the future? &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3930507381741707094?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3930507381741707094/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3930507381741707094' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3930507381741707094'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3930507381741707094'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2010/01/improving-performance-with-gzip.html' title='Improving performance with Gzip compression in PHP'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1967666810787331280</id><published>2009-12-22T21:30:00.001Z</published><updated>2009-12-22T21:30:22.586Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><category scheme='http://www.blogger.com/atom/ns#' term='OS OpenSpace'/><title type='text'>Getting boundary data out of OS OpenSpace</title><content type='html'>&lt;p&gt;&lt;a href="http://www.doogal.co.uk/blogimages/GettingboundarydataoutofOSOpenSpace_12E5D/image.png"&gt;&lt;img style="margin: 0px 0px 0px 10px; display: inline" title="County boundaries in SQL Server" alt="County boundaries in SQL Server" align="right" src="http://www.doogal.co.uk/blogimages/GettingboundarydataoutofOSOpenSpace_12E5D/image_thumb.png" width="157" height="240" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I’ve been able to pull out a single county’s boundary data from OS OpenSpace using its JavaScript API, but I wanted to get the data for all counties. Now I could have used &lt;a href="http://www.doogal.co.uk/OpenSpace.php"&gt;my little example webpage&lt;/a&gt; to do this manually for each county, but that would be extremely tedious. So based on the fact that all AJAX APIs can be considered as simple HTTP requests, I wrote a little piece of C# code that made the required HTTP request and dumped the data into my SQL Server table. This is very rough and ready but it met my needs. Some time soon I will probably put together a class to handle all the OS Open Space interaction a bit more nicely and with more functionality.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;      HttpWebRequest &lt;/span&gt;req = (&lt;span style="color: #2b91af"&gt;HttpWebRequest&lt;/span&gt;)&lt;span style="color: #2b91af"&gt;WebRequest&lt;/span&gt;.Create(
        &lt;span style="color: #a31515"&gt;&amp;quot;http://openspace.ordnancesurvey.co.uk/osmapapi/boundary?geometry=true&amp;amp;&amp;quot;&lt;/span&gt;+
        &lt;span style="color: #a31515"&gt;&amp;quot;key=74B560DED619715AE0405F0AF060615A&amp;amp;f=xml&amp;amp;&amp;quot; &lt;/span&gt;+
        &lt;span style="color: #a31515"&gt;&amp;quot;url=http%3A%2F%2Flocalhost%2FOpenSpaceBoundary%2FDefault.aspx&amp;amp;area_code=CTY&amp;amp;&amp;quot; &lt;/span&gt;+
        &lt;span style="color: #a31515"&gt;&amp;quot;bbox=0%2C0%2C1400000%2C1400000&amp;amp;resolution=1000&amp;amp;dojo.preventCache=1261317566652&amp;amp;&amp;quot; &lt;/span&gt;+
        &lt;span style="color: #a31515"&gt;&amp;quot;callback=dojo.io.script.jsonp_dojoIoScript4._jsonpCallback&amp;quot;&lt;/span&gt;);
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;WebResponse &lt;/span&gt;resp = req.GetResponse())
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Stream &lt;/span&gt;respStream = resp.GetResponseStream())
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;StreamReader &lt;/span&gt;reader = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StreamReader&lt;/span&gt;(respStream))
      {
        &lt;span style="color: blue"&gt;string &lt;/span&gt;response = reader.ReadToEnd();
        &lt;span style="color: #2b91af"&gt;XmlDocument &lt;/span&gt;xmlDoc = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;XmlDocument&lt;/span&gt;();
        xmlDoc.LoadXml(response);

        &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlConnection &lt;/span&gt;conn = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlConnection&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=OGC;Data Source=NEWDOOGAL&amp;quot;&lt;/span&gt;))
        {
          conn.Open();
          &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlCommand &lt;/span&gt;command = conn.CreateCommand())
          {
            command.CommandText = &lt;span style="color: #a31515"&gt;&amp;quot;DELETE FROM CountyBoundaries&amp;quot;&lt;/span&gt;;
            command.ExecuteNonQuery();
          }

          &lt;span style="color: #2b91af"&gt;JavaScriptSerializer &lt;/span&gt;serializer = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;JavaScriptSerializer&lt;/span&gt;();
          &lt;span style="color: #2b91af"&gt;XmlNodeList &lt;/span&gt;nodes = xmlDoc.SelectNodes(&lt;span style="color: #a31515"&gt;&amp;quot;BoundaryResultVO/items/item/geojson&amp;quot;&lt;/span&gt;);
          &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;XmlElement &lt;/span&gt;element &lt;span style="color: blue"&gt;in &lt;/span&gt;nodes)
          {
            &lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: blue"&gt;object&lt;/span&gt;&amp;gt; data = 
              (&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: blue"&gt;object&lt;/span&gt;&amp;gt;)serializer.DeserializeObject(element.InnerText);
            &lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: blue"&gt;object&lt;/span&gt;&amp;gt; properties = (&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: blue"&gt;object&lt;/span&gt;&amp;gt;)data[&lt;span style="color: #a31515"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;];
            &lt;span style="color: blue"&gt;string &lt;/span&gt;countyName = (&lt;span style="color: blue"&gt;string&lt;/span&gt;)properties[&lt;span style="color: #a31515"&gt;&amp;quot;NAME&amp;quot;&lt;/span&gt;];

            &lt;span style="color: green"&gt;// get polygon data
            &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: blue"&gt;object&lt;/span&gt;&amp;gt; geometry = (&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: blue"&gt;object&lt;/span&gt;&amp;gt;)data[&lt;span style="color: #a31515"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;];
            &lt;span style="color: blue"&gt;object&lt;/span&gt;[] coordinates = (&lt;span style="color: blue"&gt;object&lt;/span&gt;[])geometry[&lt;span style="color: #a31515"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;];
            
            &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;i = 0; i &amp;lt; coordinates.Length; i++)
            {
              &lt;span style="color: #2b91af"&gt;StringBuilder &lt;/span&gt;wktBuilder = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StringBuilder&lt;/span&gt;();
              &lt;span style="color: blue"&gt;object&lt;/span&gt;[] polygonParts = (&lt;span style="color: blue"&gt;object&lt;/span&gt;[])coordinates[i];
              wktBuilder.Append(&lt;span style="color: #a31515"&gt;&amp;quot;POLYGON(&amp;quot;&lt;/span&gt;);
              &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;j = 0; j &amp;lt; polygonParts.Length; j++)
              {
                &lt;span style="color: blue"&gt;if &lt;/span&gt;(j&amp;gt;0)
                  wktBuilder.Append(&lt;span style="color: #a31515"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;);
                wktBuilder.Append(&lt;span style="color: #a31515"&gt;&amp;quot;(&amp;quot;&lt;/span&gt;);
                &lt;span style="color: blue"&gt;object&lt;/span&gt;[] points = (&lt;span style="color: blue"&gt;object&lt;/span&gt;[])polygonParts[j];
                &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;k = 0; k &amp;lt; points.Length; k++)
                {
                  &lt;span style="color: blue"&gt;object&lt;/span&gt;[] point = (&lt;span style="color: blue"&gt;object&lt;/span&gt;[])points[k];
                  &lt;span style="color: blue"&gt;if &lt;/span&gt;(k &amp;gt; 0)
                    wktBuilder.Append(&lt;span style="color: #a31515"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;);
                  wktBuilder.Append(point[0]);
                  wktBuilder.Append(&lt;span style="color: #a31515"&gt;&amp;quot; &amp;quot;&lt;/span&gt;);
                  wktBuilder.Append(point[1]);
                }
                wktBuilder.Append(&lt;span style="color: #a31515"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;);
              }
              wktBuilder.Append(&lt;span style="color: #a31515"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;);

              &lt;span style="color: green"&gt;// put into database
              &lt;/span&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlCommand &lt;/span&gt;command = conn.CreateCommand())
              {
                command.CommandText = &lt;span style="color: blue"&gt;string&lt;/span&gt;.Format(
                  &lt;span style="color: #a31515"&gt;&amp;quot;INSERT INTO CountyBoundaries (County, Boundary) VALUES ('{0}', &amp;quot; &lt;/span&gt;+
                  &lt;span style="color: #a31515"&gt;&amp;quot;geometry::STPolyFromText('{1}', 4277))&amp;quot;&lt;/span&gt;, countyName, wktBuilder.ToString());
                command.ExecuteNonQuery();
              }
            }
          }
        }
      }&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1967666810787331280?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1967666810787331280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1967666810787331280' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1967666810787331280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1967666810787331280'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/12/getting-boundary-data-out-of-os.html' title='Getting boundary data out of OS OpenSpace'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8214119754661097047</id><published>2009-12-10T06:22:00.001Z</published><updated>2009-12-10T06:22:42.439Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Getting all available geographic reference systems out of SQL Server 2008</title><content type='html'>&lt;p&gt;As a reminder to myself, the query required to get a list of all available geographic reference systems out of SQL Server 2008 is &lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;SELECT &lt;/span&gt;&lt;span style="color: gray"&gt;* &lt;/span&gt;&lt;span style="color: blue"&gt;FROM &lt;/span&gt;&lt;span style="color: green"&gt;sys&lt;/span&gt;&lt;span style="color: gray"&gt;.&lt;/span&gt;&lt;span style="color: green"&gt;spatial_reference_systems&lt;/span&gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8214119754661097047?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8214119754661097047/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8214119754661097047' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8214119754661097047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8214119754661097047'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/12/getting-all-available-geographic.html' title='Getting all available geographic reference systems out of SQL Server 2008'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7238076939263142058</id><published>2009-12-09T21:52:00.001Z</published><updated>2009-12-10T06:19:46.906Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='GIS'/><title type='text'>uDig – free GIS</title><content type='html'>&lt;p&gt;To be frank I don’t much like anything Java based but &lt;a href="http://udig.refractions.net/"&gt;uDig&lt;/a&gt; is the exception that probably proves the rule. It’s a free GIS based on Eclipse and it is pretty user friendly. Unfortunately it doesn’t handle data stored in SQL Server 2008, which marks it down a bit in my eyes, but it does support most other data sources.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7238076939263142058?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7238076939263142058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7238076939263142058' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7238076939263142058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7238076939263142058'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/12/udig-free-gis.html' title='uDig – free GIS'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-812457449988228471</id><published>2009-11-28T22:00:00.001Z</published><updated>2009-11-28T22:00:02.347Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Using OGC functions in SQL Server 2008 part 5 – Google Maps and polygons</title><content type='html'>&lt;p&gt;&lt;a href="http://www.doogal.co.uk/blogimages/UsingOGCfunctionsinSQLServer2008part5Goo_12C24/image.png"&gt;&lt;img style="margin: 0px 0px 0px 10px; display: inline" title="County boundary " alt="County boundary " align="right" src="http://www.doogal.co.uk/blogimages/UsingOGCfunctionsinSQLServer2008part5Goo_12C24/image_thumb.png" width="240" height="180" /&gt;&lt;/a&gt;In this series of posts I’ve so far managed to &lt;a href="http://doogalbellend.blogspot.com/2009/09/using-ogc-functions-in-sql-server-2008.html"&gt;pull some spatial data into SQL Server&lt;/a&gt;, query it in a &lt;a href="http://doogalbellend.blogspot.com/2009/09/using-ogc-functions-in-sql-server-2008_27.html"&gt;number&lt;/a&gt; of &lt;a href="http://doogalbellend.blogspot.com/2009/11/using-ogc-functions-in-sql-server-2008.html"&gt;ways&lt;/a&gt; and finally been &lt;a href="http://doogalbellend.blogspot.com/2009/11/using-ogc-functions-in-sql-server-2008_25.html"&gt;able to display some of that data in Google Maps&lt;/a&gt;. The next thing I wanted to do was to display the county boundary for Lancashire, since it was the postcodes for that great county I was displaying. I’ve already got the data for the county boundary from &lt;a href="http://openspace.ordnancesurvey.co.uk/openspace/"&gt;OS OpenSpace&lt;/a&gt; but the problem is this data is in UK OS coordinates rather than latitude and longitude as used by Google Maps. There are two ways to deal with this. Convert the data to latitude/longitude before displaying it or implement the &lt;a href="http://code.google.com/apis/maps/documentation/reference.html#GProjection"&gt;GProjection interface&lt;/a&gt; to handle the conversion in the web page. The latter is something I want to look at at some point but for this I decided to do the conversion server-side. &lt;/p&gt;  &lt;p&gt;So the first thing I did was to write a simple generic handler to generate an XML document containing the points for the county boundary. This code uses the &lt;a href="http://www.doogal.co.uk/dotnetcoords.php"&gt;.NET Coordinates library&lt;/a&gt; I have converted from the &lt;a href="http://www.jstott.me.uk/jcoord/"&gt;Java original&lt;/a&gt;. This is what the handler looks like&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  public void &lt;/span&gt;ProcessRequest (&lt;span style="color: #2b91af"&gt;HttpContext &lt;/span&gt;context) 
  {
    context.Response.ContentType = &lt;span style="color: #a31515"&gt;&amp;quot;text/xml&amp;quot;&lt;/span&gt;;
    &lt;span style="color: #2b91af"&gt;StringWriter &lt;/span&gt;stringWriter = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StringWriter&lt;/span&gt;();
    &lt;span style="color: #2b91af"&gt;XmlTextWriter &lt;/span&gt;writer = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;XmlTextWriter&lt;/span&gt;(stringWriter);
    writer.WriteStartElement(&lt;span style="color: #a31515"&gt;&amp;quot;points&amp;quot;&lt;/span&gt;);
    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlConnection &lt;/span&gt;conn = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlConnection&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;ConfigurationManager&lt;/span&gt;.ConnectionStrings[&lt;span style="color: #a31515"&gt;&amp;quot;postcodesDatabase&amp;quot;&lt;/span&gt;].ConnectionString))
    {
      conn.Open();
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlCommand &lt;/span&gt;comm = conn.CreateCommand())
      {
        comm.CommandText = &lt;span style="color: #a31515"&gt;&amp;quot;SELECT Boundary.STNumPoints() FROM CountyBoundaries WHERE County='Lancashire'&amp;quot;&lt;/span&gt;;
        &lt;span style="color: blue"&gt;int &lt;/span&gt;numPoints = (&lt;span style="color: blue"&gt;int&lt;/span&gt;)comm.ExecuteScalar();
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;i = 1; i &amp;lt;= numPoints; i++)
        {
          writer.WriteStartElement(&lt;span style="color: #a31515"&gt;&amp;quot;point&amp;quot;&lt;/span&gt;);
          &lt;span style="color: green"&gt;// get X
          &lt;/span&gt;comm.CommandText = &lt;span style="color: #a31515"&gt;&amp;quot;SELECT Boundary.STPointN(&amp;quot; &lt;/span&gt;+ i + &lt;span style="color: #a31515"&gt;&amp;quot;).STX FROM CountyBoundaries WHERE County='Lancashire'&amp;quot;&lt;/span&gt;;
          &lt;span style="color: blue"&gt;double &lt;/span&gt;x = (&lt;span style="color: blue"&gt;double&lt;/span&gt;)comm.ExecuteScalar();

          &lt;span style="color: green"&gt;// get Y
          &lt;/span&gt;comm.CommandText = &lt;span style="color: #a31515"&gt;&amp;quot;SELECT Boundary.STPointN(&amp;quot; &lt;/span&gt;+ i + &lt;span style="color: #a31515"&gt;&amp;quot;).STY FROM CountyBoundaries WHERE County='Lancashire'&amp;quot;&lt;/span&gt;;
          &lt;span style="color: blue"&gt;double &lt;/span&gt;y = (&lt;span style="color: blue"&gt;double&lt;/span&gt;)comm.ExecuteScalar();

          &lt;span style="color: green"&gt;// convert to lat/long
          &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OSRef &lt;/span&gt;osRef = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OSRef&lt;/span&gt;(x, y);
          &lt;span style="color: #2b91af"&gt;LatLng &lt;/span&gt;latLong = osRef.ToLatLng();

          writer.WriteAttributeString(&lt;span style="color: #a31515"&gt;&amp;quot;x&amp;quot;&lt;/span&gt;, latLong.Longitude.ToString());
          writer.WriteAttributeString(&lt;span style="color: #a31515"&gt;&amp;quot;y&amp;quot;&lt;/span&gt;, latLong.Latitude.ToString());
          
          writer.WriteEndElement();
        } 
      }
    }
    writer.WriteEndElement(); &lt;span style="color: green"&gt;// points
    &lt;/span&gt;context.Response.Write(stringWriter.ToString());
  }&lt;/pre&gt;

&lt;p&gt;Next up was the JavaScript required in the web page to display the data. This is pretty simple&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;        // load boundary
        &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;boundReq = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
        boundReq.open(&lt;span style="color: #a31515"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;GetBoundary.ashx&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;);
        boundReq.onreadystatechange = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
          &lt;span style="color: blue"&gt;if &lt;/span&gt;(boundReq.readyState == 4) {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(boundReq.status == 200) {
              &lt;span style="color: blue"&gt;var &lt;/span&gt;nodes = boundReq.responseXML.selectNodes(&lt;span style="color: #a31515"&gt;&amp;quot;points/point&amp;quot;&lt;/span&gt;);
              &lt;span style="color: blue"&gt;var &lt;/span&gt;latLongs = &lt;span style="color: blue"&gt;new &lt;/span&gt;Array();
              &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; nodes.length; i++) {
                &lt;span style="color: blue"&gt;var &lt;/span&gt;lng = nodes[i].getAttribute(&lt;span style="color: #a31515"&gt;&amp;quot;x&amp;quot;&lt;/span&gt;);
                &lt;span style="color: blue"&gt;var &lt;/span&gt;lat = nodes[i].getAttribute(&lt;span style="color: #a31515"&gt;&amp;quot;y&amp;quot;&lt;/span&gt;);
                &lt;span style="color: blue"&gt;var &lt;/span&gt;latLong = &lt;span style="color: blue"&gt;new &lt;/span&gt;GLatLng(lat, lng);
                latLongs.push(latLong);
              }
              &lt;span style="color: blue"&gt;var &lt;/span&gt;polygon = &lt;span style="color: blue"&gt;new &lt;/span&gt;GPolygon(latLongs, &lt;span style="color: #a31515"&gt;&amp;quot;#FF0000&amp;quot;&lt;/span&gt;, 3);
              map.addOverlay(polygon);
            }
          }
        };
        boundReq.send(); &lt;/pre&gt;

&lt;p&gt;If you look at the image above you may think the boundary displayed is not correct since it doesn’t include some places that are quite obviously in Lancashire (Blackburn and Darwen for instance) and this confused me for a while. But the boundary displayed is that of Lancashire County council, rather than the full county, which doesn’t include some areas which are in unitary authorities but still within the county.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-812457449988228471?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/812457449988228471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=812457449988228471' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/812457449988228471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/812457449988228471'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/11/using-ogc-functions-in-sql-server-2008_28.html' title='Using OGC functions in SQL Server 2008 part 5 – Google Maps and polygons'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-669475619932151204</id><published>2009-11-25T21:29:00.001Z</published><updated>2009-11-28T21:21:18.974Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Using OGC functions in SQL Server 2008 part 4 – Google Maps</title><content type='html'>&lt;p&gt;&lt;a href="http://www.doogal.co.uk/blogimages/UsingOGCfunctionsinSQLServer2008part4Goo_11EEF/image.png"&gt;&lt;img style="margin: 0px; display: inline" title="Spatial data displayed in Google Maps" alt="Spatial data displayed in Google Maps" align="right" src="http://www.doogal.co.uk/blogimages/UsingOGCfunctionsinSQLServer2008part4Goo_11EEF/image_thumb.png" width="240" height="211" /&gt;&lt;/a&gt; So I’d now got to the stage where I’d pulled in some postcode data into SQL Server, got the county boundaries for one county (the mighty Lancashire) and figured out how to do a spatial query to tell me which postcodes were in that county. Now I wanted to display those postcodes on a map. So time to turn to Google Maps and Visual Studio.&lt;/p&gt;  &lt;p&gt;First I created a simple generic handler in C# that returned an XML document containing the geometries of the postcodes. This could return JSON if that floats your boat but here is what my version looks like. &lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;    public void &lt;/span&gt;ProcessRequest (&lt;span style="color: #2b91af"&gt;HttpContext &lt;/span&gt;context) 
    {
      context.Response.ContentType = &lt;span style="color: #a31515"&gt;&amp;quot;text/xml&amp;quot;&lt;/span&gt;;
      &lt;span style="color: #2b91af"&gt;StringWriter &lt;/span&gt;stringWriter = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StringWriter&lt;/span&gt;();
      &lt;span style="color: #2b91af"&gt;XmlTextWriter &lt;/span&gt;writer = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;XmlTextWriter&lt;/span&gt;(stringWriter);
      writer.WriteStartElement(&lt;span style="color: #a31515"&gt;&amp;quot;points&amp;quot;&lt;/span&gt;);
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlConnection &lt;/span&gt;conn = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlConnection&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;ConfigurationManager&lt;/span&gt;.ConnectionStrings[&lt;span style="color: #a31515"&gt;&amp;quot;postcodesDatabase&amp;quot;&lt;/span&gt;].ConnectionString))
      {
        conn.Open();
        &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlCommand &lt;/span&gt;comm = conn.CreateCommand())
        {
          comm.CommandText = &lt;span style="color: #a31515"&gt;&amp;quot;SELECT Location.STX AS X, Location.STY AS Y, City, Region, Postcode FROM Postcodes WHERE &amp;quot; &lt;/span&gt;+
            &lt;span style="color: #a31515"&gt;&amp;quot;(SELECT Boundary FROM CountyBoundaries).STContains(OsLocation)=1&amp;quot;&lt;/span&gt;;
          &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlDataReader &lt;/span&gt;reader = comm.ExecuteReader())
          {
            &lt;span style="color: blue"&gt;while &lt;/span&gt;(reader.Read())
            {
              writer.WriteStartElement(&lt;span style="color: #a31515"&gt;&amp;quot;point&amp;quot;&lt;/span&gt;);
              writer.WriteAttributeString(&lt;span style="color: #a31515"&gt;&amp;quot;description&amp;quot;&lt;/span&gt;, reader[&lt;span style="color: #a31515"&gt;&amp;quot;Postcode&amp;quot;&lt;/span&gt;].ToString() + &lt;span style="color: #a31515"&gt;&amp;quot;, &amp;quot; &lt;/span&gt;+ reader[&lt;span style="color: #a31515"&gt;&amp;quot;City&amp;quot;&lt;/span&gt;].ToString() + &lt;span style="color: #a31515"&gt;&amp;quot;, &amp;quot; &lt;/span&gt;+ reader[&lt;span style="color: #a31515"&gt;&amp;quot;Region&amp;quot;&lt;/span&gt;].ToString());
              writer.WriteAttributeString(&lt;span style="color: #a31515"&gt;&amp;quot;x&amp;quot;&lt;/span&gt;, reader[&lt;span style="color: #a31515"&gt;&amp;quot;X&amp;quot;&lt;/span&gt;].ToString());
              writer.WriteAttributeString(&lt;span style="color: #a31515"&gt;&amp;quot;y&amp;quot;&lt;/span&gt;, reader[&lt;span style="color: #a31515"&gt;&amp;quot;Y&amp;quot;&lt;/span&gt;].ToString());
              writer.WriteEndElement();
            }
          }
        }
      }
      writer.WriteEndElement(); &lt;span style="color: green"&gt;// points
      &lt;/span&gt;context.Response.Write(stringWriter.ToString());
    }&lt;/pre&gt;

&lt;p&gt;Next I created a HTML page with a bit of JavaScript to load up the postcode data, which looked like this.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;      function &lt;/span&gt;loadMap() {
      &lt;span style="color: blue"&gt;var &lt;/span&gt;map;
      &lt;span style="color: blue"&gt;if &lt;/span&gt;(GBrowserIsCompatible()) {
        map = &lt;span style="color: blue"&gt;new &lt;/span&gt;GMap2(document.getElementById(&lt;span style="color: #a31515"&gt;&amp;quot;map&amp;quot;&lt;/span&gt;));
        map.setCenter(&lt;span style="color: blue"&gt;new &lt;/span&gt;GLatLng(53.756830663572174, -2.73834228515625), 9);
        map.addControl(&lt;span style="color: blue"&gt;new &lt;/span&gt;GSmallMapControl());
        map.addControl(&lt;span style="color: blue"&gt;new &lt;/span&gt;GMapTypeControl());
        map.enableDoubleClickZoom();
        map.enableScrollWheelZoom();

        &lt;span style="color: green"&gt;// load data
        &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;req = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
        req.open(&lt;span style="color: #a31515"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;GetPoints.ashx&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;true&lt;/span&gt;);
        req.onreadystatechange = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
          &lt;span style="color: blue"&gt;if &lt;/span&gt;(req.readyState == 4) {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(req.status == 200) {
              &lt;span style="color: blue"&gt;var &lt;/span&gt;nodes = req.responseXML.selectNodes(&lt;span style="color: #a31515"&gt;&amp;quot;points/point&amp;quot;&lt;/span&gt;);
              &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; nodes.length; i++) {
                &lt;span style="color: blue"&gt;var &lt;/span&gt;lng = nodes[i].getAttribute(&lt;span style="color: #a31515"&gt;&amp;quot;x&amp;quot;&lt;/span&gt;);
                &lt;span style="color: blue"&gt;var &lt;/span&gt;lat = nodes[i].getAttribute(&lt;span style="color: #a31515"&gt;&amp;quot;y&amp;quot;&lt;/span&gt;);
                &lt;span style="color: blue"&gt;var &lt;/span&gt;marker = &lt;span style="color: blue"&gt;new &lt;/span&gt;GMarker(&lt;span style="color: blue"&gt;new &lt;/span&gt;GLatLng(lat, lng), { title: nodes[i].getAttribute(&lt;span style="color: #a31515"&gt;&amp;quot;description&amp;quot;&lt;/span&gt;) });
                map.addOverlay(marker);
              }
            }
          }
        };
        req.send();
      }
    }&lt;/pre&gt;

&lt;p&gt;Again nothing groundbreaking but the next stage could me more challenging. That is pulling out the data for the county boundary and plotting that on the map. And what is this all for? All part of my plans for world domination.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-669475619932151204?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/669475619932151204/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=669475619932151204' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/669475619932151204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/669475619932151204'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/11/using-ogc-functions-in-sql-server-2008_25.html' title='Using OGC functions in SQL Server 2008 part 4 – Google Maps'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1711352123451161377</id><published>2009-11-15T21:22:00.001Z</published><updated>2009-12-22T21:31:41.326Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><category scheme='http://www.blogger.com/atom/ns#' term='OS OpenSpace'/><title type='text'>Using OGC functions in SQL Server 2008 part 3</title><content type='html'>&lt;p&gt;It’s been a while since I last tried to do anything with &lt;a href="http://doogalbellend.blogspot.com/2009/09/using-ogc-functions-in-sql-server-2008_27.html"&gt;the spatial features in SQL Server 2008&lt;/a&gt;. My next plan was to query the database to see which postcodes were in a particular county. To do this I got hold of the boundary data for some counties using the &lt;a href="http://openspace.ordnancesurvey.co.uk/openspace/"&gt;OpenSpace API&lt;/a&gt;. I just added one county, the one and only Lancashire, with the following code.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;INSERT INTO &lt;/span&gt;CountyBoundaries &lt;span style="color: gray"&gt;(&lt;/span&gt;County&lt;span style="color: gray"&gt;, &lt;/span&gt;Boundary&lt;span style="color: gray"&gt;) &lt;/span&gt;&lt;span style="color: blue"&gt;VALUES &lt;/span&gt;&lt;span style="color: gray"&gt;(&lt;/span&gt;&lt;span style="color: red"&gt;'Lancashire'&lt;/span&gt;&lt;span style="color: gray"&gt;, 
&lt;/span&gt;&lt;span style="color: blue"&gt;geometry&lt;/span&gt;&lt;span style="color: gray"&gt;::&lt;/span&gt;STPolyFromText&lt;span style="color: gray"&gt;(&lt;/span&gt;&lt;span style="color: red"&gt;'POLYGON((335390.6 402509.3,335318.8 405919.1,337285.8 405717.9,341638.1 403012.7,341198.7 401158.3,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;345355.3 399036.6,348231.1 404146.9,349111.4 402150.3,351662.6 402905.5,353409.1 407181.8,352474 409073.9,354484.8 412190.4,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;358109.6 412599.6,358670 410970.3,360670.3 412859.2,362419.8 411151.7,366280.6 414615.7,364921.9 418843.3,366064.7 421813,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;363060.8 427981.2,363748.5 429031.4,369403 431708.6,372966.5 423266.5,375369.8 421263.6,375568.6 419018,379162.2 417655.1,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;380342.3 418953.7,382117.1 413139.9,383850.1 418586.1,385712 419187.4,387839.3 415759.4,389429.8 416106,390432.6 420649.6,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;388662.6 425190.8,391447.8 428359.4,392703.4 434383.5,396065.9 436596.5,397063 439322.3,394185.1 441332.3,392652.8 446613.3,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;388130.5 448821.7,387988.3 450699.5,387239.2 452627.7,385286.7 451690.4,384991.7 453964.6,380792.9 453190.5,379173.9 455357.1,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;376997.3 455460,377844.2 457098.1,375605.7 461521.8,372209.8 460317.7,369352.3 461239,369714.2 464470.9,363191.2 470277.6,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;363491 473169.9,365227.3 473672.8,370052.2 481319.2,370144.8 482748.7,362512.5 477911.7,358350.8 478665.3,355703.4 474137.7,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;347972.3 478292.1,345446.8 475698.7,342749 476087.5,342784.7 474615.5,340414.8 475469.9,342882.8 469480.8,341304.5 465488.7,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;344238.6 465331.3,340055 463217.2,340345.5 460141.4,335601.4 455045.4,336057.5 453597.1,332947.7 450366,329952.5 450443.2,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;330737.1 442705.2,333041.8 441857.2,333572.8 437130.7,335319.6 433501.9,333963.4 433247,334425.2 431366.8,329729.3 431451.9,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;329199.8 427517.3,328505.3 422791.4,330070.5 422095.3,336522 423531.2,337967.3 418422.4,332016.4 412241.1,332374.5 408594.8,&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color: red"&gt;330736.4 405654.3,335390.6 402509.3))'&lt;/span&gt;&lt;span style="color: gray"&gt;, &lt;/span&gt;4277&lt;span style="color: gray"&gt;))&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;This is where things got a little tricky. The county boundaries used the OS coordinate system, whereas my postcodes were stored in latitude/longitude format. I was hopeful that SQL Server would do some magic for me and handle the different coordinate systems, but it would appear this isn’t the case. &lt;/p&gt;

&lt;p&gt;So it looked like the only option was to convert my postcode points into the OS coordinate system. I’ve used &lt;a href="http://www.jstott.me.uk/phpcoord/"&gt;phpCoord&lt;/a&gt; to do this conversion in the past, but this time I was looking for a .NET solution. So I took the &lt;a href="http://www.jstott.me.uk/jcoord/"&gt;Java implementation&lt;/a&gt; and converted it C#. That took a while (and I’ll upload the code at some point when I’ve OKed it with the original author) but after modifying my code to insert postcodes into the database I was able to run the following query and successfully pull out postcodes that were in the Lancashire area.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;SELECT &lt;/span&gt;&lt;span style="color: gray"&gt;* &lt;/span&gt;&lt;span style="color: blue"&gt;FROM &lt;/span&gt;Postcodes &lt;span style="color: blue"&gt;WHERE &lt;/span&gt;&lt;span style="color: gray"&gt;(&lt;/span&gt;&lt;span style="color: blue"&gt;SELECT &lt;/span&gt;Boundary &lt;span style="color: blue"&gt;FROM &lt;/span&gt;CountyBoundaries&lt;span style="color: gray"&gt;).&lt;/span&gt;STContains&lt;span style="color: gray"&gt;(&lt;/span&gt;OsLocation&lt;span style="color: gray"&gt;)=&lt;/span&gt;1&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1711352123451161377?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1711352123451161377/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1711352123451161377' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1711352123451161377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1711352123451161377'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/11/using-ogc-functions-in-sql-server-2008.html' title='Using OGC functions in SQL Server 2008 part 3'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8119602520365042384</id><published>2009-10-17T21:39:00.001+01:00</published><updated>2009-10-17T21:39:14.605+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Postcodes'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Legal action against free postcode lookup</title><content type='html'>&lt;p&gt;I only discovered the &lt;a href="http://ernestmarples.com/"&gt;Ernest Maples website&lt;/a&gt; today. Ernest Maples was the man who introduced the UK postcode system. The website was providing a useful service that took a UK postcode and returned the latitude and longitude of that postcode, &lt;a href="http://www.doogal.co.uk/PostcodeGeocoding.php"&gt;which seems kind of familiar…&lt;/a&gt; But they’ve recently received a threatening legal letter from the Royal Mail’s lawyers telling them to remove this service from their site. &lt;/p&gt;  &lt;p&gt;This raises so many questions. When will I get my threatening letter? When will Google get their threatening letter, since they provide this exact same feature but through a JavaScript API rather than via a URL? Why is the publically owned Royal Mail charging for this data anyway, since it was taxpayer money that paid for its creation in the first place? &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8119602520365042384?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8119602520365042384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8119602520365042384' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8119602520365042384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8119602520365042384'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/10/legal-action-against-free-postcode.html' title='Legal action against free postcode lookup'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1249186710422426472</id><published>2009-10-12T21:26:00.001+01:00</published><updated>2009-10-12T21:26:24.418+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Google Maps tools</title><content type='html'>&lt;p&gt;&lt;a href="http://www.birdtheme.org/useful/googletool.html"&gt;This is an interesting tool to see what is possible with the Google Maps API&lt;/a&gt;. Something I feel missing is real-time update of the shapes being drawn as the user moves the mouse around, something our mapping tool will provide when we ship the next version very soon. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1249186710422426472?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1249186710422426472/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1249186710422426472' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1249186710422426472'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1249186710422426472'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/10/google-maps-tools.html' title='Google Maps tools'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3705031524007404608</id><published>2009-10-11T22:25:00.001+01:00</published><updated>2009-10-11T22:25:49.644+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ZX Spectrum'/><category scheme='http://www.blogger.com/atom/ns#' term='TV'/><title type='text'>Micro Men</title><content type='html'>&lt;p&gt;BBC3 and BBC4 often have some interesting programmes that are easily missed. I remembered to record &lt;em&gt;Micro Men&lt;/em&gt;, a dramatisation of the work of Clive Sinclair and Chris Curry who produced the ZX Spectrum and BBC Micro respectively. It was good fun, although Sinclair didn’t come across as a particularly likeable character, which I found disappointing since he was always something of a hero for me as a child. Acorn lives on with its &lt;a href="http://www.arm.com/products/CPUs/"&gt;ARM processor&lt;/a&gt; powering most of the world’s mobile phones and &lt;a href="http://www.sinclair-research.co.uk/"&gt;Sinclair continues to invent things&lt;/a&gt;, although with somewhat less success than the ZX Spectrum. Anyway, &lt;a href="http://www.bbc.co.uk/iplayer/episode/b00n5b92/Micro_Men/"&gt;Micro Men is available to watch on BBC iPlayer for a while&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3705031524007404608?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3705031524007404608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3705031524007404608' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3705031524007404608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3705031524007404608'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/10/micro-men.html' title='Micro Men'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1708170225582307079</id><published>2009-10-11T20:50:00.001+01:00</published><updated>2009-10-11T20:50:01.044+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><title type='text'>Zopa – 18 months on</title><content type='html'>&lt;p&gt;About 18 months ago, I started to &lt;a href="http://doogalbellend.blogspot.com/2008/04/become-money-lender.html"&gt;siphon some of my savings&lt;/a&gt; into &lt;a href="http://www.zopa.com"&gt;Zopa&lt;/a&gt; in an attempt to get a slightly better return on my money. So I thought now would be a good time to look back on how well it has gone. First up, it certainly provides better returns than having your money in a bank account, with interest rates as low as they are. On the downside, this increased return does mean increased risk, the old &lt;a href="http://doogalbellend.blogspot.com/2008/10/risk-premium.html"&gt;risk premium&lt;/a&gt; again. Those loans can sometimes turn bad and when they do, all the outstanding debt is written off. Well I guess some of it may be returned, but I’ve not had any bad debt returned yet. &lt;/p&gt;  &lt;p&gt;But the main downside is that the money is tied up for a long time, and by my calculations the return from part paying off my mortgage is still better than the returns on Zopa. And although doing that means that the money is gone forever, in the future I’ll probably be more likely to do that than put more money into Zopa. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1708170225582307079?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1708170225582307079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1708170225582307079' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1708170225582307079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1708170225582307079'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/10/zopa-18-months-on.html' title='Zopa – 18 months on'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7001371950495668211</id><published>2009-10-01T21:29:00.001+01:00</published><updated>2009-10-01T21:30:07.993+01:00</updated><title type='text'>Checking a latitude/longitude is in the UK</title><content type='html'>&lt;p&gt;I found quite a lot of duff data appearing in &lt;a href="http://www.doogal.co.uk/UKPostcodes.php"&gt;my incomplete database of UK postcodes&lt;/a&gt;, so thought I’d better filter out some of the rubbish. So the obvious thing to do was figure out which latitude and longitudes weren’t in the UK and stop them being put into in the database in the first place and also delete the ones already in there. There are definitely better ways of achieving this but my simple solution seems to have done the trick. First check the latitude is greater than 49 and less than 61, then check the longitude is greater than –12 and less than 3. It’s not perfect but I think it’s probably good enough for many applications.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7001371950495668211?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7001371950495668211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7001371950495668211' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7001371950495668211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7001371950495668211'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/10/checking-latitudelongitude-is-in-uk.html' title='Checking a latitude/longitude is in the UK'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8320788990082721922</id><published>2009-09-29T21:21:00.001+01:00</published><updated>2009-09-29T21:21:51.861+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GIS'/><category scheme='http://www.blogger.com/atom/ns#' term='Virtual Earth'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>OS OpenSpace API</title><content type='html'>&lt;p&gt;I’m pretty new to the world of GIS but one thing seems blindingly obvious to me, maps are becoming commoditised. With Google Maps and Virtual Earth already on the block, who is ever going to pay for maps anymore? New to the fray is the &lt;a href="http://openspace.ordnancesurvey.co.uk/openspace/"&gt;OS OpenSpace API&lt;/a&gt;, from the Ordnance Survey. It looks pretty decent, it includes some things not available in the other map providers, such as administrative boundaries, but misses other things like aerial imagery. When I get chance I will play with the API to see how good it is and get some ideas for our own JavaScript API.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8320788990082721922?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8320788990082721922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8320788990082721922' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8320788990082721922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8320788990082721922'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/09/os-openspace-api.html' title='OS OpenSpace API'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-368921112527281520</id><published>2009-09-27T18:00:00.001+01:00</published><updated>2009-09-27T18:00:33.490+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Using OGC functions in SQL Server 2008 part 2</title><content type='html'>&lt;p&gt;&lt;a href="http://doogalbellend.blogspot.com/2009/09/using-ogc-functions-in-sql-server-2008.html"&gt;In part 1 I created a table that contained some spatial UK postcode data&lt;/a&gt;. Now the next task is to work out how to query this data. A typical question we may want to answer is ‘what are the nearest postcodes to a particular postcode?’. OK, what we probably want to answer is ‘what is the nearest station/cash machine/post office etc to a particular postcode?’ but the only spatial data I’ve got is for postcodes, so we’ll use that. And the query to answer this question is pretty straightforward.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;SELECT TOP &lt;/span&gt;100 P&lt;span style="color: gray"&gt;.&lt;/span&gt;Postcode&lt;span style="color: gray"&gt;, &lt;/span&gt;P&lt;span style="color: gray"&gt;.&lt;/span&gt;Location&lt;span style="color: gray"&gt;.&lt;/span&gt;STDistance&lt;span style="color: gray"&gt;(&lt;/span&gt;PL&lt;span style="color: gray"&gt;.&lt;/span&gt;Location&lt;span style="color: gray"&gt;) &lt;/span&gt;&lt;span style="color: blue"&gt;AS &lt;/span&gt;Distance 
&lt;span style="color: blue"&gt;FROM &lt;/span&gt;Postcodes P&lt;span style="color: gray"&gt;, &lt;/span&gt;Postcodes PL
&lt;span style="color: blue"&gt;WHERE &lt;/span&gt;PL&lt;span style="color: gray"&gt;.&lt;/span&gt;Postcode&lt;span style="color: gray"&gt;=&lt;/span&gt;&lt;span style="color: red"&gt;'KT1 3EG'
&lt;/span&gt;&lt;span style="color: blue"&gt;ORDER BY &lt;/span&gt;Distance&lt;/pre&gt;

&lt;p&gt;The MSDN documentation for the STDistance function is a bit sparse but from what I can find from other sources, it will return the distance between two locations in metres. &lt;/p&gt;

&lt;p&gt;Obviously this has all been possible in the past by writing your own code to do the calculation, but executing it directly against the database makes life a lot simpler. I have no idea if it will be quicker but simpler generally wins out for me.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-368921112527281520?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/368921112527281520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=368921112527281520' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/368921112527281520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/368921112527281520'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/09/using-ogc-functions-in-sql-server-2008_27.html' title='Using OGC functions in SQL Server 2008 part 2'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6694258201421444866</id><published>2009-09-26T21:23:00.001+01:00</published><updated>2009-09-26T21:23:08.732+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>XML encoding in JavaScript</title><content type='html'>&lt;p&gt;As far as I know there is no cross browser way to generate valid XML documents, so it seems like string concatenation is the way to go. So here’s a little function to do the required encoding of characters that need to be specially encoded for XML. As ever, I’ve not done any kind of thorough testing on this…&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;    function &lt;/span&gt;XmlEncode(text) {
      text = text.replace(/&amp;amp;/g, &lt;span style="color: #a31515"&gt;'&amp;amp;amp;'&lt;/span&gt;);
      text = text.replace(/\&lt;span style="color: #a31515"&gt;&amp;quot;/g, '&amp;amp;quot;');
      &lt;/span&gt;text = text.replace(/\&lt;span style="color: #a31515"&gt;'/g, '&lt;/span&gt;&amp;amp;apos;&lt;span style="color: #a31515"&gt;');
      &lt;/span&gt;text = text.replace(/&amp;lt;/g, &lt;span style="color: #a31515"&gt;'&amp;amp;lt;'&lt;/span&gt;);
      text = text.replace(/&amp;gt;/g, &lt;span style="color: #a31515"&gt;'&amp;amp;gt;'&lt;/span&gt;);
      &lt;span style="color: blue"&gt;return &lt;/span&gt;text;
    }&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6694258201421444866?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6694258201421444866/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6694258201421444866' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6694258201421444866'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6694258201421444866'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/09/xml-encoding-in-javascript.html' title='XML encoding in JavaScript'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-5160502406217142168</id><published>2009-09-25T21:41:00.001+01:00</published><updated>2009-09-25T21:41:46.849+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Using OGC functions in SQL Server 2008 part 1</title><content type='html'>&lt;p&gt;&lt;a href="http://www.doogal.co.uk/blogimages/UsingOGCfunctionsinSQLServer2008_1297C/image.png"&gt;&lt;img style="margin: 0px 10px 0px 0px; display: inline" title="image" alt="image" align="left" src="http://www.doogal.co.uk/blogimages/UsingOGCfunctionsinSQLServer2008_1297C/image_thumb.png" width="334" height="103" /&gt;&lt;/a&gt; One great thing about having a new job is being faced with new technologies. So I’ve just come across the new geography and geometry data types in SQL Server 2008. These allow you to store geographical data in a database. In some ways this is nothing new, you could always store latitude and longitude or eastings and northings if you only cared about the UK area. But what is new is that the database actually understands what these values are, meaning it’s possible to write queries that are based on geographical location, which to my mind is absolutely frigging awesome. &lt;/p&gt;  &lt;p&gt;So I thought I’d play around with these new features in SQL Server. Oracle also now has spatial features, but frankly I try and avoid using Oracle wherever possible. First up I created a new table with a geography column, as shown above. Next I had to populate it. Fortunately I knew &lt;a href="http://www.doogal.co.uk/UKPostcodesCSV.php"&gt;where I could find some data&lt;/a&gt;. With a little help from C# and Visual Studio, it was pretty easy to get this into the database.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Program
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
    {
      &lt;span style="color: blue"&gt;string&lt;/span&gt;[] lines = &lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.ReadAllLines(&lt;span style="color: #a31515"&gt;@&amp;quot;C:\Users\Doogal\Desktop\Postcodes.txt&amp;quot;&lt;/span&gt;);
      &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlConnection &lt;/span&gt;conn = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlConnection&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=OGC;Data Source=NEWDOOGAL&amp;quot;&lt;/span&gt;))
      {
        conn.Open();
        &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;string &lt;/span&gt;line &lt;span style="color: blue"&gt;in &lt;/span&gt;lines)
        {
          &lt;span style="color: blue"&gt;string&lt;/span&gt;[] data = line.Split(&lt;span style="color: #a31515"&gt;','&lt;/span&gt;);
          &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;SqlCommand &lt;/span&gt;command = conn.CreateCommand())
          {
            command.CommandText = &lt;span style="color: blue"&gt;string&lt;/span&gt;.Format(&lt;span style="color: #a31515"&gt;&amp;quot;INSERT INTO Postcodes (Postcode, Location, City, Region) &amp;quot; &lt;/span&gt;+
              &lt;span style="color: #a31515"&gt;&amp;quot;VALUES ('{0}', geography::STPointFromText('POINT({2} {1})', 4326), '{3}', '{4}')&amp;quot;&lt;/span&gt;,
              SqlEscape(data[0]), data[1], data[2], SqlEscape(data[3]), SqlEscape(data[4]));
            command.ExecuteNonQuery();
          }
        }
      }
    }

    &lt;span style="color: blue"&gt;private static string &lt;/span&gt;SqlEscape(&lt;span style="color: blue"&gt;string &lt;/span&gt;sql)
    {
      &lt;span style="color: blue"&gt;if &lt;/span&gt;(sql == &lt;span style="color: blue"&gt;null&lt;/span&gt;)
        &lt;span style="color: blue"&gt;return null&lt;/span&gt;;
      &lt;span style="color: blue"&gt;return &lt;/span&gt;sql.Replace(&lt;span style="color: #a31515"&gt;&amp;quot;'&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;''&amp;quot;&lt;/span&gt;);
    }
  }&lt;/pre&gt;

&lt;p&gt;&lt;a href="http://www.doogal.co.uk/blogimages/UsingOGCfunctionsinSQLServer2008_1297C/image_3.png"&gt;&lt;img style="margin: 0px 10px 0px 0px; display: inline" title="image" alt="image" align="left" src="http://www.doogal.co.uk/blogimages/UsingOGCfunctionsinSQLServer2008_1297C/image_thumb_3.png" width="398" height="480" /&gt;&lt;/a&gt; So after getting the data in the database, the next step was to query it and see what we got. And after running a query against a spatial table in Management Studio, the first thing you notice is the new ‘Spatial results’ tab. Clicking on that showed the diagram shown on the left. And that looked remarkably like a map of the UK. &lt;/p&gt;

&lt;p&gt;I guess that in itself is not hugely impressive, but it certainly pleased me. Next I have to move onto real queries, such as ‘give me all the postcodes within 1km of this location’, which is where spatial databases potentially come into there own.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-5160502406217142168?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/5160502406217142168/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=5160502406217142168' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5160502406217142168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/5160502406217142168'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/09/using-ogc-functions-in-sql-server-2008.html' title='Using OGC functions in SQL Server 2008 part 1'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3179270909851603518</id><published>2009-09-07T20:44:00.001+01:00</published><updated>2009-09-07T20:44:31.811+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IE6'/><title type='text'>When will IE6 die?</title><content type='html'>&lt;p&gt;&lt;a href="http://www.doogal.co.uk/blogimages/WhenwillIE6die_13836/image.png"&gt;&lt;img style="margin: 0px 10px 0px 0px; display: inline" title="IE6 usage" alt="IE6 usage" align="left" src="http://www.doogal.co.uk/blogimages/WhenwillIE6die_13836/image_thumb.png" width="617" height="291" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Just like everybody else who has to do any kind of web development, I’m quite keen to see the back of IE6 so I thought I’d make an estimate of when IE6 would die. I took the Google Analytics data from the &lt;a href="http://www.randompubfinder.com/"&gt;Random Pub Finder&lt;/a&gt; and pulled out the monthly percentage of IE users who were using IE6, which produced the graph to the left. It looked pretty linear, except for the initial drop off after IE7 was released, so I added a linear trend line. And voila, IE6 will die at the beginning of 2011. Well, one can live in hope… &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3179270909851603518?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3179270909851603518/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3179270909851603518' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3179270909851603518'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3179270909851603518'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/09/when-will-ie6-die.html' title='When will IE6 die?'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3479118132805914653</id><published>2009-09-02T21:41:00.001+01:00</published><updated>2009-09-02T21:41:09.788+01:00</updated><title type='text'>RBS. Make it happen</title><content type='html'>&lt;p&gt;I’m constantly amazed at the number of adverts I see around for RBS. I guess all the sponsorship deals they signed up for before they blew up have to be honoured so they still appear all over the place. I’ve passed through Glasgow airport a couple of times recently and it is plastered in ads for RBS saying ‘RBS. Make it happen’. I’m not sure if this a command to readers of the ad or they are just telling the reader that RBS make ‘it’ happen. I’m assuming it’s the latter. So what is it they make happen? Global financial meltdown? A massive public deficit due to bailing them out? Either way, I think perhaps they should update their ads to say something like ‘Look we are really really fecking sorry’, it might be more effective.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3479118132805914653?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3479118132805914653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3479118132805914653' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3479118132805914653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3479118132805914653'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/09/rbs-make-it-happen.html' title='RBS. Make it happen'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-1274342768729335770</id><published>2009-08-28T21:51:00.001+01:00</published><updated>2009-08-28T21:51:37.114+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>Installing and debugging a .NET service built in Visual Studio 2008</title><content type='html'>&lt;p&gt;There seem to be several tutorials on the web on how to create a Windows service using .NET in Visual Studio 2005 and before but I was unable to find one for Visual Studio 2008, so thought I’d write my own since I got tripped up a couple of times when I tried to install it.&lt;/p&gt;  &lt;p&gt;The first step is easy, create a new project using New Project/Windows Service. The next obvious thing to try and do is to build it and run it. You will then be presented with the following error -&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Cannot start service from the command line or a debugger.&amp;#160; A Windows Service must first be installed (using installutil.exe) and then started with the ServerExplorer, Windows Services Administrative tool or the NET START command.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;So then you fire up installutil from the command line passing the EXE’s full path as the only parameter. If you make my schoolboy error, you’ll be faced with the following error message -&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Exception occurred while initializing the installation: System.BadImageFormatException: The format of the file 'WindowsService1.exe' is invalid.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;This is because I ran installutil from the standard command prompt, rather than the .NET command prompt and it picked up installutil from the .NET Framework 1.1. So when I figured that out I ran the correct version of installutil and got the following error message - &lt;/p&gt;  &lt;p&gt;&lt;em&gt;No public installers with the RunInstallerAttribute.Yes attribute could be found in the C:\Source\dotNET2\WindowsService1\WindowsService1\bin\Debug\WindowsService1.exe assembly.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;So the next thing to do is to add an installer to the service. This is achieved by right clicking on the service’s design surface and selecting ‘Add Installer’. after doing this and running installutil once again, the service should appear in the Services applet. &lt;/p&gt;  &lt;p&gt;Finally, to debug it you’ll need to start it from the Services applet and then use the ‘Debug/Attach to Process…’ menu item in Visual Studio, making sure the ‘Show processes in all sessions’ checkbox is checked if the service is running under a different account to your own.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-1274342768729335770?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/1274342768729335770/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=1274342768729335770' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1274342768729335770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/1274342768729335770'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/08/installing-and-debugging-net-service.html' title='Installing and debugging a .NET service built in Visual Studio 2008'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-4081922685749535137</id><published>2009-08-22T23:18:00.001+01:00</published><updated>2009-08-22T23:19:18.386+01:00</updated><title type='text'>Regards WTF?</title><content type='html'>&lt;p&gt;When I was a young lad and was taught how to write a letter (a skill that has turned out to be not especially useful) I was told to end the letter with “&lt;em&gt;yours sincerely”&lt;/em&gt;, which kind of baffled me at the time and still does. What does it mean, why would I want to be owned by the reader of the letter? why would I not be sincere?&lt;/p&gt;  &lt;p&gt;And now, when my most of communication is via email, I’m receiving more and more of them ending with &lt;em&gt;“regards”&lt;/em&gt; or &lt;em&gt;“kind regards”&lt;/em&gt;. And now I’m asking myself the same question - what does that mean? It’s even happening with people I’ve known for years who have never put any strange incantations at the end of their emails in the past and have now started this odd practice. I guess emails are the new letters and we have to have some way of signing off from them. I’d always thought of emails as a fairly informal medium, but perhaps not anymore.&lt;/p&gt;  &lt;p&gt;Regards Doogal&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-4081922685749535137?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/4081922685749535137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=4081922685749535137' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4081922685749535137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/4081922685749535137'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/08/regards-wtf.html' title='Regards WTF?'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-8404777637409644131</id><published>2009-08-15T22:38:00.001+01:00</published><updated>2009-08-15T22:38:43.748+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows 7'/><title type='text'>Virtual PC on Windows 7</title><content type='html'>&lt;p&gt;I decided to jump in and upgrade to Windows 7, because I keep hearing it’s what Vista should have been. And it’s OK. I think I’m getting too old to get excited about OS upgrades, these days I just get annoyed about what’s been broken. &lt;/p&gt;  &lt;p&gt;But today I had need to fire up a virtual machine so launched Virtual PC 2007. It told me it was unable to get the network connection up and running and offered to fix it, but then couldn’t find the location of my setup files. Not to worry I thought, probably the best plan is to just reinstall the whole thing. So I uninstalled then attempted to re-install but was told Virtual PC 2007 doesn’t work on Windows 7 so it refused to install it (even though it was clearly working at least a little bit for me). But then I noticed there was a new version of Virtual PC available that would work on Windows 7. So I downloaded that to try and fix my problem. Unfortunately this new version of Virtual PC requires a CPU that has virtualisation built-in. Sadly my CPU is lacking that feature so I couldn’t install this new version. &lt;/p&gt;  &lt;p&gt;So if you have Windows 7 and a CPU that doesn’t have the right features then you are basically stuffed. I have managed to get round the problem by rolling back my Windows installation a couple of days using System Restore. Virtual PC 2007 seems to work fine except for the lack of network support, but if you don’t already have Virtual PC installed then you will be somewhat stuck.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-8404777637409644131?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/8404777637409644131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=8404777637409644131' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8404777637409644131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/8404777637409644131'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/08/virtual-pc-on-windows-7.html' title='Virtual PC on Windows 7'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6477672107062030434</id><published>2009-08-15T13:39:00.001+01:00</published><updated>2009-08-15T13:39:20.459+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows Workflow'/><title type='text'>Using the StateDropDownEditor in state machine workflows</title><content type='html'>&lt;p&gt;&lt;a href="http://www.doogal.co.uk/blogimages/UsingtheStateDropDownEditorinstatemachin_C003/Dropdown.png"&gt;&lt;img style="margin: 0px 10px 0px 0px; display: inline" title="Dropdown" alt="Dropdown" align="left" src="http://www.doogal.co.uk/blogimages/UsingtheStateDropDownEditorinstatemachin_C003/Dropdown_thumb.png" width="406" height="216" /&gt;&lt;/a&gt; Many moons ago &lt;a href="http://doogalbellend.blogspot.com/2007/11/developing-custom-state-activities.html"&gt;I wrote about creating custom state machine activities&lt;/a&gt;. I’ve finally got back to doing some more work on this and thought it would be nice to add a dropdown to choose the target state for my custom activity. This looks pretty easy to implement, just decorate the property with the following attribute&lt;/p&gt;  &lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;Editor&lt;/span&gt;(&lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;StateDropDownEditor&lt;/span&gt;), &lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;UITypeEditor&lt;/span&gt;)), &lt;span style="color: #2b91af"&gt;DefaultValue&lt;/span&gt;((&lt;span style="color: blue"&gt;string&lt;/span&gt;) &lt;span style="color: blue"&gt;null&lt;/span&gt;)]&lt;/pre&gt;

&lt;p&gt;Unfortunately, &lt;span style="color: #2b91af"&gt;&lt;font face="Courier New"&gt;StateDropDownEditor&lt;/font&gt;&lt;/span&gt; is an internal class so this won’t compile. The way round this is to fire up Reflector and copy the code for the &lt;span style="color: #2b91af"&gt;&lt;font face="Courier New"&gt;StateDropDownEditor&lt;/font&gt;&lt;/span&gt; class. Not sure what the copyright issues are for this, but it starts to solve the problem. Unfortunately the &lt;span style="color: #2b91af"&gt;&lt;font face="Courier New"&gt;StateDropDownEditor&lt;/font&gt;&lt;/span&gt; class references a &lt;span style="color: #2b91af"&gt;&lt;font face="Courier New"&gt;StateMachineHelpers&lt;/font&gt;&lt;/span&gt; class which is also internal. We can again use Reflector to get hold of the implementation of this class, but things can start to get messy at this point. We end up having to pull in pretty much the whole state machine infrastructure code but the solution is pretty simple, just remove the &lt;font face="Courier New"&gt;GetCurrentState&lt;/font&gt; method and then no more code needs to be ‘borrowed’. I now have a nice dropdown for selecting the target state.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6477672107062030434?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6477672107062030434/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6477672107062030434' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6477672107062030434'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6477672107062030434'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/08/using-statedropdowneditor-in-state.html' title='Using the StateDropDownEditor in state machine workflows'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-7246540275938030045</id><published>2009-08-09T21:38:00.003+01:00</published><updated>2009-08-09T21:44:51.483+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Poor man's profiling</title><content type='html'>If you don't have access to a profiler or you can't be bothered to fire up your profiler to see where you've got performance problems, there's a pretty simple way to find performance bottlenecks in your code. Just keep breaking into your code and if a particular piece of code is causing problems, then chances are you will keep being taken to that piece of code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-7246540275938030045?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/7246540275938030045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=7246540275938030045' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7246540275938030045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/7246540275938030045'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/08/poor-mans-profiling.html' title='Poor man&apos;s profiling'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2147210585108606075</id><published>2009-07-09T10:06:00.001+01:00</published><updated>2009-07-09T10:06:18.618+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>More on string.Concat vs the + operator in C#</title><content type='html'>&lt;p&gt;&lt;a href="http://doogalbellend.blogspot.com/2006/01/stringconcat-vs-operator-in-c.html"&gt;A post of mine from 3 years ago about the performance differences between using string.Concat and the string class’s + operator&lt;/a&gt; got its first comment yesterday so I thought I’d flesh out what I said there to clarify what happens. First, here’s a little test program to show different ways to concatenate strings.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Program
  &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
    {
      &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;&amp;quot;a&amp;quot; &lt;/span&gt;+ &lt;span style="color: #a31515"&gt;&amp;quot;b&amp;quot; &lt;/span&gt;+ &lt;span style="color: #a31515"&gt;&amp;quot;c&amp;quot; &lt;/span&gt;+ &lt;span style="color: #a31515"&gt;&amp;quot;d&amp;quot;&lt;/span&gt;);
      &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: blue"&gt;string&lt;/span&gt;.Concat(&lt;span style="color: #a31515"&gt;&amp;quot;a&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;b&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;c&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;d&amp;quot;&lt;/span&gt;));

      &lt;span style="color: blue"&gt;string &lt;/span&gt;a = &lt;span style="color: #a31515"&gt;&amp;quot;a&amp;quot;&lt;/span&gt;;
      &lt;span style="color: blue"&gt;string &lt;/span&gt;b = &lt;span style="color: #a31515"&gt;&amp;quot;b&amp;quot;&lt;/span&gt;;
      &lt;span style="color: blue"&gt;string &lt;/span&gt;c = &lt;span style="color: #a31515"&gt;&amp;quot;c&amp;quot;&lt;/span&gt;;
      &lt;span style="color: blue"&gt;string &lt;/span&gt;d = &lt;span style="color: #a31515"&gt;&amp;quot;d&amp;quot;&lt;/span&gt;;
      &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: blue"&gt;string&lt;/span&gt;.Concat(a, b, c, d));
      &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(a + b + c + d);
    }
  }&lt;/pre&gt;

&lt;p&gt;So now lets look at the IL generated from that, using our old friend Reflector.&lt;/p&gt;

&lt;pre&gt;.method private hidebysig static &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Void"&gt;void&lt;/a&gt; &lt;b&gt;&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://Concat:1.0.0.0/Concat.Program/Main(String%5b%5d)"&gt;Main&lt;/a&gt;&lt;/b&gt;(&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;[] args) cil managed
{
    .entrypoint
    .maxstack 4
    .locals init (
        [0] &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt; &lt;b&gt;a&lt;/b&gt;,
        [1] &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt; &lt;b&gt;b&lt;/b&gt;,
        [2] &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt; &lt;b&gt;c&lt;/b&gt;,
        [3] &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt; &lt;b&gt;d&lt;/b&gt;)
    L_0000: &lt;a&gt;nop&lt;/a&gt; 
    L_0001: &lt;a&gt;ldstr&lt;/a&gt; &amp;quot;abcd&amp;quot;
    L_0006: &lt;a&gt;call&lt;/a&gt; &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Void"&gt;void&lt;/a&gt; [&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089"&gt;mscorlib&lt;/a&gt;]&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Console"&gt;System.Console&lt;/a&gt;::&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Console/WriteLine(String)"&gt;WriteLine&lt;/a&gt;(&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;)
    L_000b: &lt;a&gt;nop&lt;/a&gt; 
    L_000c: &lt;a&gt;ldstr&lt;/a&gt; &amp;quot;a&amp;quot;
    L_0011: &lt;a&gt;ldstr&lt;/a&gt; &amp;quot;b&amp;quot;
    L_0016: &lt;a&gt;ldstr&lt;/a&gt; &amp;quot;c&amp;quot;
    L_001b: &lt;a&gt;ldstr&lt;/a&gt; &amp;quot;d&amp;quot;
    L_0020: &lt;a&gt;call&lt;/a&gt; &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt; [&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089"&gt;mscorlib&lt;/a&gt;]&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;System.String&lt;/a&gt;::&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String/Concat(String,String,String,String):String"&gt;Concat&lt;/a&gt;(&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;, &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;, &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;, &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;)
    L_0025: &lt;a&gt;call&lt;/a&gt; &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Void"&gt;void&lt;/a&gt; [&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089"&gt;mscorlib&lt;/a&gt;]&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Console"&gt;System.Console&lt;/a&gt;::&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Console/WriteLine(String)"&gt;WriteLine&lt;/a&gt;(&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;)
    L_002a: &lt;a&gt;nop&lt;/a&gt; 
    L_002b: &lt;a&gt;ldstr&lt;/a&gt; &amp;quot;a&amp;quot;
    L_0030: &lt;a&gt;stloc.0&lt;/a&gt; 
    L_0031: &lt;a&gt;ldstr&lt;/a&gt; &amp;quot;b&amp;quot;
    L_0036: &lt;a&gt;stloc.1&lt;/a&gt; 
    L_0037: &lt;a&gt;ldstr&lt;/a&gt; &amp;quot;c&amp;quot;
    L_003c: &lt;a&gt;stloc.2&lt;/a&gt; 
    L_003d: &lt;a&gt;ldstr&lt;/a&gt; &amp;quot;d&amp;quot;
    L_0042: &lt;a&gt;stloc.3&lt;/a&gt; 
    L_0043: &lt;a&gt;ldloc.0&lt;/a&gt; 
    L_0044: &lt;a&gt;ldloc.1&lt;/a&gt; 
    L_0045: &lt;a&gt;ldloc.2&lt;/a&gt; 
    L_0046: &lt;a&gt;ldloc.3&lt;/a&gt; 
    L_0047: &lt;a&gt;call&lt;/a&gt; &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt; [&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089"&gt;mscorlib&lt;/a&gt;]&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;System.String&lt;/a&gt;::&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String/Concat(String,String,String,String):String"&gt;Concat&lt;/a&gt;(&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;, &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;, &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;, &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;)
    L_004c: &lt;a&gt;call&lt;/a&gt; &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Void"&gt;void&lt;/a&gt; [&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089"&gt;mscorlib&lt;/a&gt;]&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Console"&gt;System.Console&lt;/a&gt;::&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Console/WriteLine(String)"&gt;WriteLine&lt;/a&gt;(&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;)
    L_0051: &lt;a&gt;nop&lt;/a&gt; 
    L_0052: &lt;a&gt;ldloc.0&lt;/a&gt; 
    L_0053: &lt;a&gt;ldloc.1&lt;/a&gt; 
    L_0054: &lt;a&gt;ldloc.2&lt;/a&gt; 
    L_0055: &lt;a&gt;ldloc.3&lt;/a&gt; 
    L_0056: &lt;a&gt;call&lt;/a&gt; &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt; [&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089"&gt;mscorlib&lt;/a&gt;]&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;System.String&lt;/a&gt;::&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String/Concat(String,String,String,String):String"&gt;Concat&lt;/a&gt;(&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;, &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;, &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;, &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;)
    L_005b: &lt;a&gt;call&lt;/a&gt; &lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Void"&gt;void&lt;/a&gt; [&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089"&gt;mscorlib&lt;/a&gt;]&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Console"&gt;System.Console&lt;/a&gt;::&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.Console/WriteLine(String)"&gt;WriteLine&lt;/a&gt;(&lt;a href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://mscorlib:2.0.0.0:b77a5c561934e089/System.String"&gt;string&lt;/a&gt;)
    L_0060: &lt;a&gt;nop&lt;/a&gt; 
    L_0061: &lt;a&gt;ret&lt;/a&gt; 
}
&lt;font face="Trebuchet MS"&gt;&lt;/font&gt;&lt;/pre&gt;

&lt;p&gt;OK, so looking at the first method where we concatenate string literals using the + operator and we can see the compiler helps us out by concatenating the strings at compile time, which is going to be as optimal as possible. The second example shows that the compiler doesn’t do this magic when we use &lt;font face="Courier New"&gt;string.Concat&lt;/font&gt; so &lt;font face="Courier New"&gt;string.Concat&lt;/font&gt; is actually slower in this scenario.&lt;/p&gt;

&lt;p&gt;Now if we look at the next examples where we concatenate string variables, the generated IL is &lt;strong&gt;exactly the same! &lt;/strong&gt;So the performance characteristics are likely to be somewhat similar to say the least. Things get more interesting when you get beyond 4 strings since there is no version of &lt;font face="Courier New"&gt;string.Concat&lt;/font&gt; that takes more than 4 parameters, so they have to be pushed into an array but the result is the same, the + operator generates the exact same code as &lt;font face="Courier New"&gt;string.Concat&lt;/font&gt;.&lt;/p&gt;

&lt;p&gt;So I can’t see a scenario where you’d want to use &lt;font face="Courier New"&gt;string.Concat&lt;/font&gt; (unless you’re particularly fond of it) and if string concatenation performance is an issue, you probably should be using the &lt;font face="Courier New"&gt;StringBuilder&lt;/font&gt; class.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2147210585108606075?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2147210585108606075/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2147210585108606075' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2147210585108606075'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2147210585108606075'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/07/more-on-stringconcat-vs-operator-in-c.html' title='More on string.Concat vs the + operator in C#'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-3822573852379971774</id><published>2009-07-08T16:42:00.001+01:00</published><updated>2009-07-08T16:52:18.884+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Metastorm'/><category scheme='http://www.blogger.com/atom/ns#' term='JScript.NET'/><title type='text'>Encryption in Metastorm BPM</title><content type='html'>&lt;p&gt;A question came up on the Metastorm forums about encrypting sensitive data contained in custom variables so I thought I’d see what I could come up with. &lt;a href="http://blog.binaryocean.com/2006/01/08/NETSymmetricEncryption.aspx"&gt;I took this C# code&lt;/a&gt; and translated it to JScript.NET, which looks something like this&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;import System;      &lt;br /&gt;import System.IO;       &lt;br /&gt;import System.Security.Cryptography;       &lt;br /&gt;import System.Text;       &lt;br /&gt;import eWork.Engine.ScriptObject; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;package Encrypt.Encrypt      &lt;br /&gt;{       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public class Encryption       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; private static const password : String = &amp;quot;password&amp;quot;;       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; public static function Encrypt( ework: SyncProcessData, args: Object[] ) : Object       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // args[0] - string to encrypt       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // returns the encrypted string &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var encrypt : Encryption = new Encryption(password);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return encrypt.Encrypt(args[0]);       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; public static function Decrypt( ework: SyncProcessData, args: Object[] ) : Object      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // args[0] - string to decrypt       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // returns the decrypted string       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (args[0] == &amp;quot;&amp;quot;)       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return &amp;quot;&amp;quot;; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var encrypt : Encryption = new Encryption(password);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return encrypt.Decrypt(args[0]);       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; function Encryption(password : String)      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; GenerateKey(password);       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; private var Key : byte[];      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; private var Vector : byte[]; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; private function GenerateKey(password : String)      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var sha : SHA384Managed&amp;#160; = new SHA384Managed();       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var b : byte[] = sha.ComputeHash(new ASCIIEncoding().GetBytes(password)); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Key = new byte[32];      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Vector = new byte[16]; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; System.Array.Copy(b, 0, Key, 0, 32);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; System.Array.Copy(b, 32, Vector, 0, 16);       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; public function Encrypt(plainText : String) : String      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var data : byte[] = new ASCIIEncoding().GetBytes(plainText); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var crypto : RijndaelManaged = new RijndaelManaged();      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var encryptor : ICryptoTransform = crypto.CreateEncryptor(Key, Vector); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var memoryStream : MemoryStream = new MemoryStream();      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var crptoStream : CryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; crptoStream.Write(data, 0, data.Length);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; crptoStream.FlushFinalBlock(); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; crptoStream.Close();      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; memoryStream.Close(); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return Convert.ToBase64String(memoryStream.ToArray());      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; public function Decrypt(encryptedText : String) : String      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var cipher : byte[] = Convert.FromBase64String(encryptedText); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var crypto : RijndaelManaged = new RijndaelManaged();      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var encryptor : ICryptoTransform = crypto.CreateDecryptor(Key, Vector); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var memoryStream : MemoryStream = new MemoryStream(cipher);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var crptoStream : CryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Read); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var data : byte[] = new byte[cipher.Length];      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var dataLength : int = crptoStream.Read(data, 0, data.Length); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; memoryStream.Close();      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; crptoStream.Close(); &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return (new ASCIIEncoding()).GetString(data, 0, dataLength);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }       &lt;br /&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Then all that is required is to decrypt the string when the form is loaded and encrypt it when the form is saved, like so&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;%sensitive:=%ScriptEval(JScript.NET,,%Procedure.Name,%MapName,&amp;quot;Encrypt.Encrypt.Encryption.Decrypt&amp;quot;,%sensitive )&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;%sensitive:=%ScriptEval(JScript.NET,,%Procedure.Name,%MapName,&amp;quot;Encrypt.Encrypt.Encryption.Encrypt&amp;quot;,%sensitive)&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Now the user should see the unencrypted text and the encrypted version will be stored in the database. You will also need to decrypt the data anywhere else you need to use it. &lt;/p&gt;  &lt;p&gt;One thing to realise at this point is that the system is still not secure. Although it will stop casual viewers who just run a query against the custom variable table, it won’t stop a more professional hacker. The script text is also stored in the database, so a hacker can have a look at that and find the password used to encrypt/decrypt the data. A more secure implementation would store the password in a location that only the engine account has access to, assuming the engine account is also locked down. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.doogal.co.uk/files/Encrypt.xep"&gt;Download the demo procedure&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-3822573852379971774?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/3822573852379971774/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=3822573852379971774' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3822573852379971774'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/3822573852379971774'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/07/encryption-in-metastorm-bpm.html' title='Encryption in Metastorm BPM'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2424598823893412343</id><published>2009-07-04T15:28:00.001+01:00</published><updated>2009-07-04T20:49:25.386+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='HTML'/><title type='text'>MooZoom with image maps</title><content type='html'>&lt;p&gt;&lt;a href="http://www.rborn.info/products.php"&gt;MooZoom&lt;/a&gt; is a nice piece of JavaScript that adds zoom and pan functionality to images, built on top of &lt;a href="http://www.mootools.net/"&gt;MooTools&lt;/a&gt;. It wasn’t exactly what I needed. I didn’t want the zoom/pan to be constrained to the original size, I wanted the ability to zoom out beyond the original size and I wanted image maps to be handled correctly. I’m quite pleased with the results and you can &lt;a href="http://www.doogal.co.uk/files/moozoom.js"&gt;download the source here&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;To use it, in your image simply set &lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: red"&gt;class&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;moozoom&amp;quot;&lt;/span&gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2424598823893412343?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2424598823893412343/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2424598823893412343' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2424598823893412343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2424598823893412343'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/07/moozoom-with-image-maps.html' title='MooZoom with image maps'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-6085244065182032123</id><published>2009-07-03T21:36:00.001+01:00</published><updated>2009-07-03T21:39:07.957+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Work'/><title type='text'>Day 21 – where I get offered a job</title><content type='html'>&lt;p&gt;3 weeks in and I get a job offer, pretty good going I think. I have the weekend to think about it. And given that a bird in the hand is worth two in the bush (even if the birds in the bush have really nice plumage) I will probably accept it.&lt;/p&gt;  &lt;p&gt;For any other IT job hunters in the current climate, here’s my advice.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Throw your CV onto every job website out there. &lt;/li&gt;    &lt;li&gt;Make sure everyone you know is aware you are looking for work, have no shame!&lt;/li&gt;    &lt;li&gt;If you have a website or blog, make it obvious you are looking for work. Play the percentages game, every person who sees you are looking for work may be a potential employer.&lt;/li&gt;    &lt;li&gt;Don’t demand to get paid as much as you were paid before. Assuming you’re out of work like me, your current income is zero or thereabouts, so your previous salary is pretty much irrelevant.&lt;/li&gt;    &lt;li&gt;Accept all interviews. Even if the job isn’t a perfect fit, it’s good to get back into the interviewing groove, which will help when a better role turns up. And who knows, a job that doesn’t appear perfect on paper may turn out to be better than expected.&lt;/li&gt;    &lt;li&gt;When you have an interview and you get asked about something you have no knowledge of, go off and investigate it. OK, it’s too late for that particular interview but it might come up again.&lt;/li&gt;    &lt;li&gt;Learn about other technologies which may have passed you by in the past. Today the guy I spoke to said he was impressed with my use of the &lt;font face="Courier New"&gt;JavaScriptSerializer&lt;/font&gt; class, which I’d only started playing with the day before my technical test.&lt;/li&gt; &lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-6085244065182032123?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/6085244065182032123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=6085244065182032123' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6085244065182032123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/6085244065182032123'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/07/day-21-where-i-get-offered-job.html' title='Day 21 – where I get offered a job'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2999858237712743044</id><published>2009-07-02T09:25:00.001+01:00</published><updated>2009-07-02T09:25:11.502+01:00</updated><title type='text'>Technorati claim</title><content type='html'>&lt;a href="http://technorati.com/claim/3xgsn2bjea" rel="me"&gt;Technorati Profile&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2999858237712743044?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2999858237712743044/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2999858237712743044' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2999858237712743044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2999858237712743044'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/07/technorati-claim.html' title='Technorati claim'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19519354.post-2100094048390857719</id><published>2009-07-01T21:43:00.001+01:00</published><updated>2009-07-01T21:43:17.714+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HTML'/><title type='text'>Resizing an image map when zooming an image</title><content type='html'>&lt;p&gt;There are some cool libraries out there for zooming images in a HTML document, but none that I’ve seen handle resizing an image map attached to the image. My research may be incomplete so I might be recreating the wheel here, but this simple solution seems to fix the problem. Now I need to integrate with one of those libraries.&lt;/p&gt;  &lt;p&gt;First up my HTML looks like this&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;      &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;map &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;map&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;name&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;map&amp;quot;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;area &lt;/span&gt;&lt;span style="color: red"&gt;coords&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;15,92,568,247&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;alt&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;Blah&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;href&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;javascript:alert('hello');&amp;quot; /&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;area &lt;/span&gt;&lt;span style="color: red"&gt;coords&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;18,259,546,432&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;alt&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;Blah&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;href&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;javascript:alert('hello 2');&amp;quot; /&amp;gt;
      &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;map&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
      &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;input &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;button&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;value&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;+&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;onclick&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;javascript:ZoomIn();&amp;quot;/&amp;gt;
      &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;input &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;button&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;value&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;-&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;onclick&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;javascript:ZoomOut();&amp;quot;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;br &lt;/span&gt;&lt;span style="color: blue"&gt;/&amp;gt;
      &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;img &lt;/span&gt;&lt;span style="color: red"&gt;src&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;Highlight cells.png&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;usemap&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;#map&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;image&amp;quot; /&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;And then there is some JavaScript to do the resizing, that looks like this&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;  &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text/javascript&amp;quot;&amp;gt;
    function &lt;/span&gt;ZoomIn() {
      Zoom(1.1);
    }

    &lt;span style="color: blue"&gt;function &lt;/span&gt;ZoomOut() {
      Zoom(0.9);
    }

    &lt;span style="color: blue"&gt;function &lt;/span&gt;Zoom(amount) {
      &lt;span style="color: green"&gt;// resize image
      &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;image = document.getElementById(&lt;span style="color: #a31515"&gt;'image'&lt;/span&gt;);
      image.height = image.height * amount;

      &lt;span style="color: green"&gt;// resize image map
      &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;map = document.getElementById(&lt;span style="color: #a31515"&gt;'map'&lt;/span&gt;);
      &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; map.areas.length; i++) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;area = map.areas[i];
        &lt;span style="color: blue"&gt;var &lt;/span&gt;coords = area.coords.split(&lt;span style="color: #a31515"&gt;','&lt;/span&gt;);
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;j = 0; j &amp;lt; coords.length; j++) {
          coords[j] = coords[j] * amount;
        }
        area.coords = coords[0] + &lt;span style="color: #a31515"&gt;',' &lt;/span&gt;+ coords[1] + &lt;span style="color: #a31515"&gt;',' &lt;/span&gt;+ coords[2] + &lt;span style="color: #a31515"&gt;',' &lt;/span&gt;+ coords[3];
      }
    }
  &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;script&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/19519354-2100094048390857719?l=doogalbellend.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://doogalbellend.blogspot.com/feeds/2100094048390857719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=19519354&amp;postID=2100094048390857719' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2100094048390857719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19519354/posts/default/2100094048390857719'/><link rel='alternate' type='text/html' href='http://doogalbellend.blogspot.com/2009/07/resizing-image-map-when-zooming-image.html' title='Resizing an image map when zooming an image'/><author><name>Chris Bell</name><uri>https://profiles.google.com/114189457493774868409</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-S5ZKG_FQ-1k/AAAAAAAAAAI/AAAAAAAAAJ8/RT_H1s9TywE/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry></feed>
