Criando luzes do setor no QGIS?

24

Estou usando o QGIS 2.18. Preciso criar luzes do setor para fins de navegação em um mapa.

Eu tenho os dados do setor de luz como campos que fornecem becon_id, grau inicial, final e cor em um arquivo de forma com mais de 500 bóias, faróis e faróis que precisam ser mostrados no mapa. Para cada farol, pode haver muitas linhas, cada uma descrevendo um setor leve (por exemplo, um setor branco)

O resultado final deve ter a seguinte aparência: Os setores claros nas cores corretas, com a cor marcada como o caractere no campo de cor (RGW) e linhas pontilhadas de 100m a 1000m da bóia / farol / farol.

Provavelmente, isso deve ser criado como um símbolo baseado em regras, mas precisa de algum python, eu acho?

insira a descrição da imagem aqui

Aqui está um exemplo dos dados do shapefile para um farol (infelizmente não o acima), que possui um setor verde entre 114 e 154 graus, um setor branco entre 154 e 168 graus, um setor vermelho entre 168 e 237 graus, um verde setor entre 237 e 314 graus, um setor branco entre 314 e 320 graus, um setor vermelho entre 320 e 337 graus (por alguma razão, 0 não é norte, mas sul):

exemplo de tabela shapefile

Benjamin Donner
fonte
2
Por favor, você pode enviar um exemplo de conjunto de dados e editar sua pergunta elaborando qual é exatamente o resultado esperado? Na imagem em anexo, vejo apenas um universo de símbolos e cores.
MGRI
11
Dados de exemplo ajudariam aqui. Você tem um recurso por setor de iluminação ou um recurso por bóia? O plugin Wedge Buffer pode ajudar aqui, mas a facilidade com que isso depende de como seus dados são configurados.
Steven Kay
Olá @mgri e Steven, adicionei dados de exemplo e tentei esclarecer a questão :), obrigado!
Benjamin Donner
11
@mgri as linhas não são variáveis, mas as linhas que devem ser mostradas estaticamente como 900m de linhas longas entre os setores leves, como na imagem. Sistema de referência projetado.
Benjamin Donner

Respostas:

50

EDIT Eu editei a resposta para gerenciar situações particulares (devido a valores de ângulo específicos) e para não exibir as linhas pontilhadas quando um ângulo redondo é definido.


Proponho uma solução recorrendo apenas à simbologia e rotulagem baseadas em regras.

Antes de começar, quero enfatizar que vou focar a atenção na explicação das coisas mínimas a serem feitas para reproduzir o resultado desejado: isso significa que alguns outros parâmetros menores (como tamanhos, larguras etc.) devem ser facilmente ajustados por você para melhor atender às suas necessidades.

Além disso, esta solução só funciona se você assumir que o 0grau é norte em vez de sul (se 0for sul, seria suficiente somar um 180valor toda vez que aparecer um '90' em fórmulas que lidam com ângulos, por exemplo, cos(radians(90))se tornaria cos(radians(180 + 90))). Eu preferia fazer isso apenas para dar uma solução mais geral.


Styling

Renderizaremos os pontos com Single symbolae recorrendo a uma Simple Markere três Geometry generatorcamadas de símbolos:

insira a descrição da imagem aqui

Na explicação adicional, seguirei a mesma ordem dos símbolos na imagem acima.

1) Marcador Simples

Eu escolhi o símbolo padrão de uma estrela negra (essa é a parte mais fácil deste tutorial), com um tamanho de 3 mm e uma largura de 0,4 mm.

2) Gerador de geometria n ° 1

Adicione uma nova camada de símbolo e selecione o Geometry generatortipo:

insira a descrição da imagem aqui

Insira esta expressão no Expressioncampo:

CASE
WHEN abs( "ALKUKULMA" - "LOPPUKULMA") < 360
THEN
make_line(
 $geometry,
 make_point(
  $x + 1000*cos(radians(90 - "ALKUKULMA")),
  $y + 1000*sin(radians(90 - "ALKUKULMA"))
  )
)
END

Acabamos de definir a primeira linha que aponta para o ponto em que o setor de luz começa. Essa linha tem 1000 m de comprimento e é criada apenas quando o ângulo de abertura da luz do setor não é um ângulo redondo (isso acontece para evitar que a linha quebre um círculo inteiro).

