Thuật toán tìm trung tâm đa giác không đều (điểm nhãn)


13

Tôi cần tìm một centroid (hoặc điểm nhãn) cho các đa giác có hình dạng không đều trong Google Maps. Tôi đang hiển thị InfoWindows cho bưu kiện và cần một nơi để neo InfoWindow được đảm bảo ở trên bề mặt. Xem hình ảnh dưới đây.

văn bản thay thế văn bản thay thế

Trong thực tế, tôi không cần bất cứ thứ gì cụ thể trên Google Maps, chỉ cần tìm ý tưởng về cách tự động tìm điểm này.

Ý tưởng đầu tiên của tôi là tìm trung tâm "sai" bằng cách lấy lat và lng trung bình và các điểm đặt ngẫu nhiên từ đó cho đến khi tôi tìm thấy một điểm giao nhau với đa giác. Tôi đã có mã điểm đa giác. Điều này chỉ có vẻ hết sức "hack" với tôi.

Tôi nên lưu ý rằng tôi không có quyền truy cập vào bất kỳ mã phía máy chủ nào xuất ra hình học để tôi không thể làm bất cứ điều gì như ST_PointOnSurface (the_geom).

Câu trả lời:


6

Nhanh và bẩn: Nếu trung tâm "sai" không nằm trong đa giác, hãy sử dụng đỉnh gần nhất tới điểm đó.


Tôi đã không nghĩ về điều này. Lý tưởng nhất là tôi thích điểm này trong đa giác chứ không phải ở rìa, nhưng đây có thể là điều tôi rơi trở lại.
Jason

Khi bạn đã tìm thấy một điểm cạnh, bạn có thể cắt một hình vuông nhỏ có tâm ở điểm đó với đa giác và sau đó chọn tâm của giao điểm. Khi hình vuông đủ nhỏ, điều này được đảm bảo là một điểm bên trong (mặc dù tất nhiên nó sẽ rất gần với một cạnh).
whuber

@Jason Nếu bạn sử dụng centroid thực, bạn có thể ít gặp phải vấn đề này. Không nên quá khó để dịch một cái gì đó nhanh chóng sang JavaScript: github.com/cartesiananalytics/Pipra/blob/master/src/ Kẻ
Dandy

Mặc dù giải pháp của tôi (tia từ trung tâm giả) sẽ hoạt động hầu hết thời gian, tôi nghĩ giải pháp này có thể sẽ hoạt động tốt nhất vì tính đơn giản của nó và thực tế là bạn được đảm bảo tìm thấy một điểm ít nhất ở rìa và có thể dễ dàng dịch chuyển nó ở bên trong đa giác với rất ít nỗ lực.
Jason

3

Bạn có thể muốn xem cái này: http://github.com/tparkin/Google-Maps-Point-in-Polygon

Nó xuất hiện để sử dụng thuật toán Ray Casting phù hợp với trường hợp mà bạn đã trình bày.

Có một bài viết về nó ở đây. http://appdelegateinc.com/blog/2010/05/16/point-in-polygon-checking/


Nếu bạn muốn thực hiện điều này ở phía máy chủ, cả JTS (Java) và Geos (C) đều thực hiện chức năng này.
DavidF

Vâng, có lẽ tôi nên nói thêm rằng tôi đã có mã để xác định xem trọng tâm "được tính toán" của tôi có nằm trong đa giác hay không. Những gì tôi thực sự muốn là một số cách để tạo ra một centroid nằm trong đa giác.
Jason

3

Một thuật toán ESRI (cũ hơn) tính toán trọng tâm và sau khi kiểm tra nó để đưa vào đa giác, di chuyển nó theo chiều ngang nếu cần thiết cho đến khi nó nằm trong đa giác. (Điều này có thể được thực hiện theo nhiều cách tùy thuộc vào hoạt động cơ bản nào có sẵn trong môi trường lập trình của bạn.) Điều này có xu hướng tạo ra các điểm nhãn khá gần với trung tâm thị giác của đa giác: hãy thử minh họa.


1

Tôi đã giải quyết vấn đề của mình bằng cách mở rộng mã epoly phổ biến từ http://econymous.org.uk/gmap . Về cơ bản những gì tôi đã làm là:

  • Tạo một loạt các tia bắt đầu từ "centroid giả" và mở rộng đến mọi góc và cạnh (tổng cộng 8)
  • Tăng dần một điểm 10,20,30 ... phần trăm xuống mỗi tia và xem điểm này có nằm trong đa giác ban đầu của chúng tôi không

Mã epoly mở rộng dưới đây:

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;}

Vẫn còn một chút hacky nhưng nó dường như làm việc.


Phương pháp này sẽ thất bại đối với một số đa giác quanh co. Ví dụ: đệm đa tuyến {{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}} nhỏ hơn 1/2. Chẳng hạn, nó cũng không hiệu quả so với phương pháp QAD của Dandy.
whuber

1

