Saturday, July 31, 2010

Things are a bit quiet round here

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. My proper website 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.

Friday, July 16, 2010

Quick hack to convert HTML to Word documents

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).

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!

    private bool loadComplete;
    public void ConvertToWord(string htmlFile, string filename)
    {
      // open in IE
      using (WebBrowser browser = new WebBrowser())
      {
        loadComplete = false;
        browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(
          browser_DocumentCompleted);
        browser.Navigate(htmlFile);
        while (!loadComplete)
        {
          Thread.Sleep(50);
          System.Windows.Forms.Application.DoEvents();
        }

        // copy to clipboard
        browser.Document.ExecCommand("SelectAll", true, null);
        browser.Document.ExecCommand("Copy", true, null);
        
        // open Word, paste and save...
        object dummy = Type.Missing;
        ApplicationClass wordApp = new ApplicationClass();
        try
        {
          Document newDoc = wordApp.Documents.Add(ref dummy, ref dummy, ref dummy, ref dummy);

          wordApp.Selection.Paste();

          object fileName = filename;
          newDoc.SaveAs(ref fileName, ref dummy, ref dummy, ref dummy, ref dummy, ref dummy, 
            ref dummy, ref dummy, ref dummy, ref dummy, ref dummy, ref dummy, ref dummy, 
            ref dummy, ref dummy, ref dummy);
          ((_Document)newDoc).Close(ref dummy, ref dummy, ref dummy);
        }
        finally
        {
          wordApp.Quit(ref dummy, ref dummy, ref dummy);
        }
      }
    }

    void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
      loadComplete = true;
    }

Tuesday, June 29, 2010

Visio API ‘Cell is guarded’ exception

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 FormulaU property (and probably when setting the Formula property). For me it only happened on a machine with Visio 2007 installed, Visio 2003 was fine. The solution was pretty simple, use the FormulaForceU property instead (although potentially this may lead to unexpected behaviour apparently, worked fine for me though).

Thursday, June 24, 2010

Importing the Code-Point dataset into MySql

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.
So I decided it was time to grasp the nettle and use the Code-Point dataset for my UK postcodes 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.
So the first thing to do was to download the .NET connector for MySql. Then I modified my SQL Server import code to use MySql. As mentioned in that post, the code has dependencies on this CSV parser and my .NET coordinates library. 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).
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 MySql documentation on improving the performance of inserts.
using System;
using System.IO;
using DotNetCoords;
using LumenWorks.Framework.IO.Csv;
using MySql.Data.MySqlClient;

namespace ImportCodepoint
{
  class Program
  {
    static void Main(string[] args)
    {
      string[] files = Directory.GetFiles(@"C:\Users\Doogal\Downloads\codepo_gb\Code-Point Open\data\CSV");
      foreach (string file in files)
      {
        ReadFile(file);
      }

    }

    private static void ReadFile(string file)
    {
      using (StreamReader reader = new StreamReader(file))
      {
        CsvReader csvReader = new CsvReader(reader, false);
        using (MySqlConnection conn = new MySqlConnection(
          "server=127.0.0.1;uid=root;pwd=password;database=test;"))
        {
          conn.Open();
          foreach (string[] data in csvReader)
          {
            string postcode = data[0];
            // some postcodes have spaces, some don't
            if (postcode.IndexOf(' ') < 0)
              postcode = data[0].Substring(0, data[0].Length - 3) + " " + data[0].Substring(data[0].Length - 3);
            double easting = double.Parse(data[10]);
            double northing = double.Parse(data[11]);

            // there are some postcodes with no location
            if ((easting != 0) && (northing != 0))
            {
              // convert easting/northing to lat/long
              OSRef osRef = new OSRef(easting, northing);
              LatLng latLng = osRef.ToLatLng();
              latLng.ToWGS84();

              using (MySqlCommand command = conn.CreateCommand())
              {
                Console.WriteLine(postcode);
                command.CommandTimeout = 60;
                command.CommandText = string.Format("INSERT INTO Postcodes (Postcode, Latitude, Longitude) " +
                  "VALUES ('{0}', {1}, {2})",
                  postcode, latLng.Latitude, latLng.Longitude);
                command.ExecuteNonQuery();
              }
            }
          }
        }
      }
    }
  }
}

