Requirements: IP*Works! .Net Edition
Download Demo: Download
In the demo, I'll use the SOAP component from IP*Works! .Net Edition to consume the NDFD SOAP service.
National Digital Forecast Database XML Web Service
The service provides access to the following meteorological data:
- Maximum Temperature
- Minimum Temperature
- 3 hourly Temperature
- Dewpoint Temperature
- 12 hour Probability of Precipitation
- Liquid Precipitation Amounts (Quantitative Precipitation Forecast)
- Snowfall Amounts
- Cloud Cover Amounts
- Wind Direction
- Wind Speed
- Sensible Weather (Weather Type, Coverage, and Intensity)
- Wave Heights
The service offers two methods:
NDFDgen
The NDFDgen method returns a dataset for a time range. The resultant dataset can contain either all of the meteorological data listed above, or a subset specified by the request sent to the service.
NDFDgenByDay
The NDFDgenByDay method returns a dataset for a particular day count (ie, a five day forecast). This is what I'll use in my demo. I'll use the SOAP component from IP*Works! .Net Edition to consume the NWS service and the XMLp component to parse the resulting XML from the service response.
The NDFDgenByDay method takes 5 input parameters. The first two, latitude and longitude, I'll get from another service that outputs a latitude and longitude given a zip code as input (I won't talk about this here, but you can see it in the demo). For the startDate input I will use the current date. The numDays input will be hard coded to 5 in this demo, since the demo will be a "5 Day Forecast" demo. The final input, format, can be either "12 hourly" or "24 hourly". I want data specific to day and night, so I'll use the "12 hourly" format to get data for every 12 hours (day, 6am-6pm, and night, 6pm-6am).
Consume the SOAP Service
Using the /n software Web Service Proxy Generator helper app, I can automatically generate code that will consume this service. I simply give it a link to the WSDL document for the service (http://weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl), and it will generate code for the SOAP component in the language of my choice. I'll choose C#, and the code is generated for me. When I plug the code into my own application and modify it to use my own parameter inputs, it will look like so:
soap1.MethodURI = "http://www.nws.noaa.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl";
soap1.ActionURI = "http://www.nws.noaa.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl#NDFDgenByDay";
soap1.URL = "http://www.nws.noaa.gov/forecasts/xml/SOAP_server/ndfdXMLserver.php";
soap1.Method = "NDFDgenByDay";
soap1.AddParam("latitude", latitude);
soap1.AddParam("longitude", longitude);
soap1.AddParam("startDate", DateTime.Now.ToString("yyyy-MM-dd"));
soap1.AddParam("numDays", "5");
soap1.AddParam("format", "12 hourly");
soap1.SendRequest();
The Proxy Generator autmoatically found the correct MethodURI, ActionURI, URL, Method, and Parameter names from the WSDL. I modified the values of the data passed to AddParam to the values I want: latitude and longitude are variables that were set already. Their default is the location of the National Weather Service headquarters: Lat,Lon : (38.9936,-77.0224).
Parse the SOAP Response
The server response to the SOAP request can be gotten from the ReturnValue property after the SendRequest method returns. In this case, the ReturnValue will not be a simple string - instead it will be XML data containing forecast details. I'll use the XMLp component to parse this data to get out only what I want.
string xmldata = soap1.ReturnValue;
//pass the XML data to the XMLp component:
xmlp1.Reset();
xmlp1.Input(xmldata);
What day is it?
The XML response gives me three lists of "time-layouts" which establish a frame a reference for the rest of the meteorological data in the response. The first time layout shows all of the days. The second shows all of the nights, and the third shows days and nights. I'll use the first one (just the names of the days), but I could use any of them. This time-layout tells me what time periods the weather data is for. Below you can see this time-layout. It starts with today and tomorrow. Since I sent this particular soap request on a Friday, after tomorrow night comes Sunday and so on. Since I sent "5" as the numDays parameter there are 5 days listed in the time-layouts.
<time-layout time-coordinate="local" summarization="12hourly">
<layout-key>k-p24h-n5-1</layout-key>
<start-valid-time period-name="today">2005-01-14T06:00:00-05:00</start-valid-time>
<end-valid-time>2005-01-14T18:00:00-05:00</end-valid-time>
<start-valid-time period-name="tomorrow">2005-01-15T06:00:00-05:00</start-valid-time>
<end-valid-time>2005-01-15T18:00:00-05:00</end-valid-time>
<start-valid-time period-name="Sunday">2005-01-16T06:00:00-05:00</start-valid-time>
<end-valid-time>2005-01-16T18:00:00-05:00</end-valid-time>
<start-valid-time period-name="Monday">2005-01-17T06:00:00-05:00</start-valid-time>
<end-valid-time>2005-01-17T18:00:00-05:00</end-valid-time>
<start-valid-time period-name="Tuesday">2005-01-18T06:00:00-05:00</start-valid-time>
<end-valid-time>2005-01-18T18:00:00-05:00</end-valid-time>
</time-layout>
I'll use this time-layout to gather the day labels for my GUI. To strip out these names, I'll use the XMLp component to parse out all of the "period-name" attributes from the start-valid-time child of the time-layout element. I'll add this period-name to an ArrayList, like so:
xmlp1.XPath = "/dwml/data/[2]"; //this time-layout is the 2nd data child
int numtimeelements = xmlp1.XChildren;
for (int i = 1; i<=numtimeelements; i++)
{
xmlp1.XPath = "/dwml/data/[2]/[" + i.ToString() + "]";
if (xmlp1.XElement == "start-valid-time")
{
daytitles.Add(xmlp1.Attr("period-name"));
}
}
Meteorological Data
The guts of all of this weather data is found in the /dwml/data/parameters element. There are several XML child elements here with different pieces of data for each time period:
- <temperature> - the min and max temperatures.
- <probability-of-precipitation> - the chance of precipitation percentage.
- <weather> - A brief phrase describing the weather conditions in English
- <conditions-icon> - a URL to an image describing the weather conditios.
To deal with all of this data, I'll visit each child of the /dwml/data/parameters/ element and parse it appropriately:
xmlp1.XPath = "/dwml/data/parameters";
int numparameterelements = xmlp1.XChildren;
for (int i=1; i<=numparameterelements; i++)
{
xmlp1.XPath = "/dwml/data/parameters/[" + i.ToString() + "]";
switch (xmlp1.XElement)
{
case("temperature"): GetTemperatures(); break;
case("probability-of-precipitation"): GetPrecipitationChance(xmlp1.XPath); break;
case("weather"): GetWeather(xmlp1.XPath); break;
case("conditions-icon"): GetIcons(xmlp1.XPath); break;
}
}
Each of these functions above (GetTemperatures, GetPrecipitationChance, GetWeather, and GetIcons) work similarly to the code above that strips out the day titles. These functions simply populate an ArrayList with their information. After all of the ArrayLists are populated, I display the data that I've gathered, as shown in the image above.
If you'd like to see the full demo, you can download it here.