Travels

Friday, July 11, 2008

WeatherBug API Part 2: A lesson in Regular Expressions

Since my last post I've been working on a WeatherBug widget. I had some extra time to work on it so I thought I'd do something cool and make a series of particle behaviors to illustrate different weather patterns. You can see the finished product here: http://www.sizzlepopboom.com/open_source/weatherBug_2.html

This widget animates the weather patterns, allows you to change location by entering in a zip code, links to the forecast page for that area, and in the event of a weather alert, a button will appear which when clicked will open a page for the weather alerts in that area. (I've been using the recent California fires to test the alert system.) This widget is also accessible to the vision and mobility impaired (still testing to ensure ease of use).

Once I created my particles and their behavior patterns, I needed some way to parse through the 176 documented types of weather conditions that were published by WeatherBug and create appropriate behavior for each condition.

I did this by using a series of regular expressions. I made a dictionary set of regular expressions and the results I wanted them to return if there was a positive match. Then I made a function that goes through each expression in a specific order to test the weather. When it tests positive it stops the testing. This is important because changing the order will change which weather pattern is chosen when certain words are searched for. For example if there is a thunderstorm, I want rain and lightning, while if there is a chance of storms I would want rain alone. To make sure thunderstorm is chosen before my rain behavior I put its regular expression before the one for rain. If no matches are found, I default to sunny weather.


Here is a snippet of code:

//tells what type of particles to make
var behavior:String;
//store the search expressions
var tests = new Dictionary();
tests["snowReg"] = /(snow|sleet|freez|frozen|flurr|snowstorm)/i;
tests["snowReg"].result = "snow";
tests["sunReg"] = /(sun|clear|fair)/i;
tests["sunReg"].result = "sunny";
tests["thunderReg"] = /(thunder|lightning)/i;
tests["thunderReg"].result = "thunder";
tests["rainReg"] = /(rain|storm|drizzle|hurricane)/i;
tests["rainReg"].result = "rain";
tests["cloudReg"] = /(cloud|smoke)/i;
tests["cloudReg"].result = "cloudy";
tests["windReg"]= /(wind)/i;
tests["windReg"].result = "windy";
tests["fogReg"] = /(fog)/i;
tests["fogReg"].result = "fog";
tests["hazyReg"] = /(haz)/i;
tests["hazyReg"].result = "hazy";

//store the order you want to test for weather
var testList = new Array("sunReg","thunderReg","rainReg","snowReg","cloudReg","hazyReg","windReg","fogReg");

for (var i = 0; i<testList.length; i++) {
if (tests[testList[i]].exec(condition)!=null) {
behavior = tests[testList[i]].result;
break;
//if no matches then use the default sunny behavior
} else {
behavior = "sunny";
}
}


Here is the list of documented weather conditions.

As I was working on this widget I noticed two things. First there are more weather patterns than what is on this list ("Light Thunderstorms" and "Smoke" are not on the list). Second when you are testing weather patterns, Flash may cache the information so that you don't seem to be getting accurate information. I noticed this when I was working on the widget and a thunderstorm began, but the widget said it was partially cloudy. Restarting Flash solved the problem. Remember this while developing; once the widget is online the weather info will not be cached.


Another thing I did to make the widget cooler was to do another testing of the weather condition to see the magnitude of the weather. I set up three different magnitudes: light, average, and heavy. Then I searched through the weather conditions for words that indicated the amount (heavy, chance, light, etc.). Using this information I changed the number of particles to use for each weather pattern and so the animations change to match both the weather and the amount of precipitation. For a few weather patterns I further manipulated the magnitude to ensure a better looking animation.
Here is the code for determining the magnitude:

//now test the amount of precipitation

var strength:String;
var amount:Dictionary = new Dictionary();
amount["mildReg"] = /(light|chance|scatter|partly)/i;
amount["mildReg"].result ="mild";
amount["midReg"] = /(mostly|increas)/i;
amount["midReg"].result = "average";
amount["heavyReg"] = /(heavy|hurricane|smoke)/i;
amount["heavyReg"].result = "heavy";
var magnitude:Array = new Array("mildReg","midReg","heavyReg");
for (var j = 0; j<magnitude.length; j++) {
if (amount[magnitude[j]].exec(condition)!=null) {
strength = amount[magnitude[j]].result;
break;
} else {
strength = "average";
}
}
//adjust the amount of particles to use

var numParticles:uint;
switch (strength) {
case "mild" :
numParticles = 50;
break;
case "average" :
numParticles = 100;
break;
case "heavy" :
numParticles = 300;
break;
default :
numParticles = 100;
}

A widget and my updated open source WeatherBug class are available here:
http://www.sizzlepopboom.com/open_source/weatherbug.zip

For more information on how to use regular expressions in flash check out Adobe's Regex page: at:
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/RegExp.html

To learn more about writing regular expressions here is a good reference:
http://www.regular-expressions.info/reference.html

Finally here is an excerpt from an accessibility class I'm working on. This function prepares text for screen readers by stripping out any tags and capitalizing all lone "a"s and "i"s so screen readers will pronounce them correctly.

//remove any html tags from text so the tags will not be read by the screen reader

function stripTags(htmlText:String) {
var parseText = htmlText;
//Capitalize all a and i for screenreader correctness
parseText = parseText.replace(/\sa\s/," A ");
parseText = parseText.replace(/\si\s/," I ");
//strip tags
return parseText.replace(/<.*?>/g,"");

}

No comments: