Tạo các điểm nằm bên trong đa giác


30

Tôi có tính năng đa giác và muốn có thể tạo các điểm bên trong nó. Tôi cần điều này cho một nhiệm vụ phân loại.

Tạo các điểm ngẫu nhiên cho đến khi một điểm nằm trong đa giác sẽ không hoạt động vì thời gian thực sự không thể đoán trước được.


3
Trái lại, thời gian là có thể dự đoán. Nó tỷ lệ thuận với tỷ lệ diện tích phạm vi của đa giác chia cho diện tích của đa giác, nhân với thời gian cần thiết để tạo và kiểm tra một điểm duy nhất. Thời gian thay đổi một chút, nhưng sự thay đổi tỷ lệ thuận với căn bậc hai của số điểm. Đối với số lượng lớn, điều này trở nên không quan trọng. Chia đa giác quanh co thành nhiều phần nhỏ gọn hơn nếu cần thiết để giảm tỷ lệ diện tích này xuống giá trị thấp. Chỉ đa giác fractal sẽ cung cấp cho bạn rắc rối, nhưng tôi nghi ngờ bạn có chúng!
whuber



@Pablo: tìm thấy tốt. Tuy nhiên, cả hai câu hỏi này đều là phần mềm cụ thể và cả hai đều quan tâm đến việc đặt các mảng điểm thường xuyên trong đa giác, không phải là điểm ngẫu nhiên
whuber

đồng ý với sự khác biệt của whuber là điểm ngẫu nhiên so với việc tạo điểm thông thường trong một đa giác.
Mapperz

Câu trả lời:


20

Bắt đầu bằng cách phân tách đa giác thành các hình tam giác, sau đó tạo các điểm bên trong chúng . (Để phân phối đồng đều, hãy cân mỗi tam giác theo diện tích của nó.)


2
+1 Đơn giản và hiệu quả. Thật đáng để chỉ ra rằng các điểm ngẫu nhiên đồng nhất có thể được tạo trong một hình tam giác mà không bị từ chối, bởi vì có các ánh xạ bảo tồn khu vực (dễ dàng tính toán) giữa bất kỳ tam giác và tam giác vuông, là nửa hình vuông, nói là một nửa tọa độ y vượt quá tọa độ x. Tạo hai tọa độ ngẫu nhiên và sắp xếp chúng để có được một điểm ngẫu nhiên trong tam giác cân, sau đó ánh xạ trở lại tam giác ban đầu.
whuber

+1 Tôi thực sự thích các cuộc thảo luận về tọa độ tam giác được tham chiếu bởi bài viết mà bạn trích dẫn. Tôi cho rằng điều này sẽ phù hợp với hình cầu có bề mặt được biểu diễn dưới dạng một hình tam giác. Trên một mặt phẳng được chiếu, nó sẽ không phải là một bản phân phối thực sự ngẫu nhiên, phải không?
Kirk Kuykendall

@whuber - +1 lại với bạn. Một cách khác (trong liên kết, nhưng họ vẫy tay trên nó) là phản ánh các điểm bị từ chối từ tứ giác được lấy mẫu thống nhất qua cạnh được chia sẻ và quay lại vào tam giác.
Dan S.

@Kirk - liên kết trích dẫn là một liên lạc chống hữu ích ở chỗ nó liệt kê một loạt các phương pháp lấy mẫu sai (không đồng nhất), bao gồm cả tọa độ tam giác, trước cách "đúng". Nó không giống như có một cách trực tiếp để lấy mẫu tọa độ w / trilinear thống nhất. Tôi sẽ tiếp cận lấy mẫu thống nhất trên toàn bộ hình cầu bằng cách chuyển đổi các vectơ đơn vị ngẫu nhiên trong 3d thành tương đương lat / lon của chúng, nhưng đó chỉ là tôi. (. Không chắc chắn về lấy mẫu bó buộc để cầu tam giác / đa giác) (Cũng không chắc chắn về lấy mẫu thực sự thống nhất trên ví dụ WGS84: góc chỉ chọn sẽ thiên vị một chút về phía cực, tôi nghĩ.)
Dan S.

1
@Dan Để lấy mẫu đồng đều hình cầu, sử dụng phép chiếu diện tích bằng nhau hình trụ (tọa độ là kinh độ và cosin của vĩ độ). Nếu bạn muốn lấy mẫu mà không sử dụng phép chiếu, có một mẹo hay: tạo ba biến thể chuẩn thông thường độc lập (x, y, z) và chiếu chúng tới điểm (R x / n, R y / n, R * z / n ) trong đó n ^ 2 = x ^ 2 + y ^ 2 + z ^ 2 và R là bán kính trái đất. Chuyển đổi sang (lat, lon) nếu cần (sử dụng vĩ độ tự động khi làm việc trên một hình cầu). Nó hoạt động bởi vì phân phối bình thường tầm thường này là đối xứng hình cầu. Để lấy mẫu hình tam giác, hãy dán vào hình chiếu.
whuber

14

Khi bạn đặt thẻ QGIS cho câu hỏi này: Công cụ Điểm ngẫu nhiên có thể được sử dụng với lớp ranh giới.

nhập mô tả hình ảnh ở đây

Nếu bạn đang tìm kiếm mã, mã nguồn plugin cơ bản nên được giúp đỡ.


1
Ngay cả 5 năm sau, vẫn thực sự hữu ích!
Bị mắc kẹt vào

10

Bạn có thể xác định phạm vi của đa giác, sau đó hạn chế việc tạo số ngẫu nhiên cho các giá trị X và Y trong các phạm vi đó.

Quá trình cơ bản: 1) Xác định maxx, maxy, minx, miny của các đỉnh đa giác, 2) Tạo các điểm ngẫu nhiên bằng cách sử dụng các giá trị này làm giới hạn 3) Kiểm tra từng điểm để giao nhau với đa giác của bạn, 4) Dừng tạo khi bạn có đủ điểm thỏa mãn giao điểm kiểm tra

Đây là một thuật toán (C #) cho bài kiểm tra giao nhau:

bool PointIsInGeometry(PointCollection points, MapPoint point)
{
int i;
int j = points.Count - 1;
bool output = false;

for (i = 0; i < points.Count; i++)
{
    if (points[i].X < point.X && points[j].X >= point.X || points[j].X < point.X && points[i].X >= point.X)
    {
        if (points[i].Y + (point.X - points[i].X) / (points[j].X - points[i].X) * (points[j].Y - points[i].Y) < point.Y)
        {
            output = !output;
        }
    }
    j = i;
}
return output;
}

10

Có một số thư viện tốt ngoài kia làm hầu hết các công việc nặng nhọc cho bạn.

Ví dụ sử dụng [shapely] [1] trong python.

import random
from shapely.geometry import Polygon, Point

def get_random_point_in_polygon(poly):
     minx, miny, maxx, maxy = poly.bounds
     while True:
         p = Point(random.uniform(minx, maxx), random.uniform(miny, maxy))
         if poly.contains(p):
             return p

p = Polygon([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)])
point_in_poly = get_random_point_in_polygon(mypoly)

Hoặc sử dụng .representative_point()để lấy điểm trong đối tượng (như được đề cập bởi dain):

Trả về một điểm được tính toán giá rẻ được đảm bảo nằm trong đối tượng hình học.

poly.representative_point().wkt
'POINT (-1.5000000000000000 0.0000000000000000)'

  [1]: https://shapely.readthedocs.io

2
Không phải là từ nhập shapely.geometry ...?
PyMapr

1
Bạn cũng có thể sử dụng các representative_pointphương pháp: shapely.readthedocs.io/en/latest/...
dain

6

Nếu R là một tùy chọn, xem ?spsampletrong spgói. Các đa giác có thể được đọc từ bất kỳ định dạng hỗ trợ GDAL nào được tích hợp trong gói rgdal, sau đó spsamplehoạt động trực tiếp trên đối tượng được nhập với nhiều tùy chọn lấy mẫu.


+1 - Vì R là nguồn mở nếu muốn sao chép, bạn luôn có thể truy cập vào nguồn để xem cách chúng được thực hiện. Đối với các mẫu điểm, người ta cũng có thể quan tâm đến các công cụ mô phỏng trong gói spatstat.
Andy W

5

Tôi muốn đưa ra một giải pháp đòi hỏi rất ít về mặt phân tích GIS. Đặc biệt, nó không yêu cầu tam giác bất kỳ đa giác.

Thuật toán sau, được đưa ra trong mã giả, đề cập đến một số thao tác đơn giản ngoài khả năng xử lý danh sách cơ bản (tạo, tìm độ dài, nối, sắp xếp, trích xuất danh sách con và nối) và tạo ra các số float ngẫu nhiên trong khoảng [0, 1):

