Blogs

Gary's Blog

XPO – Charting Local Weather Conditions #1

     

All the talk of climate change means that there are a lot of people interested in the weather right now, and rightly so. In the first of two posts on this topic, I’m going to show you how to sample weather data, on on hourly basis, and then use XPO to store that data. In the second post we will look at charting that data. But first, how do you get weather information if you don’t have a weather station in your garden? Well, the answer is, you can use other people’s data.

You may not know this, but airports, military basis and other weather stations report weather conditions on an hourly basis and this information is held by the National Oceanic and Atmospheric Administration and is made available to the public. This system is known as METAR. It is this system we will make use of to sample the weather reports that we need.

The weather reports are filed under the ICAO code for the station they come from, so the first thing we need to do is to identify which station we are going to use and what the code is for that station. I live only a few miles from Dundee Airport and so I’m going to use that for my reports. I need to find the ICAO code for Dundee Airport and a simple Google search tells me the code is EGPN.

Next we need to know where to retrieve the report from. METAR reports are encoded and are, frankly, a pain in the bum to decode. Luckily for us, those nice people at the NOAA have taken care of that for us. They file decoded METAR reports at: ftp://tgftp.nws.noaa.gov/data/observations/metar/decoded/. All we have to do is to pull back the text file EGPN.txt from that location, parse it and store that information. So let’s go ahead now and build an application to do that.

Create a standard console application:

image

Now let’s create a METAR helper class with a function to fetch the report for us:

public static void GetMetarDataForCode(string code)
{
    if (String.IsNullOrEmpty(code))
        return;

    const string ADDRESS =
        @"ftp://tgftp.nws.noaa.gov/data/observations/metar/decoded/";

    string url = String.Format("{0}{1}.TXT", ADDRESS, code.ToUpper());

    string report;

    try
    {
        report = new WebClient().DownloadString(url);
        StoreMetarData(report);
    }

    catch (WebException wex)
    {
        Logger.Log(String.Format("{0}: {1}", DateTime.Now, wex.Message));
    }           
}

Which we’ll call from our program entry point:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XPOWeather1
{
    class Program
    {
        static void Main(string[] args)
        {
            const string METAR_CODE = "EGPN";
            MetarHelper.GetMetarDataForCode(METAR_CODE);
        }
    }
}

Next, let’s define an model class to hold our report. As this class extends XPObject we’ll need to add references to DevExpress.Data and DevExpress.Xpo at this point. We are not interested in all of the data in the report, so something like this should suffice:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DevExpress.Xpo;

namespace XPOWeather1.Model
{
    public class Report: XPObject
    {
        public Report(Session session)
            : base(session)
        { }

        private DateTime timeStamp;
        public DateTime TimeStamp
        {
            get
            {
                return timeStamp;
            }
            set
            {
                SetPropertyValue("TimeStamp", ref timeStamp, value);
            }
        }

        private string skyConditions;
        public string SkyConditions
        {
            get
            {
                return skyConditions;
            }
            set
            {
                SetPropertyValue("SkyConditions", ref skyConditions, value);
            }
        }

        private string visibility;
        public string Visibility
        {
            get
            {
                return visibility;
            }
            set
            {
                SetPropertyValue("Visibility", ref visibility, value);
            }
        }

        private string windDirection;
        public string WindDirection
        {
            get
            {
                return windDirection;
            }
            set
            {
                SetPropertyValue("WindDirection", ref windDirection, value);
            }
        }

        private int windSpeed;
        public int WindSpeed
        {
            get
            {
                return windSpeed;
            }
            set
            {
                SetPropertyValue("WindSpeed", ref windSpeed, value);
            }
        }

        private int temperature;
        public int Temperature
        {
            get
            {
                return temperature;
            }
            set
            {
                SetPropertyValue("Temperature", ref temperature, value);
            }
        }
    }
}

Having done that, we can write the function to create and store the Report:

private static void StoreMetarData(string reportText)
{
    if (String.IsNullOrEmpty(reportText))
        return;

    using (UnitOfWork uow = new UnitOfWork())
    {
        Report report = new Report(uow) 
        { 
            SkyConditions = GetSkyConditionsFromReportText(reportText), 
            Temperature = GetTemperatureFromReportText(reportText), 
            TimeStamp = GetTimeStampFromReportText(reportText), 
            Visibility = GetVisibilityFromReportText(reportText), 
            WindDirection = GetWindDirectionFromReportText(reportText), 
            WindSpeed = GetWindSpeedFromReportText(reportText) 
        };

        uow.CommitChanges();
    }
}

