"แผนที่เส้นทาง" แบบจุดต่อจุดโค้ง


39

เมื่อเร็ว ๆ นี้ฉันได้ดูหน้าเว็บของสายการบินที่แสดงเส้นทางของพวกเขาที่ออกเดินทางจากเมืองหนึ่งไปยังเมืองอื่น ๆ ที่พวกเขาให้บริการ ฉันต้องการที่จะสามารถสร้างเส้นทางโค้งที่คล้ายกันระหว่างจุด มีใครสร้างสคริปต์หรือฟังก์ชั่นที่จะสร้างส่วนโค้งแบบที่แสดงในตัวอย่างนี้หรือไม่?

เส้นทางการบิน

ใน PostGIS มีการใช้ ST_MakeLine ที่จะอนุญาตให้คุณระบุจำนวนของเส้นโค้งที่จะใช้เมื่อเชื่อมต่อ 2 จุดหรือไม่

ในขณะที่ฉันกำลังใช้ PostGIS และ QGIS ฉันยินดีรับฟังเกี่ยวกับตัวเลือกซอฟต์แวร์อื่น ๆ ที่อาจสร้างลักษณะที่เหมือนกัน


มีใครรู้บ้างเกี่ยวกับการใช้งานที่ดีของสิ่งนี้? ตัวอย่างหรืออะไร?
Mark Boulder

คำตอบ:


26

การสร้างแวดวงที่ยอดเยี่ยมสามารถให้เอฟเฟกต์ที่คุณต้องการได้

บางทีสิ่งที่ต้องการกล่าวถึงในhttp://lists.osgeo.org/pipermail/postgis-users/2008-February/018620.html

ปรับปรุง:

ผมเคยตามมากับความคิดนี้ใน"การเชื่อมต่อแสดงผลทั่วโลก" มันเป็นโซลูชันที่ใช้ PostGIS ล้วนๆโดยใช้การคัดแยกเพื่อสร้างส่วนโค้ง

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

