确定点是否位于线字符串上的两个其他点之间(SQL Server 2008地理)
我的数据库中有一个SQL Server 2008确定点是否位于线字符串上的两个其他点之间(SQL Server 2008地理),sql,sql-server,sql-server-2008,gis,geography,Sql,Sql Server,Sql Server 2008,Gis,Geography,我的数据库中有一个SQL Server 2008GEOGRAPHY数据类型,其中包含一个LINESTRING。LINESTRING描述了一条可能弯曲的道路 我有另一个表,其中包含道路上的起点和终点,这两个点都是地理位置。我需要知道道路上是否有第三个点落在这两个点之间(LINESTRING) 目前,我正在测试: 第三点是在线上 新点与起点之间的距离以及新点与终点之间的距离都小于起点与终点之间的距离 这是可行的,但如果路自动掉头,看起来真的不雅观根本不起作用!有没有可行的方法?您应该能够检查新点
GEOGRAPHY
数据类型,其中包含一个LINESTRING
。LINESTRING
描述了一条可能弯曲的道路
我有另一个表,其中包含道路上的起点和终点,这两个点都是地理位置。我需要知道道路上是否有第三个点落在这两个点之间(LINESTRING
)
目前,我正在测试:
- 第三点是在线上
- 新点与起点之间的距离以及新点与终点之间的距离都小于起点与终点之间的距离
这是可行的,但如果路自动掉头,看起来真的不雅观根本不起作用!有没有可行的方法?您应该能够检查新点与起点和终点之间的线段子集之间的距离(STDistance)。该距离应计算为零。如果我有机会深入研究地理数据类型,我将尝试组合精确的查询,但希望这能让您开始。如您所述,在以下情况下,您的方法将失败,其中S是起点,E是终点,X是您正在测试的点: 使用该方法,点X将错误地产生在点S和点E之间的结果,因为它通过了算法的测试1和测试2:即点X位于字符串上,并且从X到S和从X到E的距离都小于从S到E的距离
一种可能的解决方案 可以将线串路径“分解”为单独的线段集,每个线段集只有两个点,以便:
LINESTRING(-122.360 47.656, -122.343 47.656, -122.310 47.690, -122.310 47.670)
将被细分为:
LINESTRING(-122.360 47.656, -122.343 47.656)
LINESTRING(-122.343 47.656, -122.310 47.690)
LINESTRING(-122.310 47.690, -122.310 47.670)
然后,您将能够迭代上述每条线段,并使用STIntersects
测试该点是否位于其中一条线段上。当某个点通过此测试时,您将能够确定该点是否在起点和终点内
如果可能,我建议将起点/终点存储为线字符串路径上某个点的索引,而不是原始地理点。首先,这将使解决此问题变得更容易,但除此之外,还可以消除数据的重复,这也保证了不能有不属于线字符串的起点/终点。缺点是,在线段的中间不能有开始/结束点,但是它们必须位于路径的一个拐角上。现在,您必须确定此限制在应用程序中是否可以接受
如果您选择上述表示,我们可以使用以下递归函数解决此问题,其中@path
是表示道路的线字符串,@start\u point
和@end\u end
表示@path
上两个点的索引(第一个索引为1),@test\u point
是将要测试的地理点。测试点可以位于谎言上的任何地方
CREATE FUNCTION [dbo].[func_PointBetween](@path geography,
@start_point int,
@end_point int,
@test_point geography)
RETURNS tinyint
AS
BEGIN
DECLARE @result tinyint = 0;
DECLARE @num_points int = @path.STNumPoints();
DECLARE @line_segment geography;
IF (@start_point < @end_point) AND (@end_point < @num_points)
BEGIN
/* Generate the line segment from the current start point
to the following point (@start_point + 1). */
SET @line_segment = geography::STLineFromText('LINESTRING(' +
CAST(@path.STPointN(@start_point).Long AS varchar(32))+ ' ' +
CAST(@path.STPointN(@start_point).Lat AS varchar(32)) + ',' +
CAST(@path.STPointN(@start_point + 1).Long AS varchar(32))+ ' ' +
CAST(@path.STPointN(@start_point + 1).Lat AS varchar(32)) + ')',
4326);
/* Add a buffer of 25m to @test_point. This is optional, but
recommended, otherwise it will be very difficult to get a
point exactly on the line. The buffer value may be tweaked
as necessary for your application. */
IF @test_point.STBuffer(25).STIntersects(@line_segment) = 1
BEGIN
/* The test point is on one of the line segments between
@start_point and @end_point. Return 1 and stop the
recursion. */
SET @result = 1;
END
ELSE
BEGIN
/* The test point is not between the @start_point and
@start_point + 1. Increment @start_point by 1 and
continue recursively. */
SET @result = [dbo].[func_PointBetween](@path,
@start_point + 1,
@end_point,
@test_point);
END
END
ELSE
BEGIN
/* There are no further points. The test point is not between the
@start_point and @end_point. Return 0 and stop the recursion. */
SET @result = 0;
END
RETURN @result;
END
如果道路自行弯曲,您当前的方法是否有效?例如,如果你在U形转弯的两端各有两个点,并且你检查了一个超过U形转弯的点,那么该点与起点和终点之间的距离可能小于起点和终点之间的距离,但它不会出现在两点之间的道路上。Eep,你是对的!好的,现在我真的需要一个答案。:-)您当然是对的,但我找不到创建线段子集的方法。
DECLARE @road geography;
DECLARE @test_point_a geography;
DECLARE @test_point_b geography;
SET @road = geography::STGeomFromText('LINESTRING(-122.360 47.656,
-122.343 47.656,
-122.310 47.690,
-122.310 47.670,
-122.300 47.670,
-122.290 47.660)',
4326);
/* This point lies between point 3 and point 4 */
SET @test_point_a = geography::STGeomFromText('POINT(-122.310 47.680)', 4326);
/* This point lies outside the path */
SET @test_point_b = geography::STGeomFromText('POINT(-122.310 47.700)', 4326);
/* This returns 1, because the test point is between start and end */
SELECT dbo.func_PointBetween(@road, 2, 5, @test_point_a);
/* This returns 0 because the test point is not between start and end */
SELECT dbo.func_PointBetween(@road, 4, 5, @test_point_a);
/* This returns 0 because the test point lies outside the path */
SELECT dbo.func_PointBetween(@road, 1, 6, @test_point_b);