Một thuật toán 'bẩn' khác để làm điều đó:

  • Lấy hộp giới hạn của hình học (Xmax, Ymax, Xmin, Ymin)

  • Lặp lại cho đến khi ( Xmin+rand*(Xmax-Xmin), Ymin+rand*(Ymax-Ymin) ) tìm thấy một điểm ngẫu nhiên trong hình học (sử dụng Google-Maps-Point-in-Polygon )


+1 vì điều này có thể có cơ hội trúng lần thứ hai. Miễn là "ngẫu nhiên" của bạn có thể được tái tạo mỗi lần để không gây khó chịu cho người dùng thì đây cũng là một giải pháp hợp lệ. Tỷ lệ cược của nó không đạt được một điểm hợp lệ sớm là mỏng, đặc biệt nếu bạn bắt đầu với một điểm đoán tốt.
Dandy

1
@Dandy: Trên thực tế, trong một số trường hợp, đây có thể là một thuật toán thực sự kém. Hãy xem xét một mảnh chéo chéo hẹp chẳng hạn. Chúng tồn tại trong thực tế (ví dụ, các lô đất dài mặt tiền đường) và có thể dễ dàng chiếm ít hơn 0,1% hộp giới hạn (đôi khi ít hơn nhiều). Để chắc chắn một cách hợp lý (95% tự tin) khi đánh một đa giác như vậy với kỹ thuật này sẽ cần khoảng 3.000 lần lặp.
whuber

@Whuber: Nếu bạn chọn một vị trí bắt đầu tồi thì có thể mất một lúc để chạy đến khi hoàn thành. Nếu bạn cũng xem xét rằng theo giả thuyết, 95% số lần nhấp sẽ thuộc về hình học mong muốn hơn thì đây chỉ có thể là vấn đề 5%. Cũng như với một câu hỏi khác của GIS.se nếu hiệu suất là mục tiêu không bao giờ có một giải pháp duy nhất, tốt nhất là thay đổi chiến thuật dựa trên phương pháp phỏng đoán. Không có lý do để chạy nó trong 3000 lần lặp. Bạn luôn có thể bảo lãnh cho QAD của tôi sau 10. Tôi nghĩ rằng sẽ rất đáng để thử cái này cho một vài lần lặp vì vị trí có thể được mong muốn hơn.
Dandy

@Dandy: Nhưng vấn đề với giải pháp QAD của bạn là gì? Bạn thậm chí có thể sửa đổi nó một chút bằng cách di chuyển từ labelpoint ban đầu sang đỉnh gần nhất trong một số bộ đệm bên trong của đa giác: vẫn là QAD nhưng hiện được đảm bảo đáp ứng vị trí bên trong của tính năng gốc. BTW, chiến lược bảo lãnh của bạn sớm là một chiến lược tốt. Bất cứ khi nào tôi mã hóa một thăm dò ngẫu nhiên như thế này, tôi sẽ tính trước tỷ lệ của khu vực tính năng với hộp giới hạn của nó, sử dụng nó để tìm thời gian dự kiến ​​thành công và cảnh báo ngay lập tức cho người dùng nếu nó có thể kéo dài.
whuber

@Whuber tỷ lệ diện tích heuristic là một ý tưởng tuyệt vời bởi vì bạn chỉ cần tính toán trọng tâm khi bạn tính diện tích. Đối với vấn đề với giải pháp QAD của tôi: nó nằm ở rìa. Nếu tôi chọn điểm đó và đệm nó như bạn nói, bán kính "nhỏ" đó có thể lớn hơn chiều dài trên phần hẹp đó. Luôn luôn có một trường hợp góc. Quá nhiều thứ để xem xét, chỉ để tạo ra một quả bóng sẽ làm lộn xộn giao diện người dùng và che khuất hình học. Có lẽ tốt hơn để chọn đỉnh cao nhất hoặc thấp nhất.
Dandy

1

Theo sự làm rõ gần đây của bạn rằng bạn muốn một vị trí bên trong nghiêm ngặt, bạn có thể chọn bất kỳ điểm nào trên Biến đổi trục trung gian không nằm trên ranh giới của đa giác. (Nếu bạn không có mã cho MAT, bạn có thể ước chừng nó bằng cách đệm âm cho đa giác. Một tìm kiếm nhị phân hoặc ẩn sẽ nhanh chóng tạo ra một đa giác nhỏ bên trong xấp xỉ một phần của MAT; sử dụng bất kỳ điểm nào trên ranh giới của nó.)


Tôi hiểu những gì bạn đã nói về việc sử dụng cạnh của hình học sao cho cạnh nằm trong phần bên trong của đa giác quan tâm. Tôi không hiểu làm thế nào bạn sẽ tạo ra cạnh / đỉnh này. Điều duy nhất tôi có thể nghĩ đến là tạo ra một tam giác ảo bằng cách cắt một tia vuông góc từ điểm quan tâm đến đoạn đối diện với đoạn của điểm đã chọn. Điểm giữa giữa hai điểm đó có thể là đỉnh của tam giác ảo đó.
Dandy