(นิยาม CRS สำหรับ 953027 สามารถดูได้ที่นี่: http://spatialreference.org/ref/esri/53027/ )

ป้อนคำอธิบายรูปภาพที่นี่


4
ฉันชอบความคิดแม้ว่าจะมีแวดวงที่ยิ่งใหญ่ แต่ปัญหาที่คุณเจอคือระยะทางที่สั้นกว่าคุณจะยังคงเป็นเส้นตรงทั่วไป ฉันต้องการที่จะสามารถควบคุมปริมาณของส่วนโค้งที่ฉันใส่ในบรรทัด (ie- 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

ปัญหาคือการหาว่าโค้งงอมากแค่ไหนเพื่อเพิ่มความคมชัดของภาพ

นี่คือทางออกหนึ่ง (ในหลาย ๆ ที่เป็นไปได้) ลองพิจารณาส่วนโค้งทั้งหมดที่เปล่งออกมาจากจุดกำเนิดร่วม ส่วนโค้งแออัดมากที่สุดที่นี่ หากต้องการแยกพวกมันออกจากกันให้ดีที่สุดให้จัดเรียงมันเพื่อให้พวกมันกระจายออกไปในมุมที่เว้นระยะเท่ากัน มันเป็นปัญหาถ้าเราวาดส่วนของเส้นตรงจากจุดเริ่มต้นไปยังปลายทางเพราะโดยทั่วไปจะมีกลุ่มของจุดหมายปลายทางในทิศทางต่าง ๆ ลองใช้อิสรภาพของเราในการงอโค้งเพื่อให้พื้นที่ในมุมที่ออกไปเท่ากันให้มากที่สุด

เพื่อความเรียบง่ายลองใช้ส่วนโค้งเป็นวงกลมบนแผนที่ วัดตามธรรมชาติของ "โค้ง" ใน arc จากจุดYไปยังจุดxคือความแตกต่างระหว่างแบริ่งที่Yและแบริ่งโดยตรงจากYไปx ส่วนโค้งนี้เป็นส่วนของวงกลมที่yและxอยู่ทั้งคู่ เรขาคณิตเบื้องต้นแสดงให้เห็นว่ามุมดัดเท่ากับครึ่งหนึ่งของมุมรวมในส่วนโค้ง

เพื่ออธิบายอัลกอริทึมเราจำเป็นต้องมีสัญกรณ์เพิ่มเติมอีกเล็กน้อย ให้yเป็นจุดกำเนิด (ตามที่คาดไว้บนแผนที่) และให้x_1 , x_2 , ... , x_nเป็นจุดปลายทาง กำหนดÄ_iจะเป็นแบริ่งจากYไปx_i , ฉัน = 1, 2, ... , n

ขั้นตอนเบื้องต้นให้ถือว่าแบริ่ง (ทั้งหมดระหว่าง 0 ถึง 360 องศา) อยู่ในลำดับที่สูงขึ้น: สิ่งนี้ต้องการให้เราคำนวณแบริ่งแล้วเรียงลำดับ ทั้งสองเป็นงานที่ไม่ซับซ้อน

โดยหลักการแล้วเราต้องการให้ตลับลูกปืนของส่วนโค้งเท่ากับ 360 / n , 2 * 360 / nเป็นต้นเมื่อเทียบกับตลับลูกปืนเริ่มต้น ความแตกต่างระหว่างแบริ่งที่ต้องการและแบริ่งที่เกิดขึ้นจริงจึงเท่ากับฉัน * 360 / n - Ä_iบวกแบริ่งเริ่มต้นA0 ความแตกต่างที่ใหญ่ที่สุดคือค่าสูงสุดของความแตกต่างnเหล่านี้และความแตกต่างที่เล็กที่สุดคือค่าต่ำสุด ลองตั้งa0ให้อยู่กึ่งกลางระหว่างค่าสูงสุดและค่าต่ำสุด นี้เป็นผู้สมัครที่ดีสำหรับแบริ่งเริ่มต้นเพราะมันจะช่วยลดจำนวนเงินสูงสุดของการดัดที่จะเกิดขึ้น ดังนั้นกำหนด

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

นี้เป็นดัดที่จะใช้

มันเป็นเรื่องของเรขาคณิตเบื้องต้นในการวาดส่วนโค้งวงกลมจากyถึงxที่รองรับมุม 2 b_i ดังนั้นฉันจะข้ามรายละเอียดและตรงไปที่ตัวอย่าง นี่คือภาพประกอบของวิธีแก้ปัญหาสำหรับ 64, 16 และ 4 จุดสุ่มที่อยู่ในแผนที่รูปสี่เหลี่ยมผืนผ้า

ข้อความแสดงแทน

ข้อความแสดงแทน

ข้อความแสดงแทน

อย่างที่คุณเห็นโซลูชั่นดูเหมือนจะดีขึ้นเมื่อจำนวนจุดปลายทางเพิ่มขึ้น คำตอบสำหรับn = 4 แสดงให้เห็นอย่างชัดเจนว่าตลับลูกปืนมีระยะห่างเท่ากันอย่างไรในกรณีนี้ระยะห่างเท่ากับ 360/4 = 90 องศาและเห็นได้ชัดว่าระยะห่างนั้นทำได้อย่างแท้จริง

วิธีแก้ปัญหานี้ไม่สมบูรณ์แบบ: คุณอาจระบุอาร์คหลายอันที่สามารถปรับแต่งด้วยตนเองเพื่อปรับปรุงกราฟิก แต่มันจะไม่ทำงานที่แย่มากและดูเหมือนว่าเป็นการเริ่มต้นที่ดีจริงๆ

อัลกอริทึมยังมีข้อดีของความเรียบง่ายส่วนที่ซับซ้อนที่สุดประกอบด้วยการเรียงลำดับจุดหมายปลายทางตามแบริ่ง


การเข้ารหัส

ฉันไม่รู้ PostGIS แต่บางทีรหัสที่ฉันใช้ในการวาดตัวอย่างสามารถใช้เป็นแนวทางสำหรับการใช้อัลกอริทึมนี้ใน PostGIS (หรือ GIS อื่น ๆ )

พิจารณาสิ่งต่อไปนี้เป็น pseudocode (แต่Mathematicaจะใช้งานมัน :-) (หากเว็บไซต์นี้ได้รับการสนับสนุนเท็กซ์เป็นคณิตศาสตร์สถิติและคน TCS ทำผมสามารถทำให้เรื่องนี้เป็นจำนวนมากอ่านเพิ่มเติม.) สัญกรณ์รวมถึง:

  • ชื่อตัวแปรและฟังก์ชั่นเป็นกรณี ๆ ไป
  • [Alpha] เป็นตัวอักษรกรีกตัวพิมพ์เล็ก ([Pi] มีค่าที่คุณคิดว่าควรจะมี)
  • x [[i]] เป็นองค์ประกอบ i ของอาร์เรย์ x (ดัชนีเริ่มต้นที่ 1)
  • f [a, b] ใช้ฟังก์ชัน f กับอาร์กิวเมนต์ a และ b ฟังก์ชันในกรณีที่เหมาะสมเช่น 'Min' และ 'Table' ถูกกำหนดโดยระบบ ฟังก์ชั่นที่มีตัวอักษรตัวพิมพ์เล็กเริ่มต้นเช่น 'มุม' และ 'ออฟเซ็ต' เป็นแบบที่ผู้ใช้กำหนด ความคิดเห็นอธิบายถึงฟังก์ชั่นของระบบที่คลุมเครือ (เช่น 'Arg')
  • ตาราง [f [i], {i, 1, n}] สร้างอาร์เรย์ {f [1], f [2], ... , f [n]}
  • Circle [o, r, {a, b}] สร้างส่วนโค้งของวงกลมที่ศูนย์กลางที่ o ของรัศมี r จากมุม a ถึงมุม b (ทั้งในเรเดียนทวนเข็มนาฬิกาจากตะวันออก)
  • การสั่งซื้อ [x] จะส่งคืนอาร์เรย์ของดัชนีขององค์ประกอบที่เรียงลำดับของ x x [[การสั่งซื้อ [x]]] เป็นรุ่นที่เรียงลำดับของ x เมื่อ y มีความยาวเท่ากับ x, y [[การสั่งซื้อ [x]]] เรียงลำดับ y พร้อมกับ x

ส่วนที่ปฏิบัติการได้ของโค้ดสั้นอย่างมีเมตตา - น้อยกว่า 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]สัมพันธ์กับ x -> y

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]] ~ 2 [Pi] i / 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

