Friday, December 30, 2016

Dealing with events in the real world ...


Working on my AI project I've come to the point where I need to be able to deal with events from the real world. Say a signal from a motion detector, cat door opened or someone popping the Champagne on New Years Eve, or Cider if you are not into Champagne... I don't judge :)

So lets first define what we mean by a World Event.
  • it can be described
  • it happened Somewhere
  • it happened at a specific Time

public class WorldEvent
{
    public Guid Id { get; set; }
    public string Description { get; set; }
    public DateTime When { get; set; }
    public AveCoordinate Where { get; set; }

    public WorldEvent()
    {
        Id = Guid.NewGuid();
        Description = string.Empty;
    }

    public override string ToString()
    {
        return $"WorldEvent[{When:O}, lng:{Where.Longitude:##.000},lat:{Where.Latitude:##.000}]";
    }
}

If you wonder about the AveCoordinate used above, it is defined as follows:
public class AveCoordinate
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public double Altitude { get; set; }
    public AveCoordinate()
    {
        // for deserialization
    }
    public AveCoordinate(double lat, double lng)
    {
        Latitude = lat;
        Longitude = lng;
    }
    public AveCoordinate(double lat, double lng, double altitude)
    {
        Latitude = lat;
        Longitude = lng;
        Altitude = altitude;
    }

    public double GetDistanceTo(AveCoordinate coordinates)
    {
        return GetDistanceTo(coordinates.ToGeoCoordinate());
    }
    public double GetDistanceTo(GeoCoordinate coordinates)
    {
        return ToGeoCoordinate().GetDistanceTo(coordinates);
    }

    public GeoCoordinate ToGeoCoordinate()
    {
        return new GeoCoordinate(Latitude, Longitude, Altitude);
    }
}

You might wonder why I chose to not use the GeoCoordinate class directly, but it kind of didn't like to be serialized with fastJSON that is my go to utility for serializing/deserializing stuff. So I had to create my own coordinate class, but as you can see the calculations are done with help of the GeoCoordinate class.
At the moment the GeoCoordinate GetDistanceTo method does not take the Altitude into account so I might have to write my own at some point. But for now it does the trick.

Ok, so now we have a base class that can be used to describe things that happen in the real world.
So what now?

Event Relations

I would like to know if events could be related to each other in some way.
In the real world we work with 4 dimensions, space being the first 3 and time being the last.
So how do we figure out how related 2 events are?

Naive formulation
  • They happened at the same place or close by
  • They happened at the same time or close to
Lets add a naive Distance calculation to the WorldEvent class

public double Distance(WorldEvent other)
{
    var metres = Where.GetDistanceTo(other.Where) + 1d;
    var seconds = (When - other.When).TotalSeconds + 1d;
    var distance = Math.Sqrt(Math.Pow(metres, 2d)*Math.Pow(seconds, 2d));
    return distance;
}

Does this work?

I really try to avoid the advanced stuff with clustering algorithms etc. until I really can't solve the problem in any other way. So lets put down some tests to see if this actually works for our purposes.

[TestMethod]
public void WorldEventTest_IrrelevantDirection()
{
    var first = new WorldEvent
    {
        When = new DateTime(2016, 03, 22, 07, 0, 0),
        Where = new AveCoordinate(58.58073, 14.00119)
    };
    var second = new WorldEvent
    {
        When = new DateTime(2016, 03, 22, 07, 0, 0),
        Where = new AveCoordinate(58.58073, 14.00119)
    };
    var actualFirst = first.Distance(second);
    var actualSecond = second.Distance(first);
    Assert.AreEqual(actualFirst, actualSecond);
}
The first test just makes sure that we didn't screw things up. The distance calculation should be the same in both directions.

[TestMethod]
public void WorldEventTest_SameLocation_YearsBetweenVsMinutesBetween()
{
    var first = new WorldEvent
    {
        When = new DateTime(1934, 03, 22, 07, 0, 0),
        Where = new AveCoordinate(58.58073, 14.00119)
    };
    var second = new WorldEvent
    {
        When = new DateTime(2016, 03, 22, 07, 0, 0),
        Where = new AveCoordinate(58.58073, 14.00119)
    };
    var third = new WorldEvent
    {
        When = new DateTime(2016, 03, 22, 07, 3, 0),
        Where = new AveCoordinate(58.58073, 14.00119)
    };
    var actualFirst = first.Distance(second);
    var actualSecond = second.Distance(third);
    Assert.IsTrue(actualFirst > actualSecond);
}
Here we ensure that things that happen close-by in time at the same place will get a lesser distance then the events that happen a very long time apart.

[TestMethod]
public void WorldEventTest_SameTime_FarAwayVersusClose()
{
    // Stelvio 46.623215,10.7990846
    var first = new WorldEvent
    {
        When = new DateTime(2016, 03, 22, 07, 0, 0),
        Where = new AveCoordinate(46.623215, 10.7990846)
    };
    var second = new WorldEvent
    {
        When = new DateTime(2016, 03, 22, 07, 0, 0),
        Where = new AveCoordinate(58.58073, 14.00119)
    };
    var third = new WorldEvent
    {
        When = new DateTime(2016, 03, 22, 07, 0, 0),
        Where = new AveCoordinate(58.559686, 13.959535)
    };
    var actualFirst = first.Distance(second);
    var actualSecond = second.Distance(third);
    Assert.IsTrue(actualFirst > actualSecond);
}
And here the other way around. Events that happen at the same time, but on different parts of the globe should have a longer distance then events that happen in the same area.

Conclusion

Ok, so not the most advanced stuff but it does the trick for my project at the moment. We can always add more advanced calculations at a later stage, maybe just keep this as a preprocessing stage to filter out really non related stuff.
So far just keeping it simple. :)

All code provided as-is. This is copied from my own code-base, May need some additional programming to work. Hope this helps someone out there :)


No comments:

Post a Comment