Sunday, June 7, 2009

Virtual Earth Silverlight Control Part 2: Locate Me

It was a post by Nikhil Kothari that inspired me to create a method to report my current location on the Virtual Earth Silverlight control.

http://www.nikhilk.net/Silverlight-Locate-Me.aspx

While I was traveling around the country (see http://www.wiredwalkabout.com/), I would run Nikhil's application to see the result. For the most part, it always reported my location about 20 miles from where I really was, which I think is a problem with the web service used and not the code calling it.

http://api.hostip.info/get_html.php?position=true




Nikhil's current live sample has been updated, but it is still not at good as a GPS. The application source code is worth looking at in detail. He uses techniques that are more advanced than calling WebClient, and as a programmer it is interesting to know how to do same thing many different ways (viewing this link requires you have Silverlight 3 installed):

http://www.nikhilk.net/Content/Posts/SilverlightLocateMe/LocateMe.htm

This post is about integrating this functionality into the mapping application I am currently developing. I have decided to create a separate class of objects to mark my current location. I did this mainly because I wanted to use a different symbol to mark myself on the map -- like a "You Are Here" marker. I have extended this class to collect a time stamp, and this -- in combination with persistence -- can be used to track my movements.


public class MyLocation : PointOfInterest



{



public DateTime TimeStamp { get; set; }



public MyLocation()



: base()



{



TimeStamp = DateTime.Now;



}







#region Serializer Methods



public override XElement PersistTo(ModelSerializer oSerializer, XElement oSelf)



{



XElement oXElement = base.PersistTo(oSerializer, oSelf);







oXElement.Add(new XAttribute("TimeStamp", TimeStamp.ToString()));







return oXElement;



}



public override IMapModel RecoverFrom(ModelSerializer oSerializer, XElement oElement)



{



var oResult = base.RecoverFrom(oSerializer, oElement);







string sTimeStamp = oElement.Attribute("TimeStamp").Value;



TimeStamp = DateTime.Parse(sTimeStamp);



return oResult;



}



#endregion



}
I needed to modify the rendering code in order to display a different picture for this class. With this quick change, objects of type MyLocation now show a pushpin instead of a red rectangle.


void RenderPoint(PointOfInterest oPOI)



{







if (oPOI is MyLocation)



{



Uri oSource = new Uri(@"Pushpin.png", UriKind.Relative);



var oBitmap = new BitmapImage(oSource);



var oImage = new Image() { Source = oBitmap, Width=23, Height=27 };



m_oLayer.AddChild(oImage, oPOI.Loc, PositionMethod.BottomCenter);



}



else



{



// Create a Rectangle Shape to Show as the "Pushpin"



// Set it's Size to 10x10



var oRect = new System.Windows.Shapes.Rectangle() { Fill = new SolidColorBrush(Colors.Red), Width = 10, Height = 10 };







// Add Rectangle to MapLayer



m_oLayer.AddChild(oRect, oPOI.Loc, PositionMethod.Center);



}



}
The code to query the web service using WebClient follows. This is an asynchronous call that returns a string delimited by line feeds, so I used Nikhil's trick of reading from a stream to parse out and convert the Lat/Long information.

void WhereAmI()



{



WebClient oClient = new WebClient();



var oAddress = new Uri(@"http://api.hostip.info/get_html.php?position=true");



oClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(oClient_DownloadStringCompleted);



oClient.DownloadStringAsync(oAddress);



}







void oClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)



{



string sResult = e.Result;







using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(sResult))))



{



try



{



sr.ReadLine();



sr.ReadLine();







double lat = 0;



TryParse<Double>(sr.ReadLine(), out lat);







double lng = 0;



TryParse<Double>(sr.ReadLine(), out lng);







var oPOI = new MyLocation() { Loc = new Location(lat, lng) };



m_oList.Add(oPOI);







RenderPoint(oPOI);







}



catch { }



}



MessageBox.Show(sResult);



}


No comments: