Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Di chuyển một điểm dọc theo một đường dẫn trong SQL Server 2008

Điều này là một chút khó khăn, nhưng nó chắc chắn là có thể.

Hãy bắt đầu bằng cách tính toán vòng bi từ điểm này đến điểm khác. Với một điểm bắt đầu, một trục và một khoảng cách, hàm sau sẽ trả về điểm đích:

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

Tôi hiểu rằng bạn yêu cầu một hàm lấy chuỗi dòng làm đầu vào, không chỉ điểm bắt đầu và điểm kết thúc. Điểm phải di chuyển dọc theo một đường gồm các đoạn thẳng nối và phải tiếp tục di chuyển xung quanh các "góc" của đường dẫn. Điều này thoạt nghe có vẻ phức tạp, nhưng tôi nghĩ có thể giải quyết nó như sau:

  1. Lặp lại từng điểm trong chuỗi dòng của bạn với STPointN() , từ x =1 đến x = STNumPoints() .
  2. Tìm khoảng cách bằng STDistance() giữa điểm hiện tại trong lần lặp đến điểm tiếp theo:@linestring.STPointN(x).STDistance(@linestring.STPointN(x+1))
  3. Nếu khoảng cách trên> khoảng cách đầu vào của bạn 'n':

    ... thì điểm đích nằm giữa điểm này và điểm tiếp theo. Chỉ cần áp dụng func_MoveTowardsPoint đi qua điểm x làm điểm bắt đầu, điểm x + 1 làm điểm cuối và khoảng cách n. Trả về kết quả và ngắt lặp lại.

    Khác:

    ... điểm đích nằm xa hơn trong đường dẫn từ điểm tiếp theo trong lần lặp. Trừ khoảng cách giữa điểm x và điểm x + 1 với khoảng cách 'n' của bạn. Tiếp tục lặp lại với khoảng cách đã sửa đổi.

Bạn có thể nhận thấy rằng chúng ta có thể dễ dàng thực hiện các điều trên một cách đệ quy, thay vì lặp đi lặp lại.

Hãy làm điều đó:

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

Với điều đó, đã đến lúc thực hiện một số bài kiểm tra. Hãy sử dụng chuỗi dòng ban đầu được cung cấp trong câu hỏi và chúng tôi sẽ yêu cầu các điểm đích ở độ cao 350m, 3500m và 7000m:

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();

Thử nghiệm của chúng tôi trả về các kết quả sau:

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

Lưu ý rằng khoảng cách cuối cùng mà chúng tôi yêu cầu (7000m) đã vượt quá độ dài của chuỗi dòng, vì vậy chúng tôi đã được trả về điểm cuối cùng. Trong trường hợp này, bạn có thể dễ dàng sửa đổi hàm để trả về NULL, nếu muốn.



  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:GROUP BY hai cấp với đầu ra XML

  2. Hợp nhất 2 cơ sở dữ liệu SQL Server

  3. Lợi ích của việc sử dụng cú pháp Khối mã hàng trong câu lệnh chèn T-Sql là gì?

  4. Sự cố với chức năng cửa sổ trong SQL Server 2008 R2

  5. Cách tạo truy vấn SQL động bên trong CURSOR