MySQL选择范围内的坐标

时间:2014-01-10 10:51:04

标签: java mysql google-maps-api-3

我在我的数据库中有100 000个地址(即记录)。

每一个都有自己的坐标(纬度和经度)。

现在,考虑到用户的地理位置(纬度和经度),我想在地图上仅显示5英里范围内的地址(使用Google maps v3 API)。

这意味着通常只需要在10万个地址中显示5个或6个地址。

一个解决方案可能是检索所有记录并在Java中应用公式来计算每个地址的距离,并仅在它在范围内时显示它。

这会浪费处理能力,因为当我只需要在地图上显示5或6个记录时,我需要检索所有记录。

如何在数据库端(MySQL)解决此问题,以便仅返回5英里范围内的地址?

5 个答案:

答案 0 :(得分:15)

您可以使用所谓的Haversine formula

$sql = "SELECT *, ( 3959 * acos( cos( radians(" . $lat . ") ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(" . $lng . ") ) + sin( radians(" . $lat . ") ) * sin( radians( lat ) ) ) ) AS distance FROM your_table HAVING distance < 5";

$lat$lng是您的点的坐标,而lat / lng是您的表格列。以上将列出5 nm范围内的位置。将3959替换为6371以更改为公里。

此链接可能很有用:https://developers.google.com/maps/articles/phpsqlsearch_v3

编辑:我没有看到你提到过Java。这个例子是在PHP中,但查询仍然是你需要的。

答案 1 :(得分:0)

我认为MySQL的地理空间扩展可以为您解决这个问题:http://dev.mysql.com/doc/refman/5.7/en/gis-introduction.html

否则你可以在lattitude上索引然后查询WHERE lattitude&gt; userLat-5miles和lattitude&lt; userLat +5英里。这将大大减少在应用程序层中处理的可能行数。

答案 2 :(得分:0)

UpsideDown先生,我几周来一直在寻找这个问题的答案,并且有一个非常好的解决方案,令人惊讶的是我在其他任何地方都找不到。您必须创建一个存储过程,在将其应用于MySQL查询之前限制与Java的接近程度,并且查询将在几毫秒内返回结果,我在表中已经结束使用它900K用户,您可以使用这种技术在100ms以内通过邻近搜索(它用PHP解释,但您需要的MySQL技巧仍然存在):

http://cssshowcase.co.uk/mysql-get-distance-by-latitude-and-longitude-coordinates/

没有其他解决方案像上面一样好,而且速度快,网上的其他解决方案使得查询需要15-20秒,这显然不是很好。

答案 3 :(得分:0)

我的方法 - 我正在使用它 - 就像技术员一样思考,我很满意+/- 5%

这个解决方案并不是为了控制火箭,船只等,而只是对于低于100公里的距离,它只是一个像Fermi problem

这样的解决方案

让我们开始寻找务实的解决方案:

首先:对于许多问题我们可以忽略地球不平坦(距离<~100km),

地球的周长大约是40000公里(或多或少完全出于某种原因)

一个圆正好是360度。

1 km是:360/40000 deg =&gt; 0.00278度

然后只需在每公里0.003范围内选择纬度/经度,它就像长的&gt; 42 - 0.003且长&lt; 42 + 0.003 - 相同的lat,其中42是你的lat / long作为middpoint。数据库将使用索引。

问题:你得到一个正方形而不是一个圆(不是真正的距离)

如果您需要圆圈,请在获得结果后编写脚本。

我只是在2公里处展示官方卫生间,所以正方形还可以,准确性也是如此。原因是,有街道和房屋,所以人们不能直接走路......

编辑:技术/数学解释:

对于非常小的三角形(一个角度<&lt; <5度),您可以忽略三角学的使用。所以罪(5度)〜= 5/180 * PI

答案 4 :(得分:0)

DELIMITER $
DROP FUNCTION IF EXISTS calc_distance$

CREATE FUNCTION calc_distance(
    user_lat FLOAT(9,6),
    user_lng FLOAT(9,6),
    field_lat FLOAT(9,6),
    field_lng FLOAT(9,6)
) RETURNS INTEGER

BEGIN
    DECLARE ulat FLOAT;
    DECLARE ulng FLOAT;
    DECLARE flat FLOAT;
    DECLARE flng FLOAT;

    SET ulat = RADIANS(user_lat);
    SET ulng = RADIANS(user_lng);
    SET flat = RADIANS(field_lat);
    SET flng = RADIANS(field_lng);
    RETURN
        6371393
        * ACOS(
            COS(ulat) * COS(flat) * COS(flng - ulng)
            + SIN(ulat) * SIN(flat)
        );
END$
DELIMITER ;

用法

if (($radius = $search->getRadiusMeters())) {
    $query['where'][] = "calc_distance($lat, $lng, addr.lat, addr.lng) < $radius";
}