อัลกอริทึมสำหรับการค้นหารูปหลายเหลี่ยมที่ผิดปกติ centroid (จุดป้าย)


13

ฉันต้องการหาเซนทรอยด์ (หรือจุดป้ายกำกับ) สำหรับรูปหลายเหลี่ยมที่มีรูปร่างผิดปกติใน Google Maps ฉันกำลังแสดง InfoWindows สำหรับพัสดุและต้องการสถานที่ยึด InfoWindow ที่รับประกันว่าจะอยู่บนพื้นผิว ดูภาพด้านล่าง

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

ในความเป็นจริงฉันไม่ต้องการสิ่งใดโดยเฉพาะกับ Google แผนที่เพียงแค่มองหาแนวคิดในการหาจุดนี้โดยอัตโนมัติ

ความคิดแรกของฉันคือการหาเซนทรอยด์ที่ "ผิดพลาด" โดยการหาค่า Lat และ lngs โดยเฉลี่ยและการหาจุดแบบสุ่มจากจุดนั้นจนกว่าฉันจะพบจุดที่ตัดกันรูปหลายเหลี่ยม ฉันมีรหัสจุดในรูปหลายเหลี่ยมแล้ว ดูเหมือนว่า "แฮ็ค" อันยิ่งใหญ่สำหรับฉัน

ฉันควรทราบว่าฉันไม่สามารถเข้าถึงรหัสด้านเซิร์ฟเวอร์ใด ๆ ที่ส่งออกรูปทรงเรขาคณิตดังนั้นฉันจึงไม่สามารถทำอะไรเช่น ST_PointOnSurface (the_geom)

คำตอบ:


6

รวดเร็วและสกปรก: หากเซนทรอยด์ "เท็จ" ไม่อยู่ในรูปหลายเหลี่ยมให้ใช้จุดสุดยอดที่ใกล้ที่สุดไปยังจุดนั้น


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

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

@ Jason หากคุณใช้ centroid จริงคุณอาจมีโอกาสน้อยที่จะประสบปัญหานี้ ไม่ควรหนักเกินไปที่จะแปลบางสิ่งอย่างรวดเร็วเป็น JavaScript: github.com/cartesiananalytics/Pipra/blob/master/src/…
Dandy

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

3

คุณอาจต้องการดูสิ่งนี้: http://github.com/tparkin/Google-Maps-Point-in-Polygon

ดูเหมือนว่าจะใช้อัลกอริทึมการหล่อเรย์ที่ควรตรงกับกรณีที่คุณนำเสนอ

มีโพสต์บล็อกเกี่ยวกับที่นี่ http://appdelegateinc.com/blog/2010/05/16/point-in-polygon-checking/


หากคุณต้องการที่จะใช้สิ่งนี้ในฝั่งเซิร์ฟเวอร์ทั้ง JTS (Java) และ Geos (C) ใช้ฟังก์ชั่นนี้
DavidF

ใช่ฉันอาจจะต้องเพิ่มว่าฉันมีรหัสเพื่อตรวจสอบว่า centroid "คำนวณ" ของฉันอยู่ในรูปหลายเหลี่ยมหรือไม่ สิ่งที่ฉันต้องการคือวิธีสร้างเซนทรอยด์ที่อยู่ภายในรูปหลายเหลี่ยม
เจสัน

3

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


1

ฉันจะแก้ไขปัญหาของฉันโดยการขยายรหัส epoly ความนิยมจากhttp://econym.org.uk/gmap โดยพื้นฐานแล้วสิ่งที่ฉันทำคือ:

  • สร้างชุดรังสีที่เริ่มต้นจาก "เซนทรอยด์เท็จ" และขยายไปทุกซอกทุกมุมและด้านข้าง (ทั้งหมด 8 อัน)
  • สร้างจุดเพิ่มขึ้น 10,20,30 ... เปอร์เซ็นต์ลดรังสีแต่ละดวงและดูว่าจุดนี้อยู่ในรูปหลายเหลี่ยมดั้งเดิมของเราหรือไม่

