กำลังคำนวณความกว้างเฉลี่ยของรูปหลายเหลี่ยมหรือไม่ [ปิด]


40

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

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

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

ฉันสนใจโซลูชันที่ใช้ซอฟต์แวร์ ArcGIS 10, PostGIS 2.0 หรือ QGIS ฉันได้เห็นคำถามนี้และดาวน์โหลดเครื่องมือ Dan Patterson สำหรับ ArcGIS10 แต่ฉันไม่สามารถคำนวณสิ่งที่ฉันต้องการได้

ฉันเพิ่งค้นพบเครื่องมือเรขาคณิตขั้นต่ำใน ArcGIS 10 ซึ่งทำให้ฉันสามารถสร้างรูปหลายเหลี่ยมสีเขียวต่อไปนี้:

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

ดูเหมือนว่าจะเป็นทางออกที่ดีสำหรับถนนที่ตามตาราง แต่จะไม่ทำงานเป็นอย่างอื่นดังนั้นฉันจึงยังสนใจคำแนะนำอื่น ๆ อยู่


คุณเลิกใช้วิธีแก้ปัญหาที่เป็นไปได้ในแถบด้านข้างหรือไม่ ie gis.stackexchange.com/questions/2880/… เห็นได้ชัดว่าถูกตั้งค่าสถานะว่าเป็นคำตอบเล็กน้อยสำหรับโพสต์ซ้ำที่อาจเกิดขึ้น

@DanPatterson ฉันไม่เห็นคำถามใด ๆ เช่นนี้ (ส่วนใหญ่เกี่ยวข้องกับของหลักสูตร) คุณหมายถึงคำถามของฉันถูกตั้งค่าสถานะหรือไม่ ฉันไม่เข้าใจบรรทัดที่สองของคุณ
djq

คำถามที่เกี่ยวข้องนั้น @Dan เกี่ยวข้องกับการตีความที่แตกต่างกันของ "ความกว้าง" (จริง ๆ แล้วการตีความยังไม่ชัดเจนอย่างสมบูรณ์) คำตอบนั้นปรากฏขึ้นเพื่อมุ่งเน้นไปที่การค้นหาความกว้างณ จุดที่กว้างที่สุดแทนที่จะเป็นความกว้างเฉลี่ย
whuber

เนื่องจาก @whuber ต้องการรวบรวมการอภิปรายที่นี่ปิดคำถามอื่นฉันขอแนะนำให้ผู้อื่นเข้าใจคำถามว่า " การประมาณความกว้างเฉลี่ยของแถบสี่เหลี่ยม "
Peter Krauss

@ Peter: ในฐานะที่เป็นแถบสี่เหลี่ยมเป็นรูปหลายเหลี่ยมfortiori , ชื่อทั่วไปมากขึ้นควรจะยืน
whuber

คำตอบ:


40

ส่วนหนึ่งของปัญหาคือค้นหาคำจำกัดความที่เหมาะสมของ "ความกว้างเฉลี่ย" หลายอย่างเป็นธรรมชาติ แต่จะแตกต่างกันอย่างน้อยก็เล็กน้อย สำหรับความเรียบง่ายให้พิจารณาคำจำกัดความตามคุณสมบัติที่คำนวณได้ง่าย (ซึ่งจะแยกแยะสิ่งที่อยู่บนพื้นฐานของการแปลงแกนกลางหรือลำดับของบัฟเฟอร์เป็นต้น)

ยกตัวอย่างเช่นพิจารณาว่าสัญชาตญาณตามแบบฉบับของรูปหลายเหลี่ยมที่มี "ความกว้าง" ที่แน่นอนเป็นบัฟเฟอร์ขนาดเล็ก (พูดถึงรัศมีrด้วยส่วนปลายกำลังสอง) รอบรูปหลายเหลี่ยมที่ค่อนข้างยาวตรง (พูดถึงความยาวL ) เราคิดว่า 2r = wเป็นความกว้าง ดังนั้น:

  • เส้นรอบวงPมีค่าประมาณ 2L + 2w;

  • พื้นที่Aประมาณเท่ากับ w L

