PostGIS'te bir satırın tahmin edilmesi


19

Satır üzerinde bir nokta bulmak için bir çizgi segmentinden tahmin etmeye çalışıyorum ama yolun 3. geri, yani nokta new, aşağıda verilen puan Ave Başağıda bulmaya çalışıyorum :

resim açıklamasını buraya girin

Bir çizgi verildiğinde, boyunca herhangi bir belirli bir yüzde bir pozisyon almak için enterpolasyon yapabilirim:

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

Ters yönde çizgi boyunca bir nokta bulmak için negatif bir sayı girmeye çalıştım, ancak enterpolasyon argümanı aralıkta olması gerektiğinden başarısız oluyor [0, 1]

İlk önce çizgiyi ölçeklendirmeyi düşündüm, ama bu çizginin merkezini başlangıç ​​noktası olarak kullanmıyor, bu yüzden amacım için işe yaramaz.

Yanıtlar:


21

Daha önce benzer bir sorunu çözdüğüm başka bir yol, aşağıdaki adımlara ayırmaktır.

-- 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: yeni uzunluk atamasını 1/3 yerine 1 1/3 uzunluğunda olacak şekilde sabitledi ve A & B'yi şema eşleşecek şekilde değiştirdi.


Bir kazananımız var! Çok daha anlaşılır.
EoghanM

Bu çok havalı
Nathan W

Teşekkürler. Başlangıçta el ile kontur çizgileri arasında enterpolasyon yaptığım bazı çalışmalardan bu pasajı vardı - konturlarla ne yapmaya çalıştığım zaman kaybı oldu, ama bu snippet başka birine yardımcı oldu sevindim! :)
Jayden

6

Şununla çözdüm:

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

Açıklama:

(2-d) Başlangıç ​​noktasını, ölçekleme için başlangıç ​​noktası olarak çizgi parçasının orta noktasını alarak 1.3333 faktörü ile ölçeklendirin.

Grafik kağıdını çıkarın!

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


2

Bunun için bir fonksiyon yazdım:

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;

Kullanımı:

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

Bunun daha uzun linestleme sağladığını ancak bitiş noktasını vermediğini unutmayın.

GitHub Gist'teki kod (burada oy açarsanız, orada da bir yıldız takdir ediyorum)

Parametrelerin açıklaması (sql fonksiyonunun yorumunda kaçırmış olmanız durumunda):

  • İlk segment uzunluğu original_length * head_rate + head_constant olacaktır.
  • İki katına çıkmasını istiyorsanız, kafa hızı 2, sabit 0'dır.
  • Macaristan'da normalde sayaç tabanlı EOV projeksiyonunu kullanıyoruz. Eğer satırın sonuna 2 metre eklemek istersem, kuyruk: oran 1 ve tail_constant 2 olarak ayarlayın.

Bu çok iyi çalışıyor. Head_rate, head_constant, tail_rate ve tail_constant hakkında bilgi ekleyebilir misiniz? Burada veya GitHub'ınızda açıklanmamıştır. Bitiş noktasından sonra çizgi uzatma için kafa hızı = ölçek faktörü ve başlangıç ​​noktasından önce çizgi uzatma için ölçek faktörü = ölçek faktörü olduğunu varsayıyorum. Sabitler nasıl çalışır? Ne etkileri var?
jbalk

Bu yorumda. İlk segment uzunluğu olacak original_length * head_rate + head_constant. İki katına çıkmasını istiyorsanız, kafa hızı 2, sabit 0'dır. Macaristan'da normalde metre tabanlı EOV projeksiyonunu kullanırız. Eğer çizginin sonuna 2 metre eklemek istersem, kuyruğu ayarladım: hızı 1'e ve tail_constant'ı 2'ye
ayarlıyorum

Teşekkür ederim! Ve bu işlevi paylaştığınız için teşekkür ederiz. Mükemmel çalışır ve hızlı çalışır.
jbalk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.