Mengekstrapolasi sebuah baris dalam PostGIS

19

Saya mencoba mengekstrapolasi dari segmen garis untuk menemukan titik pada baris tetapi ke-3 dari jalan 'kembali', yaitu mencoba menemukan titik new, memberikan poin Adan di Bbawah:

masukkan deskripsi gambar di sini

Diberi garis, saya dapat menginterpolasi untuk mendapatkan posisi pada persentase tertentu di dalamnya:

=# select st_line_interpolate_point(
   st_makeline('0101000020E6100000300DC347C49418C03EE8D9ACFAA44A40', 
               '0101000020E6100000FB743C66A03218C0CDCCCCCCCC7C4A40'), 
   0.333);
0101000020E6100000ED45B41D537718C069C6A2E9EC984A40

Saya mencoba memasukkan angka negatif untuk menemukan titik di sepanjang garis dalam arah yang berlawanan, tetapi itu gagal karena argumen interpolasi harus dalam kisaran [0, 1]

Saya berpikir tentang pertama-tama menskalakan garis, tetapi itu tidak menggunakan pusat garis sebagai asal, jadi tidak ada gunanya untuk tujuan saya.

Eoghan
sumber

Jawaban:

21

Cara lain saya telah memecahkan masalah serupa sebelumnya adalah memecahnya menjadi langkah-langkah berikut.

-- get the points A and B given a line L
A := ST_STARTPOINT(L);
B := ST_ENDPOINT(L);

-- get the bearing from point B --> A
azimuth := ST_AZIMUTH(B,A);

-- get the length of the line A --> B
length := ST_DISTANCE(A,B);
newlength := length + (length * (1/3));   -- increase the line length by 1/3

-- create a new point 1/3 as far away from A as B is from A
newpoint := ST_TRANSLATE(A, sin(azimuth) * newlength, cos(azimuth) * newlength);

EDIT: memperbaiki tugas panjang baru sehingga panjangnya 1 1/3, bukan 1/3 dan beralih A & B untuk mencocokkan diagram.

Jayden
sumber
Kami punya pemenang! Jauh lebih mudah dipahami.
EoghanM
Ini cukup keren
Nathan W
Terima kasih. Saya awalnya memiliki potongan ini dari beberapa pekerjaan yang saya lakukan secara manual interpolasi antara garis kontur - ternyata itu adalah buang-buang waktu apa yang saya coba lakukan dengan kontur, tetapi senang potongan ini membantu orang lain keluar! :)
Jayden
6

Telah menyelesaikannya dengan:

F = 1.3333
st_affine(A, F, 0, 
             0, F, 
            (F-1)*-st_x(st_line_interpolate_point(st_makeline(A, B), 0.5)), 
            (F-1)*-st_y(st_line_interpolate_point(st_makeline(A, B), 0.5))
          )

Penjelasan:

(2-d) Skala titik awal dengan faktor 1,3333, dengan mengambil titik tengah segmen garis sebagai asal untuk penskalaan.

Keluar kertas grafik!

http://en.wikipedia.org/wiki/Affine_transformation

Eoghan
sumber
2

Saya menulis fungsi untuk ini:

CREATE OR REPLACE FUNCTION st_extend (
    geom geometry,
    head_rate double precision,
    head_constant double precision,
    tail_rate double precision,
    tail_constant double precision)
  RETURNS geometry AS