ความกว้างwและความยาวLสามารถกู้คืนเป็นรากของสมการกำลังสอง x ^ 2 - (P / 2) x + A; โดยเฉพาะเราสามารถประเมินได้

  • W = (P - Sqrt (P ^ 2 - 16A)) / 4

เมื่อคุณแน่ใจว่ารูปหลายเหลี่ยมมันมีความยาวและผอมเป็นประมาณเพิ่มเติมที่คุณสามารถใช้ 2L + 2w เท่ากับ 2L ไหน

  • w (หยาบ) = 2A / P

ความคลาดเคลื่อนสัมพัทธ์ในการประมาณนี้เป็นสัดส่วนกับ w / L: skinnier ของรูปหลายเหลี่ยมยิ่งใกล้ w / L เป็นศูนย์และยิ่งการประมาณดีขึ้นเท่าไร

วิธีนี้ไม่เพียงง่ายมาก (เพียงแค่แบ่งพื้นที่ด้วยเส้นรอบวงและคูณด้วย 2) ด้วยสูตรใดก็ตามไม่สำคัญว่ารูปหลายเหลี่ยมเชิงหรือที่ตั้ง (เพราะการเคลื่อนไหวแบบยุคลิดเปลี่ยนไม่ได้ทั้งพื้นที่และ ปริมณฑล).

คุณอาจลองใช้สูตรอย่างใดอย่างหนึ่งเหล่านี้เพื่อประเมินความกว้างเฉลี่ยสำหรับรูปหลายเหลี่ยมใด ๆ ที่แสดงถึงส่วนของถนน ข้อผิดพลาดที่คุณทำในการประมาณค่าดั้งเดิมของw (ด้วยสูตรสมการกำลังสอง) เกิดขึ้นเนื่องจากพื้นที่Aยังมีเวดจ์ขนาดเล็กที่โค้งงอของ polyline ดั้งเดิมแต่ละเส้นด้วย หากผลรวมของมุมโค้งงอเป็นtเรเดียน (นี่คือความโค้งสัมบูรณ์สัมบูรณ์ของโพลีไลน์) จริง ๆ แล้ว

  • P = 2L + 2w + 2 Pi tw และ

  • A = L w + Pi tw ^ 2

เสียบสิ่งเหล่านี้เข้ากับโซลูชันก่อนหน้า (สูตรสมการกำลังสอง) และทำให้ง่ายขึ้น เมื่อล้างควัน, ผลงานจากระยะโค้งทีได้หายไป! สิ่งที่ดูเหมือนการประมาณในตอนแรกนั้นมีความแม่นยำอย่างสมบูรณ์แบบสำหรับบัฟเฟอร์ polyline แบบไม่ตัดกันตัวเอง สำหรับรูปหลายเหลี่ยมที่มีความกว้างผันแปรได้นี่จึงเป็นคำจำกัดความที่เหมาะสมของความกว้างเฉลี่ย


ขอบคุณ @whuber ที่เป็นคำตอบที่ยอดเยี่ยมและมันช่วยให้ฉันคิดได้ชัดเจนยิ่งขึ้น
djq

@whuber: ฉันกำลังเขียนบทความและฉันจะต้องให้การอ้างอิงที่เหมาะสม ('วิชาการ') กับวิธีการที่คุณอธิบายที่นี่ คุณมีการอ้างอิงดังกล่าวหรือไม่? การวัดนี้มีชื่อหรือไม่? ถ้าไม่ฉันสามารถตั้งชื่อตามคุณ! สิ่งที่เกี่ยวกับ "การวัดความกว้างของฮูเบอร์"
Julien

@julien ฉันไม่มีการอ้างอิงใด ๆ รูปแบบนี้อาจใช้งานได้: MISC {20279, TITLE = {การคำนวณความกว้างเฉลี่ยของรูปหลายเหลี่ยม?}, AUTHOR = {whuber ( gis.stackexchange.com/users/664/whuber )}, HOWPUBLISHED = {GIS}, NOTE = {URL: gis.stackexchange.com/q/20279/664 (รุ่น: 2013-08-13)}, EPRINT = { gis.stackexchange.com/q/20279 }, URL = { gis.stackexchange.com/q/20279 }}
whuber

18

ที่นี่ฉันแสดงการเพิ่มประสิทธิภาพเล็กน้อยเกี่ยวกับโซลูชัน @whuber และฉันใส่ในแง่ของ "ความกว้างบัฟเฟอร์" เพราะมันมีประโยชน์สำหรับการรวมการแก้ปัญหาทั่วไปมากขึ้น: มีฟังก์ชั่นผกผัน st_buffer ที่ส่งกลับการประมาณความกว้างหรือไม่

CREATE FUNCTION buffer_width(
        -- rectangular strip mean width estimator
    p_len float,   -- len of the central line of g
    p_geom geometry, -- g
    p_btype varchar DEFAULT 'endcap=flat' -- st_buffer() parameter
) RETURNS float AS $f$
  DECLARE
    w_half float;
    w float;    
  BEGIN
         w_half := 0.25*ST_Area(p_geom)/p_len;
         w      := 0.50*ST_Area( ST_Buffer(p_geom,-w_half,p_btype) )/(p_len-2.0*w_half);
     RETURN w_half+w;
  END
$f$ LANGUAGE plpgsql IMMUTABLE;

สำหรับปัญหานี้คำถาม @celenius เกี่ยวกับความกว้างของถนน , swการแก้ปัญหาคือ

 sw = buffer_width(ST_Length(g1), g2)

ที่swเป็น "ความกว้างเฉลี่ย" g1สายกลางg2และถนนg2เป็นรูปหลายเหลี่ยม ฉันใช้ไลบรารีมาตรฐาน OGC เท่านั้นทดสอบกับPostGISและแก้ไขแอปพลิเคชั่นที่ใช้งานจริงอื่น ๆ ด้วยฟังก์ชั่น buffer_width เดียวกัน

สาธิต

A2เป็นพื้นที่ของg2, L1ความยาวของเส้นกลาง ( g1) g2ของ

สมมติว่าเราสามารถสร้างg2โดยg2=ST_Buffer(g1,w)และนั่นg1คือตรงดังนั้นg2สี่เหลี่ยมที่มีความยาวL1และความกว้าง2*wและ

    A2 = L1*(2*w)   -->  w = 0.5*A2/L1

มันไม่ใช่สูตรเดียวกันของ @whuber เพราะนี่wคือครึ่งหนึ่งของg2ความกว้างของสี่เหลี่ยมผืนผ้า ( ) มันเป็นตัวประมาณที่ดี แต่อย่างที่เราเห็นได้จากการทดสอบ (ด้านล่าง) นั้นไม่ถูกต้องและฟังก์ชั่นใช้เป็นเบาะแสเพื่อลดg2พื้นที่และเป็นตัวประมาณขั้นสุดท้าย

ที่นี่เราไม่ได้ประเมินบัฟเฟอร์ด้วย "ปิดท้ายตาราง =" หรือ "ปิดท้าย = รอบ" ที่ต้องรวมให้ พื้นที่ของบัฟเฟอร์จุดที่มีเหมือนกันA2 w

ข้อมูลอ้างอิง: ในฟอรัมที่คล้ายกันของปี 2005 W. Huber อธิบายวิธีแก้ปัญหาเช่นนี้และอื่น ๆ

การทดสอบและเหตุผล

สำหรับเส้นตรงผลลัพธ์จะเป็นไปตามที่คาดไว้ แต่สำหรับรูปทรงอื่น ๆ ผลลัพธ์อาจน่าผิดหวัง เหตุผลหลักคือบางทีแบบจำลองทั้งหมดมีไว้สำหรับสี่เหลี่ยมที่แน่นอนหรือสำหรับรูปทรงเรขาคณิตที่สามารถประมาณเป็น "สี่เหลี่ยมแถบ" นี่คือ "ชุดทดสอบ" เพื่อตรวจสอบข้อ จำกัด ของการประมาณนี้ (ดูwfactorในผลลัพธ์ด้านบน)

 SELECT *, round(100.0*(w_estim-w)/w,1) as estim_perc_error
    FROM (
        SELECT btype, round(len,1) AS len, w, round(w/len,3) AS wfactor,
               round(  buffer_width(len, gbase, btype)  ,2) as w_estim ,
               round(  0.5*ST_Area(gbase)/len       ,2) as w_near
        FROM (
         SELECT
            *, st_length(g) AS len, ST_Buffer(g, w, btype) AS gbase
         FROM (
               -- SELECT ST_GeomFromText('LINESTRING(50 50,150 150)') AS g, -- straight
               SELECT ST_GeomFromText('LINESTRING(50 50,150 150,150 50,250 250)') AS g,
            unnest(array[1.0,10.0,20.0,50.0]) AS w
              ) AS t, 
             (SELECT unnest(array['endcap=flat','endcap=flat join=bevel']) AS btype
             ) AS t2
        ) as t3
    ) as t4;

