2010-01-27 91 views
1

我的数据库中包含一个SQL Server 2008 GEOGRAPHY数据类型,其中包含LINESTRINGLINESTRING描述了一条可能弯曲的道路。确定POINT是否在LINESTRING上的两个其他POINT之间(SQL Server 2008地理)

我有一个包含起点和终点另一个表,既GEOGRAPHYPOINT的,在路上。我需要知道路上的这两点之间是否有第三个点(LINESTRING)。

目前,我测试的是:

  • 第三点是上线
  • 新点到起点和新的点和终点都之间的距离之间的距离均小于起点和终点

作品之间的距离,但它似乎真的很糟糕 不会对自身的所有话路掉头工作!有没有一种方法可行?

+2

如果道路弯曲回来,您目前的方法是否奏效?例如,如果在掉头的每一端有两个点,并且检查经过U的点,则该点与起点和终点之间的距离可能小于起点和终点之间的距离,但它并没有出现在两点之间的道路上。 – 2010-01-28 01:20:00

+0

Eep,你是对的!好的,现在我真的需要一个答案。 :-) – 2010-01-28 01:33:13

回答

3

正如你已经注意到,你的方法将在以下情况下,其中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); 
0

您应该能够检查新点与起点和终点之间的线段子集之间的距离(STDistance)。该距离应该评估为零。如果我有机会深入研究地理数据类型,我会尝试将精确查询放在一起,但希望这可以让您开始。

+0

你当然是对的,但我找不到创建该线段的子集的方法。 – 2010-01-28 01:44:55