곡선 점대 점 "라우트 맵"


39

최근에 특정 도시에서 출발하는 노선을 표시하는 항공사 웹 페이지를보고 있습니다. 점 사이에 유사한 곡선 경로를 만들 수 있기를 원합니다. 이 예제에 표시된 것과 같은 곡선 원호를 생성하는 스크립트 또는 함수를 만든 사람이 있습니까?

비행 경로

PostGIS에는 두 점을 연결할 때 사용할 곡선의 양을 지정할 수있는 ST_MakeLine의 구현이 있습니까?

현재 PostGIS와 QGIS를 사용하고 있지만 동일한 모양을 만들 수있는 다른 소프트웨어 옵션에 대한 의견을 환영합니다.


아무도 이것의 좋은 구현을 알고 있습니까? 예 또는 무엇입니까?
— Mark Boulder

답변:


26

큰 원을 만들면 원하는 효과를 얻을 수 있습니다.

아마 http://lists.osgeo.org/pipermail/postgis-users/2008-February/018620.html에서 논의 된 것과 같은 것

최신 정보:

나는 "Global Connections 시각화" 에서이 아이디어를 따랐다 . 재 투영을 사용하여 호를 생성하는 순수한 PostGIS 기반 솔루션입니다.

SELECT ST_Transform(
  ST_Segmentize(
    ST_MakeLine(
      ST_Transform(a.the_geom, 953027),
      ST_Transform(b.the_geom, 953027)
    ), 
  100000), 
4326)