Area:        Return the area of a polygon (0 for an empty polygon).
BoundingBox: Return the bounding box (extent) of a polygon.
Width:       Return the width of a rectangle.
Height:      Return the height of a rectangle.
Left:        Split a rectangle into two halves and return the left half.
Right:       ... returning the right half.
Top:         ... returning the top half.
Bottom:      ... returning the bottom half.
Clip:        Clip a polygon to a rectangle.
RandomPoint: Return a random point in a rectangle.
Search:      Search a sorted list for a target value.  Return the index  
             of the last element less than the target.
In:          Test whether a point is inside a polygon.

Tất cả đều có sẵn trong hầu hết mọi môi trường lập trình đồ họa hoặc GIS (và dễ dàng viết mã nếu không). Clipkhông được trả lại các đa giác thoái hóa (nghĩa là những đa giác có diện tích bằng 0).

Thủ tục có SimpleRandomSamplehiệu quả có được một danh sách các điểm được phân phối ngẫu nhiên trong một đa giác. Nó là một lớp bọc cho SRS, phá vỡ đa giác thành các mảnh nhỏ hơn cho đến khi mỗi mảnh đủ nhỏ gọn để được lấy mẫu một cách hiệu quả. Để làm điều này, nó sử dụng một danh sách các số ngẫu nhiên được tính toán trước để quyết định có bao nhiêu điểm để phân bổ cho mỗi phần.

SRS có thể được "điều chỉnh" bằng cách thay đổi tham số t. Đây là hộp giới hạn tối đa: tỷ lệ diện tích đa giác có thể được dung thứ. Làm cho nó nhỏ (nhưng lớn hơn 1) sẽ khiến hầu hết các đa giác bị chia thành nhiều phần; làm cho nó lớn có thể khiến nhiều điểm dùng thử bị từ chối đối với một số đa giác (hình sin, có độ dốc hoặc đầy lỗ). Điều này đảm bảo rằng thời gian tối đa để lấy mẫu đa giác ban đầu là có thể dự đoán được.

Procedure SimpleRandomSample(P:Polygon, N:Integer) {
    U = Sorted list of N independent uniform values between 0 and 1
    Return SRS(P, BoundingBox(P), U)
}

Các thủ tục tiếp theo gọi chính nó đệ quy nếu cần thiết. Biểu thức bí ẩn t*N + 5*Sqrt(t*N)ước tính một cách dè dặt một giới hạn trên về số lượng điểm sẽ cần, tính cho sự thay đổi cơ hội. Khả năng điều này sẽ thất bại chỉ là 0,3 trên một triệu cuộc gọi thủ tục. Tăng 5 đến 6 hoặc thậm chí 7 để giảm khả năng này nếu bạn muốn.

Procedure SRS(P:Polygon, B:Rectangle, U:List) {
    N = Length(U)
    If (N == 0) {Return empty list}
    aP = Area(P)
    If (aP <= 0) {
        Error("Cannot sample degenerate polygons.")
        Return empty list
    }
    t = 2
    If (aP*t < Area(B)) {
        # Cut P into pieces
        If (Width(B) > Height(B)) {
            B1 = Left(B); B2 = Right(B)
        } Else {
            B1 = Bottom(B); B2 = Top(B)
        }
        P1 = Clip(P, B1); P2 = Clip(P, B2)
        K = Search(U, Area(P1) / aP)
        V = Concatenate( SRS(P1, B1, U[1::K]), SRS(P2, B2, U[K+1::N]) )
    } Else {
        # Sample P
        V = empty list
        maxIter = t*N + 5*Sqrt(t*N)
        While(Length(V) < N and maxIter > 0) {
            Decrement maxIter
            Q = RandomPoint(B)
            If (Q In P) {Append Q to V}
        }
       If (Length(V) < N) {
            Error("Too many iterations.")
       }
    }
    Return V
}

2

Nếu đa giác của bạn là lồi và bạn biết tất cả các đỉnh, bạn có thể muốn xem xét thực hiện trọng số lồi "ngẫu nhiên" của các đỉnh để lấy mẫu một điểm mới được đảm bảo nằm bên trong thân lồi (đa giác trong trường hợp này).

Ví dụ: bạn có đa giác lồi N mặt với các đỉnh

V_i, i={1,..,N}

Sau đó tạo ngẫu nhiên trọng lượng N lồi

 w_1,w_2,..,w_N such that  w_i = 1; w_i>=0

Điểm lấy mẫu ngẫu nhiên sau đó được đưa ra bởi

Y=  w_i*V_i

Có thể có nhiều cách khác nhau để lấy mẫu N trọng lượng lồi

  • Chọn các số N-1 thống nhất ngẫu nhiên trong một phạm vi (không thay thế), sắp xếp chúng và chuẩn hóa các khoảng N giữa chúng để có trọng số.
  • Bạn cũng có thể lấy mẫu từ phân phối Dirichlet thường được sử dụng làm liên hợp trước cho phân phối đa thức, tương tự như các trọng số lồi trong trường hợp của bạn.