ผล:

ด้วย RECTANGLES (เส้นกลางเป็นเส้นตรง):

         btype          |  len  |  w   | wfactor | w_estim | w_near | estim_perc_error 
------------------------+-------+------+---------+---------+--------+------------------
 endcap=flat            | 141.4 |  1.0 |   0.007 |       1 |      1 |                0
 endcap=flat join=bevel | 141.4 |  1.0 |   0.007 |       1 |      1 |                0
 endcap=flat            | 141.4 | 10.0 |   0.071 |      10 |     10 |                0
 endcap=flat join=bevel | 141.4 | 10.0 |   0.071 |      10 |     10 |                0
 endcap=flat            | 141.4 | 20.0 |   0.141 |      20 |     20 |                0
 endcap=flat join=bevel | 141.4 | 20.0 |   0.141 |      20 |     20 |                0
 endcap=flat            | 141.4 | 50.0 |   0.354 |      50 |     50 |                0
 endcap=flat join=bevel | 141.4 | 50.0 |   0.354 |      50 |     50 |                0

กับ GEOMETRIES อื่น ๆ (พับกึ่งกลาง):

         btype          | len |  w   | wfactor | w_estim | w_near | estim_perc_error 
 -----------------------+-----+------+---------+---------+--------+------------------
 endcap=flat            | 465 |  1.0 |   0.002 |       1 |      1 |                0
 endcap=flat join=bevel | 465 |  1.0 |   0.002 |       1 |   0.99 |                0
 endcap=flat            | 465 | 10.0 |   0.022 |    9.98 |   9.55 |             -0.2
 endcap=flat join=bevel | 465 | 10.0 |   0.022 |    9.88 |   9.35 |             -1.2
 endcap=flat            | 465 | 20.0 |   0.043 |   19.83 |  18.22 |             -0.9
 endcap=flat join=bevel | 465 | 20.0 |   0.043 |   19.33 |  17.39 |             -3.4
 endcap=flat            | 465 | 50.0 |   0.108 |   46.29 |  40.47 |             -7.4
 endcap=flat join=bevel | 465 | 50.0 |   0.108 |   41.76 |  36.65 |            -16.5

 wfactor= w/len
 w_near = 0.5*area/len
 w_estim is the proposed estimator, the buffer_width function.

เกี่ยวกับbtypeดูคู่มือ ST_Bufferด้วย ilustratins ที่ดีและ LINESTRING ที่ใช้ที่นี่

สรุป :

  • ประมาณการของw_estimอยู่เสมอดีกว่าw_near;
  • สำหรับรูปg2ทรงเรขาคณิต"ใกล้ถึงเป็นรูปสี่เหลี่ยมผืนผ้า" ก็ถือว่าใช้ได้wfactor
  • สำหรับรูปทรงเรขาคณิตที่อื่น (ใกล้กับ "แถบสี่เหลี่ยม") ให้ใช้ขีด จำกัดwfactor=~0.01สำหรับ 1% w_estimของข้อผิดพลาดใน ขึ้นอยู่กับ wfactor นี้ใช้ตัวประมาณค่าอื่น

ข้อควรระวังและการป้องกัน

ทำไมข้อผิดพลาดการประเมินเกิดขึ้น? เมื่อคุณใช้ST_Buffer(g,w)คุณคาดหวังว่าโดย "สี่เหลี่ยมแถบโมเดล" ว่าพื้นที่ใหม่ที่เพิ่มโดยบัฟเฟอร์ของความกว้างwเป็นเรื่องเกี่ยวกับw*ST_Length(g)หรือw*ST_Perimeter(g)... เมื่อไม่ปกติโดยการวางซ้อน (ดูเส้นพับ) หรือโดย "สไตล์" คือเมื่อ การประมาณค่าของค่าเฉลี่ยwความผิด นี่คือข้อความหลักของการทดสอบ

