Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert meters to decimal degrees

Tags:

c#

geo

I need to convert meters to decimal degrees in C#. I read on Wikipedia that 1 decimal degree equals 111.32 km. But it is on equator, so if I'm located above/below it my the conversion will be wrong? I assume this is wrong:

long sRad = (long.Parse(sRadTBx.Text)) / (111.32*1000); 

EDIT: I need this search radius to find nearby users

    long myLatitude = 100;
    long myLongitude = 100;
    long sRad = /* right formula to convert meters to decimal degrees*/

long begLat = myLatitude - searchRad;
long endLat = myLatitude + searchRad;
long begLong = myLongitude - searchRad;
long endLong = myLongitude + searchRad;

List<User> FoundUsers = new List<User>();

foreach (User user in db.Users)
{
    // Check if the user in the database is within range
    if (user.usrLat >= begLat && user.usrLat <= endLat && user.usrLong >= begLong && user.usrLong <= endLong)
    {
        // Add the user to the FoundUsers list
        FoundUsers.Add(user);
    }
}
like image 583
Peter Avatar asked Aug 11 '14 06:08

Peter


People also ask

How many decimal points is a degree?

The units digit (one decimal degree) gives a position up to 111 kilometers (60 nautical miles, about 69 miles). It can tell us roughly what large state or country we are in. The first decimal place is worth up to 11.1 km: it can distinguish the position of one large city from a neighboring large city.


2 Answers

Also from that very same Wikipedia article:

 As one moves away from the equator towards a pole, however,
 one degree of longitude is multiplied by
 the cosine of the latitude,
 decreasing the distance, approaching zero at the pole.

So this would be a function of latitude:

double GetSRad(double latitude)
{
    return 111.32 * Math.Cos(latitude * (Math.PI / 180));
}

or similar.

edit: So for going the other way around, converting meters to decimal degrees, you need to do this:

double MetersToDecimalDegrees(double meters, double latitude)
{
    return meters / (111.32 * 1000 * Math.Cos(latitude * (Math.PI / 180)));
}
like image 112
Christofer Ohlsson Avatar answered Sep 29 '22 11:09

Christofer Ohlsson


Christopher Olsson already has a good answer, but I thought I'd fill in some of the theory too.

I've always found this webpage useful for these formulas.

A quick note on the concept

Think about the actual geometry going on.

As it stands, you are currently doing nothing more than scaling the input. Imagine the classic example of a balloon. Draw two lines on the balloon that meet at the bottom and the top. These represent lines of longitude, since they go "up and down." Quotes, of course, since there aren't really such concepts, but we can imagine. Now, if you look at each line, you'll see that they vary in distance as you go up and down their lengths. Per the original specification, they meet at the top of the balloon and the bottom, but they don't meet anywhere else. The same is true of lines of longitude. Non-Euclidean geometry tells us that lines intersect exactly twice if they intersect at all, which can be hard to conceptualize. But because of that, the distance between our lines is effectively reflected across the equator.

As you can see, the latitude greatly affects the distance between your longitudinal lines. They vary from the closest at the north and south poles, to the farthest away at the equator.

Latitudinal lines are a bit easier. They do not converge. If you're holding our theoretical balloon straight up and down, with the poles pointed straight up and straight down that is, lines of latitude will be parallel to the floor. In a more generalized sense, they will be perpendicular to the axis (a Euclidean concept) made by the poles of the longitudinal lines. Thus, the distance is constant between latitudes, regardless of your longitude.

Your implementation

Now, your implementation relies on the idea that these lines are always at a constant distance. If that was the case, you'd be able to do take a simple scaling approach, as you have. If they were, in fact, parallel in the Euclidean sense, it would be not too dissimilar to the concept of converting from miles per hour to kilometers per hour. However, the variance in distance makes this much more complicated.

The distance between longitudes at the north pole is zero, and at the equator, as your cited Wikipedia page states, it's 111.32 kilometers. Consequently, to get a truly accurate result, you must account for the latitude you're looking for. That's why this gets a little more complicated.

Getting Realistic Results

Now, the formula you want, given your recent edit, it seems that you're looking to incorporate both latitude and longitude in your assessment. Given your code example, it seems that you want to find the distance between two coordinates, and that you want it to work well at short distances. Thus, I will suggest, as the website I pointed you to at the beginning of this posts suggests, a Haversine formula. That website gives lots of good information on it, but this is the formula itself. I'm copying it directly from the site, symbols and all, to make sure I don't make any stupid typos. Thus, this is, of course, JavaScript, but you can basically just change some cases and it will run in C#.

In this, φ is latitude, λ is longitude, θ is the bearing (in radians, clockwise from north), δ is the angular distance (in radians) d/R; d being the distance travelled, R the earth’s radius

var R = 6371; // km
var φ1 = lat1.toRadians();
var φ2 = lat2.toRadians();
var Δφ = (lat2-lat1).toRadians();
var Δλ = (lon2-lon1).toRadians();

var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
        Math.cos(φ1) * Math.cos(φ2) *
        Math.sin(Δλ/2) * Math.sin(Δλ/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

var d = R * c;

I think the only thing that must be noted here is that R, as declared in the first line, is the radius of the earth. As the comment suggests, we're already working in kilometers so you may or may not have to change that for your implementation. It's easy enough, fortunately, to find the (average) radius of the earth in your favorite units by doing a search online.

Of course, you'll also want to note that toRadians is simply the input multiplied by Math.PI, then divided by 180. Simple enough.

Alternative

This doesn't really look relevant to your case, but I will include it. The aforementioned formula will give accurate results, but it will be at the cost of speed. Obviously, it's a pretty small deal on any individual record, but as you build up to handle more and more, this might become an issue. If it does, and if you're dealing in a fairly centralized locale, you could work off the immense nature of our planet and find numbers suitable for the distance between one degree of latitude and longitude, then treat the planet as "more or less Euclidean" (flat, that is), and use the Pythagorean Theorem to figure the values. Of course, that will become less and less accurate the further away you get from your original test site (I'd just find these numbers, personally, by asking Google Earth or a similar product). But if you're dealing with a dense cluster of users, that will be way, way, way faster than running a flurry of formulas to the Math class to work out.

Another, more abstract alternative

You might also want to think about where you're doing this logic. Here I begin to overstep my reach a bit, but if you happen to be storing your data in SQL Server, it already has some really cool geography functionality built right in that will handle distance calculations for you. Just check out the GEOGRAPHY type.

Edit

This is a response to a comment, suggesting that the desired result is really a rectangle denoting boundaries. Now, I would advise against this, because it isn't really a search "radius" as your code may suggest.

But if you do want to stick to that method, you'll be looking at two separate distances: one for latitude and one for longitude. This is also from that webpage. φ1 is myLatitude, and λ1 is myLongitude. This formula accepts a bearing and starting coordinates, then gives the resulting position.

var φ2 = Math.asin( Math.sin(φ1)*Math.cos(d/R) + Math.cos(φ1)*Math.sin(d/R)*Math.cos(brng) );
var λ2 = λ1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(φ1), Math.cos(d/R)-Math.sin(φ1)*Math.sin(φ2));

You could use that to determine the boundaries of your search rectangle.

like image 29
Matthew Haugen Avatar answered Sep 29 '22 11:09

Matthew Haugen