php - 使用 MySQL 空间字段查找地理围栏(圆圈)内的记录

标签 php mysql laravel geospatial

问题

我当前使用 MySQL POINT 字段类型存储某个位置的纬度经度 值,格式如下:

POINT(51.507351 -0.127758)

我以前从未使用过这种字段类型,因此对查询以及如何实际、高效地使用存储的数据没有任何经验。

我的研究

我发现许多链接演示了在指定半径内搜索项目的各种方法。但是,其中大多数都使用独立的纬度经度字段,而不是使用MySQL空间字段。

请参阅以下内容:

我的问题

我正在尝试搜索给定半径(以米为单位)内的任何记录。根据我的表的结构,搜索记录并返回指定半径(圆形而不是矩形)内的任何项目的最佳且最有效的方法是什么?

这是我到目前为止所拥有的:

SELECT
    *,
    (
        6373 * acos (
            cos ( radians( PASSED_IN_LATITUDE ) )
            * cos( radians( X(location) ) )
            * cos( radians( Y(location) ) - radians( PASSED_IN_LONGITUDE ) )
            + sin ( radians( PASSED_IN_LATITUDE ) )
            * sin( radians( X(location) ) 
        )
    ) AS distance
FROM locations
HAVING distance < PASSED_IN_RADIUS

我从另一个答案中获取了上述代码,但考虑到这个答案是在两年前发布的,我认为它已经过时了,因此这可能不再是最有效的方法...

最佳答案

假设您在位置上有一个空间键,您可以执行以下操作:

select * from locations where 
contains(geomfromtext('polygon($bounding_rect_coords)'),location) 
and earth_distance(location,point($lat,$lon)) < $radius

边界矩形坐标应使用以下公式计算:

$deg_to_rad = $PI/180.0
$rad_to_deg = 1.0/$deg_to_rad
$delta_y = $rad_to_deg *($radius / ($earth_radius * cos($lat*$deg_to_rad))) // the length of the parallel = EARTH_R * cos(lat)
$delta_x = $rad_to_deg * ($radius/$earth_radius)
$x1 = $lat - $delta_x
$x2 = $lat + $delta_x
$y1 = $lon - $delta_y
$y2 = $lon + $delta_y

然后得到矩形

geomfromtext('polygon(($x1 $y1,$x2 $y1,$x2 $y2, $x1 $y2, $x1 $y1))')

最好在应用程序中完成此操作,以减轻数据库服务器的负担。

这个矩形实际上是一个球面矩形,因此在其计算中使用了 PI 常数。这个想法很简单。对于给定的纬线,将搜索半径转换为经度。这就是我们需要从目标向东和向西移动多少度才能覆盖我们的候选点。然后计算相同的纬度 - 与经度不同,这不会依赖于坐标,因为所有经线都具有相同的长度。这就是我们需要向北和向南移动多少度。

上述计算假设搜索半径小于平行线的长度,这在美国大部分地区都是合理的搜索半径,但在阿拉斯加的某些地区可能不成立。因此,最好检查一下(如果 delta_y > 90)并相应地对其进行剪辑。您还应该检查一下您是否位于北极或南极,那里的情况完全破裂。但希望您的数据没有太多极地记录。

对于earth_distance(),您有多种选择:

尽管你的计算本身已经有两年了,但它本身还是可以的 - 据我所知,在过去两年中,就测量地球上两点之间的距离而言,没有发现任何革命性的发现。

你原来的方法也可以工作,只是效率低下。添加 contains 子句使我们能够将搜索减少到(希望)相对较小的集合,并保证很快就在搜索半径内。然后我们选取每个候选者并过滤掉那些没有在 earth_distance() 上晋级的候选者。

我必须添加一个标准免责声明,即我在 SQL 中插入了可能尚未清理的变量。在编写实际的生产代码时,请确保验证生成的 SQL 查询是否存在 SQL 注入(inject)攻击。

关于php - 使用 MySQL 空间字段查找地理围栏(圆圈)内的记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37444945/

相关文章:

php - 从子函数中打破父函数(最好是 PHP)

php - 错误的整数值 : '' for column 'user_id' at row 1

javascript - 如何使 laravel 中的对象在 JavaScript laravel blade 中使用?

php - Laravel auth 系统可以使用现有数据库吗?

php - 将 Laravel 应用程序部署到 Azure

php - 使用php将字符串转换为逗号分隔的数组

php - 如何在 MySQL 查询中添加多个 OR

php - Paypal (PHP) : unable to use client certificate (no key found or wrong pass phrase? )

mysql - 过滤Mysql表中的相同数据

php - 在 require 语句中选择正确的路径