Tuesday, June 22, 2010

MySql Workbench

I’ve used phpMyAdmin 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 MySql Workbench, 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.

Friday, June 18, 2010

FxCop custom rules

There are quite a few examples of custom rules for FxCop (this was the best I found) 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, you can grab it from here. It just contains one rule, taken from the site mentioned before, which checks for the usage of the ArrayList class.

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.

Thursday, June 17, 2010

Getting the height of a background image

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 <img> tag (at least if the height of the <img> tag hasn’t been set) since it will resize to fit the image so you just need to get the height of the <img> 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.

Even so, this should be pretty easy I thought. Load up the image using the Image 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.

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.

Another thing which seems to have tripped up other people when I was trawling the web is the need to set up the onload handler before setting the src property.

Here’s the code, hope it’s useful.

      // get the background image
      var backgroundImg = new Image();
      backgroundImg.onload = function() {
        headerHeight = backgroundImg.height;
        // do other stuff
      };
      var imgSrc = $('.header').css('background-image');
      // IE and FireFox have quotes, Safari and Chrome don't
      imgSrc = imgSrc.replace(/["']/g, '');
      imgSrc = imgSrc.substring(4);
      imgSrc = imgSrc.substring(0, imgSrc.length - 1);
      // IE seems to be too aggressive in caching
      backgroundImg.src = imgSrc + '?' + Math.random();

Sunday, June 13, 2010

Importing the Code-Point dataset into SQL Server

Many moons ago, I imported my own postcode dataset into SQL Server but ever since the Ordnance Survey have made their Code-Point dataset available for free, I’ve been meaning to import that, since it’s likely their data will be more correct than my own.
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.
So I wrote a little console application to import the data, that looks like this
using System;
using System.Data.SqlClient;
using System.IO;
using DotNetCoords;
using LumenWorks.Framework.IO.Csv;

namespace ImportCodepoint
{
  class Program
  {
    static void Main(string[] args)
    {
      string[] files = Directory.GetFiles(@"C:\Users\Doogal\Downloads\codepo_gb\Code-Point Open\data\CSV");
      foreach (string file in files)
      {
        Console.WriteLine("Importing file " + Path.GetFileName(file));
        ReadFile(file);
      }

    }

    private static void ReadFile(string file)
    {
      using (StreamReader reader = new StreamReader(file))
      {
        CsvReader csvReader = new CsvReader(reader, false);
        using (SqlConnection conn = new SqlConnection(
          "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Postcodes;Data Source=DOOGAL-LAPTOP\\SQLEXPRESS"))
        {
          conn.Open();
          foreach (string[] data in csvReader)
          {
            string postcode = data[0];
            double easting = double.Parse(data[10]);
            double northing = double.Parse(data[11]);

            // convert easting/northing to lat/long
            OSRef osRef = new OSRef(easting, northing);
            LatLng latLng = osRef.ToLatLng();
            latLng.ToWGS84();

            using (SqlCommand command = conn.CreateCommand())
            {
              command.CommandText = string.Format("INSERT INTO Postcodes (Postcode, Coordinates) " +
                "VALUES ('{0}', geography::STPointFromText('POINT({2} {1})', 4326))",
                data[0], latLng.Latitude, latLng.Longitude);
              command.ExecuteNonQuery();
            }
          }
        }
      }
    }
  }
}
A couple of points, I’ve used this CSV parser, 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.
I’ve also used my .Net coordinates library to convert GB OS references to latitude/longitude, but obviously you can store the postcodes using eastings and northings if you prefer.
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 this blog post) 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.
SELECT 
   EXPR2.*
FROM 
   (
   SELECT 
      Postcode, Coordinates,
      RowNumber,
      (EXPR1.RowNumber % 400) AS ROW_MOD
   FROM
      (
      SELECT 
         Postcode, Coordinates, 
         ROW_NUMBER() OVER ( ORDER BY Postcode ) AS 'RowNumber'
      FROM 
         Postcodes 
      ) EXPR1
   ) EXPR2
WHERE 
   EXPR2.ROW_MOD = 0

Friday, June 11, 2010

Image file locking in the .NET Bitmap constructor

Consider the following code

      Dim bitmap As New Drawing.Bitmap(fileName)
      Return bitmap

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 “The process cannot access the file '…’ because it is being used by another process.”. Which wasn’t entirely accurate, actually we couldn’t access the file because it was in use by this process. But nitpicking aside, the reason for this was the Bitmap constructor above which loads the file but doesn’t unlock it, only a call to Bitmap.Dispose() unlocks it again.

So my first attempt to fix this looked something like this

      Dim fs As New FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
      Dim bitmap As New Drawing.Bitmap(fs)
      fs.Close()
      Return bitmap

Since this opened the image via a FileStream 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.

I eventually came to the conclusion that this was down to the Bitmap constructor that took a FileStream 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.

So my final attempt at fixing this looks like this

      Dim fileBitmap As New Drawing.Bitmap(fileName)
      Dim bitmap As New Drawing.Bitmap(fileBitmap)
      fileBitmap.Dispose()
      Return bitmap

Which so far hasn’t caused any more issues…

Saturday, June 05, 2010

Debugging KML loading problems in Google Maps

I was attempting to load some KML into Google Maps via the KmlLayer class and not having much luck. Nothing was appearing. Unfortunately the KmlLayer class doesn’t provide an event to indicate an error has occurred. So what is available to debug these problems and what are some of the issues that can cause load errors?

  • Fiddler 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.
  • Check that the KML is valid using Feed Validator. I’ve found this doesn’t work too well with large KML files in IE, so use some other browser instead.
  • 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.
  • There are limits to the size of KML file supported by Google Maps, have a look at this page to see if this is your problem. I think this is what caused my issues.
  • Try loading the KML in Google Earth to see if it’s an issue with the KML or Google Maps’ handling of it.

Tuesday, June 01, 2010

Moving from Google Maps 2 to Google Maps 3

After playing with version 3 of the Google Maps API and trying to convert my ScratchPad application, 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.

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.

Google Maps version 2 Google Maps version 3
GMap2 class google.maps.Map class
GMap2.disableDoubleClickZoom() MapOptions.disableDoubleClickZoom:true passed to Map constructor or Map.setOptions()
GMap2.getDragObject().setDraggableCursor() MapOptions.draggableCursor passed to Map constructor or Map.setOptions()
GMap2.addOverlay() Call setMap() on the layer to add
GMap2.clearOverlays() ???
GMap2.removeOverlay() Call setMap(null) on the layer to remove
GLatLng class google.maps.LatLng class
GMarker class google.maps.Marker class
GMarker.bindInfoWindowHtml() google.maps.InfoWindow class, but you need to manually open the info window using

    google.maps.event.addListener(marker, 'click', function () {
    infoWindow.open(map, marker);
  });

GEvent.bind(), GEvent.addListener 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)
GEvent.removeListener() google.maps.event.removeListener()
GNavLabelControl class ???
GAdsManager class ???
GGeoXml class google.maps.KmlLayer class
GGeoXml.load event ???
GGeoXml.getDefaultCenter() KmlLayer.getDefaultViewport().getCenter()
GStreetviewOverlay class This is now integrated into the map, pass streetViewControl: true into MapOptions to make it appear
GClientGeocoder class google.maps.Geocoder class
GClientGeocoder.getLatLng() Geocoder.geocode()
GPolyline class google.maps.Polyline class
GPolyline.getLength() There is no direct replacement, but this forum post discusses how to implement something similar yourself.
GPolyline.getVertexCount() Polyline.getPath().length()
GDirections class google.maps.DirectionsService class to query for directions, google.maps.DirectionsRenderer class to render them on a map
GDirections.load() DirectionService.route()
GDirections.loadFromWaypoints() DirectionsRequest.waypoints property
GDirections.clear() No equivalent
GTrafficOverlay class google.maps.TrafficLayer class
GLayer class ??? – This is in the API issues database, star it if you want it implemented
GTileLayerOverlay class ???

