Distance function for sqlite

Posted by Dave on October 25th, 2008

As part of an iPhone SDK project, I have an sqlite database with a table full of geographic locations, each stored as a latitude and longitude value in degrees. I wanted to be able to perform an SQL SELECT on this table and ORDER BY each row’s distance from an arbitrary point. I’ve achieved this by defining a custom sqlite function. This article contains the code for the function, together with instructions on using it.

Here’s the function, together with a convenience macro to convert from degrees to radians. This function is based on an online distance calculator I found which makes use of the spherical law of cosines.


#define DEG2RAD(degrees) (degrees * 0.01745327) // degrees * pi over 180

static void distanceFunc(sqlite3_context *context, int argc, sqlite3_value **argv)
{
// check that we have four arguments (lat1, lon1, lat2, lon2)
assert(argc == 4);
// check that all four arguments are non-null
if (sqlite3_value_type(argv[0]) == SQLITE_NULL || sqlite3_value_type(argv[1]) == SQLITE_NULL || sqlite3_value_type(argv[2]) == SQLITE_NULL || sqlite3_value_type(argv[3]) == SQLITE_NULL) {
sqlite3_result_null(context);
return;
}
// get the four argument values
double lat1 = sqlite3_value_double(argv[0]);
double lon1 = sqlite3_value_double(argv[1]);
double lat2 = sqlite3_value_double(argv[2]);
double lon2 = sqlite3_value_double(argv[3]);
// convert lat1 and lat2 into radians now, to avoid doing it twice below
double lat1rad = DEG2RAD(lat1);
double lat2rad = DEG2RAD(lat2);
// apply the spherical law of cosines to our latitudes and longitudes, and set the result appropriately
// 6378.1 is the approximate radius of the earth in kilometres
sqlite3_result_double(context, acos(sin(lat1rad) * sin(lat2rad) + cos(lat1rad) * cos(lat2rad) * cos(DEG2RAD(lon2) - DEG2RAD(lon1))) * 6378.1);
}

This defines an SQL function distance(Latitude1, Longitude1, Latitude2, Longitude2), which returns the distance (in kilometres) between two points.

To use this function, add the code above to your Xcode project, and then add this line immediately after you call sqlite3_open:


sqlite3_create_function(sqliteDatabasePtr, "distance", 4, SQLITE_UTF8, NULL, &distanceFunc, NULL, NULL);

…where sqliteDatabasePtr is the database pointer returned by your call to sqlite3_open.

Assuming you have a table called Locations, with columns called Latitude and Longitude (both of type double) containing values in degrees, you can then use this function in your SQL like this:


SELECT * FROM Locations ORDER BY distance(Latitude, Longitude, 51.503357, -0.1199)

This example orders the locations in your database based on how far away they are from the London Eye, which is at 51.503357, -0.1199.



Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

Thanks for the code! This helped out a lot! Works like a charm!

Thanks heaps! Does exactly as advertised :)

All I needed, thanks a lot.

Thx for you’re code, it works well !
I have a question, I use you’re query with two parameters so it’s like that :

“SELECT * FROM spot_en ORDER BY distance(SPOT_LAT, SPOT_LON, ?, ?) LIMIT 0,20″

but when I use the sqlite3_bind_double(selectstmt, 1, lat);

it returns a 21 error, that it says that I misuse the library, do you know if it come from you’re function or it’s a mistake of my code.

Thanks

I am getting an error on distanceFunc. Do I need to declare it?

Dave, I am having a little difficulty deploying your code. I am getting a few errors I can not figure out. I am trying to use it in the iPhone SDK.

Hi Tommy,

Can you post the errors you are seeing? And which version of the SDK are you using?

Dave.

I’m having trouble integrating this code into my project. I am new to Objective-C and C but I assume this is C code. What are the details to properly integrate it? I put the function code into a .c file and also tried creating a header file. (Is a header necessary? How exactly should it look?) I #import the header file in the .m file I will be using it and follow your sqlite3_create_function instructions.

I’ve reduced it to one warning and one error.
The warning “‘distanceFunc’ used but never defined” is in my header file which looks like this…
#import
static void distanceFunc(sqlite3_context *context, int argc, sqlite3_value **argv);

The error is simply “gcc-4.0 failed with exit code 1″

What is my lack of C skills blinding me to!?

Dave,

I finally figured out my issue/

Tommy

Dave, I do have one issue. I got the code to work, but I want to substitute a variable where you are plugging the coordinates for the London Eye in your select statement. My select statement looks like this:
[NSString stringWithFormat:@"select pk, station, dial, city, state from radiostations ORDER BY distance(Latitude, Longitude, %@, %@)",presentLat, presentLng];

Tommy

Does this syntax look correct?

Very cool! Was pretty bummed when i realized sqlite didn’t include math functions, you saved me TONS of time :)

I was having the same problem with declarations. To use the code you must declare it in your header file…
static void distanceFunc(sqlite3_context *context, int argc, sqlite3_value **argv);

… just an awesome tool ! couldn’t dream better !

Thanks! saved me a tonne of time

All I needed, thanks a lot.

That’s awesome, I’m wondering if you have any solution for the problem described here (http://stackoverflow.com/questions/2099090/trouble-with-sqlite-sql-query).

Thanks

Hi Alix,

I’ve posted a comment / suggestion on stackoverflow – hope it’s useful :)

Dave.

Thank you for an excellent post

Thanks for posting this it really helped me out.

Is there any way to adapt this to javascript? I’m using phonegap to create an application, currently I have an sqlite database full of lats and lons and I want to be able to pull all of the rows from the db that are within say a 25mi radius (optionally converting to km). i have found no documentation on how to create a custom function for sqlite in javascript.

[...] sqlite does not support the math. No worries we can use a C# function instead! Just need to port this and put it all [...]

You’re my new best friend. Thanks, it works just as I needed!