Here are the definitions for those helper functions, just for completeness sake:

private static string GetSkyConditionsFromReportText(string reportText)
{
    string result = String.Empty;
    if (String.IsNullOrEmpty(reportText))
        return result;

    return GetInformationFromReportText("Sky conditions:", reportText);
}

private static string GetInformationFromReportText(string key, string reportText)
{
    string result = String.Empty;
    if (String.IsNullOrEmpty(key) || String.IsNullOrEmpty(reportText))
        return result;
    
    return reportText.Split(Environment.NewLine.ToCharArray()).
        First<string>(x => x.Contains(key)).
            Split(":".ToCharArray())[1].Trim();
}

private static int GetTemperatureFromReportText(string reportText)
{
    if (String.IsNullOrEmpty(reportText))
        return 0;

    return Convert.ToInt32(GetInformationFromReportText(
        "Temperature:", reportText).Split("(".ToCharArray())[1].
            Split(")".ToCharArray())[0].Split(" ".ToCharArray())[0]);
}

private static DateTime GetTimeStampFromReportText(string reportText)
{
    DateTime result = DateTime.MinValue;
    if (String.IsNullOrEmpty(reportText))
        return result;

    string temp = reportText.Split(Environment.NewLine.ToCharArray()).
        First<string>(x => x.Contains("UTC")).Split("/".
            ToCharArray())[1].Replace(".", "/");

    string temp2 = temp.Substring(0, temp.Length - 6);
    string temp3 = temp.Substring(temp.Length - 6);
    string temp4 = String.Format("{0}:{1}", temp2, temp3);

    return DateTime.Parse(temp4.Remove(temp4.Length - 3, 3).Trim());
}

private static string GetVisibilityFromReportText(string reportText)
{
    string result = String.Empty;
    if (String.IsNullOrEmpty(reportText))
        return result;

    return GetInformationFromReportText("Visibility:", reportText);
}

private static string GetWindDirectionFromReportText(string reportText)
{
    string result = String.Empty;
    if (String.IsNullOrEmpty(reportText))
        return result;

    return reportText.Split(Environment.NewLine.ToCharArray()).
        First<string>(x => x.StartsWith("Wind:")).Split(":".
            ToCharArray())[1].Split("(".ToCharArray())[0].Trim().
                Split(" ".ToCharArray())[2];
}

private static int GetWindSpeedFromReportText(string reportText)
{
    if (String.IsNullOrEmpty(reportText))
        return 0;

    return Convert.ToInt32(reportText.Split(Environment.NewLine.ToCharArray()).
        First<string>(x => x.StartsWith("Wind:")).Split(")".
            ToCharArray())[1].Trim().Split(" ".ToCharArray())[1]);
}

The last thing we have to do in this simple little application is to create a Logger helper class and provide a Log helper function, so we can log any web exceptions as we’re going to run this unattended:

using System;
using System.IO;

namespace XPOWeather1
{
    public class Logger
    {
        public static void Log(string message)
        {
            File.AppendAllText(@".\Files\Log.txt", message);
        }
        
    }
}

Now that we are finished, our project looks like this:

image

Running this application gives us a nice record of the weather, updated by the Dundee Airport weather station on an hourly basis:

image

The last thing we need to do in this post is to set up our application to run hourly and pick up the weather reports. We’ll do this by using the Task Scheduler:

image

In the next post, we’ll look at retrieving this information and charting some important weather information. Until then… happy XPOing!

Published Nov 02 2009, 04:51 PM by Gary Short (DevExpress)
Filed under:
Technorati tags: XPO
Bookmark and Share

Comments

 

Boris Bosnjak said:

Neat!

November 2, 2009 12:53 PM
 

Chris said:

How about showing some examples of XPO with complex datamodels including one to many / many to many and databinding etc?

November 5, 2009 9:22 AM
 

Gary's Blog said:

In the last post on this topic I showed you how to pull down weather information from your local METAR

November 17, 2009 12:21 PM
 

Gary's Blog said:

In the last post I made, looking at analysing weather information using XPO , one of the commenters asked

December 1, 2009 5:48 AM
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.