使用 MySQL 空间字段在地理围栏(圆圈)内查找记录
Find records within Geofence (circle) using MySQL Spatial fields
问题
我目前正在使用 MySQL POINT
格式的字段类型存储位置的 latitude
和 longitude
值:
POINT(51.507351 -0.127758)
我以前从未使用过这种字段类型,因此对查询以及如何实际有效地使用存储的数据没有任何经验。
我的研究
我找到了许多链接,这些链接演示了在指定半径内搜索项目的各种方法。但是,其中大多数使用独立的 latitude
和 longitude
字段,而不是使用 MySQL 空间字段。
请看以下内容:
- Fastest Way to Find Distance Between Two Lat/Long Points
- http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
- Use MySQL spatial extensions to select points inside circle
我的问题
我正在尝试搜索给定半径(以米为单位)内的任何记录。根据我的 table 的结构,搜索我的记录并返回指定半径(圆形而不是矩形)内的任何项目的最佳、最有效的方法是什么?
这是我目前拥有的:
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
我从另一个答案中获取了上面的代码,但考虑到这个答案是 2 年前发布的,我认为它已经过时了,因此这可能不是最有效的方法了...
假设你在位置上有一个空间键,你可以这样做:
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()
,您有多种选择:
- 使用我的 UDF (http://github.com/spachev/mysql_udf_bundle)(最快,但您需要能够在您的服务器上安装 UDF)
- 写一个MySQL存储函数。您可以从 http://gist.github.com/aramonc/6259563 开始并根据需要进行调整(需要创建函数的能力)。
- 只需将上面的距离计算直接粘贴到查询中即可(丑陋,但不需要特殊设置或权限)
你的计算本身是可以的,尽管它是两年前的 - 据我所知,在过去两年中,就测量地球上两点之间的距离而言,没有任何革命性的发现。
你原来的方法也可以,但效率低下。 contains
子句的添加允许我们将搜索减少到(希望)相对较小的集合,保证很快在搜索半径内。然后我们挑选每个候选人并过滤掉那些没有在 earth_distance()
.
上晋级的候选人
我必须添加一个标准的免责声明,即我将变量插入到 SQL 中,这些变量可能尚未经过清理。确保在编写实际生产代码时验证生成的 SQL 查询 SQL 注入攻击。
问题
我目前正在使用 MySQL POINT
格式的字段类型存储位置的 latitude
和 longitude
值:
POINT(51.507351 -0.127758)
我以前从未使用过这种字段类型,因此对查询以及如何实际有效地使用存储的数据没有任何经验。
我的研究
我找到了许多链接,这些链接演示了在指定半径内搜索项目的各种方法。但是,其中大多数使用独立的 latitude
和 longitude
字段,而不是使用 MySQL 空间字段。
请看以下内容:
- Fastest Way to Find Distance Between Two Lat/Long Points
- http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
- Use MySQL spatial extensions to select points inside circle
我的问题
我正在尝试搜索给定半径(以米为单位)内的任何记录。根据我的 table 的结构,搜索我的记录并返回指定半径(圆形而不是矩形)内的任何项目的最佳、最有效的方法是什么?
这是我目前拥有的:
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
我从另一个答案中获取了上面的代码,但考虑到这个答案是 2 年前发布的,我认为它已经过时了,因此这可能不是最有效的方法了...
假设你在位置上有一个空间键,你可以这样做:
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()
,您有多种选择:
- 使用我的 UDF (http://github.com/spachev/mysql_udf_bundle)(最快,但您需要能够在您的服务器上安装 UDF)
- 写一个MySQL存储函数。您可以从 http://gist.github.com/aramonc/6259563 开始并根据需要进行调整(需要创建函数的能力)。
- 只需将上面的距离计算直接粘贴到查询中即可(丑陋,但不需要特殊设置或权限)
你的计算本身是可以的,尽管它是两年前的 - 据我所知,在过去两年中,就测量地球上两点之间的距离而言,没有任何革命性的发现。
你原来的方法也可以,但效率低下。 contains
子句的添加允许我们将搜索减少到(希望)相对较小的集合,保证很快在搜索半径内。然后我们挑选每个候选人并过滤掉那些没有在 earth_distance()
.
我必须添加一个标准的免责声明,即我将变量插入到 SQL 中,这些变量可能尚未经过清理。确保在编写实际生产代码时验证生成的 SQL 查询 SQL 注入攻击。