$BODY$
-- Extends a linestring.
-- First segment get extended by length * head_rate + head_constant.
-- Last segment get extended by length * tail_rate + tail_constant.
--
-- References:
-- http://blog.cleverelephant.ca/2015/02/breaking-linestring-into-segments.html
-- /gis//a/104451/44921
-- /gis//a/16701/44921
WITH segment_parts AS (
SELECT
(pt).path[1]-1 as segment_num
,
CASE
WHEN
  (nth_value((pt).path, 2) OVER ()) = (pt).path
AND
  (last_value((pt).path) OVER ()) = (pt).path
THEN
  3
WHEN
  (nth_value((pt).path, 2) OVER ()) = (pt).path
THEN
  1
WHEN
  (last_value((pt).path) OVER ()) = (pt).path
THEN
  2
ELSE
  0
END AS segment_flag
,
(pt).geom AS a
,
lag((pt).geom, 1, NULL) OVER () AS b
FROM ST_DumpPoints($1) pt
)
,
extended_segment_parts
AS
(
SELECT
  *
  ,
  ST_Azimuth(a,b) AS az1
  ,
  ST_Azimuth(b,a) AS az2
  ,
  ST_Distance(a,b) AS len
FROM
segment_parts
where b IS NOT NULL
)
,
expanded_segment_parts
AS
(
SELECT
  segment_num
  ,
  CASE
  WHEN
    bool(segment_flag & 2)
  THEN
    ST_Translate(b, sin(az2) * (len*tail_rate+tail_constant), cos(az2) * (len*tail_rate+tail_constant))
  ELSE
    a
  END
  AS a
  ,
  CASE
  WHEN
    bool(segment_flag & 1)
  THEN
    ST_Translate(a, sin(az1) * (len*head_rate+head_constant), cos(az1) * (len*head_rate+head_constant))
  ELSE
    b
  END
  AS b
FROM extended_segment_parts
)
,
expanded_segment_lines
AS
(
SELECT
  segment_num
  ,
  ST_MakeLine(a, b) as geom
FROM
expanded_segment_parts
)
SELECT
  ST_LineMerge(ST_Collect(geom ORDER BY segment_num)) AS geom
FROM expanded_segment_lines
;
$BODY$
LANGUAGE sql;

Pemakaian:

SELECT st_extend(
st_makeline(
  '0101000020E6100000300DC347C49418C03EE8D9ACFAA44A40', 
  '0101000020E6100000FB743C66A03218C0CDCCCCCCCC7C4A40'
),
1.333::double precision,
0::double precision,
1::double precision,
0::double precision
);

Perhatikan bahwa ini menghasilkan linestring yang lebih panjang tetapi bukan titik akhir.

Kode pada GitHub Gist (jika Anda upvote di sini saya akan menghargai bintang di sana juga)

Deskripsi parameter (jika Anda melewatkannya di komentar fungsi sql):

  • Panjang segmen pertama adalah original_length * head_rate + head_constant.
  • Jika Anda ingin menjadi dua kali lipat maka laju head adalah 2, konstanta adalah 0.
  • Kami di hungary biasanya menggunakan proyeksi EOV yang berbasis meter. Jadi jika saya ingin menambahkan 2 meter ke akhir baris, saya mengatur tail: rate ke 1 dan tail_constant ke 2.
SzieberthAdam
sumber
Ini bekerja dengan sangat baik. Bisakah Anda menambahkan beberapa info tentang head_rate, head_constant, tail_rate, dan tail_constant? Mereka tidak dijelaskan di sini atau di GitHub Anda. Saya mengasumsikan bahwa tingkat kepala = faktor skala untuk ekstensi garis setelah titik akhir dan tingkat ekor = faktor skala untuk ekstensi garis sebelum titik awal. Bagaimana cara kerja konstanta? Efek apa yang mereka miliki?
jbalk
Ada dalam komentar. Panjang segmen pertama adalah original_length * head_rate + head_constant. Jika Anda ingin menjadi dua kali lipat maka laju head adalah 2, konstanta adalah 0. Kami di hungary biasanya menggunakan proyeksi EOV yang berbasis meter. Jadi jika saya ingin menambahkan 2 meter ke akhir baris, saya mengatur ekor: menilai ke 1 dan tail_constant ke 2.
SzieberthAdam
Terima kasih! Dan terima kasih telah berbagi fungsi ini. Ini bekerja dengan sempurna dan bekerja dengan cepat.
jbalk