รหัส epoly เพิ่มเติมด้านล่าง:

google.maps.Polygon.prototype.Centroid = function() {
var p = this;
var b = this.Bounds();
var c = new google.maps.LatLng((b.getSouthWest().lat()+b.getNorthEast().lat())/2,(b.getSouthWest().lng()+b.getNorthEast().lng())/2);
if (!p.Contains(c)){
    var fc = c; //False Centroid
    var percentages = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]; //We'll check every 10% down each ray and see if we're inside our polygon
    var rays = [
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getNorthEast().lat(),fc.lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(fc.lat(),b.getNorthEast().lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getSouthWest().lat(),fc.lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(fc.lat(),b.getSouthWest().lng())]}),
        new google.maps.Polyline({path:[fc,b.getNorthEast()]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getSouthWest().lat(),b.getNorthEast().lng())]}),
        new google.maps.Polyline({path:[fc,b.getSouthWest()]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getNorthEast().lat(),b.getSouthWest().lng())]})
    ];
    var lp;
    for (var i=0;i<percentages.length;i++){
        var percent = percentages[i];
        for (var j=0;j<rays.length;j++){
            var ray = rays[j];
            var tp = ray.GetPointAtDistance(percent*ray.Distance()); //Test Point i% down the ray
            if (p.Contains(tp)){
                lp = tp; //It worked, store it
                break;
            }
        }
        if (lp){
            c = lp;
            break;
        }
    }
}
return c;}

ยังคงแฮ็กเล็กน้อย แต่ดูเหมือนว่าจะทำงาน


วิธีนี้จะล้มเหลวสำหรับรูปหลายเหลี่ยมที่คดเคี้ยว ตัวอย่างเช่นบัฟเฟอร์ polyline {{0, 9}, {10, 20}, {9, 9}, {20, 10}, {9, 0}, {20, -10}, {9, -9} , {10, -20}, {0, -9}, {-10, -20}, {-9, -9}, {-20, -10}, {-9, 0}, {-20, 10}, {-9, 9}, {-10, 20}, {0,9}} น้อยกว่า 1/2 นอกจากนี้ยังไม่มีประสิทธิภาพเมื่อเทียบกับวิธีการ QAD ของ Dandy เช่น
whuber

1

อัลกอริทึม 'สกปรก' อีกอันที่ต้องทำ:

  • ใช้กล่องขอบเขตของรูปทรงเรขาคณิต (Xmax, Ymax, Xmin, Ymin)

  • วนซ้ำจนกระทั่ง( Xmin+rand*(Xmax-Xmin), Ymin+rand*(Ymax-Ymin) ) พบจุดสุ่มภายในเรขาคณิต (โดยใช้Google-Maps-Point-in-Polygon )


+1 เนื่องจากอาจมีโอกาสพอสมควรที่จะถูกโจมตีเป็นครั้งที่สอง ตราบใดที่ "สุ่ม" ของคุณสามารถทำซ้ำได้ในแต่ละครั้งเพื่อไม่รบกวนผู้ใช้นี่เป็นโซลูชันที่ถูกต้องเช่นกัน อัตราต่อรองของมันไม่ได้ไปถึงจุดที่ถูกต้องในไม่ช้าจะผอมโดยเฉพาะถ้าคุณเริ่มต้นด้วยการเดาที่ดี
Dandy

1
@Dandy: ที่จริงแล้วในบางกรณีนี่อาจเป็นอัลกอริทึมที่แย่มาก พิจารณาเศษไม้เส้นทแยงมุมแคบ ๆ สิ่งเหล่านี้มีอยู่ในทางปฏิบัติ (เช่นห่อตัวยาวด้านหน้าของถนน) และสามารถใช้พื้นที่น้อยกว่า 0.1% ของกล่อง จำกัด (บางครั้งก็น้อยกว่า) เพื่อให้แน่ใจว่าสมเหตุสมผล (มั่นใจ 95%) จากการกดปุ่มรูปหลายเหลี่ยมดังกล่าวด้วยเทคนิคนี้จะต้องใช้การวนซ้ำประมาณ 3,000 ครั้ง
whuber