ในการตรวจสอบปัญหานี้ที่ King of bufferให้ตรวจสอบพฤติกรรมของการสร้างบัฟเฟอร์:

SELECT btype, w, round(100.0*(a1-len1*2.0*w)/a1)::varchar||'%' AS straight_error,  
                 round(100.0*(a2-len2*2.0*w)/a2)::varchar||'%' AS curve2_error,
                 round(100.0*(a3-len3*2.0*w)/a3)::varchar||'%' AS curve3_error
FROM (
 SELECT
    *, st_length(g1) AS len1, ST_Area(ST_Buffer(g1, w, btype)) AS a1,
    st_length(g2) AS len2, ST_Area(ST_Buffer(g2, w, btype)) AS a2,
    st_length(g3) AS len3, ST_Area(ST_Buffer(g3, w, btype)) AS a3
 FROM (
       SELECT ST_GeomFromText('LINESTRING(50 50,150 150)') AS g1, -- straight
              ST_GeomFromText('LINESTRING(50 50,150 150,150 50)') AS g2,
              ST_GeomFromText('LINESTRING(50 50,150 150,150 50,250 250)') AS g3,
              unnest(array[1.0,20.0,50.0]) AS w
      ) AS t, 
     (SELECT unnest(array['endcap=flat','endcap=flat join=bevel']) AS btype
     ) AS t2
) as t3;

ผล:

         btype          |  w   | straight_error | curve2_error | curve3_error 
------------------------+------+----------------+--------------+--------------
 endcap=flat            |  1.0 | 0%             | -0%          | -0%
 endcap=flat join=bevel |  1.0 | 0%             | -0%          | -1%
 endcap=flat            | 20.0 | 0%             | -5%          | -10%
 endcap=flat join=bevel | 20.0 | 0%             | -9%          | -15%
 endcap=flat            | 50.0 | 0%             | -14%         | -24%
 endcap=flat join=bevel | 50.0 | 0%             | -26%         | -36%

        เตือนภัย


13

หากคุณสามารถเข้าร่วมข้อมูลรูปหลายเหลี่ยมของคุณกับข้อมูล centerline ของคุณ (โดยวิธีการเชิงพื้นที่หรือแบบตาราง) จากนั้นเพียงรวมผลรวมพื้นที่รูปหลายเหลี่ยมสำหรับการจัดแนวกลางแต่ละเส้นแล้วหารด้วยความยาวของเส้นกลาง


นั่นเป็นความจริง! ในกรณีนี้เส้นกลางของฉันไม่ยาวเท่ากัน แต่ฉันสามารถรวมเป็นหนึ่งได้เสมอและแยกออกเป็นรูปหลายเหลี่ยม
djq

หากข้อมูลของคุณอยู่ใน postgreSQL / postGIS และคุณมีฟิลด์ ID ถนนสำหรับ centerlines และรูปหลายเหลี่ยมคุณไม่จำเป็นต้องผสาน / แยกและใช้ฟังก์ชันรวมคำตอบของคุณเป็นเพียงการค้นหา ฉันช้าที่ SQL หรือฉันจะโพสต์ตัวอย่าง แจ้งให้เราทราบว่านี้เป็นวิธีการที่คุณจะไปแก้ปัญหาและฉันจะช่วยให้การจัดเรียงมันออกมา (ถ้าจำเป็น.)
Scro

ขอบคุณ Scro ตอนนี้มันไม่ได้อยู่ใน PostGIS แต่มันค่อนข้างเร็วในการโหลดฉันคิดว่าฉันจะลองใช้วิธีของ @ whuber ก่อน แต่ฉันจะเปรียบเทียบกับผลลัพธ์จาก PostGIS (และขอขอบคุณสำหรับข้อเสนอความช่วยเหลือ SQL แต่ฉัน น่าจะสามารถจัดการได้) ส่วนใหญ่พยายามให้ชัดเจนในหัวของฉันก่อน
djq

