2019-09-11
c# find the closest two numbers in a list with two columns to two number arguments
stackoverflow
Question

I am working with Geographical Lat/Lon values and given a specific Lat/Lon combination, I need to find the closest Lat/Lon from a list of Lat/Lon locations based on US Zip Codes. Once located, I need to return the Zip Code for that location.

Sample Arguments to Find the Closest Lat/Lon

X: -76.696136 Y: 40.095702

The DataTable Column includes Latitude, Longitude, ZipCode

I've tried the below, but the distance between the two numbers returned is greater than expected, e.g. the ZipCode returned is a considerable distance from the original Lat/Lon argument. I am not confident the OrderBy, ThenBy is actually providing the closest combination of numbers. Is there another approach available that would provide a closer match?

var zipMatches = zipCodeList
 .OrderBy(item => Math.Abs(Convert.ToDouble(item.Latitude) - Convert.ToDouble(searchedItem.GeoLat)))
 .ThenBy(item => Math.Abs(Convert.ToDouble(item.Longitude) - Convert.ToDouble(searchedItem.GeoLon)))
 .FirstOrDefault();

The expected result would be a Lat/Lon pair closest to the arguments provided.

X: -76.696136 Y: 40.095702

Answer
1

On a sphere distance calculation can become quite complex. If the points to compare are not too distant from each other and you are not near the date line where the degrees jump from -180 to +180 degrees, you could make the simplifying assumption that you are on a Euclidean plane and calculate the Euclidean distance using the Pythagorean theorem

distance = sqrt((x1 - x0)^2 + (y1 - y0)^2)

Since you are not interested in the absolute magnitude but only on the relative distances, you can omit the square root calculation

sqrDistance = (x1 - x0)^2 + (y1 - y0)^2

Like this

double searchLat = Convert.ToDouble(searchedItem.GeoLat);
double searchLon = Convert.ToDouble(searchedItem.GeoLon);
var zipMatches = zipCodeList
     .OrderBy(item => Math.Pow(Convert.ToDouble(item.Latitude) - searchLat, 2) +
                      Math.Pow(Convert.ToDouble(item.Longitude) - searchLon, 2))
     .FirstOrDefault();
c# find the closest two numbers in a list with two columns to two number arguments
See more ...