按最接近的纬度和经度坐标排序的SQLITE订购

9

我需要获取一个SQLite SQL语句,以便根据初始位置的最近纬度和经度坐标进行排序。

这是我在SQLite数据库中表格的示例语句:

SELECT id, name, lat, lng FROM items

EXAMPLE RESULT: 1, Museu, 41375310.0, 2175970.0

我必须使用SQLite和那张表来实现这一点。因为这是一个已经存在的SQLite数据库,我不能改变它,所以我不能使用其他技术。
在Android和SQLite中有一种方法可以实现这个吗?我查了很多stackoverflow帖子,但没有找到实现的方法。
谢谢。

你能否举个例子,说明你想要在特定的初始位置发生什么? - Vinay S Shenoy
我必须获取一个SQLite SQL语句,以便根据最近的纬度和经度坐标对初始位置进行排序。 - NullPointerException
是的,我明白了。但如果初始位置是“纽约”,你能说一下你希望发生什么事情吗?这会很有帮助。 - Vinay S Shenoy
2
41375310.0的纬度?你的半圆比我的多几度...这个问题比你想象的要复杂。你确定没有第三方GIS库可以使用吗? - David Grant
不,我必须使用SQLite来实现这个,顺便说一下4137510.0实际上是41.375100。 - NullPointerException
显示剩余2条评论
5个回答

29

Darkwater的回答几乎正确。 为了确保正确,您需要使用差的平方。 由于SqLite上没有平方函数,因此您需要将差值乘以它们自己。 无需计算平方根。

SELECT * AS distance FROM items ORDER BY ((location_lat-lat)*(location_lat-lat)) + ((location_lng - lng)*(location_lng - lng)) ASC

一个学过数学的人给出了很好的答案 :) 这应该是被采纳的答案。 - Entea
如果你靠近反子午线,难道你不需要一些逻辑来包装吗? - user3064538
谢谢,这对我非常有效。问个奇怪的问题,你能解释一下背后的数学原理吗? - user3403733
谢谢,这对我很有帮助。两点之间的平方距离。 - Shahriar

21
SELECT * AS distance FROM items ORDER BY ABS(location_lat - lat) + ABS(location_lng - lng) ASC

这应该会在MySQL中按照距离大致排序,而且应该在SQLite中有效。
如果您需要更精确地对它们进行排序,可以尝试使用勾股定理(a ^ 2 + b ^ 2 = c ^ 2)来获得精确距离。


1
谢谢,但是你的SQL语句没有正常工作,使用SQLite无法计算勾股定理,因为它没有pow和sqrt函数,我也不知道如何在SQLite和Android上添加这些函数。 - NullPointerException
最简单的!!!谢谢!!!获取精确距离更好,但我认为sqlite中没有数学函数? - Aleksey Kontsevich

15

Chris提出的解决方案是:

SELECT * AS distance FROM items ORDER BY ((location_lat-lat)*(location_lat-lat)) + ((location_lng - lng)*(location_lng - lng)) ASC

当我们接近赤道时,它是正确的。为了在其他纬度下能够正常工作,我建议:

SELECT * AS distance FROM items ORDER BY ((location_lat-lat)*(location_lat-lat)) + ((location_lng - lng)*(location_lng - lng)*cos_lat_2) ASC

我们必须预先计算cos_lat_2

cos_lat_2 = cos(location_lat * PI / 180) ^ 2

问题:

如果我们在赤道上,向东或西移动一度经度,我们将沿着周长为40,000公里的圆形移动,代表了40,000/360的距离。如果我们向南或北移动一度纬度,则会在穿过两极的圆上移动,这也涉及到40,000/360的距离(考虑到地球是一个球体)。

但是,如果我们在英格兰南部,纬度为50度,并且我们向东或西移动一度经度,则我们将在第50平行线上移动,它的周长比赤道小。距离是perimeter_parallel_50/360。计算这个周长很简单:perimeter_parallel_50 = cos (50) * 2 * PI * EARTH_RADIUS = 0.64 * 40,000公里。如果我们向南或北移动一度,这种距离的减少是看不见的。我们移动的周长仍然具有40,000公里的周长。

解决方法:

由于location_lat是预先知道的值,因此我们可以预先计算cos(location_lat)的值,以便它可以用作缩放因子,使经度和纬度的位移相等。此外,我们对其进行预平方,以避免必须两次乘以它。

注意:

这仍然是一个近似值,在移动较大距离时,特别是在靠近极点和穿越180度子午线时,它将给出错误的结果。


我认为你应该添加处理穿越180度经线的逻辑,而不是让读者自己处理 :) - user3064538

5

如果您知道1度纬度约为111111米,1度经度为111111*cos(纬度)米,则可以轻松获得特定正方形内的所有位置。

SELECT * FROM items WHERE latitude BETWEEN %f AND %f AND longitude BETWEEN %f AND %f

即使有数百万行,此查询也非常快。但不要忘记为纬度和经度创建索引:

CREATE INDEX position ON items (latitude, longitude)

我使用Objective-C获取当前位置周围3公里范围内的所有地点:

double latDist = 1.0 / 111111.0 * 3.0;
double lonDist = 1.0 / ABS(111111.0*cos(location.coordinate.latitude)) * 3.0;

FMResultSet *results = [database executeQueryWithFormat:@"SELECT * FROM items WHERE latitude BETWEEN %f AND %f AND longitude BETWEEN %f AND %f", location.coordinate.latitude - latDist, location.coordinate.latitude + latDist, location.coordinate.longitude - lonDist, location.coordinate.longitude + lonDist];

现在你可以计算准确的距离并排序结果...


1
这是一个很好的快速过滤器,似乎可以工作,但您的乘数比例偏差了1000倍。如果您的3.0是以公里为单位的,则乘数应该是111.1。 - mstenroos
是的,你说得对,我忘了提到我将所有坐标都存储为整数,并首先将它们乘以1000。 - DanielFo

3
如果你能够加载记录;将其转换为位置,然后 使用distanceTo函数 我建议这样做,但是...
你可以使用普通的SQL近似计算两点之间的距离,这里清楚地列出了各种方法。如果你使用简单的计算,则随着你比较的点之间的距离越来越远,你的值可能变得越来越不正确。
如果你自己计算这些内容并且你的位置可以在任何地方,那么如果你比较穿过国际日期线的位置,你可能需要注意值的包装。

不行,我不能这样做,我必须获取能够给我排序记录集的SQL语句。而且我也不能使用你提供的第二个链接方式,因为SQLITE没有这些可用的函数。 - NullPointerException
SQLite中缺少哪些数学函数?为什么你们的约束条件这么严格? - Paul D'Ambra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接