Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Memindahkan Titik di sepanjang Jalur di SQL Server 2008

Ini sedikit rumit, tetapi tentu saja mungkin.

Mari kita mulai dengan menghitung bantalan dari satu titik ke titik lainnya. Diberikan titik awal, bantalan, dan jarak, fungsi berikut akan mengembalikan titik tujuan:

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

Saya mengerti bahwa Anda memerlukan fungsi yang mengambil linestring sebagai input, bukan hanya titik awal dan akhir. Titik tersebut harus bergerak di sepanjang jalur segmen garis yang digabungkan, dan harus terus bergerak di sekitar "sudut" jalur tersebut. Ini mungkin tampak rumit pada awalnya, tetapi saya pikir ini dapat diatasi sebagai berikut:

  1. Ulangi setiap titik linestring Anda dengan STPointN() , dari x=1 hingga x=STNumPoints() .
  2. Temukan jarak dengan STDistance() antara titik saat ini dalam iterasi ke titik berikutnya:@linestring.STPointN(x).STDistance(@linestring.STPointN(x+1))
  3. Jika jarak di atas> jarak input Anda 'n':

    ...maka titik tujuan berada di antara titik ini dan titik berikutnya. Cukup terapkan func_MoveTowardsPoint melewati titik x sebagai titik awal, titik x+1 sebagai titik akhir, dan jarak n. Kembalikan hasilnya dan hentikan iterasi.

    Lainnya:

    ... titik tujuan lebih jauh di jalur dari titik berikutnya dalam iterasi. Kurangi jarak antara titik x dan titik x+1 dari jarak Anda 'n'. Lanjutkan melalui iterasi dengan jarak yang dimodifikasi.

Anda mungkin telah memperhatikan bahwa kami dapat dengan mudah mengimplementasikan hal di atas secara rekursif, bukan secara iteratif.

Ayo lakukan:

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

Dengan itu, saatnya untuk melakukan beberapa tes. Mari kita gunakan linestring asli yang disediakan dalam pertanyaan, dan kami akan meminta titik tujuan di 350m, di 3500m, dan di 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();

Pengujian kami mengembalikan hasil berikut:

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

Perhatikan bahwa jarak terakhir yang kami minta (7000m) melebihi panjang tali garis, jadi kami mengembalikan poin terakhir. Dalam hal ini, Anda dapat dengan mudah memodifikasi fungsi untuk mengembalikan NULL, jika Anda mau.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tidak dapat memulai transaksi terdistribusi

  2. SQL:Bagaimana cara melacak baris yang sudah cocok dalam subquery yang berkorelasi?

  3. Apa cara paling sederhana untuk mengakses mssql dengan python atau ironpython?

  4. Ikhtisar Pernyataan PRINT di SQL Server

  5. Ubah Sql-Server (express) dari instance bernama ke localhost?