INFO 424 SCHEDULE

Lab: Modify an existing file - Create a Choropleth Map based on SVG and JavaScript

You've been introduced to two tools - Edge Animate and the CreateJS Toolkit that give you some of the features of Flash (interaction and animation) using technologies that are built into browsers (as opposed to flash files - .swf files which require a plug-in). This lab introduces you to another approach to learning these technologies which is to find an existing web page that has the functionality you need and modifying it.

In this lab, I'll walk you through the modification of a file created by Eric Ness (he also created a nice tutorial which you might be interested in) http://eric.ness.net/archives/data-driven-maps-part-1-svg-choropleth-maps/

The initial file looks like this:

Colorado in place

The final file will look like this:

final

Step 1: Analyze the visual

a. Download and unzip these files - they include a HTML page and folder which contains a javascript file.

b. Open the HTML file in a browser to view the map - think about the functionality that's built in (that you might want to borrow for a different project):

Functions of this site include:

Step 2: Analyze the code

The next thing to do is to look at the source code and start to figure out what's going on. Depending upon your experience with similar code, this could be a straight-forward or daunting task (I realize that knowing HTML is not a pre-requisite for this course so some of you may find this fairly challenging...but don't get too concerned). Even at a very superficial level, if you scan through you'll probably notice:

A long section filled with clusters of numbers:

svg section

...a bunch of functions that have nice descriptive comments before them like "Add mouse out event":

function section]

A section with what looks like data values:

data section

Step 3: Change the data in the info box

This is what it looks like now when I hover over Washington:

Washington Info Box

My goal is to have the info box look like this:

final infobox

I'll deal with the different appearance later, for now I'll focus on the content.

When I hover over different states, the title and the labels stay the same but the name of the state and percentage value change. My first goal is the change the part that stays the same. I want to remove the title and make italicized data labels.

a. Search the code for "Unemployment Rate" to find where this information is located in the code.

First I come across this:

<h1>The United States Unemployment Rates</h1>

b. That's the title displayed at the top of the page. Might as well fix it - change it to:

<h1>UW building dates</h1>

new page title

Then I search for "Unemployment rate" again and find this code:

// Build the tipsy box html
function buildTipsyBox(name, value) {
var text = '<h3>Unemployment Rate<br />';
text += "State: " + name + "<br />";
text += "Value: " + value + "%";
text += '</h3>';
return text;
}

So I have a variable called "text" and I add some static text (the stuff in quotes like "State: ") and some dynamic text (which has been passed to this function as two parameters(name, value)).

c. Change the text to this:

// Build the tipsy box html
function buildTipsyBox(name, value){
var text = name + "<br />";
text += "<em>Function: </em>" + "put function here" + "<br />";
text += "<em>Built: </em>" + value;
return text;
}

NOTE: I inserted placeholder "put function here" which I'll later replace with a new parameter and I put <em> tags around the labels to make them italic (assuming that's how the browser interprets em tags).

The info box should now look like this:

new info box data appearance

The next step is to figure out where the data comes from. The function I just modified is called buildTipsyBox so I'll look for that:

I find it here:

$('#' + abbr).attr('title', buildTipsyBox(myName, value));

So I scroll up in the code to find where the parameters myName and value are getting populated and find this:

var value = states[i].value; // value
var abbr = states[i].state; // state code
var myName = states[i].name; // state name

It's pulled from an array called "states" so I look for that, and find that it takes me to the data values:

var states =
[{"name": "Alabama", "state": "AL", "value": 7.8},
{"name": "Alaska", "state": "AK", "value": 4.2},
{"name": "Arizona", "state": "AZ", "value": 8.7},
{"name": "Arkansas", "state": "AR", "value": 7.6},
{"name": "California", "state": "CA", "value": 10.9},
{"name": "Colorado", "state": "CO", "value": 7.8},
{"name": "Connecticut", "state": "CT", "value": 8},
{"name": "Delaware", "state": "DE", "value": 7},
{"name": "District of Columbia", "state": "DC", "value": 9.9},
...

I see that this data has these 3 elements:

I'll want to change that to these 4 elements:

I'll change the names of the current elements to these elements so I'll be prepared when I switch over.
I'll start by changing the "name" element to be called "buildingName":

d. Find-and-replace "name" with "buildingName" being sure to include the quotes in the search string (otherwise you'll end up changing things you don't want to change).

e. Find-and-replace states[i].name with states[i].buildingName

f. Reload the page in a browser to be sure it still works after making these changes (it should)

I made more changes, taking the same cautious approach:

g. Find-and-replace "state" with "buildingCode" being sure to include the quotes in the search string.

h. Find-and-replace states[i].state with states[i].buildingCode

i. Find-and-replace "value" with "yearBuilt" being sure to include the quotes in the search string.

j. Find-and-replace states[i].value with states[i].yearBuilt

k. Find-and-replace states[0].value with states[0].yearBuilt

l. Save and reload the page in the browser one more time:

color is messed up

Step 4: Change the appearance of the info box

I browsed the code a bit looking for something that would change the appearance of the info box (which I've seen is called "tipsyBox" in the code, and found this:

.tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; }
.tipsy-inner { background-color: #000; color: #FFF; width: 135px; padding: 5px 8px 4px 8px; text-align: left; }

(FUN FACT: you'll sometimes see colors with just 3 digits instead of 6. That's short-hand. It's interpreted as if each digit was doubled, for example #14D is interpreted as #1144DD):

I experimented with these values to get the look I'm aiming for:

a. Change this line

.tipsy-inner { background-color: #000; color: #FFF; width: 135px; padding: 5px 8px 4px 8px; text-align: left; }

to this:

.tipsy-inner { background-color: #FFF; color: #000; width: 135px; padding: 5px 8px 4px 8px; text-align: left; border: 1px solid #267C41};

...and removed the rounded corners by setting the radius values to zero:

/* Rounded corners */
.tipsy-inner { border-radius: 0px; -moz-border-radius: 0px; -webkit-border-radius: 0px; }

...and changed the color of the arrow by changingthe .tipsy-arrow border colors to this:

/* Rules to colour arrows */
.tipsy-arrow-n { border-bottom-color: #267C41; }
.tipsy-arrow-s { border-top-color: #267C41; }
.tipsy-arrow-e { border-left-color: #267C41; }
.tipsy-arrow-w { border-right-color: #267C41; }

Now it shoud look like this:

new infobox look

Figuring out how to change the arrow to be on the bottom of the info box rather than on the left side was a bit trickier, but easy to actually achieve once I figured it out. It's in the loadStates() function:

b. Change the 'w' (for 'west') to an 's' (for 'south') in this line as shown below:

$('#' + abbr).tipsy({gravity: 's', html: true });

to get this:

new box position

Step 5: Change the state colors

There are two colors for each state:

The second is more straightforward. When I searched for "color," I found many references including this one:

// Add mouse over event
function mapMouseOver(p, color, name, abbr, value) {
p.mouseover(function() {
// animate to gray
p.css('fill', color).animate({opacity: 0.25}, 5 );
p.css('fill', "#999999").animate({opacity: 0.5}, 100 );

I chose to change the hover color to orange to contrast nicely with the green buildings.

a. Change the comment to

// animate to orange

...and change this line:

p.css('fill', "#999999").animate({opacity: 0.5}, 100 );

to this:

p.css('fill', "#F7931D").animate({opacity: 0.5}, 100 );

hover color changed

The next challenge is to change the color of state when it's first loaded. This is the heart of the "data display" aspect of this map.

To figure out how the code is doing this, I searched for "color" and eventually found these two functions:

// Static color option
function getColor(code) {

// Color option based as a percentage
function getColor2(code) {

One allows me to have a continuum of colors while the other allows me to have a fixed number of colors. There are advantages and disadvantages to these two coloring strategies - read about them below:

Straegies for mapping data values to colors:

While a continuum of colors will portray a more technically precise display of the data, it is difficult for people to perceive subtle differences in color so itmay be better in the long run to limit the number of colors and have each color represent a range of values. The right decision will depend on the type of data and the type of shapes being colored.

Also note that this display uses variations on a single hue while the previous choropleth map used variations to two hues - one to represent positive values and one to represent negative values.

Check out this very cool site that helps you create good color mapping schemes (and explains why they're good).

For the campus map, I'll trying to convey ages of buildings, but I think any effort at subtle color variations will look too confusing so I'll try using just 3 colors to represent oldest buildings (built before 1930), new buildings (built since 1980) and those in the middle (1930-1980).

That means I want to use the static color option.

The logic of the code, shown below, is simple, I just pass a code and that determines the color.

function getColor(code) {
var itemColor;
if (code == 1) itemColor = "b9c5d1";
if (code == 2) itemColor = "9fb6c8";
if (code == 3) itemColor = "74a2bc";
if (code == 4) itemColor = "4785ac";
if (code == 5) itemColor = "25689d";
if (code == 6) itemColor = "084886";
if (code == 7) itemColor = "08295c";
return itemColor;
}

So I need to decide what 3 colors I want, and how to get the code to send the correct color value. So I first looked for where getColor is called:

for(var i = 0; i < states.length; i++) {
var value = states[i].yearBuilt; // value
var abbr = states[i].buildingCode; // state code
var myName = states[i].buildingName; // state name
var color = '#' + getColor(value); // static color option
color = getColor2(value); // color shaded as a %

Right now, the code passes the yearBuilt value. So I need to calculate the correct color value from the yearBuilt and send that instead.

I'm going to run into trouble trying to figure this out with the current data since the values I as "yearBuilt" are leftover from the percentage unemployment so they are numbers like 7.3 and 8.2 rather than 1974 or 2001. So...

b. Change the values for the first three states to years that fit the three categories - i.e. one earlier than 1930, one later than 1980 and one in between:

var states =
[{"buildingName": "Alabama", "buildingCode": "AL", "yearBuilt": 1920},
{"buildingName": "Alaska", "buildingCode": "AK", "yearBuilt": 1950},
{"buildingName": "Arizona", "buildingCode": "AZ", "yearBuilt": 2000},

c. Replace this line:

var value = states[i].yearBuilt; // value

with this code:

var value;
if (states[i].yearBuilt>1980){
     value = 1;
} else if(states[i].yearBuilt>1930){
     value = 2;
} else{
     value = 3;
}

d. Change the getColor function to look like the code below (for testing purposes, I made the colors very distinct).

function getColor(code) {
var itemColor;
if (code == 1) itemColor = "
F00";
if (code == 2) itemColor = "
0F0";
if (code == 3) itemColor = "
00F";
return itemColor;
}

e. Also comment out the line that then calls getColor2:

//color = getColor2(value); // color shaded as a %

f. Save the file and refresh the page. It should look like this:

recolored

Step 6: Change the svg

Look at the section of the code with all of the numbers - this is the SVG. Notice that each section each has the same pattern:

It starts with this line:

<path
style="fill-opacity:1;stroke:#ffffff;stroke-width:0.65087253;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"

...then has an id value which is a 2-digit state code (which matches the 2-digit code in the data -very important since this is now the page links a state to its data):

id="CO"

...then it has the list of numbers that define the lines that make up the outline of the state. For a state with a complex shape like Michigan, there are LOTS of numbers. For Colorado, which is just a box there are just a few numbers - just 5 (actually 5 pairs of number defining x,y coordinates) preceded by an "m" and ending with a "z":

d="m 330.54038,356.41729 4.2552,-74.91544 -98.40201,-10.9728 -10.59956,76.31624 104.74637,9.572 z"

I want to replace the states SVG code with SVG that represents the buildings on campus, so now I'll look at that data.

c. Download this Illustrator file which is a map of the UW campus. Notice that some of the buildings have been put on separate layers and the layers have been given the names of the building:

building layers

d. Save the file as an SVG file:

save as SVG

e. Open the files in DreamWeaver or a text editor:

svg code

f. Search for the name of one of the layers like "Suzzallo". You'll find this:

<g id="Suzzallo">
<polygon fill="#277D41" points="254.595,240.174 254.549,240.063 255.21,239.26 255.728,239.969 257.52,240.205 257.897,238.174 248.188,236.545 248.646,233.803 247.096,233.542 246.638,236.285 246.625,236.282 246.62,236.303 238.606,234.962 239.022,232.471 230.606,231.058 230.305,229.999 229.03,229.809 228.322,230.677 227.38,230.519 225.031,244.575 226.742,248.093 234.413,244.493 234.784,245.237 235.894,244.737 235.555,243.921 238.009,242.663 238.837,242.834 238.749,243.491 245.226,244.636 247.039,243.784 251.53,253.388 259.106,249.84 "/>
</g>

Notice that this is a polygon inside of a pair of group tags <g> </g>

What Illustrator has done is to put the contents of each layer inside a group and give that group an an id value that matches the name of the layer it came from.

For the choropleth map, I need the id to be inside the object (the polygon, in this case). So I removed the <g> tags and put the id values inside the polygon tags like this:


<polygon id="Suzzallo" fill="#277D41" points="254.595,240.174 254.549,240.063 255.21,239.26 255.728,239.969 257.52,240.205 257.897,238.174 248.188,236.545 248.646,233.803 247.096,233.542 246.638,236.285 246.625,236.282 246.62,236.303 238.606,234.962 239.022,232.471 230.606,231.058 230.305,229.999 229.03,229.809 228.322,230.677 227.38,230.519 225.031,244.575 226.742,248.093 234.413,244.493 234.784,245.237 235.894,244.737 235.555,243.921 238.009,242.663 238.837,242.834 238.749,243.491 245.226,244.636 247.039,243.784 251.53,253.388 259.106,249.84 "/>

That was a bit tedious so I'll give you a copy of what I did rather than asking you to do it too.

g. Now go back to the Choropleth map file and delete the existing svg tag and its contents (which, in my copy, starts on line 55):

beginning of SVG tag

...and ends like this on line 425:

end of SVG

..370 lines of code deleted.

h. Now open the svg file you just downloaded and copy the code startting with the opening svg tag on line 4:

opening svg tag

...and ending at the very end of the life.

i. Paste this into the choropleth map file at the same place where the deleted svg was (in my case line 55)

j. Save and view in your browser. You should see this:

added all campus buildings

Step 8: Create new data references

Hovering over these buildings doesn't bring up the info box because the data doesn't match the SVG. So...

a. Delete the data:

data section

b. ...and replace it with the text in this file.

If all goes well, you'll see the buildings that were included in the data file colored bright red, green and blue:

colored buildings

...and if you hover over a colored building, you should see its (incompleted) information:

Suzzallo info box

Step 9: Fix up the data in the info box

There are two things wrong with the information in the info box - it still has the placehold "put function here," and it's showing the color code instead of the year built.

a. Search the code for "put function here":

function buildTipsyBox(name, value){
var text = name + "<br />";
text += "<em>Function: </em>" + "put function here" + "<br />";
text += "<em>Built: </em>" + value;
return text;
}

b. You can see that this function takes two parameters - name and value. We need to add a third for the building function, so change it to read:

function buildTipsyBox(name, value, buildingFunction){
var text = name + "<br />";
text += "<em>Function: </em> " +
buildingFunction + " <br />";
text += "<em>Built: </em>" + value;
return text;
}

(Notice that I've added a space after </em> and another before <br />

c. Now go to where this function is called:

$('#' + abbr).attr('title', buildTipsyBox(myName, value));

...and add a new parameter:

$('#' + abbr).attr('title', buildTipsyBox(myName, value, buildingFunction));

...and change the name of the second parameter because "value" now contains the color code rather than the year the building was built:

$('#' + abbr).attr('title', buildTipsyBox(myName, yearBuilt, buildingFunction));

d. To give those two parameters proper values, go to the beginning of the for loop where the parameter variables are populated and add the two lines shown in orange:

for(var i = 0; i < states.length; i++) {
var yearBuilt = states[i].yearBuilt;
var buildingFunction = states[i].purpose;
var value;
if (states[i].yearBuilt>1980){

e. Save your file and refresh the page in your browser to view it:

fixed info box

Step 10: Make the colors respectable

I want to choose colors that "feel" like oldest, old and newest. I'm thinking light = new and dark = old feels right.

a. Change the colors to:

if (code == 1) itemColor = "99D097";
if (code == 2) itemColor = "
277D41";
if (code == 3) itemColor = "
4D644E";

...and you're done. You certainly could do more tweaking - e.g.remove the background color, remove the data display in the lower right-hand corner, add more buildings...feel free to experiment. What's important is that you've learned about tweaking existing code toserver your purpposes...and you now have some code that you can apply to other applications where you want to create Choropleth maps.

Final file: SVGChoroplethMap.html