Introducing the Earthquake Locator – A Bing Maps Silverlight Application, part 1
Posted
by Bobby Diaz
on Geeks with Blogs
See other posts from Geeks with Blogs
or by Bobby Diaz
Published on Sun, 07 Mar 2010 05:51:19 GMT
Indexed on
2010/03/07
23:28 UTC
Read the original article
Hit count: 1672
- Silverlight 3
- WCF RIA Services
- Bing Maps Silverlight Control *
- Managed Extensibility Framework (optional)
- MVVM Light Toolkit (optional)
- log4net (optional)
* If you are new to Bing Maps or have not signed up for a Developer Account, you will need to visit www.bingmapsportal.com to request a Bing Maps key for your application.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Web.DomainServices;
using System.Web.Ria;
using System.Xml;
using log4net;
using EarthquakeLocator.Web.Model;
namespace EarthquakeLocator.Web.Services
{
/// <summary>
/// Provides earthquake data to client applications.
/// </summary>
[EnableClientAccess()]
public class EarthquakeService : DomainService
{
private static readonly ILog log = LogManager.GetLogger(typeof(EarthquakeService));
// USGS Data Feeds: http://earthquake.usgs.gov/earthquakes/catalogs/
private const string FeedForPreviousDay =
"http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml";
private const string FeedForPreviousWeek =
"http://earthquake.usgs.gov/earthquakes/catalogs/7day-M2.5.xml";
/// <summary>
/// Gets the earthquake data for the previous week.
/// </summary>
/// <returns>A queryable collection of <see cref="Earthquake"/> objects.</returns>
public IQueryable<Earthquake> GetEarthquakes()
{
var feed = GetFeed(FeedForPreviousWeek);
var list = new List<Earthquake>();
if ( feed != null )
{
foreach ( var entry in feed.Items )
{
var quake = CreateEarthquake(entry);
if ( quake != null )
{
list.Add(quake);
}
}
}
return list.AsQueryable();
}
/// <summary>
/// Creates an <see cref="Earthquake"/> object for each entry in the Atom feed.
/// </summary>
/// <param name="entry">The Atom entry.</param>
/// <returns></returns>
private Earthquake CreateEarthquake(SyndicationItem entry)
{
Earthquake quake = null;
string title = entry.Title.Text;
string summary = entry.Summary.Text;
string point = GetElementValue<String>(entry, "point");
string depth = GetElementValue<String>(entry, "elev");
string utcTime = null;
string localTime = null;
string depthDesc = null;
double? magnitude = null;
double? latitude = null;
double? longitude = null;
double? depthKm = null;
if ( !String.IsNullOrEmpty(title) && title.StartsWith("M") )
{
title = title.Substring(2, title.IndexOf(',')-3).Trim();
magnitude = TryParse(title);
}
if ( !String.IsNullOrEmpty(point) )
{
var values = point.Split(' ');
if ( values.Length == 2 )
{
latitude = TryParse(values[0]);
longitude = TryParse(values[1]);
}
}
if ( !String.IsNullOrEmpty(depth) )
{
depthKm = TryParse(depth);
if ( depthKm != null )
{
depthKm = Math.Round((-1 * depthKm.Value) / 100, 2);
}
}
if ( !String.IsNullOrEmpty(summary) )
{
summary = summary.Replace("</p>", "");
var values = summary.Split(
new string[] { "<p>" },
StringSplitOptions.RemoveEmptyEntries);
if ( values.Length == 3 )
{
var times = values[1].Split(
new string[] { "<br>" },
StringSplitOptions.RemoveEmptyEntries);
if ( times.Length > 0 )
{
utcTime = times[0];
}
if ( times.Length > 1 )
{
localTime = times[1];
}
depthDesc = values[2];
depthDesc = "Depth: " + depthDesc.Substring(depthDesc.IndexOf(":") + 2);
}
}
if ( latitude != null && longitude != null )
{
quake = new Earthquake()
{
Id = entry.Id,
Title = entry.Title.Text,
Summary = entry.Summary.Text,
Date = entry.LastUpdatedTime.DateTime,
Url = entry.Links.Select(l => Path.Combine(l.BaseUri.OriginalString,
l.Uri.OriginalString)).FirstOrDefault(),
Age = entry.Categories.Where(c => c.Label == "Age")
.Select(c => c.Name).FirstOrDefault(),
Magnitude = magnitude.GetValueOrDefault(),
Latitude = latitude.GetValueOrDefault(),
Longitude = longitude.GetValueOrDefault(),
DepthInKm = depthKm.GetValueOrDefault(),
DepthDesc = depthDesc,
UtcTime = utcTime,
LocalTime = localTime
};
}
return quake;
}
private T GetElementValue<T>(SyndicationItem entry, String name)
{
var el = entry.ElementExtensions.Where(e => e.OuterName == name).FirstOrDefault();
T value = default(T);
if ( el != null )
{
value = el.GetObject<T>();
}
return value;
}
private double? TryParse(String value)
{
double d;
if ( Double.TryParse(value, out d) )
{
return d;
}
return null;
}
/// <summary>
/// Gets the feed at the specified URL.
/// </summary>
/// <param name="url">The URL.</param>
/// <returns>A <see cref="SyndicationFeed"/> object.</returns>
public static SyndicationFeed GetFeed(String url)
{
SyndicationFeed feed = null;
try
{
log.Debug("Loading RSS feed: " + url);
using ( var reader = XmlReader.Create(url) )
{
feed = SyndicationFeed.Load(reader);
}
}
catch ( Exception ex )
{
log.Error("Error occurred while loading RSS feed: " + url, ex);
}
return feed;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ComponentModel.DataAnnotations;
namespace EarthquakeLocator.Web.Model
{
/// <summary>
/// Represents an earthquake occurrence and related information.
/// </summary>
[DataContract]
public class Earthquake
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[Key]
[DataMember]
public string Id { get; set; }
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
[DataMember]
public string Title { get; set; }
/// <summary>
/// Gets or sets the summary.
/// </summary>
/// <value>The summary.</value>
[DataMember]
public string Summary { get; set; }
// additional properties omitted
}
}
using System;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using Microsoft.Maps.MapControl;
using GalaSoft.MvvmLight;
using EarthquakeLocator.Web.Model;
using EarthquakeLocator.Web.Services;
namespace EarthquakeLocator.ViewModel
{
/// <summary>
/// Provides data for views displaying earthquake information.
/// </summary>
public class EarthquakeViewModel : ViewModelBase
{
[Import]
public EarthquakeContext Context;
/// <summary>
/// Initializes a new instance of the <see cref="EarthquakeViewModel"/> class.
/// </summary>
public EarthquakeViewModel()
{
var catalog = new AssemblyCatalog(GetType().Assembly);
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
Initialize();
}
/// <summary>
/// Initializes a new instance of the <see cref="EarthquakeViewModel"/> class.
/// </summary>
/// <param name="context">The context.</param>
public EarthquakeViewModel(EarthquakeContext context)
{
Context = context;
Initialize();
}
private void Initialize()
{
MapCenter = new Location(20, -170);
ZoomLevel = 2;
}
#region Private Methods
private void OnAutoLoadDataChanged()
{
LoadEarthquakes();
}
private void LoadEarthquakes()
{
var query = Context.GetEarthquakesQuery();
Context.Earthquakes.Clear();
Context.Load(query, (op) =>
{
if ( !op.HasError )
{
foreach ( var item in op.Entities )
{
Earthquakes.Add(item);
}
}
}, null);
}
#endregion Private Methods
#region Properties
private bool autoLoadData;
/// <summary>
/// Gets or sets a value indicating whether to auto load data.
/// </summary>
/// <value><c>true</c> if auto loading data; otherwise, <c>false</c>.</value>
public bool AutoLoadData
{
get { return autoLoadData; }
set
{
if ( autoLoadData != value )
{
autoLoadData = value;
RaisePropertyChanged("AutoLoadData");
OnAutoLoadDataChanged();
}
}
}
private ObservableCollection<Earthquake> earthquakes;
/// <summary>
/// Gets the collection of earthquakes to display.
/// </summary>
/// <value>The collection of earthquakes.</value>
public ObservableCollection<Earthquake> Earthquakes
{
get
{
if ( earthquakes == null )
{
earthquakes = new ObservableCollection<Earthquake>();
}
return earthquakes;
}
}
private Location mapCenter;
/// <summary>
/// Gets or sets the map center.
/// </summary>
/// <value>The map center.</value>
public Location MapCenter
{
get { return mapCenter; }
set
{
if ( mapCenter != value )
{
mapCenter = value;
RaisePropertyChanged("MapCenter");
}
}
}
private double zoomLevel;
/// <summary>
/// Gets or sets the zoom level.
/// </summary>
/// <value>The zoom level.</value>
public double ZoomLevel
{
get { return zoomLevel; }
set
{
if ( zoomLevel != value )
{
zoomLevel = value;
RaisePropertyChanged("ZoomLevel");
}
}
}
#endregion Properties
}
}
using System;
using System.ComponentModel.Composition;
namespace EarthquakeLocator.Web.Services
{
/// <summary>
/// The client side proxy for the EarthquakeService class.
/// </summary>
[Export]
public partial class EarthquakeContext
{
}
}
using System;
namespace EarthquakeLocator.Web.Model
{
/// <summary>
/// Represents an earthquake occurrence and related information.
/// </summary>
public partial class Earthquake
{
/// <summary>
/// Gets the location based on the current Latitude/Longitude.
/// </summary>
/// <value>The location.</value>
public string Location
{
get { return String.Format("{0},{1}", Latitude, Longitude); }
}
/// <summary>
/// Gets the size based on the Magnitude.
/// </summary>
/// <value>The size.</value>
public double Size
{
get { return (Magnitude * 3); }
}
}
}
<UserControl x:Class="EarthquakeLocator.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:bing="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
xmlns:vm="clr-namespace:EarthquakeLocator.ViewModel"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
>
<UserControl.Resources>
<DataTemplate x:Key="EarthquakeTemplate">
<Ellipse Fill="Red" Stroke="Black" StrokeThickness="1"
Width="{Binding Size}" Height="{Binding Size}"
bing:MapLayer.Position="{Binding Location}"
bing:MapLayer.PositionOrigin="Center">
<ToolTipService.ToolTip>
<StackPanel>
<TextBlock Text="{Binding Title}" FontSize="14" FontWeight="Bold" />
<TextBlock Text="{Binding UtcTime}" />
<TextBlock Text="{Binding LocalTime}" />
<TextBlock Text="{Binding DepthDesc}" />
</StackPanel>
</ToolTipService.ToolTip>
</Ellipse>
</DataTemplate>
</UserControl.Resources>
<UserControl.DataContext>
<vm:EarthquakeViewModel AutoLoadData="True" />
</UserControl.DataContext>
<Grid x:Name="LayoutRoot">
<bing:Map x:Name="map" CredentialsProvider="--Your-Bing-Maps-Key--"
Center="{Binding MapCenter, Mode=TwoWay}"
ZoomLevel="{Binding ZoomLevel, Mode=TwoWay}">
<bing:MapItemsControl ItemsSource="{Binding Earthquakes}"
ItemTemplate="{StaticResource EarthquakeTemplate}" />
</bing:Map>
</Grid>
</UserControl>
© Geeks with Blogs or respective owner