Charting Weather Observations with Xamarin forms Syncfusion Essential Studio

In my series on creating a Xamarin Forms App I have been developing a Mountain Weather App for UK mountain regions. I want to add weather observations from various weather stations, which provide observational readings over the last 24 hours. This is particularly useful for anyone venturing out in to the mountains, especially in winter when temperature and wind variations provide an indication of conditions. I have been consuming weather data from the Met Office Data Point service. This also provides an excellent data feed of weather observations across the UK, including those provided by weather stations located at various mountain summits. I want to consume and chart this data in my application.

Searching for chart components on the Xamarin Forms Components page reveals the following components currently available.

Screen Shot 2015-03-03 at 07.22.16

I’m only interested in those that cover all platforms and are either free or inexpensive. The Syncfusion Essential Studio looks like the best option here. I have used Syncfusion before with WPF and found their components and support to be very good. The price of $99 also seems extremely good value for what you get, which includes Charts, Gauges, TreeMap and Excel, World and PDF libraries. This can be downloaded from either of the following links.

https://components.xamarin.com/view/SyncfusionEssentialStudio

http://www.syncfusion.com/products/xamarin

You will also find some good documentation in the getting started guide and on line documentation.

Follow these links to add the appropriate references to your solution projects and also take note that you must add the SFChartRenderer to the Windows Phone and iOS projects.

Firstly I need to consume the weather observation data from the Met Office site-specific observations service. (As with the other DataPoint services you will need to obtain an API Key if you are interested in using this service). I’m not going to go into any detail on this as I want this post to focus on the charting. I have created a set of data model classes based on the Json that the service returns. This is easily created in Visual Studio using the File > Paste Special > “Paste Json as Classes” feature. The resulting class model looks like this:

    public class Observations
    {
        public Siterep SiteRep { get; set; }
    }

    public class Siterep
    {
        public Wx Wx { get; set; }
        public DV DV { get; set; }
    }

    public class Wx
    {
        [JsonProperty("Param")]
        public Param[] Elements { get; set; }
    }

    public class Param
    {
        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("units")]
        public string Units { get; set; }

        [JsonProperty("$")]
        public string Description { get; set; }
    }

    public class DV
    {
        [JsonProperty("dataDate")]
        public DateTime DataDate { get; set; }

        [JsonProperty("type")]
        public string Type { get; set; }

        public Location Location { get; set; }
    }

    public class Location
    {
        [JsonProperty("i")]
        public string Id { get; set; }

        [JsonProperty("lat")]
        public string Latitude { get; set; }

        [JsonProperty("lon")]
        public string Longitude { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("country")]
        public string Country { get; set; }

        [JsonProperty("continent")]
        public string Continent { get; set; }

        [JsonProperty("elevation")]
        public string Elevation { get; set; }

        public Period[] Period { get; set; }
    }

    public class Period
    {
        public string type { get; set; }
        public string value { get; set; }
        public Rep[] Rep { get; set; }
    }

    public class Rep
    {
        //WindGust
        public string G { get; set; }

        //Temperature
        public string T { get; set; }

        //Visibility
        public string V { get; set; }

        //WindDirection
        public string D { get; set; }

        //WindSpeed
        public string S { get; set; }

        //WeatherType
        public string W { get; set; }

        //Pressure
        public string P { get; set; }

        //PressureTendency
        public string Pt { get; set; }

        //DewPoint
        public string Dp { get; set; }

        //ScreenRelativeHumidity
        public string H { get; set; }

        [JsonProperty("$")]
        public string Value { get; set; }
    }

As ever I am using an MVVM approach, so now that we have our Model let’s create the View Models. Firstly I want to display a list of the weather elements available for a given site. Each element is represented by the ObservationElementViewModel which I will use to chart each weather element.

    public class ObservationElementViewModel : ViewModelBase
    {
        private readonly INavigator _navigator;

        public ObservationElementViewModel(string location, Param element, INavigator navigator)
        {
            _navigator = navigator;
            Location = location;
            Id = element.Name;
            Name = element.Description;
            Units = element.Units;
            Title = element.Description;
            Observations = new List<ChartDataPoint>();
            ShowObservationsCommand = new Command(ShowObservations);
        }

        private async void ShowObservations()
        {
            await _navigator.PushAsync(this);
        }

        public string Name { get; set; }

        public string Units { get; set; }

        public List<ChartDataPoint> Observations { get; set; }

        public string Location { get; set; }

        public DateTime DateTimeFrom { get; set; }

        public DateTime DateTimeTo { get; set; }

        public ICommand ShowObservationsCommand { get; set; }
    }

The key thing to note here is the Observations property, which is a list of ChartDataPoint objects. ChartDataPoint is a Syncfusion class used to chart a series of data and will be used by the view to bind to the data for the series. There is also a ShowObservationCommand that will be called when an element is tapped and displays the chart using the INavigator class which I discussed in my earlier series. The other properties are for decorating the view with additional data.

The ObservationsViewModel represents the list of elements. This consumes the Siterep model and creates a list of ObservationElementViewModels as follows.

    public class ObservationsViewModel : ViewModelBase
    {
        public ObservationsViewModel(
            Siterep siterep, 
            Func<string, Param, ObservationElementViewModel> observationElementViewModelFactory)
        {
            var siterep1 = siterep;
            Title = siterep1.DV.Location.Name;
            var properties = typeof(Rep).GetRuntimeProperties().ToArray();
            var observations = new Dictionary<string, ObservationElementViewModel>();

            foreach (var period in siterep.DV.Location.Period)
            {
                foreach (var rep in period.Rep)
                {
                    foreach (var property in properties)
                    {
                        var propertyValue = property.GetValue(rep) as string;
                        if (propertyValue == null) continue;

                        double value;
                        if (!double.TryParse(propertyValue, out value))
                            continue;

                        DateTime dateTime = DateTime.Parse(period.value).AddMinutes(int.Parse(rep.Value));
                        var code = property.Name;
                        ObservationElementViewModel observationElementViewModel;

                        if (!observations.TryGetValue(code, out observationElementViewModel))
                        {
                            var element = siterep1.Wx.Elements.FirstOrDefault(x => x.Name == code);
                            if (element == null) continue;

                            observationElementViewModel = observationElementViewModelFactory(siterep1.DV.Location.Name, element);
                            observationElementViewModel.DateTimeFrom = dateTime;
                            observations.Add(code, observationElementViewModel);
                        }
                        else
                            observationElementViewModel.DateTimeTo = dateTime;

                        observationElementViewModel.Observations.Add(new ChartDataPoint(dateTime, value));
                    }
                }
            }

            Observations = observations.Values;
        }

        public IEnumerable<ObservationElementViewModel> Observations { get; set; }
    }

This iterates through the model to create the hourly observations over the last 24 hours for each element. I iterate through each property on the Rep object, create an ObservationElementViewModel for each element and add to the Observations by creating a ChartDataPoint for each element observation.

Now all that’s needed are the views. The list of observations is very simple and is represented by the ObservationsView which looks like this.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:views="clr-namespace:Silkweb.Mobile.Core.Views;assembly=Silkweb.Mobile.Core"
             x:Class="Silkweb.Mobile.MountainWeather.Views.ObservationsView"
             Title="{Binding Title}">
  <Grid>
    <ListView ItemsSource="{Binding Path=Observations}">
      <ListView.ItemTemplate>
        <DataTemplate>
          <views:TextCellExtended ShowDisclosure="True" Command="{Binding ShowObservationsCommand}" Text="{Binding Name}" />
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </Grid>
  
</ContentPage>

This simply contains a ListView which binds the ItemsSource to the Observation property on the ObservationsViewModel. The ItemTemplate uses an extended version of a TextCell called TextCellExtended (discussed in my earlier series) which binds it’s command to the ShowObservationsCommand on the ObservationElementViewModel and displays it’s name.

