Saturday, 23 June 2007

Synchronise Live Maps & Silverlight canvas


If you overlay a location-specific Silverlight canvas on a Live Map (or is it Virtual Earth?, you'll probably want to 'synchronise' the canvas with the underlying map as it is panned/zoomed.

Although it might seem a tough task, with the help of the Virtual Earth 5 SDK it's relatively easy.

Firstly, check out the end result - a Silverlight canvas/animation that zooms and pans with the Live Map underneath it.

To accomplish the effect, I needed to:

0. Load the map and Silverlight controls
map = new VEMap('myMap');
map.LoadMap(
new VELatLong(-33.865052673579655, 151.22375965118408),
16, VEMapStyle.Hybrid);
//...
Sys.Silverlight.createObject( "xaml/Domain.Xaml", ...
Remembering to set isWindowless:'true' and the position of the elements so they float together.

1. Add Scale and Translate Transforms to the canvas
lt;Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"
x:Name="canvasScale" />
<TranslateTransform X="0" Y="0"
x:Name="canvasTranslate" Name="canvasTranslate" />
</TransformGroup>
</Canvas.RenderTransform>
(this was the only change required to the Xaml). You will use the x:Names later to make it easy to transform the canvas.

2. Determine the lat-long of the canvas

So that you can always position it accurately, regardless of how the map has been panned or zoomed. In this case, the top-left of the canvas should be is at 33.858210507117995, 151.2127733230591, and the map center when loaded is -33.865052673579655, 151.22375965118408.

3. Support panning
Simply shift the map and canvas by the same pixel delta, eg. simply call the Pan function on the map object, and modify the named TranslateTransform on the canvas.
function MoveNorth()
{
map.Pan(0, -100);
var sl8 = document.getElementById("WpfeControlHost_1");
var canvasT = sl8.content.findName("canvasTranslate");
//canvasT.X = canvasT.X - 100;
canvasT.Y = canvasT.Y + 100;
}

4. Support zooming
Other than a little bit of hardcoding, the zoom javascript is pretty simple
function ZoomIn()
{
map.ZoomIn();
var zoom = map.GetZoomLevel();
media_scale(zoom);
return false;
}
function media_scale(zoomLevel)
{
var sl8 = document.getElementById("WpfeControlHost_1");
var canvasT = sl8.content.findName("canvasTranslate");
var canvasS = sl8.content.findName("canvasScale");

var canvasLatLong = new VELatLong(-33.858210507117995, 151.2127733230591);
var canvasPixel = map.LatLongToPixel(canvasLatLong);

canvasT.X = canvasPixel.x;
canvasT.Y = canvasPixel.y;

if (zoomLevel == 17)
{
canvasS.ScaleX = 2;
canvasS.ScaleY = 2;
}
else if (zoomLevel == 16)
{
canvasS.ScaleX = 1;
canvasS.ScaleY = 1;
}
else if (zoomLevel == 15)
{
canvasS.ScaleX = 0.5;
canvasS.ScaleY = 0.5;
}
else if (zoomLevel == 14)
{
canvasS.ScaleX = 0.25;
canvasS.ScaleY = 0.25;
}
else if (zoomLevel == 13)
{
canvasS.ScaleX = 0.125;
canvasS.ScaleY = 0.125;
}
else if (zoomLevel == 12)
{
canvasS.ScaleX = 0.0625;
canvasS.ScaleY = 0.0625;
}
// haven't supported tinier zoom levels - might need to just hide content?
}

As long as your canvas and map loaded 'in sync', the panning should work without trouble, and if the top-left lat-long of your canvasis set, zooming should work too. Note that the scale-factors will depend on what 'resolution' your original Xaml was drawn in - I was tracing a map at zoom level 16 (as you can see).

There's a lot more you could add to this - including animating the zoom and supporting 'drag' for the canvas and map...


2 comments:

  1. Hi,

    I can't see the live maps underlay I'm afraid on your demo. Great work though.

    ReplyDelete
  2. Hey woodced (or anyone else who finds this doesn't "work" for them),
    If not too much trouble, could you send some details on the 'platform' which isn't working. I've tested IE6/7 and FF2 on PC; FF on Mac and would love to know any issues that 'break' it.
    Thanks in advance

    ReplyDelete