@Whuber: ถ้าคุณเลือกตำแหน่งเริ่มต้นที่ไม่ดีใช่นั่นอาจใช้เวลาสักครู่ก่อนจะทำงานให้เสร็จ หากคุณพิจารณาด้วยว่า 95% ของการคลิกจะอยู่ในรูปทรงเรขาคณิตที่ต้องการมากกว่าซึ่งอาจเป็นปัญหา 5% ของเวลาเท่านั้น เช่นเดียวกับคำถามอื่น ๆ ของ GIS.se หากประสิทธิภาพเป็นเป้าหมายที่ไม่เคยมีวิธีแก้ปัญหาเดียวมันเป็นการดีที่สุดที่จะเปลี่ยนกลยุทธ์ตามการวิเคราะห์พฤติกรรม ไม่มีเหตุผลใดที่จะใช้งานซ้ำ 3,000 รายการ คุณสามารถประกันตัว QAD ของฉันได้ตลอดเวลาหลังวันที่ 10 ฉันคิดว่ามันคุ้มค่าที่จะลองทำสิ่งนี้สักสองสามครั้งเนื่องจากตำแหน่งอาจเป็นที่ต้องการมากกว่า
Dandy

@Dandy: แต่สิ่งที่สำคัญกับโซลูชัน QAD ของคุณ? คุณสามารถแก้ไขได้นิดหน่อยด้วยการย้ายจาก labelpoint ทดลองดั้งเดิมไปยังจุดสุดยอดที่ใกล้ที่สุดในบัฟเฟอร์ภายในของรูปหลายเหลี่ยม: ยัง QAD แต่ตอนนี้รับประกันได้ว่าจะลงที่ตำแหน่งภายในของคุณสมบัติดั้งเดิม BTW กลยุทธ์ในการประกันตัวของคุณในไม่ช้าเป็นกลยุทธ์ที่ดี เมื่อใดก็ตามที่ฉันโค้ดโพรบแบบสุ่มฉันจะคำนวณอัตราส่วนของพื้นที่ของฟีเจอร์กับขอบเขตของกล่องคุณสมบัติใช้เพื่อค้นหาเวลาที่คาดว่าจะประสบความสำเร็จและเตือนผู้ใช้ทันทีหากใช้เวลานาน
whuber

@ อัตราส่วนของการแก้ปัญหาในพื้นที่เป็นความคิดที่ดีเพราะคุณเพียงคำนวณเซนทรอยด์เมื่อคุณคำนวณพื้นที่ สำหรับปัญหาเกี่ยวกับโซลูชัน QAD ของฉัน: มันอยู่บนขอบ ถ้าฉันเลือกจุดนั้นและบัฟเฟอร์เหมือนที่คุณพูดรัศมีนั้น "เล็ก" อาจใหญ่กว่าความยาวของส่วนที่แคบ มีตัวเรือนมุมเสมอ สิ่งที่ควรพิจารณามากเพียงแค่สร้างบอลลูนที่จะทำให้ UI ยุ่งเหยิงและทำให้เรขาคณิตไม่ชัดเจนอยู่ดี อาจดีกว่าที่จะเลือกจุดสุดยอดสูงสุดหรือต่ำสุด
Dandy

1

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


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

@Dandy: นั่นคือหัวใจของมัน มีหลายวิธีที่จะทำสิ่งนี้ขึ้นอยู่กับสิ่งที่ GIS ของคุณทำขึ้นมาเอง ตัวอย่างเช่นเมื่อคุณพบรังสีที่ตัดกันคุณลักษณะดั้งเดิมในชุดของความยาวเป็นบวกการแยกนั้นจะเป็นการรวมกันของส่วนของเส้นที่ไม่ปะติดปะต่อ ใช้ศูนย์กลางของกลุ่มใด ๆ เหล่านั้น อีกวิธีคือเริ่มจากจุดใดก็ได้บนคุณสมบัติ (ควรอยู่ใกล้กับตรงกลางซึ่งเป็นวิธีที่ QED ของคุณทำสำเร็จ) สร้างรูปหลายเหลี่ยมแบบง่าย ๆ (เช่นสี่เหลี่ยมจัตุรัส) ที่กึ่งกลางตรงนั้นตัดกับคุณลักษณะดั้งเดิม องค์ประกอบ ...
whuber

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