(953,027에 대한 CRS의 정의는 여기에서 찾을 수 있습니다 : http://spatialreference.org/ref/esri/53027/ )

여기에 이미지 설명을 입력하십시오


4
큰 원이 있지만 문제는 짧은 거리에서 여전히 직선으로 끝나는 것입니다. 선에 넣은 호의 양을 제어 할 수 있기를 원합니다 (즉, arclength = distance * 2).
— RyanDalton

1
다음은 단순히 훌륭한 원을 사용하는 문제의 좋은 예입니다. gc.kls2.com/cgi-bin/…
— RyanDalton

1
몇 가지 추가 연구를 한 후이 게시물을 통해이 방법을 지원하는 데 도움이 될 수 있습니다. mail-archive.com/postgis-users@postgis.refractions.net/…
— RyanDalton

장래의 독자의 사용을 위해, 나는이 주제를 다루는 @underdark의 최근 블로그 게시물에 링크하려고 생각했습니다. underdark.wordpress.com/2011/08/20/...
— RyanDalton

대단해 !! 프로젝트에서 사용자 체크인과 장소 위치 사이에 선을 그리는 데 사용, Forsquare에서 픽업
— Lorenzo Barbagli

24

문제는 시각적 해상도를 향상시키기 위해 호를 얼마나 많이 구부릴지를 알아내는 것입니다.

여기에 하나의 솔루션이 있습니다 (가능한 많은 것 중에서). 공통 원점에서 나오는 모든 호를 고려해 봅시다. 호는 가장 혼잡합니다. 그것들을 최상으로 분리하기 위해, 그것들이 같은 간격의 각도로 퍼지도록 배열합시다 . 출발지에서 목적지까지 직선 선분을 그리면 문제가됩니다. 일반적으로 여러 방향으로 목적지 클러스터가 있기 때문입니다. 이탈 각도를 가능한 한 균일하게 배치하기 위해 자유 곡선을 사용하여 호를 구부려 봅시다.

간단하게하기 위해지도에 원호를 사용하겠습니다. 점 y 에서 점 x 까지의 호에서 "굽힘"의 자연스러운 척도는 y 에서의 베어링과 y 에서 x 까지의 베어링 간 차이 입니다. 이러한 호는 y 와 x가 모두 있는 원의 섹터입니다 . 기본 형상은 굽힘 각도 가 원호에 포함 된 각도의 절반 임을 나타냅니다 .

알고리즘을 설명하려면 조금 더 표기법이 필요합니다. 하자 y는 원점이 될 (지도에 투영로) 및하자 X_1 , X_2를 , ..., x_n 대상 포인트합니다. a_i를 y 에서 x_i , i = 1, 2, ..., n 의 베어링으로 ​​정의하십시오 .

예비 단계로서 베어링 (0에서 360도 사이)이 오름차순으로 가정합니다.이를 위해서는 베어링을 계산 한 다음 정렬해야합니다. 둘 다 간단한 작업입니다.

이상적으로, 우리는 원호 베어링이 일부 시작 베어링에 비해 360 / n , 2 * 360 / n 등과 같기를 원합니다 . 원하는 베어링과 실제 베어링의 차이는 i * 360 / n -a_i + 시작 베어링 a0과 같습니다 . 가장 큰 차이는 이러한 n 차이 의 최대 값이고 가장 작은 차이는 최소값입니다. a0 을 최대 값과 최소값의 중간으로 설정 합니다. 이는 발생하는 최대 굽힘 양을 최소화하기 때문에 시작 베어링에 적합 합니다 . 결과적으로

b_i = i * 360 / n - a0 -a_i :

이것은 사용하는 굽힘 입니다.

2 b_i의 각도에 해당하는 y 에서 x 까지 원호를 그리는 것은 기본 기하학의 문제 이므로 세부 사항을 건너 뛰고 예제로 바로 넘어갑니다. 다음은 직사각형 맵 내에 배치 된 64, 16 및 4 개의 임의 지점에 대한 솔루션을 보여줍니다.

대체 텍스트

대체 텍스트

대체 텍스트

보시다시피 목적지 포인트 수가 증가함에 따라 솔루션이 더 멋지게 보입니다 . 용 용액 N 이 경우 간격은 간격이 정확하게 이루어진다 것이 분명 4분의 360 = 90도 같고위한 베어링 균등하게 이격되어 얼마나 명확 = 4 보여준다.

이 솔루션은 완벽하지 않습니다. 그래픽을 개선하기 위해 수동으로 조정할 수있는 여러 개의 호를 식별 할 수 있습니다. 그러나 그것은 끔찍한 일을하지 않으며 정말 좋은 시작 인 것 같습니다.

이 알고리즘은 단순하다는 장점도 있습니다. 가장 복잡한 부분은 베어링에 따라 대상을 정렬하는 것입니다.


코딩

PostGIS는 모르지만 예제를 그리는 데 사용한 코드는 PostGIS (또는 다른 GIS)에서이 알고리즘을 구현하기위한 가이드 역할을 할 수 있습니다.

다음은 의사 코드라고 생각하십시오 (그러나 Mathematica 는 그것을 실행합니다 :-). (이 사이트가 수학, 통계 및 TCS와 같이 TeX를 지원하는 경우, 이것을 훨씬 더 읽기 쉽게 만들 수 있습니다.) 표기법에는 다음이 포함됩니다.

  • 변수 및 함수 이름은 대소 문자를 구분합니다.
  • [알파]는 소문자 그리스 문자입니다. ([Pi]는 당신이 생각해야 할 가치가 있습니다.)
  • x [[i]]는 배열 x의 요소 i입니다 (1부터 시작하여 색인화 됨).
  • f [a, b]는 함수 f를 인수 a와 b에 적용합니다. 'Min'및 'Table'과 같은 적절한 경우 함수는 시스템 정의입니다. 'angles'및 'offset'과 같이 초기 소문자가있는 함수는 사용자 정의됩니다. 주석은 모호한 시스템 기능 (예 : 'Arg')을 설명합니다.
  • Table [f [i], {i, 1, n}]은 배열 {f [1], f [2], ..., f [n]}을 만듭니다.
  • 원 [o, r, {a, b}]은 각도 a에서 각도 b까지 반경 r의 o에 중심을 둔 원호를 만듭니다 (둘 다 동쪽에서 반 시계 방향으로 라디안으로 표시).
  • Ordering [x]는 x의 정렬 된 요소의 인덱스 배열을 반환합니다. x [[Ordering [x]]]는 x의 정렬 버전입니다. y가 x와 길이가 같으면 y [[Ordering [x]]]는 x와 병렬로 y를 정렬합니다.

코드의 실행 부분은 절반 이상이 선언적인 오버 헤드이거나 주석이기 때문에 20 줄 미만으로 짧습니다.

지도 그리기

z목적지 목록이며 y출발지입니다.

circleMap[z_List, y_] := 
Module[{\[Alpha] = angles[y,z], \[Beta], \[Delta], n},
    (* Sort the destinations by bearing *)
    \[Beta] = Ordering[\[Alpha]];
    x = z[[\[Beta] ]]; (* Destinations, sorted by bearing from y *)
    \[Alpha] = \[Alpha][[\[Beta]]]; (* Bearings, in sorted order *)
    \[Delta] = offset[\[Alpha]];
    n = Length[\[Alpha]];
    Graphics[{(* Draw the lines *)
        Gray, Table[circle[y, x[[i]],2 \[Pi] i / n + \[Delta] - \[Alpha][[i]]], 
             {i, 1, Length[\[Alpha]]}],
        (* Draw the destination points *)
        Red, PointSize[0.02], Table[Point[u], {u, x}]
    }]
]

지점에서 원호 생성 x지점 y각도에서 시작 \[Beta]> Y 베어링 -는 x에 대하여.

circle[x_, y_, \[Beta]_] /; -\[Pi] < \[Beta] < \[Pi] := 
Module[{v,  \[Rho], r, o, \[Theta], sign},
    If[\[Beta]==0, Return[Line[{x,y}]]];

    (* Obtain the vector from x to y in polar coordinates. *)
    v = y - x; (* Vector from x to y *)
    \[Rho] = Norm[v]; (* Length of v *)
    \[Theta] = Arg[Complex @@ v]; (* Bearing from x to y *)

    (* Compute the radius and center of the circle.*)
    r = \[Rho] / (2 Sin[\[Beta]]); (* Circle radius, up to sign *)
    If[r < 0, sign = \[Pi], sign = 0];
    o = (x+y)/2 + (r/\[Rho]) Cos[\[Beta]]{v[[2]], -v[[1]]}; (* Circle center *)

    (* Create a sector of the circle. *)
    Circle[o, Abs[r], {\[Pi]/2 - \[Beta] + \[Theta] + sign, \[Pi] /2 + \[Beta] + \[Theta] + sign}]
]

원점에서 점 목록까지 베어링을 계산합니다.

angles[origin_, x_] := Arg[Complex@@(#-origin)] & /@ x;

베어링 세트의 잔차의 중간 범위를 계산합니다.

x정렬 된 순서로 베어링 목록입니다. 이상적으로, x [[i]] ~ 2Pii / n.

offset[x_List] :=
Module[
    {n = Length[x], y},
    (* Compute the residuals. *)
    y = Table[x[[i]] - 2 \[Pi] i / n, {i, 1, n}];
    (* Return their midrange. *)
    (Max[y] + Min[y])/2
]

이 솔루션은 목적지가 출발지를 둘러싸고 있다고 가정합니다. 그렇지 않은 경우 (균일 한 베어링의) 전체 아이디어는 좋지 않습니다. 그러나 각도 간격 내에 일부 가짜 목적지를 도입하고 나중에 해당 목적지 (및 해당 호)를 제거하여 쉽게 고칠 수 있습니다. 이 과정은 베어링 간의 평균 거리를 계산하고이를 사용하여 큰 간격 등 을 식별함으로써 자동화 할 수 있습니다 .
— whuber

멋진 그래픽. 항공사가 기내 매거진 뒷면에 표시된 노선도를 그릴 때 자동화 도구를 사용하는지 궁금합니다.
— 커크 Kuykendall

1
@Kirk 그들은지도 제작을 수동으로 수행하도록 누군가에게 돈을 지불했을 것입니다 :-). 간단한 접근 방식이 합리적으로 좋은 그래픽을 만들 수 있는지 여부를 확인하기 위해이 질문에서 영감을 얻었습니다. 답은 유망 해 보인다. 그런데 Mathematica 8은 Circle and Point 프리미티브와 원 중심을 찾기 위해 약간의 벡터 연산을 사용하여 이러한 그래픽을 제작했습니다 .
— whuber

나는 당신이 보여준 결과를 좋아하고 이것이 나에게가는 길입니다. 솔직히 말해서, 나는 기술적으로 생각하지만 당신이 준 공식에서 조금 잃어 버렸으므로 PostGIS 코드로 바꾸는 방법은 거의 불가능 해졌습니다. whuber의 개념을 실행 가능한 코드로 변환하는 방법에 대한 아이디어가 있습니까? 나는 그것을 검토하고 이동하려고 노력할 것이지만, 도움은 크게 감사하겠습니다.
— RyanDalton

@ whuber- 업데이트 된 의사 코드에 감사드립니다. PostGIS에서 실제로 구현할 수 있는지 확인해야합니다.
— RyanDalton



3

@Nicklas Avén이 제안한 ST_CurveToLine 함수를 사용하여 "2 점"선 스트링을 구부리기 위해 이것을 시도했습니다.

ST_OffsetCurve 함수에 다음 3 개의 좌표 세트를 전달했습니다.

  1. 원래 줄의 시작
  2. 원래 선과 평행 한 선 오프셋의 중간 점
  3. 원래 줄의 끝

필자는 ST_OffsetCurve 함수를 사용하여 예제에서 원래 줄 길이의 1/10 인 오프셋을 계산했습니다.

다음은 원래 직선에서 곡선을 생성하는 데 사용한 SQL입니다.

    ST_CurveToLine('CIRCULARSTRING(' || st_x(st_startpoint(the_geom)) || ' ' || st_y(st_startpoint(the_geom)) || ', ' || st_x(st_centroid(ST_OffsetCurve(the_geom, st_length(the_geom)/10, 'quad_segs=4 join=bevel'))) || ' ' || st_y(st_centroid(ST_OffsetCurve(the_geom, st_length(the_geom)/10, 'quad_segs=4 join=bevel'))) || ', ' || st_x(st_endpoint(the_geom)) || ' ' ||  st_y(st_endpoint(the_geom)) || ')') AS the_curved_geom

정말 유용하지만 어떤 이유로 든 결과는 내 건조를 존중하지 않습니다. 왜 그런지 알아?
— DMS02

입력 지오메트리의 srid, 출력 srid가 누락되었거나, 다르고, 오류가 발생하는 경우 (어플리케이션-QGIS, PostgreSQL)에 대해 좀 더 자세히 설명해 주시겠습니까?
— Brent Edwards

결과 곡선을 삽입하려는 테이블에는 enforce_srid_geom 제약 조건이 있습니다. 쿼리를 실행할 때이 쿼리가 해당 제약 조건을 위반한다는 오류가 발생합니다. 제약 조건이없는 테이블을 사용하면 작동하지만 QGIS에 추가 할 때 srid 0으로 나열됩니다. 내 쿼리 : INSERT INTO test (the_curved_geom) SELECT [여기에서 SQL] FROM 줄
— DMS02

기하 열 (the_curved_geom) 에서 postgis.net/docs/ST_GeometryType.html 및 postgis.net/docs/ST_SRID.html 함수를 실행하고 테스트 테이블 및 enforce_srid_geom과 충돌하는지 확인하십시오. 그렇다면 필요에 따라 지오메트리를 변환하거나 흘리거나 테스트 테이블 / 제약을 수정할 수 있습니다.
— 브렌트 에드워즈
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.