Wednesday, 22 August 2007

Lat/Long to pixel conversion (for Silverlight, in Javascript)

Greg Schechter's Silverlight 1.1 VirtualEarth Viewer is a very cool sample (ot: as is tafiti Silverlight search interface, which I just came across today).

However the VirtualEarth Viewer is written for Silverlight 1.1, and I want RaceReplay.net to be able to display & animate maps in Silverlight 1.0. Hence the work on a custom TileClient for Silverlight - the most recent sample of which uses a few lines of Greg's codes (converted from C# to Javascript) to dynamically translate screen coordinates to latitude/longitude values (and vice versa)

See it in action here, and check out the ported code below.

/* Original Author http://blogs.msdn.com/greg_schechter
License http://msdn.microsoft.com/vstudio/eula.aspx?id=c8bf88e7-841c-43fd-c63d-379943617f36 */
function RadiansToDegrees (rad)
{
return (rad / Math.PI * 180.0);
}
function DegreesToRadians (deg)
{
return (deg * Math.PI / 180.0);
}
/* SLVirtualEarthViewer */
// Extent maps to +-180 for Longitude and +-85 for Latitude
function LongLatFromCoordinate (coordinate, worldRect)
{
var x = ((coordinate.X - worldRect.Left) * 360.0 / worldRect.Width) - 180.0;
var y = RadiansToDegrees (2 * Math.atan(Math.exp(Math.PI * (1.0 - 2.0 * (coordinate.Y - worldRect.Top) / worldRect.Height))) - Math.PI * 0.5);
var point = eval('({"X":'+x+', "Y":'+y+'})');
return point;
}
function CoordinateFromLongLat (longLat, worldRect)
{
var coordinate = eval('({"X":0, "Y":0})');
coordinate.X = worldRect.Left + (longLat.X + 180.0) * worldRect.Width / 360.0;
coordinate.Y = worldRect.Top + (worldRect.Width * 0.5) * (1 - (Math.log(Math.tan(DegreesToRadians(longLat.Y) * 0.5 + Math.PI * 0.25))) / Math.PI);
return coordinate;
}


And to use it...

var world = eval('({"Left":0, "Top":0, "Width":512, "Height":512})');
/* wired up to Silverlight event */
function onMouseMove (sender, mouseEventArgs)
{
var currX = mouseEventArgs.getPosition(null).x;
var currY = mouseEventArgs.getPosition(null).y;
var coo = eval('({"Y":'+currY+', "X":'+currX+'})');

var ll= LongLatFromCoordinate(coo, world);
document.getElementById('output').innerHTML = 'Lat/long: ' + ll.X + ', ' + ll.Y;

var coord = CoordinateFromLongLat(ll, world);
document.getElementById('output').innerHTML += '

Screen: ' + coord.X + ', ' + coord.Y;
}

Now to hook this up with a conversion to Virtual Earth image tile 'quadkey' values...

p.s. if you are interested in a slightly different version of the lat/long-coordinate conversion, download the sample code for Roll Your Own Tile Server and examine the XToLongitudeAtZoom() and YToLatitudeAtZoom() javascript functions. It's also a very cool sample.

5 comments:

  1. do you know what projections this will work for?

    ReplyDelete
  2. Mercator i think, which is the big flat square (+/-85 latitude) that microsoft and google use for their respective mapping sites.

    ReplyDelete
  3. Can you tell me where I can get the code for zoom for the current lat lon to xy conversion.

    ReplyDelete
  4. Pavan - not sure what you mean: zoom is just a function of the width/height of the map. In the above example it's 512x512 - but if you modify those values you will get the 'zoom'ed coordinates...

    ReplyDelete
  5. I think there is a small bug (width/height swapped).
    Instead of:
    coordinate.Y = worldRect.Top + (worldRect.Width * 0.5) * (1 - (Math.log(Math.tan(DegreesToRadians(longLat.Y) * 0.5 + Math.PI * 0.25))) / Math.PI);

    it should be:
    coordinate.Y = worldRect.Top + (worldRect.Height * 0.5) * (1 - (Math.log(Math.tan(DegreesToRadians(longLat.Y) * 0.5 + Math.PI * 0.25))) / Math.PI);

    ReplyDelete