Определите, находится ли ТОЧКА между двумя другими ТОЧКАМИ НА СТРОКЕ (география SQL Server 2008) - PullRequest
1 голос
/ 28 января 2010

В моей базе данных есть тип данных SQL Server 2008 GEOGRAPHY, содержащий LINESTRING. LINESTRING описывает потенциально извилистую дорогу.

У меня есть еще одна таблица, которая содержит начальную и конечную точки, обе GEOGRAPHY POINT на дороге. Мне нужно знать, падает ли третья точка между этими двумя точками на дороге (LINESTRING).

В настоящее время я проверяю, что:

  • третья точка на линии
  • расстояние между новой точкой до начальной точки и расстояние между новой точкой и конечной точкой меньше расстояния между начальной точкой и конечной точкой

Это работает, но кажется действительно не элегантным вообще не работает, если дорога разворачивается сама собой! Есть ли способ, который работает?

Ответы [ 2 ]

3 голосов
/ 28 января 2010

Как вы заметили, ваш метод потерпит неудачу в следующем случае, где S - начальная точка, E - конечная точка, а X - точка, которую вы тестируете:

Определить, находится ли ТОЧКА между двумя другими ТОЧКАМИ НА СТРОКЕ http://img10.imageshack.us/img10/4937/gmap.png

Используя этот метод, точка 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_point и @end_end - индексы двух точек на * 1027. * (первый индекс - 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 голосов
/ 28 января 2010

Вы должны быть в состоянии проверить расстояние (STDistance) между новой точкой и подмножеством отрезка линии между начальной и конечной точками.Это расстояние должно оцениваться до нуля.Если у меня будет возможность углубиться в типы географических данных, я постараюсь составить точный запрос, но, надеюсь, это поможет вам начать работу.

...