How to create a Silverlight application from Map of Europe in svg format

Silverlight EU Countries application
(Click on image above to test the application - Silverlight 2, 3 or 4 runtime required)



TUTORIAL SUMMARY:
This tutorial is a bit more complex than the previous one that shows how to convert an excel graph to an animated and dynamic graph in a Silverlight application (available here). For this reason we will not start from stretch, but will work with the final solution. The steps in the tutorial will explain the main parts of the solution and how the data is prepared.

1) From Map in svg file to Silverlight
Start with original EU map in svg file. ViewerSvg is used to convert svg file to Silverlight xaml. The Eu map is shown in a Silverlight application (in web browser).

2) Select country on mouse over
Show the country under the mouse in other color and with thicker border.

3) Prepare country data
Get data for countries and convert them into xml. Create a CountryInfo and CountryInfoDataSource classes to provide access to the data for the application.

4) Show country data
Show the data of the selected country.

5) Add EU countries by year slider
Add slider that will show how the EU was growing by the year.


LEVEL: Advanced

Requirements:
  • Visual Studio 2008 SP1 or Visual Studio 2010
  • Silverlight Tools for Visual Studio 2008 - get from Silverlight - Get Started
  • ViewerSvg with Ab2d.ReaderSvg - get from Downloads
  • Microsoft Excel and Access 2007 (optional - for preparing data)

Finished sample project and sample data files are part of ViewerSvg with Ab2d.ReaderSvg package (from version 4.0 on) - see Samples\SilverlightEuPackage directory.


STEP 1
From Map in svg file to Silverlight


The source of the EU map is Wikimedia commons that, besides lots of other images, contains also a lot of images in svg file format. The EU map we will use can be found here. The original map looks like:

EU Accesson map svg file

The map contains EU countries colored by the year when they joined the EU. The size of the svg file is 553 KB. If the svg file is converted to xaml it is still 280 KB. This is quite a lot for a web application. So I asked a friend, who is an export with Adobe Illustrator, to simplify the map. He also removed the EU flag and text and data on the left side of the image so the new svg file contains only a map of EU. The size of the new svg file is 145 KB - this makes the size of xaml only 114 KB. This is still something, but not so much. The new file is also included in the package: EuropeMap_simplified.svg.

Now we start ViewerSvg and open the simplified file in it.

EU map in ViewerSvg

In the right part of the ViewerSvg a tree view is showing the structure of the read svg file. You can see that all of the countries on the map are already named. So if you click on the name, a country should be selected on the map. This is great news. With this we will know on which country the mouse is. The countries are also grouped together - the groups are named with the year when the countries in the group joined the European Union.

Now we can export the svg file to Silverlight xaml. Click the export button. The export dialog shows up. Select Export as Silverlight 2.0. The Root element will be changed to Canvas. The original canvas's size is 12690 x 7959 - this is too big for any screen resolution. So we will resize it - just enter 800 into the first TextBox after "Resize Canvas". This will set the width of the result canvas to 800 points and the height will be automatically set to 501. To reduce the size of xaml you can also slide the No. of decimals sliders to the left - the exported xaml will have no decimal digits. This will make the xaml 114 KB large. Click SAVE button. The saved xaml is used for EuMap.xaml file in the package.

With just a few clicks we have converted a map in svg file to the xaml that can be used in Silverlight application.



STEP 2
Select country on mouse over

The code to select the countries is almost the same as with the previous tutorial about how to create a Silverlight application from excel graph.

This is done in code bihind file - EuMap.xaml.cs.

In the RegisterCountryEvents method the event handlers are added to the MouseEnter and the MouseEnter event - this is recursivly done for all Path elements.

In the MouseEnter event handler the country name is get from the Path's Name. If the name represents a country than we simply set Path's StrokeThickness and its Fill. Than we fire a SelectedCountryChanged event to notify the parent that the country is changed - this will be needed when we will display the country info.

In the MouseLeave event handler the path properties are set to its original values and the SelectedCountryChanged is fired again.



STEP 3
Prepare country data

I have found data for the countries on the following site: http://www.photius.com/rankings/spreadsheets_2007/. There are 6 excel tables with the data for all the world countries. Because I needed only some basic data I used geography and population data only. I have also filtered the data to the countries that are shown in the EU map. The final excel file with the data is "eu data.xls" from the package.

Now we need to convert the data form excel file to xml file. The process is described here. In short: the excel file is opened in Microsoft Access and it is than saved into xml file. The EU_Data.xml contains the final countries data. There is also EU_Data.xsd that represents the xml data schema. Both files are also included in the Silverlight application.

Now the data needs to be imported into the application. But before this we need a class that will hold the data. This is the CountryInfo class. Note that because Silverlight 2 is based on .Net 3.5, it also has all the goodies of the C# for .Net 3.5. This includes Auto-Implemented Properties (the whole list can be found here) so we do not need to write all the private fields and its properties with getters and setters, but we can simple write:

    public class CountryInfo
    {
        
public string Name { get; set; }
        
public int Area { get; set; }
        
public int Population { get; set; }
        
public double PopulationDensity { get; set; }
        
public double BirthRate { get; set; }
        
public double LifeExpectancy { get; set; }
    }

So CountryInfo class contains properties that describe the country.

Now we need to read the data from xml to a collection of CountryInfo classes. This is done in the CountryInfoDataSource class in its FillCountries method. The data retrieval is done with XDocument class. This class in part of System.Xml.Linq assembly, so we need to add reference to it. The xml file is simply loaded into the XDocument class. The data about one country are enclosed into EU_Data elements. So we iterate through all the EU_Data elements with a foreach. Inside the loop we simply convert each element to its type and write it to the appropriate CountryInfo property:

