Since volcanos are often associated with earthquakes, and vice versa, I decided to show recent volcanic activity on the Earthquake Locator map. I am pulling the data from a website created for a joint project between the Smithsonian's Global Volcanism Program and the US Geological Survey's Volcano Hazards Program, found here. They provide a Weekly Volcanic Activity Report as an RSS feed.
I started implementing this new functionality by creating a new Volcano entity in the domain model and adding the following to the EarthquakeService class (I also factored out the common reading/parsing helper methods to a separate FeedReader class that can be used by multiple domain service classes):
private static readonly string VolcanoFeedUrl =
ConfigurationManager.AppSettings["VolcanoFeedUrl"];
/// <summary>
/// Gets the volcano data for the previous week.
/// </summary>
/// <returns>A queryable collection of <see cref="Volcano"/> objects.</returns>
public IQueryable<Volcano> GetVolcanos()
{
var feed = FeedReader.Load(VolcanoFeedUrl);
var list = new List<Volcano>();
if ( feed != null )
{
foreach ( var item in feed.Items )
{
var quake = CreateVolcano(item);
if ( quake != null )
{
list.Add(quake);
}
}
}
return list.AsQueryable();
}
/// <summary>
/// Creates a <see cref="Volcano"/> object for each item in the RSS feed.
/// </summary>
/// <param name="item">The RSS item.</param>
/// <returns></returns>
private Volcano CreateVolcano(SyndicationItem item)
{
Volcano volcano = null;
string title = item.Title.Text;
string desc = item.Summary.Text;
double? latitude = null;
double? longitude = null;
FeedReader.GetGeoRssPoint(item, out latitude, out longitude);
if ( !String.IsNullOrEmpty(title) )
{
title = title.Substring(0, title.IndexOf('-'));
}
if ( !String.IsNullOrEmpty(desc) )
{
desc = String.Join("\n\n", desc
.Replace("<p>", "")
.Split(
new string[] { "</p>" },
StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.ToArray())
.Trim();
}
if ( latitude != null && longitude != null )
{
volcano = new Volcano()
{
Id = item.Id,
Title = title,
Description = desc,
Url = item.Links.Select(l => l.Uri.OriginalString).FirstOrDefault(),
Latitude = latitude.GetValueOrDefault(),
Longitude = longitude.GetValueOrDefault()
};
}
return volcano;
}
I then added the corresponding LoadVolcanos() method and Volcanos collection to the EarthquakeViewModel class in much the same way I did with the Earthquakes in my previous article in this series.
Now that I am starting to add more information to the map, I wanted to give the user some options as to what is displayed and allowing them to choose what gets turned off. I have updated the MainPage.xaml to look like this:
<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:basic="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
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>
<DataTemplate x:Key="VolcanoTemplate">
<Polygon Fill="Gold" Stroke="Black" StrokeThickness="1" Points="0,10 5,0 10,10"
bing:MapLayer.Position="{Binding Location}"
bing:MapLayer.PositionOrigin="Center"
MouseLeftButtonUp="Volcano_MouseLeftButtonUp">
<ToolTipService.ToolTip>
<StackPanel>
<TextBlock Text="{Binding Title}" FontSize="14" FontWeight="Bold" />
<TextBlock Text="Click icon for more information..." />
</StackPanel>
</ToolTipService.ToolTip>
</Polygon>
</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:MapItemsControl ItemsSource="{Binding Volcanos}"
ItemTemplate="{StaticResource VolcanoTemplate}" />
</bing:Map>
<basic:TabControl x:Name="tabs" VerticalAlignment="Bottom" MaxHeight="25" Opacity="0.7">
<basic:TabItem Margin="90,0,-90,0" MouseLeftButtonUp="TabItem_MouseLeftButtonUp">
<basic:TabItem.Header>
<TextBlock x:Name="txtHeader" Text="Options"
FontSize="13" FontWeight="Bold" />
</basic:TabItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Earthquakes:" FontWeight="Bold" Margin="3" />
<StackPanel Margin="3">
<CheckBox Content=" < 4.0"
IsChecked="{Binding ShowLt4, Mode=TwoWay}" />
<CheckBox Content="4.0 - 4.9"
IsChecked="{Binding Show4s, Mode=TwoWay}" />
<CheckBox Content="5.0 - 5.9"
IsChecked="{Binding Show5s, Mode=TwoWay}" />
</StackPanel>
<StackPanel Margin="10,3,3,3">
<CheckBox Content="6.0 - 6.9"
IsChecked="{Binding Show6s, Mode=TwoWay}" />
<CheckBox Content="7.0 - 7.9"
IsChecked="{Binding Show7s, Mode=TwoWay}" />
<CheckBox Content="8.0 +"
IsChecked="{Binding ShowGe8, Mode=TwoWay}" />
</StackPanel>
<TextBlock Text="Other:" FontWeight="Bold" Margin="50,3,3,3" />
<StackPanel Margin="3">
<CheckBox Content="Volcanos"
IsChecked="{Binding ShowVolcanos, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
</basic:TabItem>
</basic:TabControl>
</Grid>
</UserControl>
Notice that I added a VolcanoTemplate that uses a triangle-shaped Polygon to represent the Volcano locations, and I also added a second <bing:MapItemsControl /> tag to the map to bind to the Volcanos collection. The TabControl found below the map houses the options panel that will present the user with several checkboxes so they can filter the different points based on type and other properties (i.e. Magnitude). Initially, the TabItem is collapsed to reduce it's footprint, but the screen shot below shows the options panel expanded to reveal the available settings:
I have updated the Source Code and Live Demo to include these new features.
Happy Mapping!