ASP.NET NOAA Weather Service Control Redux

This is actually a rework of something I did back in 2005. I've updated the solution for Visual Studio 2010 and have added a compression utility which now uses the QuickLZSharp compression class. NOAA (the "National Weather Service", among other neat things it does) publishes a WebService that allows anybody to get weather forecasts if they care to parse the WSDL and include a WebReference in their .NET project.

In fact, since that time, the only thing the Weather Service has changed is that they added a simple "Unit" parameter to the main method, which defaults to "US-English".  The only real remaining problem I saw was that the webservice requires the Latitude and Longitude as input parameters.

There were a number of blog posts about it  at that time and they all had one theme: "I can't get it to work", "It times out", etc. That's because it's in RPC format which pretty much reduces one to writing custom code to parse the returned XML. Mikhail Arkhipov of Microsoft actually  did just that, providing a parser for the RPC results, an effort that still works today, some six years later.

Fortunately, I have a US ZipCode database that has the latitude and longitude for every zipcode in the United States. So what I did was to export the SQL Server Zipcode table to a flat file "ZIP CITY STATE LATITUDE LONGITUDE", delimited with the pipe ( | ) symbol.

I then used QuickLzSharp to compress this and added it to the WeatherControl project as an embedded resource.


Having extracted and decompressed the Text file of results from the assembly at runtime, it was a simple matter to split it into rows and the rows into arrays and add all this to a DataTable - which now allows you to create a Primary Key, perform Select queries and all that cool stuff. So I now had a Zipcode database in an assembly, and I wrote a nice ZipCode lookup component with the technique.

The code that gets the compressed resource looks like this, and includes the use of the QuickLz Decompress extension method:

#region Resource Methods
private string GetDecompressedResourceString(string resource)
{
try
{
Assembly asm = Assembly.GetExecutingAssembly();
Stream stm=asm.GetManifestResourceStream(resource);
BinaryReader br = new BinaryReader(stm);
long siz = stm.Length;
byte[] bytInput =null;
bytInput=br.ReadBytes((int)siz);
string theResource =System.Text.Encoding.UTF8.GetString(bytInput.Decompress());
br.Close();
stm.Close();
return theResource;
}
catch(Exception ex)
{
throw new ApplicationException(ex.Message);
}
}    
#endregion


Then I have the code for the control which uses this decompressed Zipcode.txt string:

#region ctor
public WeatherLookup()
{
this.IsDesignMode = (System.Web.HttpContext.Current == null);
if(IsDesignMode)return;
if(System.Web.HttpContext.Current.Cache["ZipCodeTable"]==null)
{
                theZips = GetDecompressedResourceString("WeatherControl.Zipcodes.dat");
zipCodeTable.Columns.Add("zip", typeof(string));
zipCodeTable.Columns.Add( "city", typeof(string) );
zipCodeTable.Columns.Add( "state", typeof(string) );
zipCodeTable.Columns.Add( "Latitude", typeof(string) );
zipCodeTable.Columns.Add( "Longitude", typeof(string) );   
// Set PrimaryKey
zipCodeTable.Columns[ "zip" ].Unique = true;
zipCodeTable.PrimaryKey = new DataColumn[] { zipCodeTable.Columns["zip"] };
string[] zippies = theZips.Split(new Char[] {'\r','\n'});
for (int i=0;i<zippies.Length;i++)
{  
object[] theRow=zippies[i].Split(new Char[] {'|'});
if((string)theRow[0]!="")
zipCodeTable.Rows.Add( theRow);
}
zipCodeTable.AcceptChanges();
System.Web.HttpContext.Current.Cache["ZipCodeTable"]=zipCodeTable;
}
else
zipCodeTable =
(DataTable)System.Web.HttpContext.Current.Cache["ZipCodeTable"];
}

#endregion

Finally, I get my needed latitude and longitude from the DataTable by Zipcode:

private void FindLatitudeLongitudeByZipCode(string zip)
{
string strExpr;  
strExpr = "zip='" +zip +"'";
// Use the Select method to find all rows matching the filter.
try
{
DataRow[] foundRows = zipCodeTable.Select(strExpr);
this.latitude=Convert.ToDecimal(foundRows[0].ItemArray[3]);
this.longitude = Convert.ToDecimal(foundRows[0].ItemArray[4]);
foundZip=foundRows[0].ItemArray[0].ToString();
foundCity=foundRows[0].ItemArray[1].ToString();
foundState=foundRows[0].ItemArray[2].ToString();
}
catch(Exception ex)
{
throw new Exception(ex.Message);
}
}

The rest of the code is simply standard Custom Control code to render the weather service's returned information into a nice table, complete with the NOAA's little pics for sunny, cloudy, etc.:



You can download the complete working Visual Studio 2010 solution here. This includes the Windows Forms Utility project to compress any file so that it can be inserted into a project as an embedded resource.

By Peter Bromberg   Popularity  (6298 Views)