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.
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.
Câu trả lời:
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ó.)
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.
Nếu bạn đang tìm kiếm mã, mã nguồn plugin cơ bản nên được giúp đỡ.
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;
}
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
representative_point
phương pháp: shapely.readthedocs.io/en/latest/...
Nếu R là một tùy chọn, xem ?spsample
trong sp
gó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 đó spsample
hoạ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.
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). Clip
khô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ó SimpleRandomSample
hiệ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
}
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
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.
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.
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.
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ả ================================================= =====================
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.
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ả ================================================= ==================
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ả ================================================= =========================