Sunday, November 8, 2009

Exploring the Weather Underground with Objective-XML and Objective-Smalltalk

Having taken up various forms of flying last year, I have developed a strong interest in the weather, particularly wind information. While there are various web-sites with relevant information, for example Jeff Greenbaum's excellent Wind Conditions Page for Pacifica page, they don't really present the information quite the way I need, and also don't really work well on small mobile devices...

Fixing that should hopefully just be ASMOP. The Weather Underground fortunately has some reasonably well-documented XML APIs, let's see what they have to offer and wether we can get to the data we want.

First, let's fire up the interactive Smalltalk Shell (stsh) and load the Objective-XML framework.

marcel@spock[Projects]stsh
> context loadFramework:'MPWXmlKit'
Next, let's have a look at the raw XML returned by the Weather Underground APIs.
> urlstr:='http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1'
> url:=NSURL URLWithString: urlstr
> NSString stringWithContentsOfURL:url.
> 
Hmm...no result. (It turns out that Weather Underground checks the user agent and errors if it doesn't find one. The various convenience methods do not send a User Agent). Maybe curl can help?
>context addExternalCommand:'curl'.
>curl run:'http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1'
<?xml version="1.0"?>
	<current_observation>
		<credit>Weather Underground Personal Weather Station</credit>
		<credit_URL>http://wunderground.com/weatherstation/</credit_URL>
		<image>
		<url>http://icons.wunderground.com/graphics/bh-wui_logo.gif</url>
		<title>Weather Underground</title>
		<link>http://wunderground.com/weatherstation/</link>
		</image>
		<location>
		<full>Mussel Rock, Daly City, CA</full>
		<neighborhood>Mussel Rock</neighborhood>
		<city>Daly City</city>
		<state>CA</state>
		<zip></zip>
		<latitude>37.667347</latitude>
		<longitude>-122.489342</longitude>
		<elevation>514 ft</elevation>
		</location>
		<station_id>KCADALYC1</station_id>
		<station_type>Fan-aspirated Davis Vantage Pro 2 Plus</station_type>
		<observation_time>Last Updated on November 7, 1:55 PM PST</observation_time>
		<observation_time_rfc822>Sat, 07 November 2009 21:55:21 GMT</observation_time_rfc822>
		<weather></weather>
		<temperature_string>56.9 F (13.8 C)</temperature_string>
		<temp_f>56.9</temp_f>
		<temp_c>13.8</temp_c>
		<relative_humidity>83</relative_humidity>
		<wind_string>From the NW at 15.0 MPH Gusting to 16.0 MPH</wind_string>
		<wind_dir>NW</wind_dir>
		<wind_degrees>313</wind_degrees>
		<wind_mph>15.0</wind_mph>
		<wind_gust_mph>16.0</wind_gust_mph>
		<pressure_string>30.07" (1018.2 mb)</pressure_string>
		<pressure_mb>1018.2</pressure_mb>
		<pressure_in>30.07</pressure_in>
		<dewpoint_string>51.8 F (11.0 C)</dewpoint_string>
		<dewpoint_f>51.8</dewpoint_f>
		<dewpoint_c>11.0</dewpoint_c>
		
		<heat_index_string></heat_index_string>
		<heat_index_f></heat_index_f>
		<heat_index_c></heat_index_c>
		
		<windchill_string></windchill_string>
		<windchill_f></windchill_f>
		<windchill_c></windchill_c>
		
		<solar_radiation>483.00</solar_radiation>
		<UV>2.5</UV>
		<precip_1hr_string>0.00 in (0.0 mm)</precip_1hr_string>
		<precip_1hr_in>0.00</precip_1hr_in>
		<precip_1hr_metric>0.0</precip_1hr_metric>
		<precip_today_string>0.01 in (0.0 mm)</precip_today_string>
		<precip_today_in>0.01</precip_today_in>
		<precip_today_metric>0.0</precip_today_metric>
		<history_url>http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=KCADALYC1</history_url>
		<ob_url>http://www.wunderground.com/cgi-bin/findweather/getForecast?query=37.667347,-122.489342</ob_url>
	</current_observation>
<!-- 0.029:0 -->
Much better. Now let's see if we can parse that XML data into a Cocoa Property List.
> parser := MPWMAXParser parser.
> parser parsedDataFromURL: 'http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1'
{
    UV = "2.5";
    credit = "Weather Underground Personal Weather Station";
    "credit_URL" = "http://wunderground.com/weatherstation/";
    "dewpoint_c" = "11.1";
    "dewpoint_f" = "51.9";
    "dewpoint_string" = "51.9 F (11.1 C)";
    "heat_index_c" =     {
    };
    "heat_index_f" =     {
    };
    "heat_index_string" =     {
    };
    "history_url" = "http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=KCADALYC1";
    image =     {
        link = "http://wunderground.com/weatherstation/";
        title = "Weather Underground";
        url = "http://icons.wunderground.com/graphics/bh-wui_logo.gif";
    };
    location =     {
        city = "Daly City";
        elevation = "514 ft";
        full = "Mussel Rock, Daly City, CA";
        latitude = "37.667347";
        longitude = "-122.489342";
        neighborhood = "Mussel Rock";
        state = CA;
        zip =         {
        };
    };
    "ob_url" = "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=37.667347,-122.489342";
    "observation_time" = "Last Updated on November 7, 1:55 PM PST";
    "observation_time_rfc822" = "Sat, 07 November 2009 21:55:51 GMT";
    "precip_1hr_in" = "0.00";
    "precip_1hr_metric" = "0.0";
    "precip_1hr_string" = "0.00 in (0.0 mm)";
    "precip_today_in" = "0.01";
    "precip_today_metric" = "0.0";
    "precip_today_string" = "0.01 in (0.0 mm)";
    "pressure_in" = "30.07";
    "pressure_mb" = "1018.2";
    "pressure_string" = "30.07\" (1018.2 mb)";
    "relative_humidity" = 83;
    "solar_radiation" = "482.00";
    "station_id" = KCADALYC1;
    "station_type" = "Fan-aspirated Davis Vantage Pro 2 Plus";
    "temp_c" = "13.9";
    "temp_f" = "57.0";
    "temperature_string" = "57.0 F (13.9 C)";
    weather =     {
    };
    "wind_degrees" = 342;
    "wind_dir" = NNW;
    "wind_gust_mph" = "24.0";
    "wind_mph" = "18.0";
    "wind_string" = "From the NNW at 18.0 MPH Gusting to 24.0 MPH";
    "windchill_c" =     {
    };
    "windchill_f" =     {
    };
    "windchill_string" =     {
    };
}
That looks good, we can see the wind information near the bottom of the output, with keys "wind_degrees" and "wind_mph". So let's grab the values for those keys using the collect Higher Order Message and -objectForKey:.
> (parser parsedDataFromURL:'http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1' ) collect objectForKey: #( wind_mph wind_dir wind_string ) each. 
21.0
NW
From the NW at 21.0 MPH Gusting to 24.0 MPH
Almost what we wanted, except that we grabbed the wind direction as a string instead of the exact numeric direction. Easy fix:
> (parser parsedDataFromURL: 'http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1' ) collect objectForKey: #( wind_mph wind_degrees wind_string ) each.
12.0
318
From the NW at 12.0 MPH Gusting to 24.0 MPH
>
Perfect. We have the wind speed, the direction and an informative text in case we want to display that.

1 comment:

cutelyaware said...

Try the following URL for better structured data:
"http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=KCADALYC1&format=1"

I'm creating an Android Widget this way and it's working well.