Eu tenho o recurso de polígono e quero poder gerar pontos dentro dele. Eu preciso disso para uma tarefa de classificação.
Gerar pontos aleatórios até que um esteja dentro do polígono não funcionaria porque é realmente imprevisível o tempo que leva.
Eu tenho o recurso de polígono e quero poder gerar pontos dentro dele. Eu preciso disso para uma tarefa de classificação.
Gerar pontos aleatórios até que um esteja dentro do polígono não funcionaria porque é realmente imprevisível o tempo que leva.
Respostas:
Comece decompondo o polígono em triângulos e gere pontos dentro deles . (Para uma distribuição uniforme, pese cada triângulo pela sua área.)
Ao colocar uma tag QGIS nesta pergunta: A ferramenta Random Points pode ser usada com uma camada de limite.
Se você estiver procurando por código, o código-fonte do plug-in subjacente deve ser útil.
Você pode determinar a extensão do polígono e restringir a geração de números aleatórios para os valores X e Y dentro dessas extensões.
Processo básico: 1) Determine maxx, maxy, minx, miny de vértices poligonais, 2) Gere pontos aleatórios usando esses valores como limites 3) Teste cada ponto para interseção com seu polígono, 4) Interrompa a geração quando houver pontos suficientes que satisfaçam a interseção teste
Aqui está um algoritmo (C #) para o teste de interseção:
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;
}
Existem algumas boas bibliotecas por aí que fazem a maior parte do trabalho pesado para você.
Exemplo usando [shapely] [1] em 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)
Ou use .representative_point()
para obter um ponto dentro do objeto (como mencionado por dain):
Retorna um ponto computado barato que é garantido estar dentro do objeto geométrico.
poly.representative_point().wkt
'POINT (-1.5000000000000000 0.0000000000000000)'
[1]: https://shapely.readthedocs.io
representative_point
método: shapely.readthedocs.io/en/latest/…
Se R é uma opção, veja ?spsample
no sp
pacote. Os polígonos podem ser lidos em qualquer formato suportado pelo GDAL embutido no pacote rgdal e, em seguida, spsample
trabalha diretamente no objeto importado com uma variedade de opções de amostragem.
Eu gostaria de oferecer uma solução que requer muito pouco em termos de análise GIS. Em particular, ele não requer triangulação de polígonos.
O algoritmo a seguir, fornecido no pseudocódigo, refere-se a algumas operações simples, além dos recursos básicos de manipulação de lista (criar, encontrar comprimento, anexar, classificar, extrair sublistas e concatenar) e geração de flutuações aleatórias no intervalo [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.
Eles estão disponíveis em quase todos os ambientes de GIS ou de programação gráfica (e fáceis de codificar, se não). Clip
não deve retornar polígonos degenerados (ou seja, aqueles com área zero).
O procedimento SimpleRandomSample
obtém com eficiência uma lista de pontos distribuídos aleatoriamente em um polígono. É um invólucro para SRS
, que quebra o polígono em pedaços menores até que cada peça seja suficientemente compacta para ser amostrada com eficiência. Para fazer isso, ele usa uma lista pré-computada de números aleatórios para decidir quantos pontos alocar para cada peça.
O SRS pode ser "ajustado" alterando o parâmetro t
. Essa é a proporção máxima da caixa delimitadora: área do polígono que pode ser tolerada. Se o tamanho for pequeno (mas maior que 1), a maioria dos polígonos será dividida em várias partes; torná-lo grande pode fazer com que muitos pontos de teste sejam rejeitados em alguns polígonos (sinuosos, com lascas ou cheios de orifícios). Isso garante que o tempo máximo para amostrar o polígono original é previsível.
Procedure SimpleRandomSample(P:Polygon, N:Integer) {
U = Sorted list of N independent uniform values between 0 and 1
Return SRS(P, BoundingBox(P), U)
}
O próximo procedimento chama a si próprio recursivamente, se necessário. A expressão misteriosa t*N + 5*Sqrt(t*N)
estima conservadoramente um limite superior de quantos pontos serão necessários, respondendo pela variabilidade do acaso. A probabilidade de que isso falhe é de apenas 0,3 por milhão de chamadas de procedimento. Aumente 5 para 6 ou até 7 para reduzir essa probabilidade, se quiser.
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
}
Se seu polígono é convexo e você conhece todos os vértices, considere fazer uma ponderação convexa "aleatória" dos vértices para provar um novo ponto que é garantido que fica dentro do casco convexo (neste caso, polígono).
Por exemplo, digamos que você tenha um polígono convexo em N lados com vértices
V_i, i={1,..,N}
Gere aleatoriamente pesos N convexos
w_1,w_2,..,w_N such that ∑ w_i = 1; w_i>=0
O ponto amostrado aleatoriamente é então dado por
Y= ∑ w_i*V_i
Pode haver uma maneira diferente de amostrar pesos convexos N
Quando o polígono não é muito severamente não convexo, considere primeiro convertê-lo em um casco convexo. Isso deve pelo menos limitar o número de pontos fora do polígono em grande parte.
A tarefa é muito fácil de resolver no GRASS GIS (um comando) usando o v.random .
Abaixo um exemplo de como adicionar 3 pontos aleatórios em polígonos selecionados (aqui áreas de CEP da cidade de Raleigh, NC) na página de manual. Modificando a instrução "where" do SQL, os polígonos podem ser selecionados.
Link de resposta
https://gis.stackexchange.com/a/307204/103524
Três algoritmos usando abordagens diferentes.
Função =================================================== ==================
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;
Use a função com uma consulta simples, a geometria deve ser válida e polígono, multi-polígonos ou envelope
SELECT I_Grid_Point_Distance(geom, 50, 61) from polygons limit 1;
Resultado =================================================== =====================
Segunda função baseada no algoritmo Nicklas Avén . Já tentei lidar com qualquer SRID.
Eu apliquei após as alterações no algoritmo.
Função =================================================== ==================
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;
Use-o com uma consulta simples.
SELECT I_Grid_Point(geom, 22, 15, false) from polygons;
Resultado =================================================== ==================
Função =================================================== =================
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;
Use-o com uma consulta simples.
SELECT I_Grid_Point_Series(geom, 22, 15, false) from polygons;
Resultado =================================================== =========================