กราฟิกที่ดี ฉันสงสัยว่าสายการบินใช้เครื่องมืออัตโนมัติเมื่อวาดแผนผังเส้นทางที่แสดงอยู่ด้านหลังของนิตยสารบนเครื่องบินของพวกเขาหรือไม่
Kirk Kuykendall

1
@ เคิร์กพวกเขาอาจจ่ายคนให้ทำแผนที่ด้วยตนเอง :-) ฉันได้รับแรงบันดาลใจจากคำถามนี้เพื่อดูว่าวิธีการง่ายๆสามารถสร้างกราฟิกที่ดีพอสมควรได้หรือไม่ คำตอบนั้นดูมีแนวโน้ม กราฟิกเหล่านี้ได้ถูกผลิตโดยMathematica 8 โดยใช้แบบดั้งเดิมของCircleและPointและใช้เลขคณิตเวกเตอร์เล็กน้อยเพื่อค้นหาจุดศูนย์กลางวงกลม
whuber

ฉันชอบผลลัพธ์ที่คุณแสดงและฉันก็เป็นวิธีที่จะไป ฉันจะซื่อสัตย์ฉันคิดว่าตัวเองเป็นเทคนิค แต่ฉันได้สูญเสียไปเล็กน้อยในสูตรที่คุณให้และวิธีการเปลี่ยนเป็นรหัส PostGIS จึงกลายเป็นไปไม่ได้เกือบ ใครบ้างที่มีความคิดเกี่ยวกับวิธีการแปลแนวคิดของ whuber เป็นรหัสที่ใช้การได้? ฉันจะพยายามทบทวนและให้มันไป แต่ความช่วยเหลือจะได้รับการชื่นชมอย่างมาก
RyanDalton

