March Mapness and the 4-Color Map

Derek Harmon / Thursday, March 5, 2009

In celebration of this month's official launch of our Silverlight mapping control, the xamWebMap, we would like you all to join us for MARCH MAPNESS!

Throughout this month I and others will be blogging like mad about our map experiences, so that wherever you see this logo:

you can count on seeing some new trick or tip that will help you develop your Silverlight mapping applications. If you have a map experience to share, please blog about it and link back here to let everyone know where it started!

About Four Color Maps

Map coloring afficianados will have heard of a theorem called the "Four Color Theorem." This mathematical theorem states that a political map (such as that of the United States) comprised of contiguous regions can be colored with four colors or less, such that no two regions that touch each other will have the same color.

If you have ever let your computer randomly color a map, you can probably attest to the times when Nevada and California (to take two entirely arbitrary, adjacent states) end up with the same color. By using the Four Color Theorem to color your map, you can entirely get around this.

Now you are probably asking, why isn't this built in? Why aren't all Silverlight maps automatically painted as four-colored? The answer to that is performance. The algorithm for the four-color theorem has a cuartic expected run-time (that is, it is proportional to the number of regions, n, raised to the fourth power, n4). You wouldn't want to execute this expensive computation everytime you repaint the map.

Instead, what I will show you is how the xamWebMap lets you apply a four-color scheme to it before it ever paints itself, so you only have to compute its colors once, and then it will take care of the rest.

Editing the Shapefile DBF

One of the benefits of Shapefiles (polygonal shapefiles in the case of the political map we are dealing with) is they come with easily editable dBase (.dbf) data files. You can open up dBase data files in Microsoft Access or another application and add a new column of data to it for each region of the map. What I am going to do is add a Color column to the "usa_st.dbf" included in our Silverlight data visualization samples.

By default, Access will open the .dbf so you are accessing it as linked tables (to which you cannot extend the schema). You can get around this by importing the .dbf, since dBase III is one of many database file formats that Access can speak.

Once you have "usa_st.dbf" imported, you can go to Design View in Access to add the "COLOR" column. For best compatibility, make sure the COLOR column that you add is of data type "Number" (specifically, you may have to change it to "Double" from "Long Integer"). Save changes to the table in Design View, and then go to row editing view.

I used the freeze column feature to fix the state's NAME column in place, and then work on the COLOR column at the end of each record. Fill-in the values (from 1 to 4) based on what color each region in the map should be. You could use different values, but the values 1-4 will work with the LinearValueScale I will show you later in the xamWebMap. In the attached "usa_st.dbf" you will see that I have done this in the COLOR column for each state in a United States map.

When you have finished, go to Export Data Files and choose to export (as a dBase III file) your extended, edited "usa_st.dbf" back into your ClientBin folder. In the next step, you will set-up xamWebMap to read it into and show a US map.

Loading a Shapefile

You are probably familiar with getting a Silverlight map application going with the xamWebMap and a Shapefile in Microsoft Visual Studio 2008. If not, please see our helpful walkthrough in the online help topic, "Displaying xamWebMap Using Shapefiles."

All you need to do in getting started with any map application is create a new Silverlight application (with an ASP.NET Web application host), add a pair of Infragistics Silverlight Data Visualization assembly references to the project (these will be deployed as part of your XAP file later on), and add an XML namespace URI declaration for our "igMap" prefix to your page's XAML file. I should note that you could do everything I am about to show you here on out in C# or VB.NET -- but to cut-down on how many solutions you have to choose from to download I am going to do everything entirely in XAML. (That's right, there is no code involved!)

A word on deployment of Shapefiles. By default, the xamWebMap will try to load Shapefiles from your ASP.NET host application's "ClientBin" folder. To promote better organization of the assets kept in ClientBin, we conventionally store Shapefiles in our samples in /ClientBin/Shapefiles. This is why you will frequently see the Uri on the ShapeFileReader set to a value like "Shapefiles/usa_st". Depending on how you are going to deploy your Silverlight mapping application, it's important to know where xamWebMap is looking to load map assets. If xamWebMap doesn't find your Shapefile where Uri says beneath the ClientBin, then it will gracefully not load that Shapefile as a layer into your map.

