0

I have this:

public class LatLon
{
    public double lat {get;set;}
    public double lon {get;set;}
}

in a List<LatLon>. It contains of

p1 { lat = 49.9429989, lon = 3.9542134 } 
p2 { lat = 49.9429989, lon = 3.9542133 }
p3 { lat = 49.9429989, lon = 3.9542136 } 

etc..

My goal is to remove coordinates from this list whose difference to other coordinates is lower than the boundaries of lat_bound and lon_bound, so even though the person recording stood at a place for a long time, it means there is only one coordinate left. What would be the LINQ command?

Example:

p1 { lat = 4.555, lon = 6.555 } 
p2 { lat = 4.556, lon = 6.556 }

.

Then Math.Abs(p1.lat - p2.lat) = 0.001 and Math.Abs(p1.lon - p2.lon) = 0.001. p1.lon - p2.lon is the lon difference to one other coordinate's lon-value. Let's say lon_bound equals 0.0005 then this very coordinate is being removed if lat_bound is also 0.0005, as 0.001 > 0.0005.

EDIT: I decided to pipe to http://www.gpsbabel.org instead.

Vitalis Hommel
  • 830
  • 2
  • 8
  • 19
  • 1
    I'm struggling to understand what you're trying to do. Can you clarify what you mean by 'difference to other coordinates in the list'? – Baldrick Jan 15 '16 at 09:43
  • @Baldrick Yes. `Coordinate_1_lat = 4.555`, `Coordinate_1_lon = 6.555` and `Coordinate_2_lat = 4.556`, `Coordinate_2_lon = 6.556`. Then `Math.Abs(Coordinate_1_lat - Coordinate_2_lat) = 0.001` and `Math.Abs(Coordinate_1_lon - Coordinate_2_lon = 0.001`. `Coordinate_1_lon - Coordinate_2_lon` is the lon difference to *one* other coordinate's lon-value. Let's say `lon_bound` equals 0.0005 then this very coordinate is being removed if `lat_bound` is also 0.0005. – Vitalis Hommel Jan 15 '16 at 09:49
  • It seems you have an implementation, or at least definition for which points should be included. Post your non-LINQ implementation. This will be much clearer than explaining with words. – Baldrick Jan 15 '16 at 09:51
  • @Baldrick I don't. This was my approach to removing almost-duplicates. If there is a better one, I am all for it. – Vitalis Hommel Jan 15 '16 at 09:52
  • @VitalisHommel - What if I have three points - A, B, & C - and A & B are within the bounds of each other, and B & C are too, but A & C are not? – Enigmativity Jan 15 '16 at 09:56
  • I might be completely misunderstanding the problem, but it seems to me that you are trying to remove consecutive points with small differences. Why don't you reduce the precision appropriately and remove the duplicates? – Zdeslav Vojkovic Jan 15 '16 at 09:59
  • @ZdeslavVojkovic What does your idea look like? – Vitalis Hommel Jan 15 '16 at 09:59
  • If you have points {A, B, C} and A and C are close, but B is not, I assume that returned collection should also be {A, B, C}. Is that correct? – Zdeslav Vojkovic Jan 15 '16 at 10:30

4 Answers4

3

LINQ Does not make wonders. The problem you are referring to is not just a "Distict" type problem.

1st You have to make a function to measure distance between 2 points.

2nd You need to detect clusters of points..(organize close points into groups)

Finally the easiest thing to do is to Group By a Belonging cluster and keep only 1point from each group.....

But then again.....there are several other problems which might not produce accurate results.

For example whats the one point that represents its group best?

Community
  • 1
  • 1
Anestis Kivranoglou
  • 6,398
  • 4
  • 38
  • 42
1

You can use Math.Round to round the values to the precision that you want. Then use Linq Distinct to remove the duplicates.