3) Gerador de Geometria No. 2

O mesmo que acima, mas, nesta etapa, você precisa usar esta expressão:

CASE
WHEN abs( "ALKUKULMA" - "LOPPUKULMA") < 360
THEN
make_line(
 $geometry,
 make_point(
  $x + 1000*cos(radians(90 - "LOPPUKULMA")),
  $y + 1000*sin(radians(90 - "LOPPUKULMA"))
  )
)
END

Acabamos de definir a primeira linha que aponta para o ponto em que o setor de luz termina. Essa linha tem 1000 m de comprimento e é criada apenas quando o ângulo de abertura da luz do setor não é um ângulo redondo (isso acontece para evitar que a linha quebre um círculo inteiro).

4) Gerador de geometria n ° 3

Insira esta expressão no Expressioncampo:

CASE

WHEN abs("ALKUKULMA" - "LOPPUKULMA") <= 180 AND "ALKUKULMA" >= "LOPPUKULMA"
THEN
difference(
 boundary(
  buffer(
   $geometry, 900)
   ),
  make_polygon(
   geom_from_wkt(
    geom_to_wkt(
     make_line(
      $geometry,
      make_point($x + 2000*cos(radians(90 - "ALKUKULMA" )), $y + 2000*sin(radians((90 - "ALKUKULMA" )))),
      make_point($x + 2000*cos(radians(90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )), $y + 2000*sin(radians((90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )))),
      make_point($x + 2000*cos(radians(90 - "LOPPUKULMA")), $y + 2000*sin(radians((90 - "LOPPUKULMA")))),
      $geometry)
   )  
  )
 )
)

WHEN abs("ALKUKULMA" - "LOPPUKULMA") <= 180 AND "ALKUKULMA" <= "LOPPUKULMA"
THEN
intersection(
 boundary(
  buffer(
   $geometry, 900)
   ),
  make_polygon(
   geom_from_wkt(
    geom_to_wkt(
     make_line(
      $geometry,
      make_point($x + 2000*cos(radians(90 - "ALKUKULMA" )), $y + 2000*sin(radians((90 - "ALKUKULMA" )))),
      make_point($x + 2000*cos(radians(90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )), $y + 2000*sin(radians((90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )))),
      make_point($x + 2000*cos(radians(90 - "LOPPUKULMA")), $y + 2000*sin(radians((90 - "LOPPUKULMA")))),
      $geometry)
   )  
  )
 )
)

WHEN abs("ALKUKULMA" - "LOPPUKULMA") > 180 AND "ALKUKULMA" >= "LOPPUKULMA"
THEN
intersection(
 boundary(
  buffer(
   $geometry, 900)
   ),
  make_polygon(
   geom_from_wkt(
    geom_to_wkt(
     make_line(
      $geometry,
      make_point($x + 2000*cos(radians(90 - "ALKUKULMA" )), $y + 2000*sin(radians((90 - "ALKUKULMA" )))),
      make_point($x - 2000*cos(radians(90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )), $y - 2000*sin(radians((90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )))),
      make_point($x + 2000*cos(radians(90 - "LOPPUKULMA")), $y + 2000*sin(radians((90 - "LOPPUKULMA")))),
      $geometry)
   )  
  )
 )
)

WHEN abs("ALKUKULMA" - "LOPPUKULMA") > 180 AND "ALKUKULMA" <= "LOPPUKULMA"
THEN
difference(
 boundary(
  buffer(
   $geometry, 900)
   ),
  make_polygon(
   geom_from_wkt(
    geom_to_wkt(
     make_line(
      $geometry,
      make_point($x + 2000*cos(radians(90 - "ALKUKULMA" )), $y + 2000*sin(radians((90 - "ALKUKULMA" )))),
      make_point($x - 2000*cos(radians(90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )), $y - 2000*sin(radians((90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )))),
      make_point($x + 2000*cos(radians(90 - "LOPPUKULMA")), $y + 2000*sin(radians((90 - "LOPPUKULMA")))),
      $geometry)
   )  
  )
 )
)


END

