Estrapolare una linea in PostGIS


19

Sto cercando di estrapolare da un segmento di linea per trovare un punto sulla linea ma un terzo della via "indietro", ovvero cercare di trovare un punto new, dati punti Ae Bsotto:

inserisci qui la descrizione dell'immagine

Data una linea, posso interpolarla per ottenere una posizione in qualsiasi percentuale particolare lungo di essa:

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

Ho provato a inserire un numero negativo per trovare un punto lungo la linea nella direzione opposta, ma ciò non riesce poiché l'argomento di interpolazione deve essere compreso nell'intervallo [0, 1]

Ho pensato di ridimensionare prima la linea, ma che non utilizza il centro della linea come origine, quindi è inutile per i miei scopi.

Risposte:


21

Un altro modo in cui ho risolto un problema simile in precedenza è quello di suddividerlo nei seguenti passaggi.

-- 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: risolto l'assegnazione di newlength in modo che sia 1 1/3 della lunghezza, anziché 1/3 e commutato A e B in modo che corrispondessero al diagramma.


Abbiamo un vincitore! Molto più comprensibile.
EoghanM,

Questo è abbastanza bello
Nathan W

Grazie. Inizialmente avevo questo frammento da un lavoro che stavo facendo interpolando manualmente tra le curve di livello - ho scoperto che era una perdita di tempo quello che stavo cercando di fare con i contorni, ma sono contento che questo frammento abbia aiutato qualcun altro! :)
Jayden,

6

L'ho risolto con:

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

Spiegazione:

(2-d) Ridimensiona il punto iniziale di un fattore di 1,3333, prendendo il punto medio del segmento di linea come origine per il ridimensionamento.

Tira fuori la carta millimetrata!

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


2

Ho scritto una funzione per questo:

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;

Uso:

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

Si noti che ciò produce la stringa di linea più lunga ma non l'endpoint.

Codice su GitHub Gist (se voti qui apprezzerei anche una stella lì)

Descrizione dei parametri (nel caso in cui tu li abbia persi nel commento della funzione sql):

  • La lunghezza del primo segmento sarà original_length * head_rate + head_constant.
  • Se vuoi che sia raddoppiato, la frequenza di testa è 2, la costante è 0.
  • In Ungheria normalmente usiamo la proiezione EOV che è basata su un metro. Quindi, se voglio aggiungere 2 metri alla fine della linea, imposto tail: rate su 1 e tail_constant su 2.

Funziona molto bene Puoi aggiungere alcune informazioni su head_rate, head_constant, tail_rate e tail_constant? Non sono spiegati qui o sul tuo GitHub. Sto assumendo che la frequenza di testa = fattore di scala per l'estensione della linea dopo il punto finale e la velocità di coda = fattore di scala per l'estensione della linea prima del punto iniziale. Come funzionano le costanti? Che effetto hanno?
jbalk

È nel commento. La lunghezza del primo segmento sarà original_length * head_rate + head_constant. Se vuoi che sia raddoppiato, la frequenza di testa è 2, la costante è 0. In Ungheria normalmente usiamo la proiezione EOV che si basa sul metro. Quindi, se voglio aggiungere 2 metri alla fine della linea, ho impostato la coda: rate su 1 e tail_constant su 2.
SzieberthAdam

Grazie! E grazie per aver condiviso questa funzione. Funziona perfettamente e funziona rapidamente.
jbalk,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.