Khi đa giác của bạn không quá lồi lõm, bạn có thể cân nhắc trước tiên chuyển đổi nó thành một thân tàu lồi. Điều này ít nhất nên giới hạn số lượng điểm nằm ngoài đa giác của bạn ở một mức độ lớn.


2

Nhiệm vụ này rất dễ giải quyết trong GRASS GIS (một lệnh) bằng cách sử dụng v.random .

Dưới đây là một ví dụ về cách thêm 3 điểm ngẫu nhiên vào các đa giác được chọn (ở đây là các vùng mã ZIP của thành phố Raleigh, NC) từ trang hướng dẫn. Bằng cách sửa đổi câu lệnh "where" của SQL, đa giác có thể được chọn.

Tạo điểm ngẫu nhiên trong đa giác được chọn


1
Nhắc nhở bắt buộc rằng mã zip là dòng, không phải đa giác.
Richard

Bạn có thể xây dựng? Đối với tôi cũng ở đây, nó đề cập đến các khu vực: en.wikipedia.org/wiki/ZIP_Code#Primary_state_prefixes
markusN

Chắc chắn: mã zip tham chiếu các bưu điện cụ thể và các tuyến gửi thư của họ. Kết quả là, mã zip là dòng, không phải đa giác. Chúng có thể chồng chéo lên nhau, chứa các lỗ hổng và không nhất thiết phải bao phủ toàn bộ Hoa Kỳ hoặc bất kỳ tiểu bang nào. Sử dụng chúng để phân chia khu vực là nguy hiểm vì lý do này. Các đơn vị điều tra dân số (như các nhóm khối) là một lựa chọn tốt hơn. Xem thêm: đâyđây .
Richard

1
Cảm ơn! Có lẽ nó cũng phụ thuộc vào quốc gia, xem ví dụ en.wikipedia.org/wiki/Postal_codes_in_Germany - tuy nhiên, mã ZIP không phải là chủ đề cốt lõi của tôi, chỉ muốn minh họa và trả lời câu hỏi ban đầu "Tạo các điểm nằm trong đa giác" chứ không phải thảo luận về định nghĩa mã ZIP là OT tại đây :-)
markusN

1
Lưu ý về cả hai tính. Tôi có lẽ nên tạo một mục blog nhỏ để tôi có thể nói điều này ngắn gọn hơn vào lần tới :-)
Richard

1

Liên kết trả lời

https://gis.stackexchange.com/a/307204/103524

Ba thuật toán sử dụng các phương pháp khác nhau.

Liên kết Git Repo

  1. Đây là một cách tiếp cận đơn giản và tốt nhất, sử dụng khoảng cách thực tế của tọa độ từ hướng x và y. Thuật toán bên trong sử dụng WGS 1984 (4326) và biến đổi kết quả thành SRID được chèn.

Hàm ================================================= ==================

CREATE OR REPLACE FUNCTION public.I_Grid_Point_Distance(geom public.geometry, x_side decimal, y_side decimal)
RETURNS public.geometry AS $BODY$
DECLARE
x_min decimal;
x_max decimal;
y_max decimal;
x decimal;
y decimal;
returnGeom public.geometry[];
i integer := -1;
srid integer := 4326;
input_srid integer;
BEGIN
CASE st_srid(geom) WHEN 0 THEN
    geom := ST_SetSRID(geom, srid);
        ----RAISE NOTICE 'No SRID Found.';
    ELSE
        ----RAISE NOTICE 'SRID Found.';
END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_min := ST_XMin(geom);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    y := ST_YMin(geom);
    x := x_min;
    i := i + 1;
    returnGeom[i] := st_setsrid(ST_MakePoint(x, y), srid);
<<yloop>>
LOOP
IF (y > y_max) THEN
    EXIT;
END IF;

CASE i WHEN 0 THEN 
    y := ST_Y(returnGeom[0]);
ELSE 
    y := ST_Y(ST_Project(st_setsrid(ST_MakePoint(x, y), srid), y_side, radians(0))::geometry);
END CASE;

x := x_min;
<<xloop>>
LOOP
  IF (x > x_max) THEN
      EXIT;
  END IF;
    i := i + 1;
    returnGeom[i] := st_setsrid(ST_MakePoint(x, y), srid);
    x := ST_X(ST_Project(st_setsrid(ST_MakePoint(x, y), srid), x_side, radians(90))::geometry);
