You should be able to let SQL do the work inside the database:
select ((x - ?)*(x - ?) + (y - ?)*(y - ?)) as distsq from entries
order by dist limit 20
Unfortunately sqlite doesn't provide exponentiation, so the duplicated terms are needed.
If this is still not fast enough, another approach would be to make bounding box queries centered on your location, adjusting the size of the bounding box by binary search until you have 30 or a few more entries. Indexes on each of the x and y dimension will speed these along.
Edit Since the OP says earth curvature is important, a bounding box technique is probably the best approach we can get with unextended sqlite
. Here is a proposed algorithm:
Let P be the current position
Let Slat = lat0 be the bounding box latitude half-size initialized with a "best guess"
Let Slon = lon0 be the bounding box longitude half-size initialized with a "best guess"
// NB the best guesses should cover an approximately square area on the ground
loop
Let W = P.lon - Slon, E = P.lon + Slon, N = P.lat + Slat, S = P.lat - Slat
C = select count(*) from entries
where W <= lon and lon <= E and S <= lat and lat <= N
if C indicates the result is too big (e.g. for memory or read time),
Slat = 0.5 * Slat
Slon = 0.5 * Slon
else
Let R be the result of the same query for * instead of count(*)
Let D be the geometric distance from P to the nearest point on bounding box
Compute r.dist for all r in R (in memory)
Sort R by dist (in memory)
Throw away the tail elements of R where r.dist > D
// Can't use these because points outside bounding box might be closer!
If at least 20 remaining R elements,
return top 20
else
Slat = 2 * Slat
Slon = 2 * Slon
end if
end if
end loop
Note you need indices for lat and lon. I don't know how good the SQLite query optimizer is in this case. A good optimizer will pick either the lat or lon index based on statistics accumulated from past queries, use this to quickly find all points in the bounding box range for that dimension, then do a scan of this result to get the final. If the optimizer is not that clever, you want to index only the dimension likely to produce the smallest initial result: in the average case this is the one with greatest geometric extent (distance covered).
The r* tree index will make the bounding box queries much faster, but at least through Jelly Bean, you'd have to provide your own SQLite instance with this extension included. Perhaps later Android versions included it? I don't know.
Additionally, if you get to the point of including a custom SQLite with the app, it would be pretty easy to add the distance (with curvature) function as an extension.