Re-localizando pontos aleatoriamente a certa distância no QGIS
Tenho coordenadas de GPS que identificam projetos de ajuda na África. Agora, quero realocar esses projetos, de modo que cada projeto seja realocado por 100 km em uma direção aleatória de sua localização original.
(Extra: eu tenho camadas de país; posso forçar o QGIS a se re-localizar apenas dentro do país original?)
Eu tentei o Gerador de Geometria e Camada Virtual, ambos não funcionaram até agora. Alguma pista?
Eu uso o QGIS 3.14.15.
Respostas
Essa resposta foi atualizada várias vezes para obter uma solução que gradualmente adiciona complexidade à expressão. Para a solução final, você deve ir diretamente ao fundo (etapa 5). O histórico de atualização da resposta original, entretanto, se soma a um guia passo a passo de como essa solução funciona, começando de uma maneira muito fácil. Dá uma ideia de como as expressões QGIS mais complexas funcionam e o que pode ser feito com elas. Cada etapa de complexidade é separada por uma régua horizontal e denominada Etapa 1, Etapa 2 ... até Etapa 5 para que você possa pular facilmente para essas etapas. A etapa 4 é uma explicação detalhada sobre como todas as diferentes partes das expressões se juntam.
Passo 1
Você pode fazer isso usando a seguinte expressão (veja abaixo o extra para realocar dentro dos países):
project ( $geometry , 100000 , radians ( rand ( 0,360) ) )
Se precisar apenas para fins de visualização, adicione uma camada de símbolo, defina-a como gerador de geoemtry e cole a expressão acima. Altere 100000 se as unidades do seu CRS não estiverem em metros. Esteja ciente de que toda vez que você movimenta, aplica zoom ou realiza outra atividade, a camada é atualizada e os pontos aleatórios estarão em um novo local. Se você também quiser uma distância aleatória entre, digamos, 50 e 100 km, substitua 100000 por rand ( 50000, 100000)
.
Se você quiser a geometria real, use Menu processing / toolbox / geoemtry by expression
para gerar uma nova camada, use a mesma expressão.
Resultado: pontos vermelhos: original; pontos azuis: pontos realocados.

Existe a possibilidade de criar coordenadas xey para os pontos aleatórios em sua camada de pontos original sem criar uma nova geometria. Para fazer isso, use a calculadora de campo e crie um novo campo, chamado digamos x_coordinate
e y_coordinate
. Use a expressão x(geometry)
e y(geometry)
para criar um campo separado para cada um que contém as coordenadas dos pontos reposicionados aleatoriamente, enquanto a geometria é a expressão para criar os pontos reposicionados (na captura de tela abaixo, usei a expressão mais sofisticada abaixo). Você obterá dois novos campos com valores de coordenadas x- / y- substituídas. Eles serão armazenados de forma parmanente e não serão mais alterados / atualizados (você pode usar campos virtuais para alterar o conteúdo).

Com isso, você pode usar estes campos para visualizar seus pontos na posição reposicionada, usando uma camada de símbolo com a expressão:
make_point(
"x_coordinate" ,
"y_coordiante"
)
Essa expressão gera os pontos brancos, recolocados, na captura de tela seguinte. Se desejar, você pode remover a representação original dos pontos originais, vermelhos, para manter apenas os pontos substituídos:

Passo 2
Uma versão refinada para realocar os pontos apenas dentro do país. Mesma solução, mas em vez da expressão acima, use esta aqui. Lembre-se de alterar 'countries'
o nome da camada que contém os polígonos do seu país. Ambas as camadas, bem como o projeto, devem estar no mesmo CRS, é melhor usar um CRS projetado (com um CRS geográfico, medições de distância não fazem sentido).
Novamente, essa camada deve ter um campo 'fid'
que contém um número inteiro exclusivo (e não , como declarado em uma versão anterior, qualquer valor de campo exclusivo como o nome do país). Você pode substituir 'fid'
na seguinte expressão por qualquer nome de campo da camada de seu país que contenha um número inteiro único:
with_variable (
'boundary',
boundary (
intersection (
buffer ( $geometry, 100000),
geometry(
get_feature(
'countries',
'fid',
to_int ( array_to_string (
overlay_within (
'countries',
fid)
))
)
)
)
),
line_interpolate_point (
@boundary,
rand (
0,
length(@boundary)
)
)
)
etapa 3
E mais um aperfeiçoamento da expressão: na versão acima, se os pontos originais estão próximos da fronteira (<100 km), os pontos substituídos às vezes estão localizados na própria fronteira e, portanto, a menos de 100 km de distância do ponto original . A expressão acima cria um círculo ao redor do ponto original e faz uma interseção com o país atual. Ele então pega a linha externa dessa interseção e coloca um ponto aleatório nessa fronteira externa - e essa fronteira inclui algumas vezes segmentos das fronteiras do país se o ponto original estiver a uma distância de 100 km.
Para evitar isso, alterei a expressão de acordo, adicionando uma operação de diferença: a partir do limite que acabamos de descrever, ele corta (extrai) todas as fronteiras do país. Nas linhas restantes (apenas as partes do círculo-tampão original dentro do país atual do ponto permanecem), os novos pontos são posicionados aleatoriamente. (veja no final desta resposta uma versão revisada da expressão, já que nem todos os segmentos de borda são cortados completamente):
with_variable (
'boundary',
difference(
boundary (
intersection (
buffer ( $geometry, 100000),
geometry(
get_feature(
'countries',
'fid',
to_int ( array_to_string (
overlay_within (
'countries',
fid)
))
)
)
)
),
boundary (
aggregate (
'countries',
'collect',
$geometry)
)),
line_interpolate_point (
@boundary,
rand (
0,
length(@boundary)
)
)
)
Passo 4
Seguindo uma descrição com capturas de tela para entender melhor como a expressão funciona:
- crie um buffer de 100 km em torno dos pontos:
buffer ( $geometry, 100000)
(polígono)

- obtenha as formas poligonais dos países da outra camada:
expressão:
geometry(
get_feature(
'countries',
'fid',
to_int (
array_to_string (
overlay_within (
'countries',
fid)
)
)
)
)
(polígono)

- Intersecção de 1 e 2: apenas as partes dos buffers (1) que estão dentro do país atual (2)
intersection ( a, b)
, enquanto a é a expressão de 1, b a expressão de 2

- crie o limite externo de 3 (parte do buffer que está dentro do país)
boundary (c)
:, enquanto c é a expressão de 3 (linha)

- obtenha as fronteiras do país como uma linha a partir dos limites do polígono da camada de países:
expressão:
boundary (
aggregate (
'countries',
'collect',
$geometry)
)
(linha)

- cortar (extrair) as fronteiras do país (5) de 4 (limite externo do buffer dentro do país)
difference(d, e)
:, enquanto d é a expressão de 4 e e é a expressão de 5

- Agora, defina um ponto aleatório em algum lugar na linha a partir do não. 6 com a expressão
line_interpolate_point(geometry,distance)
enquanto
- geometria é a expressão de 6 (assim: o círculo de 100 km exceto as partes situadas fora do país e sem as fronteiras do país)
- distância nesta linha é criada aleatoriamente com a expressão
rand(min,max)
com min = 0 e max = comprimento total destageometry
linha (a partir do nº 6) - então o ponto será alocado aleatoriamente em algum lugar entre o início e o fim da linha.
- A última (ou primeira parte) da expressão é
with_variable(name,value,expression)
: isto define uma variável e dentro desta função temos todos os elementos que criamos de 1 a 7 juntos.
R: A variável tem o nome (primeiro argumento)
'boundary'
(a ser referenciada em C, veja abaixo) - o nome é arbitrário e pode ser qualquer outro.B: seu valor (2º argumento) é toda a expressão criada no nº. 6 (com o elemento de 1 a 5), portanto, a linha circular sem fronteiras e
C: a expressão (3º argumento) é a
line_interpolate_point
expressão do nº. 7 que leva a geometria de entrada (o valor de B) como variável@boundary
, o nome definido acima (consulte A) (o caractere @ na frente do nome da variável é a convenção QGIS para endereçar uma variável).
O resultado é semelhante a este - azul: ponto original com id-label, branco: ponto realocado com id-label:

Etapa 5
E uma (esperançosamente última) melhoria: como percebi apenas agora, em alguns casos os pontos ainda podem estar na fronteira, a menos de 100 km de distância do ponto original. Os segmentos de borda que são cruzados pelo círculo não são cortados, consulte:

Este é o motivo pelo qual fiz esta pergunta: Obtendo o segmento de uma linha, cruzado por outro com expressão QGIS
Mas há uma solução mais fácil que incluo aqui para ser completa, para ter uma resposta "final": na etapa 4, parte 5 (obter as fronteiras do país), em vez de usar as fronteiras do país, crio um buffer de, digamos, 1 metro ao redor da borda, para que esta parte da expressão pareça um pouco diferente:
buffer (
boundary (
aggregate (
'countries',
'collect',
$geometry)
) ,
1
)
Esteja ciente de que agora, leva algum tempo para renderizar a imagem do mapa porque o QGIS tem que calcular o buffer ao redor das bordas. Esta última solução é melhor para geometry by expression tool
criar uma vez e para todas as novas geometrias, não para camadas de símbolo que começam a renderizar cada vez que você faz panorâmica ou zoom.
Aumentando o número 1 na expressão, você tem o controle de quão longe os pontos aleatórios devem estar da fronteira - definindo para 20000 [metros, no caso de você usar um CRS projetado com metros como unidades] significa que os pontos realocados deve ter uma distância de pelo menos 20 km da fronteira, embora permaneça 100 km do ponto original.

Então, aqui está a expressão final para gerar esses pontos aleatórios:
with_variable (
'boundary',
difference(
boundary (
intersection (
buffer ( $geometry, 100000),
geometry(
get_feature(
'countries',
'fid',
to_int ( array_to_string (
overlay_within (
'countries',
fid)
))
)
)
)
),
buffer (boundary (
aggregate (
'countries',
'collect',
$geometry)
),1)),
line_interpolate_point (
@boundary,
rand (
0,
length(@boundary)
)
)
)
Isso é tudo, pessoal!