Here is what the XAML of your xamWebMap control should look like once you have set-up loading of the Shapefile as described in the afforementioned "Displaying xamWebMap Using Shapefiles" walkthrough:

<igMap:XamWebMap x:Name="map">
    <igMap:XamWebMap.Layers>
        <igMap:MapLayer x:Name="statesLayer">
            <igMap:MapLayer.Reader>
                <igMap:ShapeFileReader 
                   Uri="Shapefiles/usa_st" DataMapping="Caption=STATE_ABBR" />
            </igMap:MapLayer.Reader>
         </igMap:MapLayer>
    </igMap:XamWebMap.Layers>
</igMap:XamWebMap>

When you run this application, you will get the default random color selection because we have not specified a FillMode, a Value Scale, or the set of four Brushes to use. I will show you how to do that in the next step.

Coloring the Map

We have implemented a map coloring technique called Choropleth that you can use to associate the color of each state in the United States of this map with a color derived from the data value in the COLOR column that I added to "usa_st.dbf" a little earlier. There will be other blog entries on all of the rich features of Choropleth shading but for right now, all you have to do is set it on the MapLayer's FillMode attribute.

        <igMap:MapLayer x:Name="statesLayer" FillMode="Chloropleth">

For this shade-by-value to work, we need to define a value scale. Although xamWebMap supports a variety of value scales (logarithmic, distribution, etc), a simple linear value scale will be sufficient. I will give it a range myself, from 1 to 4, just as I had set-up the values in the COLOR column earlier. This is what this XAML would look like:

            <igMap:MapLayer.ValueScale>
                 <igMap:LinearScale IsAutoRange="False"
                     MinimumValue="1"
                     MaximumValue="4"  />
            </igMap:MapLayer.ValueScale>

The third step is that I need to define my set of Brushes. Not being a graphic designer, I am just going to play it conservatively here by choosing four different solid brush colors, however you can choose any four brushes you want and they can be anything Silverlight 2 can support (e.g., gradients, etc.)

            <igMap:MapLayer.Brushes>
                 <ig:BrushCollection>
                     <SolidColorBrush Color="Red" />
                     <SolidColorBrush Color="Orange" />
                     <SolidColorBrush Color="Blue" />
                     <SolidColorBrush Color="Green" />
                 </ig:BrushCollection>
            </igMap:MapLayer.Brushes>

The final change I need to make is to associate the Value of each map element (i.e., state) with what I entered earlier in the COLOR column of "usa_st.dbf". This is done through the DataMapping attribute of the ShapeFileReader. Observe it already binds the Caption of each map element to the two-letter postal abbreviation for that state, which you may have already noticed being rendered on the map. This list of map element characteristics to dBase column names continues, separated by semicolons, as you see in the following listing showing the consolidated XAML for my four-color xamWebMap control:

<igMap:XamWebMap x:Name="map">
    <igMap:XamWebMap.Layers>
        <igMap:MapLayer x:Name="statesLayer" FillMode="Chloropleth">
            <igMap:MapLayer.Reader>
            <igMap:ShapeFileReader Uri="Shapefiles/usa_st" DataMapping="Caption=STATE_ABBR; Value=COLOR" />
            </igMap:MapLayer.Reader>
            <igMap:MapLayer.ValueScale>
                 <igMap:LinearScale IsAutoRange="False"
                     MinimumValue="1"
                     MaximumValue="4"  />
            </igMap:MapLayer.ValueScale>
            <igMap:MapLayer.Brushes>
                 <ig:BrushCollection>
                     <SolidColorBrush Color="Red" />
                     <SolidColorBrush Color="Orange" />
                     <SolidColorBrush Color="Blue" />
                     <SolidColorBrush Color="Green" />
                 </ig:BrushCollection>
            </igMap:MapLayer.Brushes>
        </igMap:MapLayer>
    </igMap:XamWebMap.Layers>
</igMap:XamWebMap>