@Dandy: Điều đó đến trung tâm của nó. Có nhiều cách để giải quyết vấn đề này tùy thuộc vào những gì mà hệ thống thông tin địa lý của bạn thực hiện. Ví dụ: một khi bạn đã tìm thấy một tia giao nhau với tính năng ban đầu trong một tập hợp độ dài dương, giao điểm đó sẽ là một liên kết rời rạc của các đoạn đường. Sử dụng trung tâm của bất kỳ phân khúc nào. Một cách khác là bắt đầu với bất kỳ điểm nào trên đối tượng địa lý (tốt nhất là gần điểm giữa của nó, đó là điều mà phương thức QED của bạn đã hoàn thành), tạo một đa giác nhỏ đơn giản (ví dụ: hình vuông) ở giữa, giao với tính năng ban đầu, chọn kết nối duy nhất thành phần ...
whuber

(tiếp tục) ... chứa điểm bắt đầu và đệ quy chọn một trung tâm cho thành phần đó. Sẽ có vô số phương thức khả dụng khi GIS của bạn cho phép bạn lặp lại các chuỗi các đỉnh mô tả ranh giới của đối tượng địa lý. Nếu bộ đệm âm được hỗ trợ, bạn có thể lặp đi lặp lại tìm một tập hợp các điểm bên trong khoảng cách tối đa ("bộ xương", là một tập hợp con của MAT). Đây là một ít tốn kém nhưng khá dễ dàng để lập trình và tạo ra các điểm nhãn tuyệt vời.
whuber

0

Tại sao không sử dụng trọng tâm cho vị trí thẳng đứng (vĩ độ)? Sau đó, bạn có thể định vị nhãn theo chiều ngang bằng cách chọn kinh độ trung bình ở vĩ độ đó . (Đối với điều này, bạn cần tìm giá trị kinh độ cho cạnh đa giác ở một vĩ độ cụ thể, điều này sẽ không gây rắc rối cho bạn).

Ngoài ra, hãy cẩn thận của hình chữ U, và những hình phức tạp hơn. :) Có thể đối với những người đó, chọn trung bình của cặp kinh độ ngoài cùng bên phải (mỗi cặp sẽ tương ứng với một lát đa giác), vì cửa sổ thông tin được định hướng theo cách đó?

Điều này cũng cho phép bạn kiểm soát nhiều hơn một chút về định vị; ví dụ, có thể tốt khi đặt cửa sổ thông tin ở mức 66 hoặc 75% theo chiều dọc, để có thể nhìn thấy nhiều đa giác hơn. (Hoặc có thể không! Nhưng bạn có núm để điều chỉnh.)


0

Làm thế nào về việc chỉ sử dụng điểm mà người dùng đã nhấp để chọn nó, nếu đó là do người dùng chọn.


Nó có thể được chọn bằng cách nhấp chuột hoặc truy vấn không theo không gian, vì vậy điều này sẽ không luôn hoạt động.
Jason

0

Tôi cũng đang cố gắng giải quyết điều này. Tôi đã áp đặt một điều kiện cho đa giác của mình rằng chúng không thể có các đường chéo đi vào những gì tôi sẽ mô tả.

Vì vậy, cách tiếp cận của tôi sử dụng triangulation. Lấy một đỉnh ngẫu nhiên (có thể lấy một đỉnh ở cực trị N, E, W hoặc S có thể đơn giản hóa mọi thứ).

Từ đỉnh này, vẽ các đường thẳng tới đỉnh một đỉnh, tức là nếu đỉnh của bạn là đỉnh 3, hãy nhìn vào đỉnh 3 + 2.

Xây dựng một dòng từ đỉnh ban đầu của bạn đến đỉnh này. Nếu đường được xây dựng:

  1. vượt qua không có dòng khác và
  2. trung điểm của nó không nằm ngoài đa giác

Sau đó, bạn đã xây dựng một hình tam giác nằm trong đa giác. Nếu đỉnh thành công là n + 2, thì tam giác của bạn là {n, n + 1, n + 2}, chúng ta sẽ gọi là {v, v1, v2}. Nếu không, hãy thử đỉnh tiếp theo và tiếp tục cho đến khi tất cả các đỉnh đã được thử.

Khi bạn tìm thấy một hình tam giác, hãy tìm tâm của nó bằng cách lấy một đường thẳng từ đỉnh v đến trung điểm của v1 và v2. Điểm giữa của đường thẳng đó được đảm bảo nằm bên trong tam giác và bên trong đa giác.

Tôi chưa mã hóa điều này, nhưng tôi có thể thấy khi tôi nghĩ rằng một đa giác với các đường chéo trên thực tế sẽ gây ra một số điều kiện kỳ ​​lạ khi điều này không hoạt động. Nếu đó là loại đa giác bạn có, bạn cần kiểm tra từng phân đoạn dòng trên đa giác và chắc chắn rằng nó không bị vượt qua. Bỏ qua các phân đoạn dòng được gạch chéo và tôi nghĩ nó sẽ hoạt động.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.