jsk*_*dd3 15 mysql sql geospatial
我有一个名为table的表flags
,其中包含一个coordinates
充满MySQL'points' 的列.我需要执行一个查询,我根据半径为100米的纬度和经度位置得到圆内的所有标志.
从使用的角度来看,这是基于用户的位置.例如,移动电话将给出用户的纬度和经度位置,然后将其传递给API的这一部分.然后由API在半径为100米的用户周围创建一个不可见的圆圈,然后返回此圆圈中的标志.
这是API的这一部分我不知道如何创建,因为我不确定如何使用SQL创建这个不可见的圆并仅在此半径内选择点.
这可能吗?是否有MySQL空间函数可以帮助我做到这一点?
我相信该Buffer()
函数可以做到这一点,但我找不到任何关于如何使用它的文档(例如示例SQL).理想情况下,我需要一个答案,告诉我如何使用此函数或最接近它.在我将这些坐标存储为地理空间点的地方,我应该使用地理空间函数来执行我要求的最大化效率.
旗帜表:
示例行:
1 | [几何 - 25B] | Tenacy AB
对于旗帜表我有纬度,经度位置和东向和北向(UTM)
用户的位置只是标准的纬度/经度,但我有一个可以将此位置转换为UTM的库
O. *_*nes 19
MySQL中没有支持纬度/经度距离计算的地理空间扩展功能. 从MySQL 5.7开始.
你在地球表面要求接近圆圈.您在问题中提到flags
表中每行的纬度/经度值,以及几个不同UTM区域之一的通用横向墨卡托(UTM)投影值.如果我记得我的英国军械测量局正确映射,UTM对于在这些地图上定位项目非常有用.
计算UTM中同一区域中两点之间的距离是一件简单的事情:笛卡尔距离就是诀窍.但是,当点位于不同的区域时,该计算不起作用.
因此,对于您的问题中描述的应用,有必要使用Great Circle Distance,它是使用hasrsine或其他合适的公式计算的.
MySQL增加了地理空间扩展,支持一种将各种平面形状(点,折线,多边形等)表示为几何图元的方法.MySQL 5.6实现了一个未记录的距离函数st_distance(p1, p2)
.但是,此函数返回笛卡尔距离.所以它完全不适合基于纬度和经度的计算.在温带地区,纬度对应的表面距离(南北)几乎是经度(东西向)的两倍,因为纬度线在靠近极点的地方越来越靠近.
因此,圆形接近公式需要使用真正的纬度和经度.
在您的应用程序中,您可以通过以下查询找到flags
给定的十个法定里程内的所有点latpoint,longpoint
:
SELECT id, coordinates, name, r,
units * DEGREES( ACOS(
COS(RADIANS(latpoint))
* COS(RADIANS(X(coordinates)))
* COS(RADIANS(longpoint) - RADIANS(Y(coordinates)))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(X(coordinates))))) AS distance
FROM flags
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint,
10.0 AS r, 69.0 AS units
) AS p ON (1=1)
WHERE MbrContains(GeomFromText (
CONCAT('LINESTRING(',
latpoint-(r/units),' ',
longpoint-(r /(units* COS(RADIANS(latpoint)))),
',',
latpoint+(r/units) ,' ',
longpoint+(r /(units * COS(RADIANS(latpoint)))),
')')), coordinates)
Run Code Online (Sandbox Code Playgroud)
如果要搜索20公里范围内的点,请更改此查询行
20.0 AS r, 69.0 AS units
Run Code Online (Sandbox Code Playgroud)
例如,对此
20.0 AS r, 111.045 AS units
Run Code Online (Sandbox Code Playgroud)
r
是您要搜索的半径. units
是地球表面每个纬度的距离单位(英里,公里,弗隆,你想要的任何东西).
此查询使用边界纬度/长度MbrContains
来排除距离起点太远的点,然后使用大圆距离公式生成剩余点的距离.可在此处找到对所有这些的解释.如果您的表使用MyISAM访问方法并具有空间索引,MbrContains
则会利用该索引来快速搜索.
最后,上面的查询选择矩形内的所有点.要将其缩小到仅限于圆圈中的点,并按邻近顺序排序,请将查询包起来,如下所示:
SELECT id, coordinates, name
FROM (
/* the query above, paste it in here */
) AS d
WHERE d.distance <= d.r
ORDER BY d.distance ASC
Run Code Online (Sandbox Code Playgroud)
And*_*ode 10
这假定表中的坐标作为POINT()数据类型存储在标记为"point"的列中.函数X(点)和Y(点)分别从点值提取纬度和经度值.
SET @lat = the latitude of the point
SET @lon = the longitude of the point
SET @rad = radius in Kilometers to search from the point
SET @table = name of your table
SELECT
X(point),Y(point),*, (
6373 * acos (
cos ( radians( @lat ) )
* cos( radians( X(point) ) )
* cos( radians( Y(point) ) - radians( @lon ) )
+ sin ( radians( @lat ) )
* sin( radians( X(point) ) )
)
) AS distance
FROM @table
HAVING distance < @rad
Run Code Online (Sandbox Code Playgroud)
如果您想以英里为单位,请将常数6373替换为3959
对于那些想要减少查询语法的人来说,这里是用户定义的MySQL函数的通用实现,用于实现基于Haversine公式的距离函数.
CREATE FUNCTION HAVERSINE ( coord1 POINT, coord2 POINT )
RETURNS DOUBLE
DETERMINISTIC
BEGIN
DECLARE dist DOUBLE;
SET rlat1 = radians( X( coord1 ) );
SET rlat2 = radians( X( coord2 ) );
SET rlon1 = radians( Y( coord1 ) );
SET rlon2 = radians( Y( coord2 ) );
SET dist = ACOS( COS( rlat1 ) * COS( rlon1 ) * COS( rlat2 ) * COS( rlon2 ) + COS( rlat1 ) * SIN( rlon1 ) * COS( rlat2 ) * SIN( rlon2 ) + SIN( rlat1 ) * SIN( rlat2 ) ) * 6372.8;
RETURN dist;
END
Run Code Online (Sandbox Code Playgroud)
缓冲区对MySQL <5.6没什么帮助,因为缓冲区是一个多边形,MySQL <5.6中的多边形操作被实现为"最小边界矩形"(MBR),这是非常没用的.
从MySQL 5.6开始,实现了完整的非MBR st_*
操作.但是,对于你来说,最好的解决方案是使用未记录的函数st_distance
:
select *
from waypoints
where st_distance(point(@center_lon, @center_lat), coordinates) <= radius;
Run Code Online (Sandbox Code Playgroud)
很难找到,因为它没有文档:-)但是在这个博客上提到过,其作者也填写了上面提到的bug报告.但有一些警告(引用博客):
坏消息是:
1)所有功能仍然只使用平面系统坐标.不支持不同的SRID.
2)空间索引(RTREE)仅支持MyISAM表.可以使用InnoDB表的函数,但不使用空间键.
点1)表示距离单位与坐标单位(WGS84的情况下的度数)相同.如果您需要以米为单位的距离,则必须使用具有与米对应的单位的预计协调系统(例如UTM或类似).
因此,如果您不想使用这些警告,或者在MySQL <5.6的情况下,您将必须编写自己的自定义距离函数.
为了完整起见,从 MySQL 5.7.6 开始。您可以使用ST_Distance_Sphere函数来实现相同的结果:
SET @pt1 = ST_GeomFromText('POINT(12.3456 34.5678)');
SELECT * from
(SELECT * ,(ST_Distance_Sphere(@pt1, location, 6373)) AS distance FROM mydb.Event ORDER BY distance) x WHERE x.distance <= 30;
Run Code Online (Sandbox Code Playgroud)
在本例中,我们提供以公里为单位的地球近似半径 (6373) 和一个点 (@pt1)。此代码将计算该点(经度 12.3456,纬度 34.5678)与数据库中包含的所有距离不超过 30 公里的点之间的距离(以公里为单位)。