@ whuber- ขอบคุณสำหรับ pseudocode ที่อัปเดต เราจะต้องตรวจสอบว่าเราสามารถนำไปใช้จริงใน PostGIS หรือไม่
RyanDalton

5

ลองST_CurveToLine

ตัวอย่างเช่น:

SELECT ST_CurveToLine('CIRCULARSTRING(1 1,5 3,10 1)'::geometry) as the_geom;

คุณสามารถเห็นภาพนี้ได้โดยจัดการแบบสอบถามลงในกล่องข้อความและกด Map1 ที่http://www.postgisonline.org/map.php


ฉันลงเอยด้วยการลองสิ่งนี้เพื่อทำให้เส้นโค้งชุดของ "สองจุด" ส่องสว่าง
Brent Edwards


3

ในที่สุดฉันก็ลองทำสิ่งนี้เพื่อโค้งชุดของ "จุดสองจุด" โดยใช้ฟังก์ชัน ST_CurveToLine ตามที่ @Nicklas Avénแนะนำ

ฉันส่งชุดพิกัด 3 ชุดต่อไปนี้ไปยังฟังก์ชัน ST_OffsetCurve:

  1. เริ่มต้นของบรรทัดเดิม
  2. จุดกึ่งกลางของเส้นตรงข้ามขนานกับเส้นเดิม
  3. ท้ายบรรทัดเดิม

ฉันใช้ฟังก์ชัน ST_OffsetCurve เพื่อคำนวณออฟเซ็ต - 1 / 10th ของความยาวของบรรทัดต้นฉบับในตัวอย่างของฉัน

นี่คือ 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

มีประโยชน์จริง ๆ แต่ด้วยเหตุผลบางอย่างผลลัพธ์ไม่เคารพ srid ของฉัน มีความคิดอะไรไหม
DMS02

คุณสามารถให้รายละเอียดเพิ่มเติมได้ไหม - srid ของเรขาคณิตอินพุท, srid เอาท์พุตหายไป, ต่างกัน, เกิดข้อผิดพลาด (แอปพลิเคชัน - QGIS, PostgreSQL)
Brent Edwards

ตารางที่ฉันต้องการแทรกเส้นโค้งผลลัพธ์มีข้อ จำกัด enforce_srid_geom เมื่อฉันเรียกใช้แบบสอบถามฉันได้รับข้อผิดพลาดที่ระบุว่าแบบสอบถามนี้ละเมิดข้อ จำกัด นั้น ด้วยตารางที่ไม่มีข้อ จำกัด นั้นจะใช้งานได้ แต่เมื่อเพิ่มลงใน QGIS จะแสดงรายการด้วย srid 0 แบบสอบถามของฉัน: INSERT INTO test (the_curved_geom) เลือก [ที่นี่ SQL ของคุณ] จากบรรทัด
DMS02

ลองเรียกใช้ฟังก์ชัน postgis.net/docs/ST_GeometryType.htmlและpostgis.net/docs/ST_SRID.htmlในคอลัมน์รูปทรงเรขาคณิต (the_curved_geom) และตรวจสอบว่ามีข้อขัดแย้งกับตารางทดสอบและ enforce_srid_geom ของคุณหรือไม่ ถ้าเป็นเช่นนั้นคุณสามารถแปลงรูปทรงเรขาคณิต / srid ตามต้องการหรือปรับเปลี่ยนตารางทดสอบ / ข้อ จำกัด ของคุณ
Brent Edwards
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.