곡선 점대 점 "라우트 맵"


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 까지의 베어링 간 차이 입니다. 이러한 호는 yx가 모두 있는 원의 섹터입니다 . 기본 형상은 굽힘 각도 가 원호에 포함 된 각도의 절반 임을 나타냅니다 .

알고리즘을 설명하려면 조금 더 표기법이 필요합니다. 하자 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.htmlpostgis.net/docs/ST_SRID.html 함수를 실행하고 테스트 테이블 및 enforce_srid_geom과 충돌하는지 확인하십시오. 그렇다면 필요에 따라 지오메트리를 변환하거나 흘리거나 테스트 테이블 / 제약을 수정할 수 있습니다.
브렌트 에드워즈
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.