Extrapolation einer Zeile in PostGIS

19

Ich versuche aus einem Liniensegment zu extrapolieren, um einen Punkt auf der Linie zu finden, aber ein Drittel des Rückwegs, dh, um einen Punkt zu finden new, gegebene Punkte Aund Bdarunter:

Bildbeschreibung hier eingeben

Bei einer gegebenen Linie kann ich sie interpolieren, um eine Position mit einem bestimmten Prozentsatz zu erhalten:

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

Ich habe versucht, eine negative Zahl einzugeben, um einen Punkt entlang der Linie in die entgegengesetzte Richtung zu finden. Dies schlägt jedoch fehl, da das Interpolationsargument im Bereich [0, 1] liegen muss.

Ich habe darüber nachgedacht, zuerst die Linie zu skalieren, aber dabei wird die Mitte der Linie nicht als Ursprung verwendet. Daher ist dies für meine Zwecke nutzlos.

EoghanM
quelle

Antworten:

21

Eine andere Möglichkeit, ein ähnliches Problem zu lösen, besteht darin, es in die folgenden Schritte aufzuteilen.

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

BEARBEITEN: Die Zuweisung der neuen Länge wurde so korrigiert, dass sie 1 1/3 der Länge und nicht 1/3 beträgt, und A & B wurde entsprechend dem Diagramm umgeschaltet.

Jayden
quelle
Wir haben einen Sieger! Viel verständlicher.
EoghanM,
Das ist ziemlich cool
Nathan W
Vielen Dank. Ich hatte ursprünglich dieses Snippet von einer Arbeit, die ich manuell zwischen Konturlinien interpolierte - es stellte sich heraus, dass es eine Zeitverschwendung war, was ich mit den Konturen versuchte, aber froh, dass dieses Snippet jemand anderem half! :)
Jayden
6

Habe es gelöst mit:

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

Erläuterung:

(2-d) Skalieren Sie den Startpunkt um den Faktor 1,3333, wobei Sie den Mittelpunkt des Liniensegments als Ausgangspunkt für die Skalierung verwenden.

Hol das Millimeterpapier raus!

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

EoghanM
quelle
2

Ich habe dazu eine Funktion geschrieben:

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;

Verwendung:

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

Beachten Sie, dass dies die längere Linienfolge ergibt, jedoch nicht den Endpunkt.

Code auf GitHub Gist (wenn du hier upvotest, würde ich mich auch über einen Stern freuen)

Beschreibung der Parameter (falls Sie sie im Kommentar der SQL-Funktion verpasst haben):

  • Die erste Segmentlänge ist original_length * head_rate + head_constant.
  • Wenn Sie möchten, dass es verdoppelt wird, ist die Kopfrate 2, die Konstante ist 0.
  • In Ungarn wird normalerweise die EOV-Projektion verwendet, die auf Messgeräten basiert. Wenn ich also 2 Meter zum Ende der Linie hinzufügen möchte, setze ich den tail: rate auf 1 und die tail_constant auf 2.
SzieberthAdam
quelle
Das funktioniert sehr gut. Können Sie einige Informationen zu head_rate, head_constant, tail_rate und tail_constant hinzufügen? Sie werden hier oder auf Ihrem GitHub nicht erklärt. Ich gehe davon aus, dass Head Rate = Skalierungsfaktor für die Linienverlängerung nach dem Endpunkt und Tail Rate = Skalierungsfaktor für die Linienverlängerung vor dem Startpunkt. Wie funktionieren die Konstanten? Welchen Effekt haben sie?
Jbalk
Es steht im Kommentar. Die erste Segmentlänge beträgt original_length * head_rate + head_constant. Wenn Sie möchten, dass es sich verdoppelt, ist die Kopffrequenz 2, die Konstante 0. In Ungarn wird normalerweise die EOV-Projektion verwendet, die auf einem Meter basiert. Wenn ich also 2 Meter zum Ende der Linie hinzufügen möchte, setze ich den Tail: rate auf 1 und die tail_constant auf 2.
SzieberthAdam
Vielen Dank! Und danke, dass Sie diese Funktion geteilt haben. Es funktioniert perfekt und es läuft schnell.
Jbalk