Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Преместване на точка по път в SQL Server 2008

Това е малко сложно, но със сигурност е възможно.

Нека започнем с изчисляване на лагера от една точка до друга. Като се има предвид начална точка, азимут и разстояние, следната функция ще върне крайната точка:

CREATE FUNCTION [dbo].[func_MoveTowardsPoint](@start_point geography,
                                              @end_point   geography,  
                                              @distance    int)  /* Meters */   
RETURNS geography
AS
BEGIN
    DECLARE @ang_dist float = @distance / 6371000.0;  /* Earth's radius */
    DECLARE @bearing  decimal(18,15);
    DECLARE @lat_1    decimal(18,15) = Radians(@start_point.Lat);
    DECLARE @lon_1    decimal(18,15) = Radians(@start_point.Long);
    DECLARE @lat_2    decimal(18,15) = Radians(@end_point.Lat);
    DECLARE @lon_diff decimal(18,15) = Radians(@end_point.Long - @start_point.Long);
    DECLARE @new_lat  decimal(18,15);
    DECLARE @new_lon  decimal(18,15);
    DECLARE @result   geography;

    /* First calculate the bearing */

    SET @bearing = ATN2(sin(@lon_diff) * cos(@lat_2),
                        (cos(@lat_1) * sin(@lat_2)) - 
                        (sin(@lat_1) * cos(@lat_2) * 
                        cos(@lon_diff)));

    /* Then use the bearing and the start point to find the destination */

    SET @new_lat = asin(sin(@lat_1) * cos(@ang_dist) + 
                        cos(@lat_1) * sin(@ang_dist) * cos(@bearing));

    SET @new_lon = @lon_1 + atn2( sin(@bearing) * sin(@ang_dist) * cos(@lat_1), 
                                  cos(@ang_dist) - sin(@lat_1) * sin(@lat_2));

    /* Convert from Radians to Decimal */

    SET @new_lat = Degrees(@new_lat);
    SET @new_lon = Degrees(@new_lon);

    /* Return the geography result */

    SET @result = 
        geography::STPointFromText('POINT(' + CONVERT(varchar(64), @new_lon) + ' ' + 
                                              CONVERT(varchar(64), @new_lat) + ')', 
                                   4326);

    RETURN @result;
END

Разбирам, че се нуждаете от функция, която приема ред като вход, а не само начална и крайна точка. Точката трябва да се движи по траектория от свързани линейни сегменти и трябва да продължи да се движи около „ъглите“ на траекторията. Това може да изглежда сложно на пръв поглед, но мисля, че може да се справи по следния начин:

  1. Повторете всяка точка от вашия низ с STPointN() , от x=1 до x=STNumPoints() .
  2. Намерете разстоянието с STDistance() между текущата точка в итерацията до следващата точка:@linestring.STPointN(x).STDistance(@linestring.STPointN(x+1))
  3. Ако горното разстояние> вашето въведено разстояние 'n':

    ... тогава крайната точка е между тази точка и следващата. Просто приложете func_MoveTowardsPoint преминаване през точка x като начална точка, точка x+1 като крайна точка и разстояние n. Върнете резултата и прекъснете итерацията.

    Друго:

    ...целевата точка е по-нататък в пътя от следващата точка в итерацията. Извадете разстоянието между точка x и точка x+1 от вашето разстояние 'n'. Продължете през итерацията с промененото разстояние.

Може би сте забелязали, че можем лесно да приложим горното рекурсивно, вместо итеративно.

Да го направим:

CREATE FUNCTION [dbo].[func_MoveAlongPath](@path geography, 
                                           @distance int, 
                                           @index int = 1)   
RETURNS geography
AS
BEGIN
    DECLARE @result       geography = null;
    DECLARE @num_points   int = @path.STNumPoints();
    DECLARE @dist_to_next float;

    IF @index < @num_points
    BEGIN
        /* There is still at least one point further from the point @index
           in the linestring. Find the distance to the next point. */

        SET @dist_to_next = @path.STPointN(@index).STDistance(@path.STPointN(@index + 1));

        IF @distance <= @dist_to_next 
        BEGIN
            /* @dist_to_next is within this point and the next. Return
              the destination point with func_MoveTowardsPoint(). */

            SET @result = [dbo].[func_MoveTowardsPoint](@path.STPointN(@index),
                                                        @path.STPointN(@index + 1),
                                                        @distance);
        END
        ELSE
        BEGIN
            /* The destination is further from the next point. Subtract
               @dist_to_next from @distance and continue recursively. */

            SET @result = [dbo].[func_MoveAlongPath](@path, 
                                                     @distance - @dist_to_next,
                                                     @index + 1);
        END
    END
    ELSE
    BEGIN
        /* There is no further point. Our distance exceeds the length 
           of the linestring. Return the last point of the linestring.
           You may prefer to return NULL instead. */

        SET @result = @path.STPointN(@index);
    END

    RETURN @result;
END

С това на място е време да направите някои тестове. Нека използваме оригиналния низ, който беше предоставен във въпроса, и ще поискаме точките на местоназначение на 350 м, на 3500 м и на 7000 м:

DECLARE @g geography;
SET @g = geography::STGeomFromText('LINESTRING(-122.360 47.656, 
                                               -122.343 47.656, 
                                               -122.310 47.690)', 4326);

SELECT [dbo].[func_MoveAlongPath](@g, 350, DEFAULT).ToString();
SELECT [dbo].[func_MoveAlongPath](@g, 3500, DEFAULT).ToString();
SELECT [dbo].[func_MoveAlongPath](@g, 7000, DEFAULT).ToString();

Нашият тест връща следните резултати:

POINT (-122.3553270591861 47.6560002502638)
POINT (-122.32676470116748 47.672728464582583)
POINT (-122.31 47.69)

Имайте предвид, че последното разстояние, което поискахме (7000 м), надвишава дължината на линията, така че ни беше върната последната точка. В този случай можете лесно да промените функцията, за да върне NULL, ако предпочитате.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да разположа SQL Server Compact Edition 4.0?

  2. SQL заявка за 7-дневна подвижна средна стойност в SQL Server

  3. еквивалент на mysqldump за SQL Server

  4. Направете while цикъл в SQL Server 2008

  5. Как да видя параметрите на текущо изпълняваните процедури в SQL Server 2008