Quanthema
Quanthema

Reputation: 59

Why are my GeoCoordinate values not in the correct range?

I created 2 GeoCoordinate objects, LocA and LocB. I am continually updating the location info in LocA and LocB by using a timer and storing the values in a tuple list. In my code, LocA is the last added point and LocB is the second to last added point.
But I always face an exception during runtime.

How can I prevent this?

enter image description here

Here is my code;

public partial class Form1 : Form
{
    List<Tuple<double, double>> myTuple2 = new List<Tuple<double, double>>();
    GeoCoordinate LocA, LocB;


    private void timer1_Tick(object sender, EventArgs e)
    {
        myTuple2.Add(new Tuple<double, double>(Convert.ToSingle(gPathBoylam), Convert.ToSingle(gPathEnlem))); 
        //gPathBoylam=Longitude info coming from gps,
        //gPathEnlem=Lattitude info coming from gps,                          
        if (myTuple2 != null && myTuple2.Any())
        {
            for (int i = 1; i < myTuple2.Count; i++)
            {
                LocA = new GeoCoordinate(myTuple2[i].Item1, myTuple2[i].Item2);
                LocB = new GeoCoordinate(myTuple2[i-1].Item1, myTuple[i-1].Item2);        
            }            
        }

Upvotes: 0

Views: 676

Answers (1)

Flater
Flater

Reputation: 13783

The problem you are experiencing is twofold:

  1. The for loop is completely unnecessary as you are continually overwriting the same variables. After the for loop has run, you will only have the results of the last iteration. Since your code does not care about the outcome of the previous iterations (since it blindly ignores and overwrites it), you should avoid performing them.
  2. The exception you are seeing is not related to your code, but rather the data that you have received from the GPS.

Let me elaborate on both:

1 - Remove the for loop

Let's say you have 4 items in your list. You then run the for loop:

for (int i = 1; i < myTuple2.Count; i++)
{
    LocA = new GeoCoordinate(myTuple2[i].Item1, myTuple2[i].Item2);
    LocB = new GeoCoordinate(myTuple2[i-1].Item1, myTuple[i-1].Item2);        
 }

Let me work out the iteration, number by number, so you see where you are going wrong.

//i = 1
LocA = new GeoCoordinate(myTuple2[1].Item1, myTuple2[1].Item2);
LocB = new GeoCoordinate(myTuple2[0].Item1, myTuple[0].Item2);
//i = 2
LocA = new GeoCoordinate(myTuple2[2].Item1, myTuple2[2].Item2);
LocB = new GeoCoordinate(myTuple2[1].Item1, myTuple[1].Item2);
//i = 3
LocA = new GeoCoordinate(myTuple2[3].Item1, myTuple2[3].Item2);
LocB = new GeoCoordinate(myTuple2[2].Item1, myTuple[2].Item2);
//the for loop stops here because when i = 4; it violates the "i < tuples.Count" condition

There is no point to the first two times where you set LocA and LocB. You overwrite the values immediately. So instead, just do the last iteration manually:

int lastItemIndex = myTuple.Count - 1;
int secondToLastItemIndex = lastItemIndex - 1;

LocA = new GeoCoordinate(myTuple2[lastItemIndex].Item1, myTuple2[lastItemIndex].Item2);
LocB = new GeoCoordinate(myTuple2[secondToLastItemIndex].Item1, myTuple[secondToLastItemIndex].Item2);

Note: You will want to add checks to prevent errors if there is only 1 item in the list. I kept my example simple to address the core issue

2 - The exception you see

The exception you see tells you the following:

System.ArgumentOutOfRangeException
The value of the parameter must be from -180.0 to 180.0

This is not a standard .Net exception. This is a custom exception that is being thrown intentionally. And the message of the exception reveals the problem:

Both latitude and longitude can only have relevant values between -180 and +180. Any number greater or smaller than this range is "incorrect" since there can only be a range of 360° in a circle.

LocA = new GeoCoordinate(-15, 75); //correct
LocA = new GeoCoordinate(0, 180); //correct
LocA = new GeoCoordinate(525, 12545); //INCORRECT (as per the exception message)

My GUESSES as to the cause of this issue:

1 There is a culture difference between the decimal , (comma) and . (dot). If this is the case, a value like 175,5 might be parsed as 1755. You need to make sure that this is not an issue.

Stop reading here and check this first. If this is the case, nothing else can help you except fixing the culture issue.

2 There could be something seriously wrong with your data. Maybe you are getting nonsensical numbers. I highly suggest using breakpoints to see which values you are getting. You might be getting garbage numbers that have no meaning.
If this is the case, you will have to read the GPS documentation.

It's not your fault that the data you are provided with is incorrect. However, it should be your duty to make sure that an unexpected value does not crash the system and instead handles this internally (or gives feedback to the user in a nice way).

3 The GPS is returning values between 0 and 360, but your library wants values between -180 and 180. (This is the most likely example. Technically speaking, your GPS could return values from any arbitrary range that spans 360°, but it seems weird to use anything other than (0)<->(360) or (-180)<->(180).

It's possible to calculate the correct number based on an incorrect value by adding or subtracting 360° until you get in the correct range. E.g. a value of 359° can be converted to a value of -1°.

So the question becomes: how do I make sure that my value is between -180 and +180? Here's a simple method that will ensure your value is within the proper range:

public static double NormalizeAngle(double theAngle)
{
    if(theAngle <= -180)
    {
        //we keep adding 360° until we reach an acceptable value.
        while(!IsValidAngle(theAngle))
        {
            theAngle += 360;
        }

        return theAngle;
    }

    if(180 <= theAngle)
    {
        //we keep subtracting 360° until we reach an acceptable value.
        while(!IsValidAngle(theAngle))
        {
            theAngle -= 360;
        }

        return theAngle;
    }

    //if neither if block was entered, then the angle was already valid!
    return theAngle;
}

private static bool IsValidAngle(double theAngle)
{
    return (-180 <= theAngle) && (theAngle <= 180);
}

If you add this (somewhere in your code base), you can then do the following:

LocA = new GeoCoordinate(
             NormalizeAngle(myTuple2[lastItemIndex].Item1), 
             NormalizeAngle(myTuple2[lastItemIndex].Item2)
         );

LocB is the same of course.

Upvotes: 2

Related Questions