使用MySQL空间扩展来select圆内的点
我有一个名为flags
的表,其中包含一个称为coordinates
的列,充满了MySQL的“点”。 我需要执行一个查询,根据100米半径的经度和纬度位置获得一个圆圈内的所有标志。
从使用的angular度来看,这是基于用户的位置。 例如,手机会给用户的经纬度位置,然后将其传递给API的这一部分。 然后由API在用户周围创build一个半径为100米的不可见圆圈,然后返回此圆圈中的标志。
这是API的这一部分我不知道如何创build,因为我不确定如何使用SQL来创build这个不可见的圆,并只在这个半径内select点。
这可能吗? 有没有一个MySQL的空间function ,可以帮助我做到这一点?
我相信Buffer()
函数可以做到这一点,但我找不到任何文档如何使用它(例如SQL)。 理想情况下,我需要一个答案,告诉我如何使用这个function或最接近它。 在将这些坐标存储为地理空间点的地方,我应该使用地理空间函数来做我要求的最大效率。
标志表:
- ID
- 坐标
- 名称
示例行:
1 | [几何 – 25B] | Tenacy AB
旗标表有经纬度位置和东,北(UTM)
用户的位置只是标准的纬度/经度,但我有一个库可以将此位置转换为UTM
MySQL中没有支持纬度/经度距离计算的地理空间扩展函数。
你要求在地球表面接近圆。 在你的问题中,你提到你的flags
表中的每行都有纬度/长度的值,还有在几个不同的UTM区域之一中的通用横向墨卡托 (UTM)投影值。 如果我正确记住我的英国军械测量地图,UTM对于在这些地图上查找物品很有用。
计算UTM中相同区域中两点之间的距离是一件简单的事情:笛卡尔距离就是一个窍门。 但是,当点位于不同区域时,该计算不起作用。
因此,对于您的问题中描述的应用程序,有必要使用大圆距离 ,这是使用海峡或其他合适的公式计算的。
MySQL增加了地理空间扩展,支持将各种平面形状(点,多义线,多边形等)表示为几何图元的方法。 MySQL 5.6实现了一个未st_distance(p1, p2)
距离函数st_distance(p1, p2)
。 但是,这个函数返回笛卡尔距离。 所以这是完全不适合基于经度和纬度的计算。 在温带地区,由于纬度线在两极之间越来越近,所以一度纬度的纬度(南北)几乎是东经(东西)的两倍。
所以,一个圆形的近似公式需要使用真正的经度和纬度。
在你的应用程序中,你可以在给定的点的十个法定英里内find所有的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)
如果要search20公里范围内的点,请更改查询的这一行
20.0 AS r, 69.0 AS units
对此,例如
20.0 AS r, 111.045 AS units
r
是要search的半径。 units
是地球表面纬度的距离单位(英里,公里,弗隆,无论你想要的)。
这个查询使用边界纬度/长度与MbrContains
一起排除绝对离开始点太远的点,然后使用大圆距离公式来生成剩余点的距离。 所有这些的解释可以在这里find 。 如果您的表使用MyISAM访问方法并具有空间索引,则MbrContains
将利用该索引来快速search。
最后,上面的查询select矩形内的所有点。 要缩小到只有圆圈中的点,并按照近似值sorting,请按如下方式包装查询:
SELECT id, coordinates, name FROM ( /* the query above, paste it in here */ ) AS d WHERE d.distance <= dr ORDER BY d.distance ASC
这假定表中的坐标作为POINT()数据types存储在标记为“点”的列中。 函数X(point)和Y(point)分别从点值中提取纬度和经度值。
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
如果你想以英里来做,用3959replace常量6373
对于那些想要减less查询语法的用户,下面是一个用户定义的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
UPDATE
使用ST_Distance_Sphere()使用lat / long来计算距离
因为缓冲区是一个多边形,MySQL <5.6中的多边形操作被实现为“最小边界矩形”(MBR),所以在MySQL <5.6中缓冲区并不会有多大帮助。
自MySQL 5.6以来, 完整的非MBR st_*
操作得以实现 。 但是对于你来说,最好的解决scheme就是使用无证函数st_distance
:
select * from waypoints where st_distance(point(@center_lon, @center_lat), coordinates) <= radius;
很难find,因为它没有logging:-)但是在这个博客上提到,他的作者也填写了所提到的bugreport 。 有一些警告(援引博客):
坏消息是:
1)所有function仍然只使用平面系统坐标。 不支持不同的SRID。
2)空间索引(RTREE)仅支持MyISAM表格。 可以使用InnoDB表的函数,但不会使用空间键。
点1)表示距离的单位将与坐标单位相同(在WGS84的情况下为度数)。 如果需要以米为单位的距离,则必须使用具有对应于米的单位的预计协调系统(例如UTM或类似的)。
所以,如果你不想要这些警告,或者如果MySQL <5.6,你将不得不编写自己的自定义距离函数。
希望我的版本帮助
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
来自: 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;
(记住要replace所有的常量,这个例子是以公里为单位的)
您可以使用:
SELECT name, lat, lng FROM vw_mytable WHERE ST_Contains(ST_Buffer( ST_GeomFromText('POINT(12.3456 34.5678)'), (0.00001*1000)) , mypoint) = 1
上面里面的expression式:0.00001 * 1000给你一个直径1000米的圆,它应用在一个视图上,名称列只是一个标签,点mypoint是我的点列的名称,lat是在里面计算的用ST_X(mytable.mypoint)查看,用ST_Y(mytable.mypoint)查看lng,他们只是显示lat和lng的文字值。 它会给你所有属于圆的坐标。