0

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

ระวังรูปร่างของ U และรูปร่างที่ซับซ้อนขึ้น :) เป็นไปได้สำหรับสิ่งเหล่านั้นเลือกค่าเฉลี่ยของ longitudes คู่ขวาสุด (แต่ละคู่จะสอดคล้องกับชิ้นส่วนของรูปหลายเหลี่ยม) เนื่องจากหน้าต่างข้อมูลนั้นเป็นแบบนั้น?

สิ่งนี้จะช่วยให้คุณควบคุมตำแหน่งได้มากขึ้นเช่นกัน ตัวอย่างเช่นอาจเป็นการดีหากวางตำแหน่งหน้าต่างข้อมูลที่ 66 หรือ 75% ในแนวตั้งเพื่อให้สามารถมองเห็นรูปหลายเหลี่ยมได้มากขึ้น (หรืออาจจะไม่ใช่! แต่คุณมีปุ่มปรับแต่ง)


0

วิธีการเพียงแค่ใช้จุดที่ผู้ใช้คลิกเพื่อเลือกถ้ามันถูกเลือกโดยผู้ใช้ที่เป็น


มันสามารถเลือกได้โดยการคลิกเมาส์หรือแบบสอบถามที่ไม่ใช่เชิงพื้นที่ดังนั้นสิ่งนี้จะไม่ทำงาน
Jason

0

ฉันพยายามที่จะแก้ปัญหานี้เช่นกัน ฉันได้กำหนดเงื่อนไขในรูปหลายเหลี่ยมของฉันที่พวกเขาไม่สามารถมีเส้นข้ามซึ่งเข้าสู่สิ่งที่ฉันจะอธิบาย

ดังนั้นวิธีการของฉันใช้การคำนวณแบบสามเหลี่ยม ใช้จุดสุดยอดแบบสุ่ม (อาจใช้จุดสุดยอดที่มาก N, E, W หรือ S อาจลดความซับซ้อนของสิ่งต่าง ๆ )

จากจุดสุดยอดนี้ลากเส้นไปยังจุดสุดยอดหนึ่งจุดยอดออกไปเช่นถ้าจุดยอดของคุณคือจุดสุดยอด 3 ให้ดูที่จุดยอด 3 + 2

สร้างเส้นจากจุดสุดยอดดั้งเดิมของคุณไปยังจุดสุดยอดนี้ หากสายที่สร้างขึ้น:

  1. ข้ามไม่มีสายอื่นและ
  2. จุดกึ่งกลางไม่อยู่นอกรูปหลายเหลี่ยม

จากนั้นคุณได้สร้างรูปสามเหลี่ยมที่อยู่ภายในรูปหลายเหลี่ยม หากจุดสุดยอดที่ประสบความสำเร็จคือ n + 2 สามเหลี่ยมของคุณคือ {n, n + 1, n + 2} ซึ่งเราจะเรียกว่าเป็น {v, v1, v2} ถ้าไม่ลองจุดสุดยอดถัดไปและทำต่อไปจนกว่าจะถึงจุดสูงสุดทั้งหมดแล้ว

เมื่อคุณพบรูปสามเหลี่ยมให้หาจุดศูนย์กลางของจุดนั้นโดยการลากเส้นจากจุดยอด v ไปยังจุดกึ่งกลางของ v1 และ v2 จุดกึ่งกลางของเส้นนั้นรับประกันว่าจะอยู่ในรูปสามเหลี่ยมและด้านในของรูปหลายเหลี่ยม

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


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