Monday, May 31, 2010

Improving the performance of REGEXP queries in MySql

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

SELECT * FROM Postcodes WHERE Postcode LIKE 'B%'

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

SELECT * FROM Postcodes WHERE Postcode REGEXP'^B[0-9]'

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

SELECT * FROM Postcodes WHERE Postcode LIKE 'B%' AND Postcode REGEXP'^B[0-9]'

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.

Sunday, May 30, 2010

The Fibonacci Sequence

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 it is still available. Being something of a maths geek, I did enjoy reading it, but it has been languishing in my loft for a few years now.

Jump forward a couple of decades and I was reading “The Rabbit Problem” 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.

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.


Free XML sitemap generator with unlimited pages

I’ve been happily using this free XML sitemap generator 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 this alternative generator. 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.

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.

Friday, May 28, 2010

Google Maps elevation API

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 the example provided by Google is pretty good itself.

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.

Google Maps styled maps

Version 3 of the Google Maps API adds support for styled maps, 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?

Anyway I had a play with the styled map wizard (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.

So it looks good as an initial implementation, but I think more control is required to be a completely useful API.

Tuesday, May 25, 2010

Google Maps geolocation API

Google have had an API to get the user’s location for a while (via the Google loader), but I didn’t think it was very good. But now there are two new methods of figuring out the user’s location, 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.

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.

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. 

Friday, May 21, 2010

Google Maps geocoding still sucks

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 it was added to the API and failed to work too well three years ago. But no, it still sucks big time for postcode geocoding, as this test page demonstrates. 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 Ordnance Survey now provides geocoded postcode data for free so I assume it’s down to Google stuffing up.

Converting to Google Maps version 3

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

  <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjtZCgAx5i04BiZDO6HlxhRQUdBDpWCOMRMbgTcqadX0jQ8HOERSxXxhk24TIBUpivovAKLrnpSio9w" type="text/javascript"></script>
  <script type="text/javascript">
    window.onload = function () {
      if (GBrowserIsCompatible()) {
        var map = new GMap2(document.getElementById("map"));
        map.setCenter(new GLatLng(51.49506473014368, -0.130462646484375), 10);
        map.addControl(new GSmallMapControl());
        map.addControl(new GMapTypeControl());
        map.enableDoubleClickZoom();
        map.enableScrollWheelZoom();
        var layer = new GGeoXml("http://www.doogal.co.uk/LondonStationsKML.php");
        map.addOverlay(layer);
      }
    }
  </script>

And this is what the version 3 code looks like

  <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
  <script type="text/javascript">
    window.onload = function () {
      var latlng = new google.maps.LatLng(51.49506473014368, -0.130462646484375);
      var options = {
        zoom: 10,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      };

      var map = new google.maps.Map(document.getElementById("map"), options);

      var georssLayer = new google.maps.KmlLayer('http://www.doogal.co.uk/LondonStationsKML.php');
      georssLayer.setMap(map);
    }
  </script>

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.

Friday, April 23, 2010

JavaScript not executing in Internet Explorer 8

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

<script type="text/javascript" src="Scripts/jquery-1.4.1.min.js" />

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

<script type="text/javascript" src="Scripts/jquery-1.4.1.min.js"></script>