// Read EU_Data.xml into XDocument
XDocument countries XDocument.Load("EU_Data.xml");

// Go throug all the EU_Data elements
foreach (XElement oneCountryElement in countries.Descendants("EU_Data"))
{
    CountryInfo oneCountry 
= new CountryInfo();

    
// Fill data 
    
oneCountry.Name oneCountryElement.Element("CountryName").Value;

    
oneCountry.Area Int32.Parse(oneCountryElement.Element("Area").Value);

    
oneCountry.Population 
        
Int32.Parse(oneCountryElement.Element("Population").Value);

    
oneCountry.PopulationDensity 
        double
.Parse(oneCountryElement.Element("PopulationDensity").Value, 
        System.Globalization.CultureInfo.InvariantCulture)
;

    
oneCountry.BirthRate 
        double
.Parse(oneCountryElement.Element("BirthRate").Value, 
        System.Globalization.CultureInfo.InvariantCulture)
;

    
oneCountry.LifeExpectancy 
        double
.Parse(oneCountryElement.Element("LifeExpectancy").Value, 
        System.Globalization.CultureInfo.InvariantCulture)
;

    
_countries.Add(oneCountry.Name, oneCountry);
}

Note that this was just the most basic use of the XDocument class. The class can be also used to create a much more complex linq quieries. Search for samples on the net to see more.

Now it is time to show the data.



STEP 4
Show country data

The country information is shown with a CountryInfoControl user control. The simplest way to show the data is to use the data binding and Value converters. The following line displays the country name:

<TextBlock Grid.Column="1" Grid.Row="0" FontWeight="Bold" Text="{Binding Name}"/>

The text is simply data binded to the Name property. But where does CountryInfo comes into the play - which Name to show?

This is done by setting the DataContext of the user control. Remember that we have a SelectedCountryChanged event on the EuMap control. So we should subscribe to the event and when the selected country is changed set the ContryInfoControl's DataContext to the appropriate ContryInfo class:

// Simply just change the DataContext to the selected country info
CountryInfoCountrol.DataContext = _countriesData.GetCountry(selectedCountryName);

So we set the DataContext to the appropriate ContryInfo class. This sets the Text properties of the TextBlocks to the value of binded property (for example Name). This works well for strings, but numbers are not formatted well.

Solution for this is to implement a custom ValueConverter that will be used to convert integer and double values to string with a appropriate format. This is done by the NumberToStringConverter class. Its Convert method gets integer or double as value and returns a formatted string. For integer values a simple format by thousands is used. For double values an additional parameter can be used to specify number of decimal digits. To use the converter it must be specified with the Binding settings:

    <TextBlock Grid.Column="1" Grid.Row="5" 
               Text
="{Binding LifeExpectancy, 
                      Converter={StaticResource NumberConverter}, 
                      ConverterParameter=0}"/>  

The TextBlock above shows the value of LifeExpectancy property displayed with no decimal digits.

We also need to add the converter class to the resources of the UserControl. This is done with firstly adding my namespace and than registering the class in the resources. The first lines of UserControl xaml look like:

<UserControl x:Class="SilverlightEuCountries.CountryInfoControl"
    xmlns
="http://schemas.microsoft.com/client/2007" 
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:my
="clr-namespace:SilverlightEuCountries"               
    Width
="400" Height="160">
    
<UserControl.Resources>
        
<my:NumberToStringConverter x:Key="NumberConverter"/>
    </
UserControl.Resources>

Our user control is prepared. It can be now added to the Page.xaml.



STEP 5
Add EU countries by year slider

The original EU map contains data when the countries joined the EU. It would be nice if the user of the application could see how the countries were joining the EU. This could be simply done with a slider that would change the year. This step does exaclty that.

Because the Slider control is not part of the default Silverlight assemblies, a reference to the System.Windows.Controls.Extended must be added to the Silverlight project.

The following xaml adds a Slider and appropriate texts to the application:

<StackPanel Orientation="Vertical" VerticalAlignment="Bottom" 
            HorizontalAlignment
="Left" Margin="5 0 0 0">
    
<TextBlock FontSize="15" Margin="5 0 0 0">EU Countries by year:</TextBlock>
    
<StackPanel Orientation="Horizontal">
        
<Slider Name="EuYearSlider" Width="150" Minimum="1950" Maximum="2008" 
                Value
="2008" SmallChange="1" ValueChanged="Slider_ValueChanged"/>
        <
TextBlock Name="EuYearText" FontSize="15" Margin="5 0 0 0" Text="2008"/>
    
</StackPanel>
</StackPanel>

The change of slider is handlered in UpdateEuForCurrentYear method. In the method we firstly change the EuYearText Text. Than ShowEuCountriesForYear method of the EuMap class is called.

ShowEuCountriesForYear method get a year as parameter and shows the countries that were in the year already members of EU with its color - all other countries are displayed with default gray color. To do this a _euYearsBrushes Dictionaries is filled with the initial Brushes (colors) of the countries groups.

This ends this tutorial. For additional information see also the comments in the code.



>  TRY THE APPLICATION



Additional notes:

Silverlight 2 is in beta phase, so there can be some problems with it. It is also possible that some of the classes, properties and methods will be renamed or changed when it comes to final. So I recommend some patience when dealing with it. However Silverlight is very good technology and will surely be a successful as it means a giant leap ahead for online content.