正如你已经注意到,你的方法将在以下情况下,其中S为起点失败,E是终点,而X是要测试的点:
Determine if a POINT is between two other POINTs on a LINESTRING http://img10.imageshack.us/img10/4937/gmap.png
使用该方法中,点X将错误地导致位于点S和点E之间,因为它通过了算法的测试1和测试2:即。点X是对线串,和距离从X到S和从X到E都是比从S到E.
可能的解决方案的距离较小
可以“爆炸“你的线串路径成单独的只有两点segmenets每个,使:
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_point
和@end_end
代表在@path
的两点指数(第一指数为1) ,@test_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
为了测试上述函数,我定义了上图中显示的6点线串。然后我们将定义两个测试点:@test_point_a
(恰好位于第三点和第四点之间)和@test_point_b
(位于路径之外)。
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);
如果道路弯曲回来,您目前的方法是否奏效?例如,如果在掉头的每一端有两个点,并且检查经过U的点,则该点与起点和终点之间的距离可能小于起点和终点之间的距离,但它并没有出现在两点之间的道路上。 – 2010-01-28 01:20:00
Eep,你是对的!好的,现在我真的需要一个答案。 :-) – 2010-01-28 01:33:13