+1 นี่เป็นวิธีแก้ปัญหาง่ายๆสำหรับสถานการณ์ที่พร้อมใช้งาน
whuber

9

ฉันได้พัฒนาสูตรสำหรับความกว้างเฉลี่ยของรูปหลายเหลี่ยมและใส่ลงในฟังก์ชัน Python / ArcPy สูตรของฉันมาจาก (แต่มีการขยายอย่างมาก) ความคิดที่ตรงไปตรงมาที่สุดของความกว้างเฉลี่ยที่ฉันเคยเห็นมาพูดถึงที่อื่น นั่นคือเส้นผ่านศูนย์กลางของวงกลมที่มีพื้นที่เดียวกับรูปหลายเหลี่ยมของคุณ อย่างไรก็ตามในคำถามข้างต้นและในโครงการของฉันฉันสนใจในความกว้างของแกนที่แคบที่สุด นอกจากนี้ฉันยังสนใจในความกว้างเฉลี่ยสำหรับรูปร่างที่อาจไม่ซับซ้อนและไม่นูน

ทางออกของฉันคือ:

(perimeter / pi) * area / (perimeter**2 / (4*pi))
= 4 * area / perimeter

นั่นคือ:

(Diameter of a circle with the same perimeter as the polygon) * Area / (Area of a circle with the same perimeter as the polygon)

ฟังก์ชั่นคือ:

def add_average_width(featureClass, averageWidthField='Width'):
    '''
    (str, [str]) -> str

    Calculate the average width of each feature in the feature class. The width
        is reported in units of the feature class' projected coordinate systems'
        linear unit.

    Returns the name of the field that is populated with the feature widths.
    '''
    import arcpy
    from math import pi

    # Add the width field, if necessary
    fns = [i.name.lower() for i in arcpy.ListFields(featureClass)]
    if averageWidthField.lower() not in fns:
        arcpy.AddField_management(featureClass, averageWidthField, 'DOUBLE')

    fnsCur = ['SHAPE@LENGTH', 'SHAPE@AREA', averageWidthField]
    with arcpy.da.UpdateCursor(featureClass, fnsCur) as cur:
        for row in cur:
            perim, area, width = row
            row[-1] = ((perim/pi) * area) / (perim**2 / (4 * pi))
            cur.updateRow(row)

    return averageWidthField

นี่คือแผนที่ที่ส่งออกที่มีความกว้างเฉลี่ย (และคุณลักษณะทางเรขาคณิตอื่น ๆ สำหรับการอ้างอิง) ทั่วทั้งรูปร่างที่หลากหลายโดยใช้ฟังก์ชันจากด้านบน:

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


4
area / perimeter * 4หากคุณลดความซับซ้อนของการแสดงออกมันจะเป็นเพียงแค่
culebrón

ขอบคุณ @ culebrón ฉันต้องการความชัดเจนของแนวคิดเรื่องความเรียบง่ายของสูตรและฉันไม่เคยคิดที่จะทำให้สมการง่ายขึ้น สิ่งนี้จะช่วยฉันประหยัดเวลาในการดำเนินการ
Tom

0

โซลูชันอื่นที่มีแกนอยู่ตรงกลางโดยประมาณ:

  1. คำนวณแกนอยู่ตรงกลางของรูปหลายเหลี่ยม
  2. รับความยาวของแกนอยู่ตรงกลางโดยประมาณ;
  3. รับระยะทางจากปลายทั้งสองของแกนไปจนถึงเส้นขอบของรูปหลายเหลี่ยม
  4. ความยาวของแกนผลรวมและระยะทางจากขั้นตอนที่ 3 - เป็นความยาวของรูปหลายเหลี่ยมโดยประมาณ
  5. ตอนนี้คุณสามารถแบ่งพื้นที่ของรูปหลายเหลี่ยมด้วยความยาวนี้และรับความกว้างเฉลี่ยของรูปหลายเหลี่ยม

ผลลัพธ์จะผิดอย่างแน่นอนสำหรับรูปหลายเหลี่ยมเหล่านั้นที่แกนกลางอยู่ตรงนั้นไม่ใช่เส้นต่อเนื่องเดี่ยวดังนั้นคุณสามารถตรวจสอบได้ก่อนขั้นตอนที่ 1 และส่งคืนNULLหรือบางสิ่งบางอย่าง

