Como abrir (muitos) layers raster no QGIS 3.18 de uma vez, por processamento em lotes, e como alterar suas simbologias em grupo
Tutorial simples com códigos e snippets em Python
Colegas pesquisadores e entusiastas de SIG de código livre,
Olá e bem-vindos ao meu blog!
Gostaria de começar com um aviso - posso ser uma pesquisadora desta área, mas isso não significa que tudo o que faço ou escrevo aqui funcionará para você, em suas próprias configurações de desktop e versões de packages. Me eximo de responsabilidade se você perder dados ou bagunçar sua instalação. Eu também não autorizo nenhum tipo de cópia do meu conteúdo (exceto para os trechos de código fornecidos, para eles, a licença do tipo MIT se aplica).
Hoje, vou escrever sobre abrir muitas camadas (layers) raster por meio de processamento em lotes (batch processing), e alteração de suas simbologias, também em lotes, no QGIS. Este tutorial foi elaborado com base no QGIS 3.18 Zürich e utiliza seu console Python para a parte de carregamento dos layers.
Abrir muitas camadas raster pode ser uma tarefa tediosa. Talvez você tenha uma profusão de grânulos de satélites que devem ser verificados antes de fazer Merge, talvez você esteja trabalhando com uma área tão grande que precise ter partes do seu projeto em diferentes fusos UTM, ou ainda, talvez você tenha 80 bandas raster do mesmo local salvas separadamente, mas precisa usar todas de uma vez. Para mim, abrir mais de cerca de 20 arquivos, um por um, já é tedioso.
Na verdade, abrir os meus arquivos um por um era tão tedioso que decidi programar um pequeno script para abri-los todos juntos. E você também pode!
Neste post, estou fornecendo meu código para você para que você possa alterá-lo para atender às suas necessidades (fornecido sob licença de uso e de cópia do tipo MIT).
Vamos lá!
Abra o QGIS e no menu, em Plugins, abra o console do Python.
Clique em “Show Editor”
Abra o script Python
Clique em “Abrir Script” e abra o script que estou fornecendo, ou copie e cole o código abaixo em um arquivo de texto, dando um nome do tipo “qualquer_nome.py”
O gist Python no GitHub:
# | |
# add multiple raster layers to QGIS based on naming patterns | |
# | |
# created by Luisa Lucchese | |
# in April 2021 | |
# | |
# run directly from QGIS | |
# | |
# MIT License | |
#Copyright (c) 2021 Luisa V. Lucchese | |
#Permission is hereby granted, free of charge, to any person obtaining a copy | |
#of this software and associated documentation files (the "Software"), to deal | |
#in the Software without restriction, including without limitation the rights | |
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
#copies of the Software, and to permit persons to whom the Software is | |
#furnished to do so, subject to the following conditions: | |
#The above copyright notice and this permission notice shall be included in all | |
#copies or substantial portions of the Software. | |
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
#SOFTWARE. | |
# where are your files | |
caminho_base = "your path here" | |
# how many files are there to open | |
MAX_ARQ=10 | |
for i in range(1,MAX_ARQ+1): | |
# the number i in strings, with different leading zeros | |
num_1dig=str(i); # 1 digit | |
num_2dig=str(i).zfill(2); # 2 digit | |
num_3dig=str(i).zfill(3); # 3 digit | |
# Suppose your files have a pattern in their names. | |
# Now is the time to implement it: | |
caminho_arquivo = caminho_base + 'folder_' + num_2dig + '/' + 'raster.tif' | |
# Other possibilities | |
# Check luisalucchese.com/post/batch-process-load-raster-layers-QGIS/ for more information. | |
# Case 2 | |
# caminho_arquivo = caminho_base + 'folder_' + num_2dig + '/' + 'raster_' + num_3dig + '.tif' | |
# Case 3 | |
# num_div2=math.floor(i/2) | |
# num_div2_str_2dig=str(num_div2).zfill(2) | |
# caminho_arquivo = caminho_base + 'folder_' + num_div2_str_2dig + '/' + 'raster_' + num_2dig + '.tif' | |
# Case 5 | |
# caminho_arquivo = caminho_base + 'folder/' + 'raster_' + num_2dig+ '.tif' | |
# For case 6 (dates) I prepared another gist "abrir_layers_qgis_baseado_datas.py". | |
# Your layers can also have unique names | |
nome_layer= 'camada_' + num_1dig | |
raster_camada = QgsRasterLayer(caminho_arquivo, nome_layer) | |
QgsProject.instance().addMapLayers([raster_camada]) |
Primeiro, coloque a pasta padrão na variável “caminho_base” (base_path). Esta é a parte do caminho dos arquivos que se mantém e não muda de arquivo para arquivo.
“MAX_ARQ” é o número de arquivos que você deseja abrir neste processamento em lote. Se você não tem esse número, em geral, não tem problema, mas vai precisar de uma forma mais elaborada para usar o código. Falei um pouco disso na seção de extras.
Eu faço o meu loop for começando em 1 e terminando no número de arquivos mais um, porque o loop do Python clássico começa em zero. A seguir, eu transformo meus dígitos (números inteiros) em strings. Isso vai ser útil se o nome dos arquivos contiver o do número do raster que você deseja abrir. Exemplo: raster_1.tif, raster_2.tif…
num_1dig é uma string com, no mínimo, um dígito (1, 2,… 10,11,… 99,100,101,…)
num_2dig é uma string com, no mínimo, dois dígitos (01, 02,… 10,11,… 99,100,101,…)
num_3dig é uma string com, no mínimo, três dígitos (001, 002,… 010,011,… 099,100,101,…)
Altere o código para atender às suas necessidades
Em “caminho_arquivo” deve ser colocada a lógica que compõe o nome dos arquivos que se deseja abrir. Alguns casos comuns + fragmentos (snippets) de código abaixo:
Caso 1. Cada arquivo está em uma pasta diferente, e as pastas são nomeadas “folder_01”, “folder_02”, etc. Todos os arquivos têm o mesmo nome “raster.tif”.
caminho_arquivo = caminho_base + ‘folder_’ + num_2dig + ‘/’ + ‘raster.tif’
Caso 2. Cada arquivo está em uma pasta diferente, e as pastas são nomeadas “folder_01”, “folder_02”, etc. Os arquivos são nomeados “raster_001.tif”, “raster_002.tif”, e estão um em cada pasta.
caminho_arquivo = caminho_base + ‘folder_’ + num_2dig + ‘/’ + ‘raster_’ + num_3dig + ‘.tif’
Caso 3. Cada pasta possui dois arquivos. As pastas são nomeadas “folder_01”, “folder_02”, etc. Os arquivos são nomeados “raster_01.tif” e “raster_02.tif” em folder_01,… “raster_51.tif” e “raster_52.tif” em folder_25.
import math # add on the beginning of the .py file
num_div2=math.floor(i/2)
num_div2_str_2dig=str(num_div2).zfill(2)
caminho_arquivo = caminho_base + ‘folder_’ + num_div2_str_2dig + ‘/’ + ‘raster_’ + num_2dig + ‘.tif’
Caso 4. Cada pasta possui dois arquivos. As pastas são nomeadas “folder_01”, “folder_02”, etc. Os arquivos são nomeados “raster_01.tif” e “raster_02.tif” em folder_01,… “raster_01.tif” e “raster_02.tif” em folder_25. Eles têm sempre os nomes de “raster_01.tif” e “raster_02.tif”.
Substitua MAX_ARQ pela metade do número total de arquivos. Ou seja, coloque o número total de pastas.
Copie tudo de “caminho_arquivo” para baixo e faça uma versão para “raster_01.tif” e outra para “raster_02.tif”, ambas podem ser baseadas no trecho mostrado para o Caso 1.
Caso 5. Os rasters estão todos na mesma pasta, é o nome do arquivo que muda.
caminho_arquivo = caminho_base + ‘folder/’ + ‘raster_’ + num_2dig+ ‘.tif’
Caso 6. Os arquivos raster estão em pastas diferentes e os nomes das pastas são dados por datas, e não por números.
Bem, este é um dos casos mais difíceis. Primeiro, obtenha as datas de início e fim. Se você não tiver a data final, você pode usar este snippet que criei para obter sua data final:
from datetime import date #add to the beginning
from datetime import datetime #add to the beginning
startyear=1970
startmonth=1
startday=1
date_start=date.toordinal(date(startyear,startmonth,startday))
date_end=date_start+MAX_ARQ
end_datetime=date.fromordinal(date_end)
endyear = end_datetime.strftime("%Y")
endmonth = end_datetime.strftime("%m")
endday = end_datetime.strftime("%d")
No entanto, você não precisará apenas das datas de início e de término, você precisará de todas as outras também. Nesse caso, date_end é substituído por loop_date, a data dentro do loop for.
from datetime import date #add to the beginning
from datetime import datetime #add to the beginning
startyear=1970 # before the loop
startmonth=1 # before the loop
startday=1 # before the loop
date_start=date.toordinal(date(startyear,startmonth,startday)) # before the loop
date_loop=date_start+i
loop_datetime=date.fromordinal(date_loop)
year = loop_datetime.strftime("%Y")
month = loop_datetime.strftime("%m")
day = loop_datetime.strftime("%d")
Agora, crie o nome da sua pasta.
caminho_arquivo = caminho_base + year+ ’-’ + month + ‘-’ + day + ’/’ + ‘raster.tif’
ou…
caminho_arquivo = caminho_base + year+ ’_’ + month + ‘_’ + day + ’/’ + ‘raster.tif’
Outras formulações também são possíveis.
Eu codifiquei também uma versão do código específica para este caso, que você pode usar como base para a sua:
# | |
# add multiple raster layers to QGIS based on naming patterns, dates version | |
# | |
# created by Luisa Lucchese | |
# in April 2021 | |
# | |
# run directly from QGIS | |
# | |
# MIT License | |
#Copyright (c) 2021 Luisa V. Lucchese | |
#Permission is hereby granted, free of charge, to any person obtaining a copy | |
#of this software and associated documentation files (the "Software"), to deal | |
#in the Software without restriction, including without limitation the rights | |
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
#copies of the Software, and to permit persons to whom the Software is | |
#furnished to do so, subject to the following conditions: | |
#The above copyright notice and this permission notice shall be included in all | |
#copies or substantial portions of the Software. | |
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
#SOFTWARE. | |
# load packages to work with dates in Python | |
from datetime import date | |
from datetime import datetime | |
# where are your files | |
caminho_base = "your path here" | |
# how many files are there to open | |
MAX_ARQ=10 | |
# the starting date | |
startyear=1970 | |
startmonth=1 | |
startday=1 | |
date_start=date.toordinal(date(startyear,startmonth,startday)) | |
for i in range(1,MAX_ARQ+1): | |
# the number i in strings, with different leading zeros | |
num_1dig=str(i); # 1 digit | |
num_2dig=str(i).zfill(2); # 2 digit | |
num_3dig=str(i).zfill(3); # 3 digit | |
# dates in strings | |
date_loop=date_start+i | |
loop_datetime=date.fromordinal(date_loop) | |
year = loop_datetime.strftime("%Y") | |
month = loop_datetime.strftime("%m") | |
day = loop_datetime.strftime("%d") | |
# Suppose your files have a pattern in their names. | |
# Now is the time to implement it: | |
caminho_arquivo = caminho_base + year+ '-' + month + '-' + day + '/' + 'raster.tif' | |
# Other possibilities | |
# caminho_arquivo = caminho_base + year+ '_' + month + '_' + day + '/' + 'raster.tif' | |
# Check luisalucchese.com/post/batch-process-load-raster-layers-QGIS/ for more information. | |
# | |
# If your file path does not include dates, there is a gist for you | |
# called "abrir_layers_qgis.py" | |
# Your layers can also have unique names | |
nome_layer= 'camada_' + num_1dig | |
raster_camada = QgsRasterLayer(caminho_arquivo, nome_layer) | |
QgsProject.instance().addMapLayers([raster_camada]) |
Outra coisa que você pode alterar é o nome da camada no QGIS. Você pode escolher o nome que quiser, mas eu sugiro incluir a iteração do loop para que você saiba qual camada é qual.
nome_layer= 'camada_' + num_1dig
Depois de alterar o código para atender às suas necessidades, execute o script para iniciar o processo de carregamento dos arquivos em processamento por lotes
Depois de executar o script, os layers devem aparecer carregados
Certo!
Simbologia em lote
Agora, como posso aplicar o mesmo estilo a todos eles sem ter que mudar cada um separadamente? (novamente, isso seria muito tedioso!)
É bem simples, na verdade. E não envolve nada de Python!
Abra as Propriedades de uma de suas camadas e vá para a guia Symbology (Simbologia)
Ajuste a simbologia da camada como quiser.
Clique em Apply e em OK
Crie um novo grupo em Add Group
Selecione todos os seus layers ao mesmo tempo, clicando no primeiro, segurando Shift e clicando no último. A seguir, arraste-as para o grupo de camadas que você acabou de criar, para que se tornem parte dele.
Copie o estilo da camada que você alterou
Clique com o botão direito na camada que você ajustou a simbologia e escolha Styles, Copy Style (copiar o estilo).
Você pode colar o estilo que foi copiado em um editor de texto (você não precisa e nem deve fazer isso, este é só um comentário). Se você fizer isso, e se já viu um arquivo XML, o estilo é algo parecido com isso.
Cole o estilo no grupo onde estão as camadas
Agora, cole o estilo no grupo. Para isso, clique com o botão direito do mouse no grupo e clique em “Paste Style” (colar estilo).
Todas as camadas devem estar com a representação escolhida, agora.
Acho que é isso!
Extras:
- Tenho tantos arquivos que nem sei quantos são. Não sei os números, e tenho apenas uma lista com os nomes deles.
Uma lista com os nomes é o suficiente para conseguir abri-los. Você só precisa copiar esta lista para o código em Python e declará-la como um list. Em seguida, faça o loop for sobre a sua lista. Exemplo abaixo:
file_list = ['rasterXYN.tif', 'anotherraster.tif', 'thirdraster.tif', 'OKraster.tif', 'Nice_raster.tif']
for item in file_list:
…
- Por que você simplesmente não faz um mosaico de todos os arquivos juntos? Daí você não terá mais que abrir todos esses arquivos.
Há uma infinidade de razões pelas quais alguém pode não querer fazer um mosaico de seus rasters. Os principais motivos incluem: eles estão em diferentes sistemas de coordenadas projetadas, como por exemplo, em diferentes fusos UTM, ou, eles gerariam um arquivo muito grande após o mosaico, ou mesmo, todos os rasters se referem ao mesmo local no espaço, mas em bandas espectrais ou períodos de tempo diferentes.
- Basta adicionar todos os arquivos uma vez e salvar o projeto, e você não terá que se preocupar nunca mais em abrir eles!
Desculpe, mas se você muda constantemente de estação de trabalho ou costuma trabalhar em vários computadores diferentes, isso não funcionará, pois você ainda terá que especificar a localização dos rasters quando abrir o projeto em um computador diferente.
Além disso, em alguns casos, até mesmo abrir todos os arquivos uma única vez pode acarretar um desperdício de tempo. Imagine o caso de você ter mil rasters. Se você for muito rápido(a), consegue carregar cada um deles em uns 20 segundos. Portanto, levariam 5,56 horas para abrir todo o conjunto de dados. Seguindo a lógica, se você tiver cem arquivos, abrir todos eles demoraria cerca de meia hora, o que eu já acho um pouco demais, considerando que ler este post inteiro leva só cerca de 9 minutos!
- Eu tenho mais de mil rasters, como vou selecionar e arrastar todos para dentro de um grupo de camadas? Não existe uma maneira mais simples de fazer isso?
Sim, você pode, antes de tudo, criar o grupo, selecionar o grupo e, enquanto estiver selecionado, execute o código de abrir os rasters em lote. Quando as camadas carregarem, elas aparecerão dentro do grupo de camadas que estava selecionado.
- Tenho muitos layers para carregar, mas são vetores! Este procedimento funciona apenas para raster?
Por enquanto, sim, mas ele pode ser adaptado para carregar shapefiles e geopackages (entre outros). Estou pensando em adaptá-lo a dados vetoriais. Quando eu fizer isso, edito esta postagem para adicionar o código.