我有大约12 000个条目的数据库。每个条目都给出了纬度,经度和空距离。我需要做的是从当前GPS位置找到25个最近的条目。我的ORM是greenDao。
有2个问题: 我不知道我和条目之间的距离,我无法将所有条目加载到RAM,因为当我这样做时,堆上升到70MB并且应用程序在OutOfMemoryException崩溃(所以我需要使用延迟加载)。
我试过这种方法:
这是有效的,但从第1-3点开始它非常慢(在Nexus 7上大约需要25秒)。休息大约需要1.5秒。
每次用户启动应用或请求数据刷新时,我都必须这样做。 任何想法如何更好地解决它?
由于
编辑: 这是计算距离的函数,因此在SQL中很难做到这一点:(
double getDistance(GPSCoords myPos, Place place) {
double dlong = (place.getLongitude() - myPos.getLongitude()) * d2r;
double dlat = (place.getLatitude() - myPos.getLatitude()) * d2r;
double a = Math.pow(Math.sin(dlat / 2.0), 2) + Math.cos(myPos.getLatitude() * d2r)
* Math.cos(place.getLatitude() * d2r) * Math.pow(Math.sin(dlong / 2.0), 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double d = 6367 * c;
return d;
}
答案 0 :(得分:2)
您应该能够让SQL在数据库中完成工作:
select ((x - ?)*(x - ?) + (y - ?)*(y - ?)) as distsq from entries
order by dist limit 20
不幸的是,sqlite不提供取幂,因此需要重复的术语。
如果这还不够快,另一种方法是使边界框查询以您的位置为中心,通过二分查找调整边界框的大小,直到您有30个或更多条目。每个x和y维度的索引都会加速这些索引。
编辑由于OP说地球曲率很重要,因此边界框技术可能是我们使用未扩展sqlite
获得的最佳方法。这是一个提出的算法:
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
注意你需要lat和lon的索引。在这种情况下,我不知道SQLite查询优化器有多好。一个好的优化器将根据过去查询中累积的统计信息选择lat或lon索引,使用它来快速查找该维度的边界框范围内的所有点,然后扫描此结果以获得最终结果。如果优化器不那么聪明,您只想索引可能产生最小初始结果的维度:在平均情况下,这是具有最大几何范围(覆盖距离)的维度。
The r* tree index将使边界框查询更快,但至少通过Jelly Bean,您必须提供自己的SQLite实例,并包含此扩展。也许以后的Android版本包含它?我不知道。
此外,如果您在应用程序中包含自定义SQLite,则将距离(带曲率)功能添加为扩展名非常容易。
答案 1 :(得分:0)
有许多使用不同风格的SQL进行距离计算的例子。从数据库加载每一行并计算它的距离,然后排序和获取最接近的行将从前后到数据库变慢。在SQL中进行计算并且仅检索所需的计算将会更加高效。
答案 2 :(得分:0)
您可以尝试将距离计算移动到sql db。 你也可以放一些更聪明的代码,这些代码会运行距离计算,直到他找到25个位置,它们与当前位置的距离小于x(你选择)。或者甚至少于25个项目(也许你只需要7个填充屏幕)并且当用户已经在应用程序中时继续在后台进行计算。 这将是一个更好的用户体验。
答案 3 :(得分:0)
我不明白为什么你觉得你需要懒得加载你的参赛作品。只有12k条目,70MB的堆数听起来很可疑。您是否只是为了计算距离而抓住整行?尝试抓住您需要的列:
假设每个字节为8个字节,即24 * 12000
个字节,或大约280个千字节。给它一些只是Java的开销空间,但你仍然在寻找非常可管理的东西。
然后你可以在代码中进行计算,然后让它为每个最近的点吐出主键。第二个查询可以抓住那些25(这次是整行),你就完成了!