我有一个名为flags
的表,其中包含一个名为coordinates
的列,其中包含MySQL'点'。我需要执行一个查询,我根据半径100米的纬度和经度位置得到一个圆圈内的所有标志。
从使用的角度来看,这是基于用户的位置。例如,移动电话将给出用户的纬度和经度位置,然后将其传递给API的这一部分。然后由API在半径为100米的用户周围创建一个不可见的圆圈,然后返回此圆圈中的标记。
API的这一部分我不知道如何创建,因为我不确定如何使用SQL创建此隐形圆并仅选择此半径内的点。
这可能吗?是否有 MySQL空间函数可以帮助我做到这一点?
我相信Buffer()
函数可以做到这一点,但我找不到任何关于如何使用它的文档(例如示例SQL)。理想情况下,我需要一个答案,告诉我如何使用此函数或最接近它。在我将这些坐标存储为地理空间点的地方,我应该使用地理空间函数来做我要求最大化效率的问题。
旗帜表:
示例行:
1 | [几何 - 25B] | Tenacy AB
对于旗帜表,我有纬度,经度位置以及东向和北向(UTM)
用户的位置只是标准纬度/经度,但我有一个可以将此位置转换为UTM的库
答案 0 :(得分:19)
MySQL中没有支持纬度/经度距离计算的地理空间扩展功能。 There is as of MySQL 5.7。
你要求在地球表面上接近圆圈。您在问题中提到,flags
表格中每行的纬度/经度值以及universal transverse Mercator(UTM)投影值均来自多个UTM zones之一。如果我记得我的英国军械测量局正确映射,UTM对于定位这些地图上的项目非常有用。
在UTM中计算同一区域中的两个点之间的距离是一件简单的事情:笛卡尔距离起作用。但是,当点位于不同的区域时,该计算不起作用。
因此,对于您问题中描述的应用,必须使用Great Circle Distance,这是使用半正弦或其他合适的公式计算的。
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)
如果要搜索20公里范围内的点,请更改查询的这一行
20.0 AS r, 69.0 AS units
,例如
20.0 AS r, 111.045 AS units
r
是您要搜索的半径。 units
是地球表面上每个纬度的距离单位(英里,公里,弗隆,你想要的任何东西)。
此查询使用边界纬度/长度和MbrContains
来排除距离起点太远的点,然后使用大圆距离公式生成剩余点的距离。一个explanation of all this can be found here。如果您的表使用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
答案 1 :(得分: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
如果您想以英里为单位,请将常数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
答案 2 :(得分:8)
<强>更新强>
使用ST_Distance_Sphere()使用纬度/经度计算距离
答案 3 :(得分:5)
缓冲区对MySQL的帮助不大5.6,因为缓冲区是一个多边形,而MySQL中的多边形操作&lt; 5.6实现为“最小边界矩形”(MBR),这是非常无用的。
自MySQL 5.6以来,full non-MBR st_*
operations were implemented。但对于您而言,最佳解决方案是使用undocumented函数st_distance
:
select *
from waypoints
where st_distance(point(@center_lon, @center_lat), coordinates) <= radius;
很难找到,因为它没有文档:-)但它在this blog上提到,其作者也填充了mentioned bugreport。但有一些警告(引用博客):
坏消息是:
1)所有功能仍然只使用平面系统坐标。 不支持不同的SRID。
2)仅MyISAM表支持空间索引(RTREE)。一 可以使用InnoDB表的函数,但它不会使用空间 密钥。
点1)表示距离单位与坐标单位(WGS84的情况下的度数)相同。如果您需要以米为单位的距离,则必须使用具有与米相对应的单位的预计协调系统(例如UTM或类似系统)。
所以,如果您不想使用这些警告,或者在MySQL&lt; 5.6,你必须编写自己的自定义距离函数。
答案 4 :(得分:1)
希望我的版本有所帮助
SELECT
*
FROM
`locator`
WHERE
SQRT(POW(X(`center`) - 49.843317 , 2) + POW(Y(`center`) - 24.026642, 2)) * 100 < `radius`
详情请http://dexxtr.com/post/83498801191/how-to-determine-point-inside-circle-using-mysql
答案 5 :(得分:1)
自: https://gis.stackexchange.com/questions/31628/find-points-within-a-distance-using-mysql
SELECT
id, (
6371 * acos (
cos ( radians(78.3232) )
* cos( radians( lat ) )
* cos( radians( lng ) - radians(65.3234) )
+ sin ( radians(78.3232) )
* sin( radians( lat ) )
)
) AS distance
FROM markers
HAVING distance < 30
ORDER BY distance
LIMIT 0 , 20;
(记得替换所有常量,这个例子是公里数)
答案 6 :(得分:0)
您可以使用:
SELECT name, lat, lng
FROM vw_mytable
WHERE ST_Contains(ST_Buffer(
ST_GeomFromText('POINT(12.3456 34.5678)'), (0.00001*1000)) , mypoint) = 1
上面的表达式:0.00001 * 1000,给你一个直径1000米的圆圈, 它被应用在这里的视图中,name列只是一个要指向的标签,mypoint是我的point列的名称, lat是使用ST_X(mytable.mypoint)和 与ST_Y(mytable.mypoint)一起使用,它们只显示lat和lng的文字值。 它会给你所有属于圆圈的坐标。
答案 7 :(得分:0)
为了完整起见,从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;
在这种情况下,我们以千米(6373)和一个点(@ pt1)提供地球的近似半径。此代码将计算该点(长12.3456,纬度34.5678)与距离为30km或更短的数据库中包含的所有点之间的距离(以千米为单位)。