void Main()
{
    var list = new List<Coordinate>()
    {
        new Coordinate(25.25251, 100.21254),
        new Coordinate(25.25252, 100.21255),
        new Coordinate(25.25253, 100.21256),
        new Coordinate(25.80000, 100.90000)
    };
    int precision = 4;
    var res = list.Select(x => new Coordinate(
                               Math.Round(x.Lon, precision), 
                               Math.Round(x.Lat, precision))).Distinct().ToList();
}

public struct Coordinate
{
    private double lon;
    private double lat;

    public Coordinate(double lon, double lat)
    {
        this.lon = lon;
        this.lat = lat;
    }

    public double Lat { get { return lat; } }
    public double Lon { get { return lon; } }
}

(Note that I have I'm using a struct and not a class for the Coordinate's)

Magnus
  • 41,888
  • 7
  • 70
  • 108
  • There are 2 issues with this: returned values are modified, not the original ones (which might be desired or not), and if you reverse third and fourth point, only 2 point will be returned, while my understanding is that the last one should be preserved. – Zdeslav Vojkovic Jan 15 '16 at 10:26
  • @ZdeslavVojkovic With this test data 3 points will be returned. As for points beeing modified, yes that is true, but if they were not how to choose which of the points in the group to return? – Magnus Jan 15 '16 at 10:51
  • If you reduce precision to 3 it will return 2 values which is wrong if I understood correctly (with 4 it works, as third value `lat` is rounded to 100.2126 and first two are 100.2125). WRT other question, I assume that every value is valid as 'correct value', but it is not clear about changing. – Zdeslav Vojkovic Jan 15 '16 at 10:58
  • @Vitalis Hommel I saw you proposed edit. Structs should be immutable (Should not change after they have been created). Hence there should be no setters for the properties. – Magnus Jan 15 '16 at 10:59
  • @ZdeslavVojkovic if precision is set to 3 it will return 2 values as expected. – Magnus Jan 15 '16 at 11:00
  • I believe that he wants them to behave like classes. I agree with you that this is essentially a value type – Zdeslav Vojkovic Jan 15 '16 at 11:01
  • Sorry, I wasn't clear. I am talking about scenario when points 3 and 4 are reversed, so you have {A, B, C, D} where A, B and D are 'close'. My understanding is that the output should be {A or B, C, D}. – Zdeslav Vojkovic Jan 15 '16 at 11:03
0

If you have a function Func<LatLon, LatLon, bool> bounded that returns true if the two points are within your bound and false if not then this query works:

var keeps =
    latlons
        .Aggregate(new List<LatLon>(), (xs, y) =>
        {
            if (!xs.Any(x => bounded(x, y)))
            {
                xs.Add(y);
            }
            return xs;
        });
Enigmativity
  • 97,521
  • 11
  • 78
  • 153
0

You can filter by proximity like this:

public class LatLon
{
    public double lat {get;set;}
    public double lon {get;set;}
}

class ProximityFilter
{
    private LatLon m_ref = null;

    internal bool DifferentFromPrevious(LatLon arg)
    {
        if (m_ref == null)
        {
            m_ref = arg;
            return true;
        }

        var are_different = Math.Abs(arg.lat - m_ref.lat) > 0.001 || Math.Abs(arg.lon - m_ref.lon) > 0.001;
        if (are_different)
            m_ref = arg;
        return are_different;
    }
}

class Program
{
    static int Main(string[] args)
    {
        var p1 = new LatLon { lat = 49.9429989, lon = 3.9542134 };
        var p2 = new LatLon { lat = 49.9529989, lon = 3.9642134 };
        var p3 = new LatLon { lat = 49.9429989, lon = 3.9542133 };
        var p4 = new LatLon { lat = 49.9429989, lon = 3.9542136 };
        var list = new List<LatLon> {p1, p2, p3, p4};

        var filter = new ProximityFilter();

        var cleaned = list.Where(filter.DifferentFromPrevious);
        // ...
    }

}

You can't use Distinct as it will remove the point with a value seen before, even if there is a different value between them.

Additionally, this approach has O(N) complexity, so at least theoretically it performs better than Distinct. It also works both with structs and classes.

Zdeslav Vojkovic
  • 13,736
  • 25
  • 41