C# Postcode to OS Coordinates

I needed to translate some postcodes to Ordnance Survey coordinates for the Random Pub Finder. Now I could have paid money to do this, which I wasn't too keen on doing, so I wrote a bit of code to rip them off from Streetmap. This simple class will do the trick, at least until Streetmap change their URLs. It does a bit of screen-scraping to get the OS coordinates.

The coordinates generated are the distance East and North (in metres) from some point in the Scilly Isles.

The class also has methods to translate from OS coordinate to postcode, to get the longitude and latitude from a postcode and translate from longitude/latitude to postcode. The code works with .NET 1.1 and .NET 2.0.

If you're not interested in the code, download my test application, which requires .NET 2 to run.

The postcode to OS test application
download the source code and project (requires Visual Studio 2010)
using System;
using System.Net;
using System.IO;

namespace PostcodeToOS
{
  /// <summary>
  /// Helper class to convert from postcodes to Ordnance Survey coordinates and longitude/latitude, 
  /// via screen-scraping of the Streetmap.co.uk site
  /// </summary>
  public class Postcode
  {
    private static string MakeRequest(string request)
    {
      HttpWebRequest myReq =
        (HttpWebRequest)WebRequest.Create("http://www.streetmap.co.uk/streetmap.dll?GridConvert?" + request);

      // Sends the HttpWebRequest and waits for the response.
      HttpWebResponse myHttpWebResponse = myReq.GetResponse() as HttpWebResponse;
      try
      {
        Stream response = myHttpWebResponse.GetResponseStream();
        StreamReader readStream = new StreamReader(response, System.Text.Encoding.GetEncoding("utf-8"));
        return readStream.ReadToEnd();
      }
      finally
      {
        // Releases the resources of the response.
        myHttpWebResponse.Close();
      }
    }

    /// <summary>
    /// Converts a postcode to an Ordnance Survey coordinate.
    /// </summary>
    /// <param name="Postcode">Postcode</param>
    /// <param name="XPos">Ordnance Survey X coordinate</param>
    /// <param name="YPos">Ordnance Survey Y coordinate</param>
    public static void ToOS(string Postcode, out int XPos, out int YPos)
    {
      XPos = 0;
      YPos = 0;
      string HTML = MakeRequest("type=Postcode&name=" + Postcode);
      // find the link
      string linkToFind = "http://www.streetmap.co.uk/streetmap.dll?grid2map?";
      int pos = HTML.IndexOf(linkToFind);
      HTML = HTML.Substring(pos+linkToFind.Length, HTML.Length-pos-linkToFind.Length);

      // split into separate query strings
      char [] delimiter = new char[] {'&'};
      string [] QueryStrings = HTML.Split(delimiter);

      // see if we can find the x and y query strings
      foreach (string qry in QueryStrings)
      {
        if (qry.StartsWith("X="))
        {
          XPos = int.Parse(qry.Substring(2));
        }
        if (qry.StartsWith("Y="))
        {
          YPos = int.Parse(qry.Substring(2));
        }
      }

      if ((XPos <= 0) | (YPos <= 0))
        throw new Exception("Failed to get OS coordinates for postcode " + Postcode);
    }

    private static string GetStringAfterText(string html, string text)
    {
      int indexOfStart = html.IndexOf(text);
      return html.Substring(indexOfStart + text.Length, html.Length-indexOfStart-text.Length);
    }

    /// <summary>
    /// Converts an Ordnance Survey coordinate to a postcode.
    /// </summary>
    /// <param name="XPos">Ordnance Survey X coordinate</param>
    /// <param name="YPos">Ordnance Survey Y coordinate</param>
    /// <returns>The postcode.</returns>
    public static string FromOS(int XPos, int YPos)
    {
      string HTML = MakeRequest("type=OSgrid&name=" + XPos.ToString() + "," + YPos.ToString());
      const string startOfPostcode =
        "Nearest Post Code</strong> </td> <td width=\"50%\" align=\"center\" valign=\"middle\">";

      string startOfPostcodeString = GetStringAfterText(HTML, startOfPostcode);
      int indexOfEnd = startOfPostcodeString.IndexOf(" <");
      return startOfPostcodeString.Substring(0, indexOfEnd);
    }

    /// <summary>
    /// Converts latitude/longitude to a postcode.
    /// </summary>
    /// <param name="latitude">Latitude</param>
    /// <param name="longitude">Longitude</param>
    /// <returns>The postcode.</returns>
    public static string FromLatLong(float latitude, float longitude)
    {
      string HTML = MakeRequest("type=LatLong&name=" + latitude.ToString() + "," + longitude.ToString());
      const string startOfPostcode =
        "Nearest Post Code</strong> </td> <td width=\"50%\" align=\"center\" valign=\"middle\">";

      string startOfPostcodeString = GetStringAfterText(HTML, startOfPostcode);
      int indexOfEnd = startOfPostcodeString.IndexOf(" <");
      return startOfPostcodeString.Substring(0, indexOfEnd);
    }

    /// <summary>
    /// Converts a postcode to a longitude and latitude coordinate.
    /// </summary>
    /// <param name="Postcode">The postcode.</param>
    /// <param name="latitude">The latitude.</param>
    /// <param name="longitude">The longitude.</param>
    public static void ToLatLong(string Postcode, out float latitude, out float longitude)
    {
      latitude = 0;
      longitude = 0;

      string HTML = MakeRequest("type=Postcode&name=" + Postcode);
      HTML = GetStringAfterText(HTML, "<strong>Lat</strong> (WGS84)");
      HTML = GetStringAfterText(HTML, " ( ");
      int indexOfEnd = HTML.IndexOf(" ) ");
      if (indexOfEnd > -1)
      {
        latitude = float.Parse(HTML.Substring(0, indexOfEnd));

        HTML = GetStringAfterText(HTML, " ( ");
        indexOfEnd = HTML.IndexOf(" ) ");
        longitude = float.Parse(HTML.Substring(0, indexOfEnd));
      }
      else
        throw new Exception("Failed to get longitude and latitude for postcode " + Postcode);
    }
  }
}