Tuesday, June 16, 2009

Virtual Earth Silverlight Control Part 3: Locating Best Buy Stores

I was searching the web for sources of Lat/Long data I could mash-up to in a Virtual Earth application and I ran across Best Buy ReMix. The API to collect store location information was easy to use and returned Lat/Long info, so I integrated it into the application. Watch this video to see how I did it:



I like to start by testing the URL in the browser:


http://api.remix.bestbuy.com/v1/stores(area(44236,20))?apiKey=[YourKey]


The Remix API returned XML describing the stores within 20 miles of my zipcode. Here is some of what I got back:


<stores currentPage="1" totalPages="1" from="1" to="6" total="6" queryTime="0.004" totalTime="0.013" canonicalUrl="/v1/stores(area("44236",20))?apiKey=fbsn9dsqvgnm42vyk4x5hman">
<store>
<storeId>758</storeId>
<name>Macedonia OH</name>
<address>470 East Aurora Road</address>
<city>Macedonia</city>
<region>OH</region>
<postalCode>44056</postalCode>
<fullPostalCode>44056-1834</fullPostalCode>
<country>US</country>
<lat>41.313461</lat>
<lng>-81.521233</lng>
<phone>330-468-6850</phone>
<hours>10:00am - 9:00pm Monday - Friday, 11:00am - 6:00pm Saturday, 11:00am - 6:00pm Sunday</hours>
<distance>6.0</distance>
</store>


The next step is to verify that Silverlight can consume this data using the WebClient Class. The code is simple but it does not always work, based on permissions (see Tim Heuer's explanation of this). I test it with this code:


void LoadBestBuyStores()
{
WebClient oClient = new WebClient();

string sUri = string.Format("http://api.remix.bestbuy.com/v1/stores(area({0},20))?apiKey=fbsn9dsqvgnm42vyk4x5hman", txtZipCode.Text);
var oAddress = new Uri(sUri);
oClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(BestBuyStores_DownloadStringCompleted);
oClient.DownloadStringAsync(oAddress);
}

void BestBuyStores_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string sResult = e.Result;
MessageBox.Show(sResult);
}



To finish the application, I needed to parse the XML that was returned and map it into an object instance, so I defined a class 'BestBuyStores':



public class BestBuyStore : PointOfInterest
{
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string Phone { get; set; }
public string Hours { get; set; }
}


and then added LinqToXML code to build my objects:


void BestBuyStores_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null )
return;

string sResult = e.Result;

var doc = XDocument.Parse(sResult);

var oStores = from element in doc.Descendants("store")
select new BestBuyStore
{
Name = element.Element("name").Value,
Address = element.Element("address").Value,
City = element.Element("city").Value,
Region = element.Element("region").Value,
Phone = element.Element("phone").Value,
Hours = element.Element("hours").Value,
Loc = new Location(double.Parse(element.Element("lat").Value), double.Parse(element.Element("lng").Value))
};

foreach (BestBuyStore oStore in oStores.ToList<BestBuyStore>())
{
RenderPoint(oStore);
m_oList.Add(oStore);
}

frmBestBuy.Visibility = Visibility.Visible;
frmBestBuy.ItemsSource = oStores;
}


Finally by extending the RenderPoint method I was able to mark each location with a custom Best Buy graphic:


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 if (oPOI is BestBuyStore)
{
Uri oSource = new Uri(@"BestBuy.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.BottomLeft);
}
else
{
var oRect = new System.Windows.Shapes.Rectangle() { Fill = new SolidColorBrush(Colors.Red), Width = 10, Height = 10 };
m_oLayer.AddChild(oRect, oPOI.Loc, PositionMethod.Center);
}
}

No comments: