Blogs

Gary's Blog

XPO Rocks Your World With Earthquakes

     

In the last post I made, looking at analysing weather information using XPO, one of the commenters asked if we could see more examples using different types of associations between objects, so I thought I’d start off by posting something on one to one associations. To do this we are going to look at earthquakes (well why not?).

Now the USGS are good enough to provide us with a feed of earthquakes in, pretty much, real time. Since the bigger the better, we’ll look at quakes magnitude 5 and above happening in the previous 7 days. The USGS publishes an atom feed of these at: http://earthquake.usgs.gov/earthquakes/catalogs/7day-M5.xml.

To use this feed, the first thing we are going to do is to define an Earthquake entity and a GeoPoint entity, these entities will have a one to one relationship:

using System;
using DevExpress.Xpo;

namespace EarthquakeFetcher.Model
{
    public class EarthQuake: XPObject
    {
        public EarthQuake(Session session)
            : base(session)
        { }

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

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

        private GeoPoint geoPoint;
        public GeoPoint GeoPoint
        {
            get
            {
                return geoPoint;
            }
            set
            {
                if (geoPoint == value)
                    return;

                //GS - Store a reference to the former geoPoint.
                GeoPoint previousGP = geoPoint;
                geoPoint = value;

                if (IsLoading) return;

                //GS - Remove the previous reference if there is one.
                if (previousGP != null && previousGP.EarthQuake == this)
                    previousGP.EarthQuake = null;

                //GS - Attach this reference
                if (geoPoint != null)
                    geoPoint.EarthQuake = this;

                //GS - Signal the change
                OnChanged("GeoPoint");
            }
        }

        //GS - Override ToString() to give us a pretty label
        public override string ToString()
        {
            return Title;
        }

        
    }
}
using System;
using DevExpress.Xpo;

namespace EarthquakeFetcher.Model
{
    public class GeoPoint : XPObject
    {
        public GeoPoint(Session session)
            : base(session)
        { }

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

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

        private EarthQuake earthQuake;
        public EarthQuake EarthQuake
        {
            get
            {
                return earthQuake;
            }
            set
            {
                if (earthQuake == value)
                    return;

                //GS - Store a reference to the former EarthQuake.
                EarthQuake previousQuake = earthQuake;
                earthQuake = value;

                if (IsLoading) return;

                //GS - Remove the previous reference if there is one.
                if (previousQuake != null && previousQuake.GeoPoint == this)
                    previousQuake.GeoPoint = null;

                //GS - Attach this reference
                if (earthQuake != null)
                    earthQuake.GeoPoint = this;

                //GS - Signal the change
                OnChanged("EarthQuake");
            }
        }
    }
}

Note the syntax on the one to one associations. Now then, the next thing we have to do is to read the feed, create objects from it and persist them:

using System;
using System.Xml.Linq;
using DevExpress.Xpo;
using EarthquakeFetcher.Model;

namespace EarthquakeFetcher
{
    class Program
    {
        static void Main()
        {                      
            //GS - Fetch the feed
            XDocument quakeFeed = 
                XDocument.Load(@"http://earthquake.usgs.gov/earthquakes/catalogs/7day-M5.xml");

            //GS - Add the required namespaces
            XNamespace nsAtom = "http://www.w3.org/2005/Atom";
            XNamespace nsGeoRSS = "http://www.georss.org/georss";

            //GS - Persist the quake information
            using (UnitOfWork uow = new UnitOfWork())
            {
                foreach (var quake in quakeFeed.Descendants(nsAtom + "entry"))
                {
                    new EarthQuake(uow)
                     {
                         Title = quake.Element(nsAtom + "title").Value,
                         TimeStamp = DateTime.Parse(quake.Element(nsAtom + "updated").Value),
                         GeoPoint = new GeoPoint(uow)
                         {
                             Point = quake.Element(nsGeoRSS + "point").Value,
                             Elevation = Convert.ToInt32(quake.Element(nsGeoRSS + "elev").Value)                             
                         }
                     };
                }
                uow.CommitChanges();                
            }
        }
    }
}

There isn’t anything in the above code worth commenting on I think, just note the addition of the namespaces for atom and GeoRSS. Running this code will cause the present list of quakes to be persisted. Note, this code is for demonstration and instructional purposes only and has had “guard” code removed for clarity. If you were to put this code into production you would have to handle the case of duplicate quake entries being persisted if you were to run this code more than once in any 7 day period.

Now that we have our persisted entities, it’s time to use them. First, we’ll create a view to display them:

image

The code behind this form looks like this:

using System;
using System.Windows.Forms;
using EarthquakeFetcher.Model;
using System.Diagnostics;

namespace EarthquakeViewer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            FillListBox();
            SelectFirstItemInListBox();
        }

        private void FillListBox()
        {
            QuakeListBox.Items.AddRange(EarthquakeHelper.GetAllEarthquakes.ToArray());            
        }

        private void SelectFirstItemInListBox()
        {
            if(QuakeListBox.Items.Count > 0)
                QuakeListBox.SelectedIndex = 0;
        }

        private void ShowOnMapButton_Click(object sender, EventArgs e)
        {
            EarthQuake quake = QuakeListBox.SelectedItem as EarthQuake;
            ShowQuakeOnMap(quake);
        }

        private static void ShowQuakeOnMap(EarthQuake quake)
        {
            if (!String.IsNullOrEmpty(quake.GeoPoint.Point))
                Process.Start(String.Format(@"http://maps.google.com/?q={0}", quake.GeoPoint.Point));
        }

        private void MapMaxElevationButton_Click(object sender, EventArgs e)
        {
            ShowQuakeOnMap(EarthquakeHelper.GetHighestElevatedQuake);
        }

        private void MapMinElevationButton_Click(object sender, EventArgs e)
        {
            ShowQuakeOnMap(EarthquakeHelper.GetLowestElevatedQuake);
        }
    }
}

So, from the code we can see that we have a helper class, EarthquakeHelper, that gives us a few helper methods, let’s take a look at those now:

using System;
using System.Collections.Generic;
using System.Linq;
using DevExpress.Xpo;
using EarthquakeFetcher.Model;

namespace EarthquakeViewer
{
    public class EarthquakeHelper
    {
        //GS - Return all quakes. This is safe as there will not be too many in a 7 day period.
        public static List<EarthQuake> GetAllEarthquakes
        {
            get
            {
                XPQuery<EarthQuake> quakeQuery = new XPQuery<EarthQuake>(XpoDefault.Session);
                return (from q in quakeQuery select q).ToList<EarthQuake>();
            }
        }

        public static EarthQuake GetHighestElevatedQuake
        {
            get
            {
                XPQuery<EarthQuake> quakeQuery = new XPQuery<EarthQuake>(XpoDefault.Session);
                return (from q in quakeQuery
                    orderby q.GeoPoint.Elevation descending
                    select q).First<EarthQuake>();
            }
        }

        public static EarthQuake GetLowestElevatedQuake
        {
            get
            {
                XPQuery<EarthQuake> quakeQuery = new XPQuery<EarthQuake>(XpoDefault.Session);
                return (from q in quakeQuery
                        orderby q.GeoPoint.Elevation ascending
                        select q).First<EarthQuake>();
            }
        }
    }
}

You can see that the GetAllEarthquakes property does what is says on the tin, it returns all of the quakes in the database. Again all “guard” has been removed for clarity and we are assuming that the database only contains the last 7 days worth of data. The other two properties use linq to XPO to return the highest and lowest earthquakes by elevation. These three properties return quakes that are then used by the three buttons on the form to show the location, via Google maps, of the required earthquakes:

image

This post is not only interesting from an XPO point of view, but also from an earthquake point of view. I mean, who of you knew there were so many earthquakes happening right now? Anyway, that’s it for this post, in the next one we’ll take a closer look at at one to many and many to many associations. Until then, happy XPOing!

Published Dec 01 2009, 10:48 AM by Gary Short (DevExpress)
Filed under:
Technorati tags: XPO
Bookmark and Share

Comments

 

Karl Rostock_2 said:

Thats Uber cool!

Good work

December 1, 2009 7:41 AM
 

José said:

What is the GeoPoint class???, can i use it in order to show geo spatial info???

December 1, 2009 12:28 PM
 

Gary Short (DevExpress) said:

Karl, thanks! Glad you are enjoying it.

Jose, no it's something I contrived for this example. The GeoPoint class just contains the point and elevation properties. Of course when you have a one to one assoc and there is no bahaviour in the class the two classes should be combined to form one class. However, as I said, this post is for demonstrational purposes only to show how XPO works and not to show good OO design. I figure it's better to be slightly "wrong" and have a fun demo, than to be totally "correct" and have a boring contrived example. :-)

December 1, 2009 6:13 PM
 

Mansoor Razzaq said:

Gary, (and Oliver) good work on all of these XAF/XPO demos.

I am a VB.net programmer and I have one major annoyance. Devexpress should follow a guideline of one or both languages. If it is going to be only C# thats fine, i will spend the time to learn c# and get it over with. But you cannot have both languages in some demos and only one in the other. Same goes for the videos, where atleast the VB code should be in the sub text or something.

I know this is a random place to put this example. Hope I don't dampen the fun spirit. Also, i am not a paying member, just a student.

December 3, 2009 11:44 AM
 

Jose Antonio Carrera said:

Thanks for your comments Gary,

But it would be amazing if the XPO can implemente some GEO data for example PostGis in order to develop some GIS....

December 3, 2009 12:24 PM
 

AjaxSmith said:

Do you have the source code available for this one? to download?

December 4, 2009 12:31 AM
 

Darren Pruitt said:

This is cool and very timely - I'm working with One to One associations now.

Are there any plans for XPO to handle One to One associations using attributes?  That would be really helpful.

December 4, 2009 10:12 AM
 

Gary Short (DevExpress) said:

@Darren, not at the moment, sorry

December 7, 2009 2:42 PM
 

Gary's Blog said:

After my last post I said we’d take a look at one to many associations next time, and that’s what we

December 16, 2009 3:29 PM
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.