ตัวอย่าง

นี่คือตัวอย่างของฟังก์ชัน PostgreSQL (หมายเหตุ: คุณต้องติดตั้งpostgisและส่วนขยายpostgis_sfcgal ):

CREATE FUNCTION ApproximatePolygonLength(geom geometry)
RETURNS float AS $$
    SELECT
        CASE
            /* in case when approximate medial axis is empty or simple line
             * return axis length
             */
            WHEN (ST_GeometryType(axis.axis) = 'ST_LineString' OR ST_IsEmpty(axis.axis))
                THEN axis_length.axis_length
                    + start_point_distance.start_point_distance
                    + end_point_distance.end_point_distance
            /* else geometry is too complex to define length */
            ELSE NULL
        END AS length
    FROM
        LATERAL (
            SELECT
                ST_MakeValid(geom) AS valid_geom
        ) AS valid_geom,
        LATERAL (
            SELECT
                /* `ST_LineMerge` returns:
                 *  - `GEOMETRYCOLLECTION EMPTY`, if `ST_ApproximateMedialAxis` is an empty line (i.e. for square);
                 *  - `LINESTRING ...`, if `ST_ApproximateMedialAxis` is a simple line;
                 *  - `MULTILINESTRING ...`, if `ST_ApproximateMedialAxis` is a complex line
                 *     that can not be merged to simple line. In this case we should return `NULL`.
                 */
                ST_LineMerge(
                    ST_ApproximateMedialAxis(
                        valid_geom.valid_geom
                    )
                ) AS axis
        ) AS axis,
        LATERAL (
            SELECT
                ST_Boundary(valid_geom.valid_geom) AS border
        ) AS border,
        LATERAL (
            SELECT
                ST_Length(axis.axis) AS axis_length
        ) AS axis_length,
        LATERAL (
            SELECT
                ST_IsClosed(axis.axis) AS axis_is_closed
        ) AS axis_is_closed,
        LATERAL (
            SELECT
                CASE WHEN axis_is_closed.axis_is_closed THEN 0
                ELSE
                    ST_Distance(
                        border.border,
                        CASE ST_GeometryType(axis.axis)
                            WHEN 'ST_LineString' THEN ST_StartPoint(axis.axis)
                            /* if approximate medial axis is empty (i.e. for square),
                             * get centroid of geometry
                             */
                            ELSE ST_Centroid(valid_geom.valid_geom)
                        END
                    )
                END AS start_point_distance
        ) AS start_point_distance,
        LATERAL (
            SELECT
                CASE WHEN axis_is_closed.axis_is_closed THEN 0
                ELSE
                    ST_Distance(
                        border.border,
                        CASE ST_GeometryType(axis.axis)
                            WHEN 'ST_LineString' THEN ST_EndPoint(axis.axis)
                            /* if approximate medial axis is empty (i.e. for square),
                             * get centroid of geometry
                             */
                            ELSE ST_Centroid(valid_geom.valid_geom)
                        END
                    )
                END AS end_point_distance
        ) AS end_point_distance;
$$ LANGUAGE SQL;

CREATE FUNCTION ApproximatePolygonWidth(geom geometry)
RETURNS float AS $$
    SELECT
        CASE
            WHEN length IS NULL THEN NULL
            ELSE area.area / length.length
        END AS width
    FROM
        (
            SELECT ApproximatePolygonLength(geom) AS length
        ) AS length,
        (
            SELECT
                ST_Area(
                    ST_MakeValid(geom)
                ) AS area
        ) AS area;
$$ LANGUAGE SQL;

ข้อด้อย:

การแก้ปัญหานี้จะไม่ทำงานกับกรณีเมื่อรูปหลายเหลี่ยมเกือบเป็นรูปสี่เหลี่ยมผืนผ้าและมนุษย์สามารถกำหนดความยาวของมันได้อย่างง่ายดาย แต่แกนอยู่ตรงกลางโดยประมาณมีกิ่งก้านเล็ก ๆ ใกล้กับขอบและอัลกอริทึมส่งกลับค่าไม่มี

ตัวอย่าง:

ตัวอย่างที่ไม่ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.