Now let’s create a view to for the chart using Sysncfusion charts called ObservationsChartView. The Xaml for this looks like this:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:chart="clr-namespace:Syncfusion.SfChart.XForms;assembly=Syncfusion.SfChart.XForms"
             x:Class="Silkweb.Mobile.MountainWeather.Views.ObservationsChartView"
             Title="{Binding Title}" Padding="10">

  <StackLayout>
    <Label Text="{Binding Location}" FontSize="Large" TextColor="Accent" HorizontalOptions="Center" />
    <StackLayout Orientation="Horizontal" HorizontalOptions="Center">
      <Label Text="{Binding DateTimeFrom, StringFormat='From {0:HH} hrs on {0:dd/MM}'}" FontSize="Small" />
      <Label Text="{Binding DateTimeTo, StringFormat='To {0:HH} hrs on {0:dd/MM}'}" FontSize="Small" />
    </StackLayout>

    <chart:SfChart VerticalOptions="FillAndExpand">

      <chart:SfChart.PrimaryAxis>
        <chart:DateTimeAxis x:Name="DateAxis" IntervalType="Hours" ShowMajorGridLines="True">

          <chart:DateTimeAxis.MajorGridLineStyle>
            <chart:ChartLineStyle StrokeWidth="1" StrokeColor="#7EDBDBDB" />
          </chart:DateTimeAxis.MajorGridLineStyle>

          <chart:DateTimeAxis.Title>
            <chart:ChartAxisTitle Text="Hour" TextColor="Accent" />
          </chart:DateTimeAxis.Title>

          <chart:DateTimeAxis.LabelStyle>
            <chart:ChartAxisLabelStyle LabelFormat="HH" TextColor="Accent" />
          </chart:DateTimeAxis.LabelStyle>

        </chart:DateTimeAxis>
      </chart:SfChart.PrimaryAxis>

      <chart:SfChart.SecondaryAxis>
        <chart:NumericalAxis ShowMajorGridLines="True">
          <chart:NumericalAxis.MajorGridLineStyle>
            <chart:ChartLineStyle StrokeWidth="1" StrokeColor="#7EDBDBDB" />
          </chart:NumericalAxis.MajorGridLineStyle>

          <chart:NumericalAxis.Title>
            <chart:ChartAxisTitle Text="{Binding Units}" TextColor="Accent" />
          </chart:NumericalAxis.Title>

          <chart:NumericalAxis.LabelStyle>
            <chart:ChartAxisLabelStyle TextColor="Accent" />
          </chart:NumericalAxis.LabelStyle>

        </chart:NumericalAxis>
      </chart:SfChart.SecondaryAxis>

      <chart:SfChart.Series>
        <chart:AreaSeries ItemsSource="{Binding Observations}" Color="#7E9FCCEC" StrokeColor="Accent">
          <chart:AreaSeries.DataMarker>
            <chart:ChartDataMarker MarkerWidth="4" MarkerHeight="4" MarkerColor="Accent" ShowLabel="False" ShowMarker="True" />
          </chart:AreaSeries.DataMarker>
        </chart:AreaSeries>
      </chart:SfChart.Series>
    </chart:SfChart>
  </StackLayout>
</ContentPage>

This uses the Syncfusion SfChart to construct a chart with a DataTime x-Axs and a Numeric y-Axis, with an AreaSeries for the data series. I found this very easy to create and was very impressed with the SfChart object. As you can see it is fairly self-explanatory. Notice the AreaSeries ItemSource is bound to the Observations property of the ObservationElementViewModel and the NumericalAxis Title Text is bound to the Units property. Also note that I have used a StackLayout and some labels to create the title rather than using the Chart title to provide me with a more detailed title showing the location and dates from and to.

Running this in my Weather Application I can now see a list of Elements for a given mountain area.

Screen Shot 2015-03-03 at 22.10.08

And tapping on each element reveals the following charts.

Screen Shot 2015-03-03 at 22.15.44Screen Shot 2015-03-03 at 22.11.26Screen Shot 2015-03-03 at 22.12.15Screen Shot 2015-03-03 at 22.14.32

Quite impressive results I think. There are lots of other series you can use including Bar, Line, Stacked Bar etc, however for this data AreaSeries seemed to be the best to highlight the variation from zero. It would perhaps be nice to have a zoom and cross wire feature but for the price and everything you get in the Essential Studio this is fantastic value for money.

I will be exploring some of the other components in future posts including the Gauge and the PDF component, which I’m very excited about.