END LOOP xloop;
END LOOP yloop;
RETURN
ST_CollectionExtract(st_transform(ST_Intersection(st_collect(returnGeom), geom), input_srid), 1);
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE;

Sử dụng hàm với một truy vấn đơn giản, hình học phải hợp lệ và đa giác, đa giác hoặc đường bao

SELECT I_Grid_Point_Distance(geom, 50, 61) from polygons limit 1;

Kết quả ================================================= ===================== nhập mô tả hình ảnh ở đây

  1. Hàm thứ hai dựa trên thuật toán Nicklas Avén . Đã cố gắng xử lý bất kỳ SRID.

    Tôi đã áp dụng những thay đổi sau đây trong thuật toán.

    1. Biến riêng cho hướng x và y cho kích thước pixel,
    2. Biến mới để tính khoảng cách trong hình cầu hoặc ellipsoid.
    3. Nhập bất kỳ SRID nào, chuyển đổi hàm Geom sang môi trường làm việc của Spheroid hoặc Ellipsoid Datum, sau đó áp dụng khoảng cách cho mỗi bên, nhận kết quả và chuyển đổi sang SRID đầu vào.

Hàm ================================================= ==================

CREATE OR REPLACE FUNCTION I_Grid_Point(geom geometry, x_side decimal, y_side decimal, spheroid boolean default false)
RETURNS SETOF geometry AS $BODY$ 
DECLARE
x_max decimal; 
y_max decimal;
x_min decimal;
y_min decimal;
srid integer := 4326;
input_srid integer; 
BEGIN
CASE st_srid(geom) WHEN 0 THEN
  geom := ST_SetSRID(geom, srid);
  RAISE NOTICE 'SRID Not Found.';
    ELSE
        RAISE NOTICE 'SRID Found.';
    END CASE;

    CASE spheroid WHEN false THEN
        RAISE NOTICE 'Spheroid False';
        srid := 4326;
        x_side := x_side / 100000;
        y_side := y_side / 100000;
    else
        srid := 900913;
        RAISE NOTICE 'Spheroid True';
    END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    x_min := ST_XMin(geom);
    y_min := ST_YMin(geom);
RETURN QUERY
WITH res as (SELECT ST_SetSRID(ST_MakePoint(x, y), srid) point FROM
generate_series(x_min, x_max, x_side) as x,
generate_series(y_min, y_max, y_side) as y
WHERE st_intersects(geom, ST_SetSRID(ST_MakePoint(x, y), srid))
) select ST_TRANSFORM(ST_COLLECT(point), input_srid) from res;
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE STRICT;

Sử dụng nó với một truy vấn đơn giản.

SELECT I_Grid_Point(geom, 22, 15, false) from polygons;

Kết quả ================================================= ==================nhập mô tả hình ảnh ở đây

  1. Chức năng dựa trên bộ tạo loạt.

Hàm ================================================= =================

CREATE OR REPLACE FUNCTION I_Grid_Point_Series(geom geometry, x_side decimal, y_side decimal, spheroid boolean default false)
RETURNS SETOF geometry AS $BODY$
DECLARE
x_max decimal;
y_max decimal;
x_min decimal;
y_min decimal;
srid integer := 4326;
input_srid integer;
x_series DECIMAL;
y_series DECIMAL;
BEGIN
CASE st_srid(geom) WHEN 0 THEN
  geom := ST_SetSRID(geom, srid);
  RAISE NOTICE 'SRID Not Found.';
    ELSE
        RAISE NOTICE 'SRID Found.';
    END CASE;

    CASE spheroid WHEN false THEN
        RAISE NOTICE 'Spheroid False';
    else
        srid := 900913;
        RAISE NOTICE 'Spheroid True';
    END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    x_min := ST_XMin(geom);
    y_min := ST_YMin(geom);

    x_series := CEIL ( @( x_max - x_min ) / x_side);
    y_series := CEIL ( @( y_max - y_min ) / y_side );
RETURN QUERY
SELECT st_collect(st_setsrid(ST_MakePoint(x * x_side + x_min, y*y_side + y_min), srid)) FROM
generate_series(0, x_series) as x,
generate_series(0, y_series) as y
WHERE st_intersects(st_setsrid(ST_MakePoint(x*x_side + x_min, y*y_side + y_min), srid), geom);
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE STRICT;

Sử dụng nó với một truy vấn đơn giản.

SELECT I_Grid_Point_Series(geom, 22, 15, false) from polygons; Kết quả ================================================= =========================

nhập mô tả hình ảnh ở đây

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.