使用 MySQL 空间扩展从一个点查找 N 个最近的 LineString
Posted
技术标签:
【中文标题】使用 MySQL 空间扩展从一个点查找 N 个最近的 LineString【英文标题】:Find N Nearest LineString From A Point Using MySQL Spatial Extensions 【发布时间】:2012-09-22 22:39:23 【问题描述】:我使用mysql Spatial Extensions 来存储有关道路和酒店的数据。我将酒店数据存储为点,而将道路数据存储为 LineString。表格是这样的
CREATE TABLE IF NOT EXISTS `Hotels` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` text,
`coordinate` point NOT NULL,
PRIMARY KEY (`id`),
SPATIAL KEY `coordinate` (`coordinate`),
)
CREATE TABLE IF NOT EXISTS `Roads` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` text,
`route` linestring NOT NULL,
PRIMARY KEY (`id`),
SPATIAL KEY `coordinate` (`route`),
)
实例的可视化是这样的。
我的问题是给定一个数字 N 和一个点 P,从点 P 找到 N 条最近的道路的 SQL 查询是什么?该距离由道路中的一段与上图所示点之间的最小垂直距离定义。 (虽然在现实中,最近的距离应该是高速公路大门和酒店之间,但在这种情况下,我们可以从任何一点进入高速公路:P)
如果这个问题没有单一的 SQL 语句解决方案,我可以接受中间 SQL 查询和后处理。但是什么是高效的 SQL 查询以及如何对数据进行后处理?
【问题讨论】:
你得到答案了吗? :) 请确保您使用的是 MySQL 5.5,否则空间功能的实现不足以回答您的问题 【参考方案1】:您可以在数据库中创建两个函数:
-
距离:这将为您提供两点之间的距离
DistanceFromLine :此处距离将从直线中的每个点计算,并为您提供最短距离。
比较你的点和线之间的距离,选择最短的。
这里是距离函数
delimiter //
CREATE FUNCTION distance (latA double, lonA double, latB double, LonB double)
RETURNS double DETERMINISTIC
BEGIN
SET @RlatA = radians(latA);
SET @RlonA = radians(lonA);
SET @RlatB = radians(latB);
SET @RlonB = radians(LonB);
SET @deltaLat = @RlatA - @RlatB;
SET @deltaLon = @RlonA - @RlonB;
SET @d = SIN(@deltaLat/2) * SIN(@deltaLat/2) +
COS(@RlatA) * COS(@RlatB) * SIN(@deltaLon/2)*SIN(@deltaLon/2);
RETURN 2 * ASIN(SQRT(@d)) * 637101;
END//
这是DistanceFromLine函数:
DROP function IF EXISTS `DistanceFromLine`;
delimiter //
CREATE FUNCTION `DistanceFromLine`(
route LINESTRING, point1 POINT
) RETURNS INT DETERMINISTIC
BEGIN
DECLARE a INT Default 0 ;
DECLARE minDistance INT Default 0;
DECLARE currentDistance INT Default 0;
DECLARE currentpoint point ;
DECLARE size INT Default 0 ;
SET size = NumPoints(route);
simple_loop: LOOP
SET a = a+1;
SET currentpoint = PointN(route,a);
SET currentDistance = Distance(X(point1), Y(point1),
X(currentpoint),Y(currentpoint));
IF a = 1 THEN
SET minDistance = currentDistance;
END IF;
IF currentDistance < minDistance THEN
SET minDistance = currentDistance;
END IF;
IF a=size THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
RETURN (minDistance);
END//
【讨论】:
【参考方案2】:这对我来说是一个非常有用的答案,但我使用的是 MySQL 5.7.18,它具有更高级或只是不同的地理查询功能。不再需要发布的距离函数 - 使用 ST_Distance_Sphere。所以这里是相同代码的更新,以使 DistanceFromLine 与现代 (5.7.6+) MySQL 兼容...
DROP function IF EXISTS `DistanceFromLine`;
delimiter //
CREATE FUNCTION `DistanceFromLine`(
route LINESTRING, point1 POINT
) RETURNS INT DETERMINISTIC
BEGIN
DECLARE a INT Default 0 ;
DECLARE minDistance INT Default 0;
DECLARE currentDistance INT Default 0;
DECLARE currentpoint point ;
DECLARE size INT Default 0 ;
SET size = ST_NumPoints(route);
simple_loop: LOOP
SET a = a+1;
SET currentpoint = ST_PointN(route,a);
SET currentDistance = ST_Distance_Sphere(point1,currentpoint);
IF a = 1 THEN
SET minDistance = currentDistance;
END IF;
IF currentDistance < minDistance THEN
SET minDistance = currentDistance;
END IF;
IF a=size THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
RETURN (minDistance);
END//
【讨论】:
【参考方案3】:我也一直在研究这个问题,但不幸的是,为酒店寻找最近的道路是一个不利的解决方案。我发现道路所在的入口是确定的答案。换句话说,地址。 这意味着有一个地址表和匹配点到最近的地址道路。
【讨论】:
以上是关于使用 MySQL 空间扩展从一个点查找 N 个最近的 LineString的主要内容,如果未能解决你的问题,请参考以下文章