Blogs

Gary's Blog

November 2009 - Posts

  • That Was TechEd that Was!

         

    And what a busy week it was! with 7,300 attendees at the Messe in Berlin the exhibition hall was busy from the word go. Of course, it may have helped that Microsoft deliberately planned the conference to coincide with the 20th anniversary of the fall of the Berlin wall, but whatever it was TechEd 09 was a sell out event and that is a rare occurrence at the moment.

    The partner event on the Monday evening was busy, but then we expected it to be and we gave away most of our T shirts, in fact Rachel had to order reinforcements for the rest of the week! What we didn’t expect of course was that the rest of the week would be just as busy. There were literally hordes (that is the collective noun for developers, right?) of developers around the booth, eager to hear the latest news about their favourite product. In the past the main topic of interest as been WPF and Silverlight, those two platforms vying for supremacy amongst developer’s interest. However, the last two conferences I’ve been too, and again here, developers have been showing more and more interest in XAF. Of course, as the evangelist for XAF that is music to my ears. In fact at this conference the very first person I spoke to wanted a demonstration of XAF! So, go us!

    On Thursday I delivered a session to the architecture track on Technical Debt. It fits in quite well with the development issues that RefactorPro! can help you with and also, it deals with the interest on Technical Debt – something CodeRush can help you reduce. So, whilst the talk doesn’t directly promote our tools, my presentation speaks to the philosophy behind them. I’d never spoken to a TechEd audience before and I must admit I thoroughly enjoyed the experience. The atmosphere is quite different from other conference and user groups that I’ve spoken at. Very intense, and let’s just say, you’d better be on top of your game or the audience will call you out. :-)

    One of the highlights of any large conference is meeting old friends, both customers and other “techies”, that I only get to meet two or three times a year at the big conferences. One of them (thanks Betsy) took, what Rachel reliably informs me is, the only half decent photograph of me at TechED. Though I think she only said that cos she’s in it too LOL. You can always rely on your friends to bring you down to earth eh?

    And before we knew it, it was Friday and it was all over. Time to pack up the booth and say goodbye to old and new friends alike, buoyed by the knowledge that we’ll all be back to do it again next year, where I’m already looking forward to demonstrating the latest and greatest product line up from DevExpress. See you there!

  • XPO – Charting Local Weather Conditions #2

         

    In the last post on this topic I showed you how to pull down weather information from your local METAR station and persist it using XPO. In this post we’ll go ahead and use that persisted information to graph some interesting weather data.

    First thing we’re going to do is to add a new win forms project to our solution to provide a front end for our graphing application. We’ll add folders for Helpers, Model and Views too:

    image

    After that we’ll start the coding by pointing XPO at the Access database we created in the last post. We’ll do this in the form load event:

    private void MainView_Load(object sender, EventArgs e)
    {
        //GS - Connect to the access database created earlier
        XpoDefault.ConnectionString =
            AccessConnectionProvider.GetConnectionString("pathToYourAccessDatabaseFile");
    }

    Next we’ll create a UI that will allows us to graph three interesting pieces of weather data; wind speed by direction, temperature fluctuation throughout a day and the prevailing wind direction for this area:

    image

    Starting with wind speed by direction, what we’ll do is handle the button click event:

    private void btnNorth_Click(object sender, EventArgs e)
    {
        //GS - Display northerly wind speeds
        DisplayWindSpeedByDirectionWithTitle("Northerly Wind Speeds", "N");
    }

    by calling a method to display the data:

    private void DisplayWindSpeedByDirectionWithTitle(string title, string direction)
    {
        //GS - Get all the Northerly wind direction reports
        XPQuery<Report> reportQuery = new XPQuery<Report>(XpoDefault.Session);
    
        var reports = from r in reportQuery
                      where r.WindDirection.StartsWith(direction)
                      select r;
    
        //GS - Ask a helper method to create a chart from this info
        ChartControl chart = ChartHelper.CreateBarChartFromLinqResult("WindSpeed", "TimeStamp", title, reports);
    
        //GS - Ask a helper method to display this chart
        ViewHelper.DisplayChart(chart);
    }

    This method will firstly use a linq statement to get all the weather reports where the wind direction starts with the required direction: “N” for North; “S” for South; etc. The next thing it will do is call a helper method, passing in the chart title, the qualitative and quantitative properties of the Report object and the result of the linq query. Lastly, this method then calls another helper method in order to display the chart returned by the first helper method.

    Let’s have a look at this first helper method:

    public static ChartControl CreateBarChartFromLinqResult(
        string valueMember, 
        string dataMember, 
        string title, 
        IQueryable<Report> reports)
    {
        ChartControl chart = new ChartControl();
        chart.Titles.Add(new ChartTitle { Text = title });
        chart.Legend.Visible = false;
    
        //GS - Create an empty Bar series and add it to the chart.
        Series series = new Series(title, ViewType.Bar);
        chart.Series.Add(series);
    
        //GS - Supply the data
        series.DataSource = reports.ToList<Report>();
    
        //GS - Bind the data
        series.ArgumentScaleType = ScaleType.Qualitative;
        series.ArgumentDataMember = dataMember;
        series.ValueScaleType = ScaleType.Numerical;
        series.ValueDataMembers.AddRange(new string[] { valueMember });
    
        //GS - Dock the chart into its parent
        chart.Dock = DockStyle.Fill;
    
        //GS - Return the chart
        return chart;
    }

    This method is fairly simple, all it does is create a new chart object and add a series to it. We then attach our linq results as the data source and then tell the chart how to bind to that data. Finally, we set the dock style to fill and return the chart to the calling method.

    The second helper method is simpler still:

    public static void DisplayChart(ChartControl chart)
    {
        ChartViewer viewer = new ChartViewer();
        viewer.Controls.Add(chart);
        viewer.Show();
    }

    It takes in a chart object and instantiates a ChartViewer form before adding the chart to the form’s controls and then showing the form.

    So having explained how we do it, let’s have a look at the results, pressing the “North” button yields this chart:

    image

    The remaining “South”, “East” and “West” buttons are a repeat of the same pattern so there is no point in going over those.

    The next thing we are going to look at is charting the temperature fluctuations throughout the day. To do this we select a date and press the “Chart” button:

    image

    The event handler for the “Chart” button is shown below:

    private void bntChartWindSpeedByDay_Click(object sender, EventArgs e)
    {
        //GS - Get the selected data
        DateTime target = dateTimePicker1.Value;
        
        XPQuery<Report> reportQuery = new XPQuery<Report>(XpoDefault.Session);
    
        var reports = from r in reportQuery
                      where r.TimeStamp.Date == target.Date
                      select r;            
    
        //GS - If there is no matching data for the target date then we're done
        if (reports.Count<Report>() < 1)
            return;
    
        //GS - Ask a helper method to create a chart from this info
        ChartControl chart = ChartHelper.CreateBarChartFromLinqResult(
            "Temperature", "TimeStamp", 
            String.Format("Temperatures for: {0}", target.Date.ToLongDateString()), 
            reports);
    
        //GS - Ask a helper method to display this chart
        ViewHelper.DisplayChart(chart);
    }

    As you can see, we follow a similar pattern to that already discussed, whereby we execute a linq query against the XPO source, passing the result to a helper method which constructs and returns a chart object and we then pass that chart object to another helper function which displays the chart. The result of selecting a date and pressing the “Chart” button is shown below:

    image

    The last interesting piece of information that we are going to graph is the prevailing wind direction. The event handler for the “Show Prevailing Wind Direction” button is shown below:

    private void btnMeanWindDirection_Click(object sender, EventArgs e)
    {
        //GS - Fetch all the reports
        XPQuery<Report> reportQuery = new XPQuery<Report>(XpoDefault.Session);
    
        var reports = from r in reportQuery
                      select r;
    
        //GS - Ask a helper method to create a pie chart of the directionCounts
        ChartControl chart = ChartHelper.CreateWindDirectionPieChart(reports);
    
        //GS - Ask a helper method to display this chart
        ViewHelper.DisplayChart(chart);
    }  

    You will recognise this now familiar pattern of executing a linq statement and then passing the result to a helper method to construct and return a chart object before passing that chart object, in turn, to another helper method to display the chart. The only difference this time is the helper method which constructs the chart is different, let’s take a look at it now:

    public static ChartControl CreateWindDirectionPieChart(IQueryable<Report> reports)
    {
        ChartControl chart = new ChartControl();
        chart.Titles.Add(new ChartTitle { Text = "Prevailing Wind Direction" });
        chart.Legend.Visible = true;
    
        //GS - Create an empty Bar series and add it to the chart.
        Series series = new Series(String.Empty, ViewType.Pie3D);
        series.LegendPointOptions.PointView = PointView.ArgumentAndValues;
        series.PointOptions.ValueNumericOptions.Format = NumericFormat.Percent;
        chart.Series.Add(series);
    
        //GS - Supply the data
        List<WindDirectionCount> directionCounts = new List<WindDirectionCount>();
        directionCounts.Add(
            new WindDirectionCount
            {
                Direction = "North",
                Count = reports.Count<Report>(x => x.WindDirection.StartsWith("N"))
            });
    
        directionCounts.Add(
            new WindDirectionCount
            {
                Direction = "South",
                Count = reports.Count<Report>(x => x.WindDirection.StartsWith("S"))
            });
    
        directionCounts.Add(
            new WindDirectionCount
            {
                Direction = "East",
                Count = reports.Count<Report>(x => x.WindDirection.StartsWith("E"))
            });
    
        directionCounts.Add(
            new WindDirectionCount
            {
                Direction = "West",
                Count = reports.Count<Report>(x => x.WindDirection.StartsWith("W"))
            });
    
    
        series.DataSource = directionCounts;
    
        //GS - Bind the data
        series.ArgumentScaleType = ScaleType.Qualitative;
        series.ArgumentDataMember = "Direction";
        series.ValueScaleType = ScaleType.Numerical;
        series.ValueDataMembers.AddRange(new string[] { "Count" });
    
        //GS - Dock the chart into its parent
        chart.Dock = DockStyle.Fill;
    
        //GS - Return the chart
        return chart;
    }

    This helper method is similar, but a little different, to the previous one. Again it creates a chart object and adds a series to it. The series is a little different this time though:

    //GS - Create an empty Pie series and add it to the chart.
    Series series = new Series(String.Empty, ViewType.Pie3D);
    series.LegendPointOptions.PointView = PointView.ArgumentAndValues;
    series.PointOptions.ValueNumericOptions.Format = NumericFormat.Percent;
    chart.Series.Add(series);

    As you can see we are creating a pie chart this time and so we use the PointView.ArgumentAndValues enum to specify that we want to show the wind direction as well as the values and the NumericFormat.Percent enum to specify that we want to show the values as a percentage of the total.

    We then go on to count the number of weather reports that state the wind was from a particular direction (N, NNW all count as North etc.) and we go ahead and store this information in a list of objects we created for that purpose. These objects are very simple in nature and they are defined like so:

    using System;
    
    namespace XPOWeather2
    {
        public class WindDirectionCount
        {
            public string Direction { get; set; }
            public int Count { get; set; }        
        }
    }

    Then we set the data and bind to is as before, specifying the qualitative and the quantitative properties, before finishing by asking the same helper method to display the chart. The result of all this is shown below:

    image

    As you can see, the prevailing wind direction in this part of the world is shown as westerly (which it actually is!).

    Well that about wraps it up for this post, ‘til the next time, happy XPOing!

  • 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!

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.