views:

410

answers:

3

I am using an SQLITE database to store latitudes and longitudes of locations.

I want to be able to sort the results by rough distance from the current location. I already have the current location of the device as a double (lat, lng), the lat and lng in the database are also doubles.

What I want is a query that will create a virtual column that I am able to sort the results by.

I currently use a function to show the distance for a selected record:

float pk = (float) (180/3.14159);
float a1 = (float) (db_lat / pk);
float a2 = (float) (db_lon / pk);
float b1 = (float) (current_lat / pk);
float b2 = (float) (current_lon / pk);
float t1 = FloatMath.cos(a1)*FloatMath.cos(a2)*FloatMath.cos(b1)*FloatMath.cos(b2);
float t2 = FloatMath.cos(a1)*FloatMath.sin(a2)*FloatMath.cos(b1)*FloatMath.sin(b2);
float t3 = FloatMath.sin(a1)*FloatMath.sin(b1);
double tt = Math.acos(t1 + t2 + t3);
double dist = (6366000*tt);

For example, a MySQL select could be (taken from: www.movable-type.co.uk):

Select Lat, Lon, acos(sin($lat)*sin(radians(Lat)) + cos($lat)*cos(radians(Lat))cos(radians(Lon)-$lon))$R As dist From MyTable ORDER BY dist DESC

Currently I select locations using the following:

public Cursor locationGetAllRows(long groupid) { try { return db.query(LOCATION_DATABASE_TABLE, new String[] { "_id", "lat","lon","groupid"}, "groupid="+groupid, null, null, null, null); } catch (SQLException e) { Log.e("Exception on query: ", e.toString()); return null; } }

OK so is it possible to use the SQLITE database in this way? If not the only option I can think of is to have an extra column, iterate through the rows running the above function on each row and filling out an extra column on the row, then sorting on that column?

+1  A: 

yeah, that works perfectly.

it can be made into a stored procedure like so:

http://www.thismuchiknow.co.uk/?p=71 [Distance function for sqlite]

there's also the Perst spatial database for android which is excellent, and the SpatiaLite spatial database which is also awesome, which you could link to in your app.

w/out using a specialized lib, you could approximate the distance a few ways (calculate it just as if it were planar (rectangular) then use the Haversine formula to sort the subset later, use a lookup table that approximates cos, sin, etc, group locations into 5 sq. mile zones and search neighboring cells up to the max, etc...)

jspcal
The technique you describe does not seem to be applicable to Android, which cannot use sqlite3_create_function().
CommonsWare
yeah with a big location database would be good to use your own sqlite/perst/spatialite instead of android's built-in
jspcal
+2  A: 

This won't completely help, but for situations like this, seriously consider using rawQuery() instead of query(), so you can pass in a full SQL statement vs. having to chop it into pieces.


Your bigger problem is that I don't see that SQLite has trigonometric functions.

You do not indicate how you are using the Cursor you are getting back from your query. For example, if you are putting the Cursor into some sort of CursorAdapter, you could:

  • convert the Cursor into an ArrayList<Position>, where Position is some Java class you define with your data
  • close the Cursor, to release the RAM it takes up
  • sort the ArrayList<Position> using Arrays.sort()
  • wrap the ArrayList<Position> in an ArrayAdapter<Position> and use that where you had been using your CursorAdapter
CommonsWare
OK so for testing, I have created a ArrayList<HashMap<String, String>> and filled it with all the details required. I also created a simple class which extends object, ArrayList<locationRowDist>. I iterate through the cursor and place the data, currently, into both lists as I do I perform the distance calculation and add that.How would I then perform the sort? The class is: class locationRowDist extends Object { public String title; public double lat; public double lon; public double dist; }
Scoobler
If "locationRowDist" is a class, have it implement the Comparable interface, then use Arrays.sort() to sort it.
CommonsWare
A: 

I've just written an app that needs to sort a set of coordinates based on distance. What I did was create an array of IDs and Distances and then sorted them within Java. Then I could find the nearest locations and select them from the Database. Of course this approach may not work for you depending on how many points you have and how you access the database. This worked fine for ~350 points, in my Nando's Finder app.

Additionally I used Location.distanceBetween(..) from the SDK to calculate the distances for me. I would hope this method would be implemented in C to ensure it is fast, however, a quick look at the SDK source shows it is written in Java :(.

bramp