Acabamos de definir o arco entre os pontos inicial e final do setor de luz (observe que 2000é um valor arbitrário, porque estou tentando criar um polígono para interceptar o limite do círculo com um raio de 900 m).

Além disso, precisamos definir a cor que é armazenada no "VARIS"campo. Para fazer isso, precisamos especificá-lo com uma expressão personalizada. Siga a seta na imagem abaixo:

insira a descrição da imagem aqui

e digite esta expressão depois de clicar no Edit...botão:

CASE
WHEN  "VARIS" = 'vi' THEN color_rgb(51,160,44)
WHEN "VARIS" = 'v' THEN color_rgb(255,255,255)
WHEN "VARIS" = 'p' THEN color_rgb(227,26,28)
END

Observe que, para essa camada de símbolo, criei duas linhas: a linha superior define a cor a ser usada (na verdade, defino a expressão personalizada para essa), enquanto a inferior é útil para definir uma borda preta (ela terá uma largura maior que a da linha superior). Lembre-se também de definir Flatas Cap styleduas linhas para evitar sobreposição de cores.


Marcação

1) Configurando os rótulos

Vá para Layer Properties> Labelse, como sempre, siga as setas vermelhas:

insira a descrição da imagem aqui

e digite esta expressão:

CASE
WHEN "VARIS" = 'vi' THEN 'G'
WHEN "VARIS" = 'v' THEN 'W'
WHEN "VARIS" = 'p' THEN 'R'
END

Acabamos de definir a regra de cores usando o valor armazenado no "VARIS"campo.

2) Definindo o posicionamento para etiquetas

Selecione a Placementopção no LabelsMenu e selecione Offset from point.

Então, com referência à imagem abaixo:

insira a descrição da imagem aqui

siga a seta vermelha e digite esta expressão:

CASE
WHEN "ALKUKULMA" > "LOPPUKULMA"
THEN
concat(
 -1000*cos(radians(90 - ("ALKUKULMA" + "LOPPUKULMA")/2)),
  ',',
  1000*sin(radians(90 - ("ALKUKULMA" + "LOPPUKULMA")/2))
)
WHEN "ALKUKULMA" <= "LOPPUKULMA"
THEN
concat(
 1000*cos(radians(90 - ("ALKUKULMA" + "LOPPUKULMA")/2)),
  ',',
  -1000*sin(radians(90 - ("ALKUKULMA" + "LOPPUKULMA")/2))
)
END

Em seguida, siga a seta verde e digite esta expressão:

CASE
WHEN "ALKUKULMA" >= "LOPPUKULMA"
THEN
180-(("ALKUKULMA" + "LOPPUKULMA")/2)
WHEN "ALKUKULMA" < "LOPPUKULMA"
THEN
- (("ALKUKULMA" + "LOPPUKULMA")/2)
END

Resultado final

Se você executou corretamente as tarefas anteriores, poderá obter este resultado:

insira a descrição da imagem aqui

Bônus

Como os parâmetros secundários eram muitos para serem abordados completamente nesta resposta, anexei o estilo aqui : você pode abrir esse código com qualquer editor de texto e salvá-lo como um arquivo QGIS Layer Style (ou seja, com uma .qmlextensão).

O estilo acima foi criado usando o QGIS 2.18.4 (ele deve ter o mesmo nome do shapefile que você está usando).

mgri
fonte
3
Parece ótimo, realmente mostra o poder do gerador de geometria. É lento para renderizar?
HeikkiVesanto
2
@Vesanto Testei-o em um ponto com seis recursos (ou seja, seis luzes do setor) e renderizado instantaneamente. Eu acho que deveria ser rápido também ao lidar com centenas de recursos, porque não há chamada para fornecedores ou algo semelhante, mas apenas algumas operações matemáticas e geometrias como o Well Know Text.
MGRI
2
Perguntas / respostas como essas realmente mostram o quão versátil o QGIS pode ser!
Joseph
11
@gri você é o Mestre, uma solução fantasticamente boa e uma ótima explicação que envolve muito trabalho, OBRIGADO !!
Benjamin Donner
11
@gri uma montanha nesta região deve ter o seu nome, obrigado! Eu testei um pouco e não foram encontrados problemas com suas soluções adicionadas :)!
Benjamin Donner