Py QGISC
Py QGISC
https://gdatasystems.com/pyqgis/pyQGISC.pdf
https://gdatasystems.com.br/pyqgis/index.php
VISITE:
https://gdatasystems.com
Licença: CC BY 4.0
https://creativecommons.org/licenses/by/4.0/
Aprendendo Python no QGIS
Usando o Terminal Python ou o Terminal OSGeo4W
1 – O Terminal Python
Para iniciar o terminal python no QGIS vá em Complementos→Termina python ou pressione
Ctrl-Alt-P.
Tipos Numéricos
Python possui primitivamente os tipos numéricos de números inteiros (int), ponto flutuante
(float) e complexo (complex).
>>> a = 1
>>> b = 3.2
>>> c = 4 + 3j
A função type(variável) retorna o tipo da variável.
>>> type(a)
<class 'int'>
>>> type(b)
<class 'float'>
>>> type(c)
<class 'complex'>
O resultado de uma divisão sempre será do tipo float.
>>> d = a/a
>>> d
1.0
>>> type(d)
<class 'float'>
O console pode funcionar como uma calculadora também.
>>> 2+2
4
>>> 2**8 #dois elevado a oito
256
>>> 82%3 #resto inteiro da divisão
1
>>> 82//3 #quociente da divisão
27
>>> 3-2
1
>>> (50-5*6)/4 # Parênteses tem precedente na operação
5.0
Tipo Alfanumérico
Em python o tipo str é usado para palavras, textos e caracteres alfanuméricos.
>>> nome = 'Manuel'
>>> type(nome)
<class 'str'>
>>> letra = 'a'
>>> type(letra)
<class 'str'>
Um objeto da classe str nada mais é do que uma matriz de valores sequenciados onde o
primeiro valor (caractere) corresponde ao índice 0 da matriz e o último valor ao índice -
1, o penúltimo -2 e assim sucessivamente.
>>> nome[0]
'M'
>>> nome[1]
'a'
>>> nome[-1]
'l'
>>> nome[-2]
'e'
>>> nome[-6]
'M'
>>> nome[2:5] #note que o valor do índice 5 não está incluído
'nue'
Um objeto str uma vez definido é imutável e se tentarmos mudar o valor de um objeto
str uma mensagem de erro aparecerá.
>>> nome[2]='t'
Traceback (most recent call last):
File "/usr/lib/python3.7/code.py", line 90, in runcode
exec(code, self.locals)
File "<input>", line 1, in <module>
TypeError: 'str' object does not support item assignment
A função len() retorna o comprimento do objeto str.
>>> palavra='anticonstitucionalissimamente'
>>> len(palavra)
29
Podemos usar aspas simples ou duplas para delimitar um objeto str. Isso é prático para para
podermos definir strings com estas:
>>> s1 = "Bom dia senhor O'Brien"
>>> print(s1)
Bom dia senhor O'Brien
>>> s2= 'Quoth the raven "Nevermore". '
>>> print(s2)
Quoth the raven "Nevermore".
Tipo lista
Python possui diversos tipos compostos de dados, a lista (list) é uma delas.
Uma lista é um conjunto de objetos de determinado tipo ou de tipos distintos
armazenados em uma lista.
Listas são declaradas dentro de colchetes onde cada objeto (item) dela é separado por
vírgula.
>>> lista = [3200,2670,3100,3000]
>>> caipi = ['gelo', 'limão','açúcar',51,15.99]
Assim como str, cada item de uma lista pode ser acessado usando índices.
>>> lista[0]
3200
>>> lista[1]
2670
>>> lista[3]
3000
>>> lista[1:3]
[2670, 3100]
>>> type(caipi [0])
<class 'str'>
>>> type(caipi [3])
<class 'int'>
>>> type(caipi [4])
<class 'float'>
>>> caipi [-1]
15.99
Ao contrário do objeto str, os valores de itens de uma lista podem ser modificados
>>> caipi[3]='pinga'
>>> caipi
['gelo', 'limão', 'açúcar', 'pinga', 15.99]
Uma lista pode conter outras listas como itens. E acessamos cada item dessa lista interna
usando um índice adicional.
>>> caipi[1]=['abacaxi','maracujá','limão']
>>> caipi
['gelo', ['abacaxi', 'maracujá', 'limão'], 'açúcar', 'pinga', 15.99]
>>> caipi[1][1]
'maracujá'
>>> caipi[1][-1]
'limão'
Podemos adicionar itens a uma lista já existente usando adição ou a função append().
>>> caipi = caipi + ['guardanapo','canudo']
>>> caipi.append('copo')
>>> caipi
['gelo', ['abacaxi', 'maracujá', 'limão'], 'açúcar', 'pinga', 15.99,'guardanapo','canudo', 'copo']
Alguns métodos de interação com listas são mostrados abaixo:
Criamos a seguinte lista vazia inicialmente.
>>> lis=[]
>>> lis
[]
Adicionando itens na lista com o método extend().
>>> lis.extend([1,2,3])
>>> lis
[1, 2, 3]
Adicionando um item de valor 10 na posição predeterminada (0) com o método insert().
>>> lis.insert(0,10)
>>> lis
[10, 1, 2, 3]
Removendo da lista a primeira ocorrência do valor passado pelo método remove().
>>> lis.remove(2)
>>> lis
[10, 1, 3]
Podemos copiar uma lista usando o método copy().
>>> lis2=lis.copy()
>>> lis2
[10, 1, 3]
Revertemos a ordem dos itens de uma lista com o método reverse().
>>> lis2.reverse()
>>> lis2
[3, 1, 10]
Ordenamos uma lista usando o método sort().
>>> lis2.sort()
>>> lis2
[1, 3, 10]
O método pop() remove e retorna o item indicado pelo índice dado, se nenhum índice
é fornecido o último item é removido da lista e retornado.
>>> lis3 = [1,1,2,3,4,4,4,3,2,3,1]
>>> lis3.pop(1)
1
>>> lis3
[1, 2, 3, 4, 4, 4, 3, 2, 3, 1]
O método index(x[,início[,fim]]) retorna o índice (na base 0) da primeira ocorrência do
item x. O segundo argumento mostra a partir de qual e até qual índice da lista procurar.
Caso não encontre um item com o valor uma mensagem de erro é retornada.
>>> lis3.index(1,1,10)
9
>>> lis3.index(1)
0
>>> lis3.index(1,1)
9
>>> lis3.index(1,1,10)
9
>>> lis3.index(8,1,10)
Traceback (most recent call last):
File "C:/PROGRA~1/QGIS3~1.4/apps/Python37/lib/code.py", line 90, in
runcode
exec(code, self.locals)
File "<input>", line 1, in <module>
ValueError: 8 is not in list
O método count(x) retorna o número de vezes que o item x aparece na lista.
>>> lis3.count(4)
3
O método clear() remove todos os itens da lista.
>>> lis3.clear()
>>> lis3
[]
Podemos usar a instrução del para deletar itens de uma lista usando índices em vez de
valores.
>>> lis3 = [1,1,2,3,4,4,4,3,2,3,1]
>>> del lis3[2]
>>> lis3
[1, 1, 3, 4, 4, 4, 3, 2, 3, 1]
>>> del lis3[5:8]
>>> lis3
[1, 1, 3, 4, 4, 3, 1]
Tuples
Assim como str e listas, tuples são dados em sequência usados em python. As diferenças
principais entre uma tuple e uma lista é que tuples são declaradas usando vírgulas para
separar os itens e esses itens são imutáveis.
>>> t = 'banana', 3,45, False, "oi!"
>>> t
('banana', 3, 45, False, 'oi!')
>>> t[0]
'banana'
>>> doist =t,(1,2,3,4,5)
>>> doist
(('banana', 3, 45, False, 'oi!'), (1, 2, 3, 4, 5))
>>> doist[0]
('banana', 3, 45, False, 'oi!')
>>> doist[0][0]
'banana'
>>> doist[1][0]
1
>>> t[0]='maçã'
Traceback (most recent call last):
File "C:/PROGRA~1/QGIS3~1.4/apps/Python37/lib/code.py", line 90, in
runcode
exec(code, self.locals)
File "<input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Sets
Sets ou conjuntos são outro tipo de dados em sequência que python utiliza. Sets são
definidos dentro de chaves {} e resultam em uma simples aparição de cada um de seus
itens e estes não são indexados.
>>> conjunto = {'rio','terra','fogo','fogo','água','terra'}
>>> conjunto
{'terra', 'fogo', 'rio', 'água'}
>>> 'rio' in conjunto
True
>>> 'mar' in conjunto
False
>>> num ={1,2,3,2,3,2,1,23,'a'}
>>> num
{1, 2, 3, 'a', 23}
>>> cidade=set('Pindamonhongaba')
>>> cidade
{'p', 'a', 'h', 'm', 'g', 'n', 'i', 'o', 'b', 'd'}\
Dicionários
Dicionários são um tipo de dados bastante usado em python. Um dicionário possui
sempre uma chave e um valor, esta chave é usada no lugar de um índice numérico que
é usado numa lista. Essa chave deve ser única e um dicionário vazio pode ser criado
usando {}.
>>> dicio={'nome':'andre','sobrenome':'costa'}
>>> dicio['idade']=56
>>> dicio
{'nome': 'andre', 'sobrenome': 'costa', 'idade': 56}
>>> list(dicio)
['nome', 'sobrenome', 'idade']
>>> sorted(dicio)
['idade', 'nome', 'sobrenome']
>>> dicio[1]
Traceback (most recent call last):
File "C:/PROGRA~1/QGIS3~1.4/apps/Python37/lib/code.py", line 90, in
runcode
exec(code, self.locals)
File "<input>", line 1, in <module>
KeyError: 1
>>> del dicio['idade']
>>> dicio
{'nome': 'andre', 'sobrenome': 'costa'}
>>> dicio2=dict([('código', 34), ('senha', 65483), ('acessos', 8)])
>>> dicio2
{'código': 34, 'senha': 65483, 'acessos': 8}
Funções Internas
As seguintes funções internas são usadas para conversão de tipos e informações de tipos.
>>> f=-5.789
>>> round(f,2)
-5.79
>>> abs(f)
5.789
>>> int(f)
-5
>>> str(f)
'-5.789'
>>> lis=[2,3,45,12,78,1,-17,3]
>>> min(lis)
-17
>>> max(lis)
78
>>> sum(lis)
127
>>> sorted(lis)
[-17, 1, 2, 3, 3, 12, 45, 78]
>>> i=255
>>> hex(i)
'0xff'
>>> bin(i)
'0b11111111'
>>> float(i)
255.0
>>> chr(i)
'ÿ'
>>> ord('ÿ')
255
>>> oct(i)
'0o377'
>>> ascii(i)
'255'
>>> pow(2,10)
1024
>>> bool(1<2 and 3>2)
True
>>> bool(1<2 and 3>4)
False
As seguintes palavras são reservadas da linguagem python e não podem ser usadas para
definir variáveis.
and except lambda with
as finally nonlocal while
assert False None yield
break for not
class from or
continue global pass
def if raise
del import return
elif in True
else is try
Vamos usar o numpy para criar uma série sequencial de números entre 0 e 2 e plotar a
sequência linear, quadrática e cúbica da série para ilustrar como criar um gráfico com mais de
uma curva.
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> x = np.linspace(0, 2, 100)
>>> plt.plot(x, x, label='linear')
>>> plt.plot(x, x**2, label='quadrática')
>>> plt.plot(x, x**3, label='cúbica')
>>> plt.ylabel('Eixo Y')
>>> plt.xlabel('Eixo X')
>>> plt.title("Gráfico Simples")
>>> plt.legend()
>>> plt.show()
Usando pyQGIS
Introdução às Primeiras Classes
Preparando o terreno
Vamos abrir o QGIS e deixar tudo preparado para podermos operar o ambiente usando o
terminal python (CTRL+ALT+P) e clique no botão Mostrar editor:
Estamos prontos para trabalhar. No editor na parte inferior direita escreveremos o código e para
executar executaremos com:
Classes do pyQGIS
Vamos introduzir as classes do PyQGIS na medida que avançamos nos pontos cobertos.
A biblioteca PyQGIS é bastante extensa com diversas classes. Vamos aqui cobrir algumas
classes mais básicas e essenciais para a partir deste ponto termos uma boa base para
desenvolver scripts mais complexos.
Agora vamos sair do QGIS e entrar novamente para carregarmos o projeto que criamos usando
python.
projeto=QgsProject.instance()
projeto.read(' C:/Users/User/Desktop/dash/meu_projeto.qgs')
print(projeto.fileName())
C:/Users/User/Desktop/dash /meu_projeto.qgs
Na medida que formos vendo as outras classes, vamos ver outros métodos associados à classe
Projeto.
Um objeto do tipo camada vetorial é usado para carregarmos e interagirmos com camadas do
tipo vetor. Vamos carregar o nosso projeto e adicionar nele três camadas vetor criadas
anteriormente usando os métodos de projeto addMapLayer e addMapLayers. Criamos um
objeto de classe camada vetorial usando o método QgsVectorLayer() passando o caminho para
o arquivo vetorial, o identificador que nossa camada terá, e a biblioteca a ser usada (ogr nesse
caso). Por último salvamos o nosso projeto com o método write().
projeto=QgsProject.instance()
projeto.read(' C:/Users/User/Desktop/dash/meu_projeto.qgs')
aoi=QgsVectorLayer("C:/Users/User/Desktop/dash/aoi.shp","AOI","ogr")
mag=QgsVectorLayer("C:/Users/User/Desktop/dash/mag.shp","MAG","ogr")
poi=QgsVectorLayer("C:/Users/User/Desktop/dash/poi.shp","PONTOS","ogr")
projeto.addMapLayers([mag,aoi])
projeto.addMapLayer(poi)
projeto.write('C:/Users/User/Desktop/dash/meu_projeto.qgs')
Algo similar ao apresentado abaixo deverá aparecer:
Agora vamos fazer uso de uma outra classe para podermos carregar uma camada vetorial
localizada em um banco de dados Postgis remoto. A classe é a QgsDataSourceUri e usaremos
os métodos setConnection() e setDataSource() para extrairmos uma tabela espacial vetorial.
Criaremos um projeto novo, adicionaremos uma camada local e uma camada remota Postgis e
por último vamos gravar o projeto.
projeto = QgsProject.instance()
#ajustar caminho dos arquivos de acordo com seu sistema
projeto.write('C:/Users/User/Desktop/dash /meu_projeto2.qgs')
uri = QgsDataSourceUri()
uri.setConnection("pg.gdatasystems.com","5432","dnpmam","droid", "devcor")
uri.setDataSource("public", "indio", "geom")
rindig = QgsVectorLayer(uri.uri(False), "Reserva indígena", "postgres")
uri.setDataSource("public", "amca", "geom")
am=QgsVectorLayer(uri.uri(False), "Amazonas","postgres")
projeto.addMapLayer(am)
projeto.addMapLayer(rindig)
projeto.write('C:/Users/User/Desktop/dash/meu_projeto2.qgs')
O método setConnection() tem como parâmetros o endereço do servidor (IP ou DNS), a porta
(geralmente 5432), o banco de dados, o usuário e a senha. O método setDataSource() tem como
parâmetros o esquema da tabela, o nome da tabela e a coluna com o elemento geométrico
espacial. Um projeto conforme o ilustrado abaixo deverá aparecer.
Alternativamente podemos adicionar uma cláusula SQL WHERE como o quarto argumento.
Vamos ver um exemplo onde extraímos uma camada vetorial somente os requerimentos de
garimpo e permissão de lavra garimpeira dos requerimentos do estado do Amazonas e
adicionamos ela no nosso projeto já criado acima.
uri.setDataSource("public", "gis", "geom","fase ilike '%garimp%'")
garimpo = QgsVectorLayer(uri.uri(False), "Garimpo", "postgres")
projeto.addMapLayer(garimpo)
projeto.write()
Antes de movermos para o próximo tópico vamos dar uma olhada em alguns métodos da Classe
Projeto (QsgProject) relacionados a classe Camadas Vetoriais (QsgVectorLayer).
3
mapLayers retorna um mapa das camadas existentes do projeto.
projeto.mapLayers()
Podemos obter diversas informações sobre objetos vetoriais tais como, projeções, extensão,
número de elementos, valores e nomes dos campos de atributos (colunas) e até criar um
metadata da camada (com a informação existente).
Vamos agora acessar as informações mais usadas de maneira geral. Outras informações
existem no objeto camada, veja a documentação para mais detalhes.
O método crs() retorna o sistema de referência de coordenada original do objeto camada que o
invoca.
crs=rmp.crs()
print(crs.description())
Unknown datum based upon the GRS 1980 ellipsoid
A extensão da Camada
Com o método extent() de um objeto camada podemos obter os valores máximos e mínimos
das coordenadas em X (Easting ou Longitude) e Y (Northing ou Latitude). O método retorna um
objeto do tipo retângulo com diversos parâmetros além de X e Y máximos e mínimos tais como
area, width, height, center, invert, etc. Veja a documentação para mais informações.
extensão=rmp.extent()
min_x=extensão.xMinimum()
max_x=extensão.xMaximum()
min_y=extensão.yMinimum()
max_y=extensão.yMaximum()
print(min_x,min_y,max_x,max_y)
-70.1 -9.53860000030878 -56.7497 2.21390000007294
Quantidade de itens
Com o método fields() obtemos a informação sobre todos os campos de atributos tais como
nome, tipo etc.
for field in rmp.fields():
print (field.name(),field.typeName())
codigo_obj text
TOPONIMIA text
latitude float8
longitude float8
SUBST_PRIN text
subst_sec text
abrev text
STATUS_ECO text
grau_de_im text
metodo_geo text
erro_metod text
data_cad text
classe_uti text
tipologia text
classe_gen text
modelo_dep text
assoc_geoq text
rocha_enca text
rocha_hosp text
textura_mi text
tipos_alte text
extrmin_x_ text
assoc_mine text
origem text
municipio text
uf text
O método htmlMetadata() gera um metadata da camada no formato html que pode ser copiado
para um novo arquivo e visualizado em um navegador da web;
metadata=rmp.htmlMetadata()
print (metadata)
< html>
<body>
<h1>Informação do provedor</h1>
<hr>
<table class="list-view">
<tr><td class="highlight">Nome</td><td>Ocor. Mineral
Amazonas</td></tr>
<tr><td class="highlight">fonte</td><td>dbname='dnpmam'
host=pg.amazeone.com.br port=5432 user='droid' key='tid'
checkPrimaryKeyUnicity='1' table="public"."rmp" (geom)</td></tr>
<tr><td class="highlight">Armazenamento</td><td>PostgreSQL database
with PostGIS extension</td></tr>
<tr><td class="highlight">Comentário</td><td></td></tr>
<tr><td class="highlight">Codificação</td><td></td></tr>
<tr><td class="highlight">Geometria</td><td>Point (Point)</td></tr>
<tr><td class="highlight">SRC</td><td>EPSG:4019 - Unknown datum based
upon the GRS 1980 ellipsoid - Geográfico</td></tr>
<tr><td class="highlight">Extensão</td><td>-70.0999999999999943,-
9.5386000003087794 : -56.7496999999999971,2.2139000000729401</td></tr>
<tr><td class="highlight">Unidade</td><td>graus</td></tr>
<tr><td class="highlight">Contagem de feições</td><td>624</td></tr>
</table>
<br><br><h1>Identificação</h1>
<hr>
<table class="list-view">
<tr><td class="highlight">Identifier</td><td></td></tr>
<tr><td class="highlight">Parent Identifier</td><td></td></tr>
<tr><td class="highlight">Title</td><td></td></tr>
<tr><td class="highlight">Type</td><td>dataset</td></tr>
<tr><td class="highlight">Language</td><td></td></tr>
<tr><td class="highlight">Abstract</td><td></td></tr>
<tr><td class="highlight">Categories</td><td></td></tr>
<tr><td class="highlight">Keywords</td><td>
</td></tr>
</table>
<br><br>
<h1>Extensão</h1>
<hr>
<table class="list-view">
<tr><td class="highlight">CRS</td><td>EPSG:4019 - Unknown datum based
upon the GRS 1980 ellipsoid - Geographic</td></tr>
<tr><td class="highlight">Spatial Extent</td><td></td></tr>
<tr><td class="highlight">Temporal Extent</td><td></td></tr>
</table>
<br><br>
<h1>Acesso</h1>
<hr>
<table class="list-view">
<tr><td class="highlight">Fees</td><td></td></tr>
<tr><td class="highlight">Licenses</td><td></td></tr>
<tr><td class="highlight">Rights</td><td></td></tr>
<tr><td class="highlight">Constraints</td><td></td></tr>
</table>
<br><br>
<h1>Campos</h1>
<hr>
<table class="list-view">
<tr><td class="highlight">Contagem</td><td>26</td></tr>
</table>
<br><table width="100%" class="tabular-view">
<tr><th>Campo</th><th>Tipo</th><th>Comprimento</th><th>Precisão</th><t
h>Comentário</th></tr>
<tr ><td>codigo_obj</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>TOPONIMIA</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>latitude</td><td>float8</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>longitude</td><td>float8</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>SUBST_PRIN</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>subst_sec</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>abrev</td><td>text</td><td>-1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>STATUS_ECO</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>grau_de_im</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>metodo_geo</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>erro_metod</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>data_cad</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>classe_uti</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>tipologia</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>classe_gen</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>modelo_dep</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>assoc_geoq</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>rocha_enca</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>rocha_hosp</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>textura_mi</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>tipos_alte</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>extrmin_x_</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>assoc_mine</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>origem</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr ><td>municipio</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
<tr class="odd-row"><td>uf</td><td>text</td><td>-
1</td><td>0</td><td></td></tr>
</table>
<br><br><h1>Contatos</h1>
<hr>
<p>No contact yet.</p><br><br>
<h1>Links</h1>
<hr>
<p>No links yet.</p>
<br><br>
<h1>Histórico</h1>
<hr>
<p>No history yet.</p>
<br><br>
</body>
</html>
Com o método getFeatures() carregamos todos os dados da camada, onde cada item é
armazenado como uma lista. O código abaixo imprime cada um dos itens em formato de lista.
elementos=rmp.getFeatures()
for e in elementos:
attr=e.attributes()
print (attr)
['25368', 'APUI', -7.7972, -58.8558, 'Calcário', NULL, 'cc', 'Não
explotado', 'Depósito', 'Levantamento em Carta 1:250.000', '250 a
1.000 m', '2001/11/26', 'Insumos para agricultura', NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, '7', 'APUI', 'AM']
…
…
['46734', 'RIO MANICORÉ', -6.11, -61.5669, 'Argila', NULL, 'arg',
'(Não determinado)', 'Depósito', 'GPS Manual pré 25/05/2000', '50 a
200 m', '2006/11/24', 'Material de uso na construção civil', NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '2', 'MANICORE',
'AM']
3.4 Criando objeto vetorial
Vamos agora ver os passos para criarmos objetos vetoriais usando python no Qgis. Vamos criar
objetos do tipo ponto, linha e polígono para ilustrar o processo.
Ponto
Primeiro definimos o objeto ponto com CRS 4326 (WGS84) com o nome Cidades na memória.
Nesse objeto usamos um dataProvider para criar os campos de atributos do objeto vetorial
ponto com três atributos (nome, população e IDH) e adicionamos eles no objeto ponto (vponto).
vponto = QgsVectorLayer("Point?crs=EPSG:4326", "Cidades","memory")
dPr = vponto.dataProvider()
dPr.addAttributes([QgsField("nome", QVariant.String),QgsField("populacao",
QVariant.Int), QgsField("idh", QVariant.Double)])
vponto.updateFields()
Uma vez criado o objeto ponto e seus campos de atributo vamos adicionar dados nele usando
um objeto feature (elemento). Definimos a geometria que será um ponto nesse caso com
coordenadas x e y e adicionaremos os atributos deste ponto.
elem = QgsFeature()
elem.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-59.9936,-3.0925)))
elem.setAttributes(["Manaus", 2182763, 0.737])
dPr.addFeature(elem)
elem.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-60.6253,-3.2872)))
elem.setAttributes(["Manacapuru ", 97377, 0.614])
dPr.addFeature(elem)
elem.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-60.1883,-3.2756)))
elem.setAttributes(["Iranduba ", 48296, 0.613])
dPr.addFeature(elem)
elem.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-59.7014,-2.6968)))
elem.setAttributes(["Rio Preto Da Eva ", 33347, 0.611])
dPr.addFeature(elem)
Para finalizar vamos configurar a aparência do símbolo mostrado no mapa como estrelas de
cor laranjas e de tamanho 8. Atualizamos a extensão do mapa e adicionamos nosso objeto no
mapa.
symbol = QgsMarkerSymbol.createSimple({'name': 'star',
'color':'orange','size':'8'})
vponto.renderer().setSymbol(symbol)
vponto.updateExtents()
QgsProject.instance().addMapLayer(vponto)
A aparência do mapa e de sua tabela de atributos será conforme a imagem mostrada abaixo.
'angle': '0',
'color': '255,165,0,255',
'horizontal_anchor_point': '1',
'joinstyle': 'bevel',
'name': 'star',
'offset': '0,0',
'offset_map_unit_scale': '3x:0,0,0,0,0,0',
'offset_unit': 'MM',
'outline_color': '35,35,35,255',
'outline_style': 'solid',
'outline_width': '0',
'outline_width_map_unit_scale': '3x:0,0,0,0,0,0',
'outline_width_unit': 'MM',
'scale_method': 'diameter',
'size': '8',
'size_map_unit_scale': '3x:0,0,0,0,0,0',
'size_unit': 'MM',
'vertical_anchor_point': '1'
Veja a documentação para cada uma das opções que podem ser usadas com cada parâmetro
listado acima. Vamos Modificar a aparência do símbolo que criamos acima.
prp = vponto.renderer().symbol().symbolLayer(0).properties()
prp['color'] = 'blue'
prp['name'] = 'square'
vponto.renderer().setSymbol(QgsMarkerSymbol.createSimple(prp))
vponto.triggerRepaint()
Veja o resultado abaixo.
QgsVectorFileWriter.writeAsVectorFormat(vponto,'C:/Users/User/Deskto
p/dash/cidaIDH.shp','utf-8', driverName='ESRI Shapefile')
Linha
De forma semelhante ao que fizemos com pontos, criar linhas usando python/Qgis é só uma
questão de usarmos uma série (lista) de pontos para cada elemento criado.
Definimos o objeto linha (linestring) com CRS 4326 (WGS84) com o nome Vias na memória.
Criamos o dataProvider para adicionar os campos de atributos do objeto vetorial linestring com
dois atributos (nome e número) e adicionamos eles no objeto linestring (vlinha).
vlinha = QgsVectorLayer("Linestring?crs=EPSG:4326", "Vias","memory")
dPr = vlinha.dataProvider()
dPr.addAttributes([QgsField("nome",QVariant.String),QgsField("número",QVari
ant.Int)])
vlinha.updateFields()
Adicionamos as linhas usando um objeto feature (elemento). Mas antes criamos a lista de
pontos que farão parte de cada um dos elementos do tipo linha e inserimos os atributos de
cada elemento. Vamos inserir três linhas.
elem = QgsFeature()
pontos =[QgsPoint(-124,48.4), QgsPoint(-123.5,48.6 ), QgsPoint(-
123,48.9),QgsPoint(-122.8,48.7)]
elem.setGeometry(QgsGeometry.fromPolyline(pontos))
elem.setAttributes(["Rota ", 1])
dPr.addFeature(elem)
pontos =[QgsPoint(-121,48.4), QgsPoint(-120.5,48.6 ), QgsPoint(-
120,48.9),QgsPoint(-119.8,48.7)]
elem.setGeometry(QgsGeometry.fromPolyline(pontos))
elem.setAttributes(["Rota ", 2])
dPr.addFeature(elem)
pontos =[QgsPoint(-124,45.4), QgsPoint(-123.5,45.6 ), QgsPoint(-
123,45.9),QgsPoint(-122.8,45.7)]
elem.setGeometry(QgsGeometry.fromPolyline(pontos))
elem.setAttributes(["Rota ", 3])
dPr.addFeature(elem)
Para finalizar, vamos gravar o recém-criado vetor num arquivo do tipo shapefile.
QgsVectorFileWriter.writeAsVectorFormat(vlinha,'C:/Users/User/Deskto
p/dash/vias.shp', 'utf-8', driverName='ESRI Shapefile')
Polígono
Criamos polígonos usando python/Qgis de forma idêntica à forma que criamos linhas só que
nesse caso o último ponto se liga ao primeiro ponto informado. Definimos o objeto polígono com
CRS 4326 (WGS84) com o nome Fazendas na memória.
Criamos o dataProvider para adicionar os campos de atributos do objeto vetorial polígono com
dois atributos (nome e número) e adicionamos eles no objeto polígono (vpgon).
vpgon = QgsVectorLayer("Polygon?crs=EPSG:4326", "Fazendas","memory")
dPr = vpgon.dataProvider()
dPr.addAttributes([QgsField("nome",QVariant.String),QgsField("número
",QVariant.Int)])
vpgon.updateFields()
Adicionamos os polígonos usando um objeto feature (elemento). Mas antes criamos a lista de
pontos que farão parte de cada um dos elementos do tipo polígono (em colchete duplo) e
inserimos os atributos de cada elemento. Vamos inserir dois polígonos.
elem = QgsFeature()
pontos =[[QgsPointXY(-124,48), QgsPointXY(-123,48 ), QgsPointXY(-
123,49),QgsPointXY(-124,49)]]
elem.setGeometry(QgsGeometry.fromPolygonXY(pontos))
elem.setAttributes(["Fazenda Abre Campo ", 1])
dPr.addFeature(elem)
pontos =[[QgsPointXY(-122,48), QgsPointXY(-121,49 ), QgsPointXY(-
121,48),QgsPointXY(-122,49)]]
elem.setGeometry(QgsGeometry.fromPolygonXY(pontos))
elem.setAttributes(["Fazenda Vista Alegre ", 2])
dPr.addFeature(elem)
Similar à forma que adicionamos camadas vetoriais, podemos adicionar imagens raster no
nosso projeto usando objeto da a classe raster. Vamos criar um projeto e adicionar uma imagem
raster nele.
projeto=QgsProject.instance()
camadaR =QgsRasterLayer("C:/Users/user/desktop/dash/dem2.tif","img")
projeto. addMapLayer(camadaR)
projeto.write('C:/Users/user/desktop/dash/meu_projeto3.qgs')
Podemos também adicionar dados do tipo raster usando provedores do tipo TMS
(TileMapService) ou WMS (WebMapaService).
uri="type=xyz&url=https://tile.openstreetmap.org/{z}/{x}/{y}.png&zma
x=19&zmin=0"
mts_layer=QgsRasterLayer(uri,'OSM','wms')
projeto.addMapLayer(mts_layer)
projeto.write()
Interagindo com informações de objetos da classe Raster
Podemos extrair informações relevantes de um objeto raster também tais como dimensões,
resoluções, número de bandas, valor de um pixel, etc. Vamos carregar uma imagem
inicialmente.
projeto=QgsProject.instance()
camadaR = QgsRasterLayer("C:/Users/user/desktop/dash/dem2.tif ","img")
projeto. addMapLayer(camadaR)
(3649, 3650)
camadaR.extent() #extensão da imagem na unidade da coordenada
30.08495478213209
camadaR.rasterUnitsPerPixelY() #resolução em Y
30.08219178082192
Essas importantes informações sobre o raster poderão ser usadas para análises espacias
futuras. Existem outras formas de usar os métodos para obtermos a mesma informação.
30.08495478213209
Vamos ver agora métodos para raster de uma banda. Os métodos abaixo informam o número de
bandas e o tipo da imagem raster. 0 para cinza ou não definido de banda única, 1 para paletado
de banda única e 2 para multibanda.
camadaR.bandCount()
1
camadaR.rasterType()
<RasterLayerType.GrayOrUndefined: 0>
A função dataProvider() funciona como uma interface entre o objeto raster os seus dados
individuais, seu método sample() toma dois valores, um objeto ponto (coordenadas XZ) e o
número da banda. Se a coordenada for dentro da imagem e a banda existir o resultado será um
tuple com o valor do pixel e se o dado é verdadeiro ou não.
valor=camadaR.dataProvider().sample(QgsPointXY(687567, 7460876),1)
valor
(nan, False)
valor2=camadaR.dataProvider().sample(QgsPointXY(547802,8043049), 1)
valor2
(980.0, True)
A rampa de cor assinalada ao objeto raster pode ser checada usando o método type() do
método renderer(). O tipo singlebandgray é o padrão inicial.
>>> camadaR.renderer().type()
'singlebandgray'
Podemos alterar via python a rampa de cores, o processo é mostrado abaixo. O processo
envolve na criação de um objeto do tipo ColorRampShader e definimos a rampa de cor de
preenchimento como sendo do tipo interpolado.
fcn = QgsColorRampShader()
fcn.setColorRampType(QgsColorRampShader.Interpolated)
Criamos agora uma lista com as cores representando os dois valores extremos do raster (0 e
2046 que serão interpolados entre azul e amarelo. Em seguida adicionamos esta lista como item
do ColorRampShader criado acima.
lista = [ QgsColorRampShader.ColorRampItem(0, QColor(0,0,255)),
QgsColorRampShader.ColorRampItem(2046, QColor(255,255,0))]
fcn.setColorRampItemList(lista)
Finalmente criamos o objeto renderizador de cor com: dados do objeto raster, banda 1 e shader
acima. Em seguida aplicamos este ao objeto raster e chamamos a repintura do objeto.
renderer = QgsSingleBandPseudoColorRenderer(camadaR.dataProvider(), 1, shader)
camadaR.setRenderer(renderer)
camadaR.triggerRepaint()
'singlebandpseudocolor'
O resultado é mostrado na imagem abaixo.
Vamos ver agora métodos para raster de mais de uma banda. Vamos trabalhar um pouco agora
com imagem raster de 3 bandas. Carregamos o raster de forma similar e vamos extrair algumas
de suas informações.
projeto=QgsProject.instance()
camadaR = QgsRasterLayer("c:/users/user/desktop/dash/cabral.tif",
"img")
projeto. addMapLayer(camadaR)
Podemos ver algumas das informações usando:
camadaR.bandCount()# número de bandas
<RasterLayerType.MultiBand: 2>
camadaR.renderer().type()
'multibandcolor'
Vamos ver abaixo como modificar a imagem para que a banda 1 fique no canal azul (B) e a banda
3 fique no canal Vermelho (R).
camadaR.renderer().setBlueBand(1)
camadaR.renderer().setRedBand(3)
camadaR.triggerRepaint()
Note que o histograma da imagem não foi apropriadamente ajustado porque ainda usa os
valores de máximo e mínimo das bandas anteriores.
Imagens raster também podem ser criadas via script de forma bem eficiente usando uma lista
de dados pontuais com um determinado valor. Vamos aqui criar um raster mostrando a
temperatura média de uma área com base em informações pontuais de vários locais. O arquivo
CSV tfinal.csv tem os dados com coordenadas, e respectivos valores. Vamos carregar a
informação em um objeto do tipo QsgInterpolator camada de dados (layerData).
uri="file:///c:/users/user/desktop/dash/tfinal.csv?
type=csv&xField=LONGITUDE&yField=LATITUDE&crs=epsg:4326"
camada = QgsVectorLayer(uri, 'Converte', "delimitedtext")
c_data = QgsInterpolator.LayerData()
c_data.source = camada
c_data.zCoordInterpolation = False
c_data.interpolationAttribute = 6
c_data.sourceType = QgsInterpolator.SourcePoints
Agora definimos qual arquivo será criado e os parâmetros do grid a ser usado.
arquivo = "c:/users/user/desktop/dash/rasterDeTeste.asc"
rect = camada.extent()
res = 0.01
ncol = int( ( rect.xMaximum() - rect.xMinimum() ) / res )
nrows = int( (rect.yMaximum() - rect.yMinimum() ) / res)
saida = QgsGridFileWriter(interpolado,arquivo,rect,ncol,nrows)
saida.writeFile()
No módulo anterior usamos o iface em duas ocasiões, uma para acessar uma ferramenta da
barra de ferramenta de raster, e outra para carregar um raster gerado por interpolação. Vamos
agora ver outras funções da classe iface.
Mudamos o ícone usando o código abaixo. O os.path.expanduser('~') te leva para o pasta raiz
de usuário no seu sistema.
import os
icone = 'icon.png'# use um arquivo png 64x64 pixels
dire = os.path.join(os.path.expanduser('~'), 'desktop/dash/')
path = os.path.join(dire, icone)
icone2 = QIcon(path)
iface.mainWindow().setWindowIcon(icone2)
Com o seguinte código adicionamos um nove item no menu Ajuda que direciona para a nossa
página do Aprendendo pyQGIS.
import webbrowser
def abreSite():
webbrowser.open('https://gdatasystems.com/pyqgis')
Como já fizemos no módulo anterior podemos adicionar novas camadas usando o iface.
uri = "c:/users/user/desktop/dash/rasterDeTeste.asc"
iface.addRasterLayer(uri, "raster_interpolado")
uri2="C:/Users/User/Desktop/dash/contorno.shp"
iface.addVectorLayer(uri2, "contorno_interpolado","ogr")
PyQt
QMessageBox
Ao clicar OK teremos:
Agora somente um exemplo mais elaborado para ilustrar como a interação entre QGIS e QT pode
ser feita, mas não se preocupe, temos os Plugins para facilitar tudo. Isso é somente uma
demonstração. Para os pligins usaremos o QtDesigner para criar as Interfaces Gráficas
visualmente.
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Rampa de cor")
self.setGeometry(100, 100, 400, 200)
central_widget = QWidget()
self.setCentralWidget(central_widget)
dialog_button = QPushButton("Cria Rampa de cor.")
dialog_button.clicked.connect(self.rampa)
layout = QVBoxLayout()
layout.addWidget(dialog_button)
central_widget.setLayout(layout)
def rampa(self):
camadaR = iface.activeLayer()
renderer = camadaR.renderer()
provider = camadaR.dataProvider()
rect = camadaR.extent()
stats = provider.bandStatistics(1,
QgsRasterBandStats.All,rect, 0)
min= stats.minimumValue
max = stats.maximumValue
range = max - min
add = range//2
interval = min + add
colDic = {'re':'#ff0000', 'ye':'#ffff00','bl':'#0000ff'}
valueList =[min, interval, max]
lst = [QgsColorRampShader.ColorRampItem(valueList[0],
QColor(colDic['re'])),
QgsColorRampShader.
ColorRampItem(valueList[1], QColor(colDic['ye'])),
QgsColorRampShader.ColorRampItem(valueList[2],
QColor(colDic['bl']))]
myRasterShader = QgsRasterShader()
myColorRamp = QgsColorRampShader()
myColorRamp.setColorRampItemList(lst)
myRasterShader.setRasterShaderFunction(myColorRamp)
myPseudoRenderer=
QgsSingleBandPseudoColorRenderer(camadaR.dataProvider(),
camadaR.type(), myRasterShader)
camadaR.setRenderer(myPseudoRenderer)
camadaR.triggerRepaint()
arquivo = "c:/users/user/desktop/dash/rasterDeTeste.asc"
iface.addRasterLayer(arquivo, "raster")
window = MyWindow()
window.show()
Ao executar teremos:
Podemos executar processamentos do QGIS sem iniciar a interface gráfica, usando somente
scripts. Para fazer isso temos que criar um script com a seguinte estrutura mínima.
from qgis.core import *
# indique onde o programa qgis está localizado no seu computador
QgsApplication.setPrefixPath("/usr", True)
# Crie uma referência à QgsApplication. Usando False como segundo
argumento
# para não ativar a interface gráfica.
qgs = QgsApplication([], False)
# inicie o qgis
qgs.initQgis()
###########################################
# Escreva o código de processamento aqui###
###########################################
# finalize o script usando:
qgs.exitQgis()
Vamos mostrar como isso funciona criando um script que criará um grid raster a partir de um
arquivo texto CSV com alguns pontos. O mesmo procedimento do último exemplo do mõdulo
anterior. Nomeie o arquivo de criaRaster.py. Substitua users/user/desktop/dash
apropriadamente para o seu sistema.
from qgis.core import *
from qgis.analysis import *
import os
QgsApplication.setPrefixPath("C:/ProgramFiles/QGIS3.34.1/bin/",
True)
qgs = QgsApplication([], False)
qgs.initQgis()
uri="file:///users/user/desktop/dash/tfinal.csv?type=csv&xField=LONG
ITUDE&yField=LATITUDE&crs=epsg:4326"
camada = QgsVectorLayer(uri, 'Converte', "delimitedtext")
if not camada.isValid():
print("A camada não carregou apropriadamente!")
else:
c_data = QgsInterpolator.LayerData()
c_data.source = camada
c_data.zCoordInterpolation = False
c_data.interpolationAttribute = 6
c_data.sourceType = QgsInterpolator.SourcePoints
interpolado = QgsIDWInterpolator([c_data])
interpolado.setDistanceCoefficient(2)
arquivo = os.path.expanduser('~')+"/desktop/dash/rasterScript.asc"
rect = camada.extent()
res = 0.001
ncol = int( ( rect.xMaximum() - rect.xMinimum() ) / res )
nrows = int( (rect.yMaximum() - rect.yMinimum() ) / res)
saida = QgsGridFileWriter(interpolado,arquivo,rect,ncol,nrows)
saida.writeFile()
qgs.exitQgis()
Abrir o shell OSGeo4W, navegar até a pasta do script criaRaster.py e executar usando:
python-qgis criaRaster.py
Ao abrirmos o raster rasterScript.asc criado pelo script acima no QGIS veremos o seguinte.
Colinha do pyQGS
A seguir temos uma cola (cheat sheet) das classes principais para uma rápida referência. Em
azul temos as bibliotecas a serem carregadas no caso de usar em Plugins ou em scripts fora do
QGIS.
QgsInterface() iface
Aparência
from qgis.PyQt.QtWidgets import QApplication
app = QApplication.instance()
app.setStyleSheet(".QWidget {color:blue;background-color:yellow;}")
Barra de Ferramentas
Remove e adiciona barra de ferramenta
toolbar = iface.helpToolBar()
parent = toolbar.parentWidget()
parent.removeToolBar(toolbar)
parent.addToolBar(toolbar)
Menus
Remove e adiciona Menu
menu = iface.helpMenu()
menubar = menu.parentWidget()
menubar.removeAction(menu.menuAction())
menubar.addAction(menu.menuAction())
Camadas
Adicionando uma camada vetor do tipo shapefile
camada = iface.addVectorLayer("caminho/aoi.shp", "Camada", "ogr")
if not camada or not camada.isValid():
print("Falha ao carregar camada!")
Obtendo os elementos
for f in camada.getFeatures():
print (f)
url= 'https://tile.openstreetmap.org/{z}/{x}/{y}.png&zmax=19&zmin=0'
loadXYZ(url, 'OpenStreetMap')
Arquivo __init__.py
#-------------------------------------------------------------------
from PyQt5.QtWidgets import QAction, QMessageBox
def classFactory(iface):
return Minimo(iface)
class Minimo:
def __init__(self, iface):
self.iface = iface
def initGui(self):
self.action = QAction('Teste', self.iface.mainWindow())
self.action.triggered.connect(self.run)
self.iface.addToolBarIcon(self.action)
def unload(self):
self.iface.removeToolBarIcon(self.action)
del self.action
def run(self):
QMessageBox.information(None,'Plugin Mínimo','Kreegah
bundolo!')
Depois de ter criado os arquivos na nova pasta dentro da pasta plugins do QGIS, inicie o QGIS.
Vá no menu Complementos->Gerenciar e instalar Complementos.
Clique nele.
Pronto, seu primeiro plugin com pyQGIS foi executado mostrando a seguinte mensagem:
O arquivo metadata.txt contém as informações sobre o plugin tais como quem criou, e-mail de
quem criou, repositório web dos arquivos e informações que serão apresentadas quando
adicionamos o plugin.
O arquivo __init__.py é requerido pelo sistema import do Python. Ele contém a função
classFactory() que é chamada quando carregamos o plugin no QGIS.
Aqui colocamos o código fonte da classe do plugin dentro do arquivo também, mas
normalmente a classe do plugin (class Minimo) iria em um arquivo separado. Veremos mais
adiante como fazemos isso.
Dentro da classe Mínimo temos 4 funções:
__init__
Onde ganhamos acesso à interface do QGIS.
initGui
Essa função é chamada quando o plugin é carregado. Aqui se faz toda a inicialização das
variáveis do plugin, inclusive as da interface gráfica do usuário (GUI) que detalharemos adiante.
unload
Essa função é chamada quando desativamos o plugin
run
Nessa função temos a execução do plugin propriamente dita.
Neste exemplo ainda não temos nenhuma interface gráfica de usuário (GUI). A seguir
mostraremos como construir um plugin básico mais completo que esse.
2 - O básico
O plugin básico apresenta mais arquivos que são basicamente os diálogos (GUI) do plugin, os
recursos para o diálogo e plugin.
O diálogo consiste em um arquivo XML com a extensão .ui que será transformado em um arquivo
.py para ser acionado quando rodamos o plugin.
O arquivo de recurso é o resources.qrc que também é em XML e será transformado em arquivo
resources.py para ser acionado ao executarmos o plugin. Um exemplo de recurso é um ícone
para o plugin que aqui será o arquivo icon.png (download em
https://gdatasystems.com/pyqgis/index.php ).
Vamos separar a classe principal do plugin do arquivo __init__.py conforme mencionamos
anteriormente, assim teremos basicamente que escrever o código do plugin nesse arquivo
somente.
Na pasta de plugin crie uma nova pasta chamada básico e carregue os seguintes arquivos:
metadata.txt
[general]
name=Basico
description=Plugin Basico
about=Sobre este plugin
version=1.0
qgisMinimumVersion=3.0
author=Você
[email protected]
repository=URL para o repositório do código na web
__init__.py
def classFactory(iface):
from .basico import Basico
return Basico(iface)
basico_dialog.py
import os
from PyQt5 import uic
from PyQt5 import QtWidgets
FORM_CLASS, _ = uic.loadUiType(os.path.join(
os.path.dirname(__file__), 'basico.ui'))
basico.ui (esse arquivo pode ser feito automaticamente com o QtDesigner que é instalado
junto com o QGIS. Use este por praticidade, mostraremos como usar o QtDesigner mais
adiante.)
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>201</width>
<height>115</height>
</rect>
</property>
<property name="windowTitle">
<string>Básico</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>171</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QgsFileWidget" name="mQgsFileWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>151</width>
<height>27</height>
</rect>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>QgsFileWidget</class>
<extends>QWidget</extends>
<header>qgsfilewidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
basico.py
from PyQt5.QtCore import QSettings, QTranslator, QCoreApplication
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QMessageBox
from qgis.gui import QgsFileWidget
import os.path
from .resources import *
from .basico_dialog import BasicoDialog
class Basico:
def __init__(self, iface):
self.iface = iface
self.actions = []
self.menu = '&Básico'
self.first_start = None
def add_action(
self,
icon_path,
text,
callback,
enabled_flag=True,
add_to_menu=True,
add_to_toolbar=True,
status_tip=None,
whats_this=None,
parent=None):
icon = QIcon(icon_path)
action = QAction(icon, text, parent)
action.triggered.connect(callback)
action.setEnabled(enabled_flag)
if add_to_toolbar:
self.iface.addToolBarIcon(action)
if add_to_menu:
self.iface.addPluginToMenu(
self.menu,
action)
self.actions.append(action)
return action
def initGui(self):
icon_path = ':/plugins/basico/icon.png'
self.add_action(
icon_path,
text='Básico',
callback=self.run,
parent=self.iface.mainWindow())
self.first_start = True
def unload(self):
for action in self.actions:
self.iface.removePluginMenu(
'&Básico',
action)
self.iface.removeToolBarIcon(action)
def run(self):
if self.first_start == True:
self.first_start = False
self.dlg = BasicoDialog()
self.dlg.show()
result = self.dlg.exec_()
if result:
arqui=self.dlg.mQgsFileWidget.filePath()
Assim o arquivo resources.py é criado na pasta certa. Pode digitar exit na linha de comando
para fechar a shell.
Inicie o QGIS e instale o plugin Basico da mesma forma que fizemos com o plugin Minimo acima.
Um item no menu complementos e um ícone na barra de ferramenta serão criados para acessar
o plugin.
Este plugin não faz nada no momento, foi só para exemplificar como construir um plugin
diretamente.
No próximo módulo vamos criar o primeiro plugin funcional e usaremos o QtDesigner e o
PluginBuilder para automatizar a criação de plugins.
Criando Plugins QGIS com pyQGIS
QtDesigner, PluginBuilder e um plugin de verdade
1 - O QtDesiner e o PluginBuilder
Para facilitar a nossa vida existem duas ferramentas que auxiliam, e bastante, na criação
de plugins. A primeira é o QtDesigner para construirmos as interfaces gráficas do usuário
(GUI) e o PluginBuilder que cria praticamente todos os arquivos do plugin para nós.
O QtDesigner já vem com o QGIS e a tela inicial dele é:
Vamos agora usar essas ferramentas na construção do nosso primeiro plugin funcional.
2 - Construindo o esqueleto do Grid_Builder no Plugin Builder 3
Vamos iniciar pelo Plugin Builder:
Esse primeiro formulário será usado na criação do arquivio metadata.txt e na definição do nome
das classes do plugin.
Descrição mais detalhada sobre o plugin que também será colocado no arquivo metadata.txt.
Template (tipo) do plugin, texto que vai aparecer no menu e em qual menu será listado o plugin.
Desmarque todos para esse plugin,
Cheque a caixa de plugin experimental pois não iremos distribuir esse plugin no momento.
A pasta de plugin do sistema (nesse caso em sistema Windows). Clique Generate após
selecionar o diretório de plugins.
Pronto, os arquivos base de seu plugin foram criados na pasta:
C:/Users/User/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins\grid_builder
Os arquivos gerados:
Os oito arquivos necessários mais dois arquivos README com instruções do PluginBuilder
foram criados automaticamente.
Com base no README gerado abaixo vamos aos próximos passos.
Vamos adicionar 5 widgets do tipo Label, 4 widgets do tipo Line Edit e um widget do tipo
MapLayerComboBox. Basta clicar no Widget e arrastar até a janela do diálogo.
self.dlg.show()
result = self.dlg.exec_()
if result:
if self.dlg.lineEditSpY.text()=='' or self.dlg.lineEditSpX.text()=='' or
self.dlg.lineEditRota.text()=='' or self.dlg.lineEditBuf.text()=='':
QMessageBox.warning(self.iface.mainWindow(),
'Erro',
"Entre todos os campos por favor\nSaindo...")
return
layer = self.dlg.mMapLayerComboBox.currentLayer()
feats = [ feat for feat in layer.getFeatures()]
points = []
spacing_y = int(self.dlg.lineEditSpY.text())
spacing_x = int(self.dlg.lineEditSpX.text())
rotacao= int(self.dlg.lineEditRota.text())
extensao= int(self.dlg.lineEditBuf.text())
#executar o código
#------------------
#
for feat in feats:
centroid = feat.geometry().centroid().asPoint()
extent = feat.geometry().boundingBox()
xmin=int(round(extent.xMinimum()-extensao, -2))
ymin=int(round(extent.yMinimum()-extensao, -2))
xmax=int(round(extent.xMaximum()+extensao, -2))
ymax=int(round(extent.yMaximum()+extensao, -2))
rows = int(((ymax) - (ymin))/spacing_y)
cols = int(((xmax) - (xmin))/spacing_x)
x = xmin
y = ymax
geom_feat = feat.geometry()
for i in range(rows+1):
for j in range(cols+1):
pt = QgsPointXY(x,y)
tmp_pt = QgsGeometry.fromPointXY(pt)
tmp_pt.rotate(rotacao, centroid)
if tmp_pt.within(geom_feat):
points.append(tmp_pt.asPoint())
x += spacing_x
x = xmin
y -= spacing_y
epsg = layer.crs().postgisSrid()
#gerando pontos
uri = "PointZ?crs=epsg:" + str(epsg) + "&field=id:integer""&index=yes"
mem_layer = QgsVectorLayer(uri,'gridpoints','memory')
prov = mem_layer.dataProvider()
feats = [ QgsFeature() for i in range(len(points)) ]
for i, feat in enumerate(feats):
feat.setAttributes([i])
feat.setGeometry(QgsPoint(points[i].x(),points[i].y(),0.0))
prov.addFeatures(feats)
QgsProject.instance().addMapLayer(mem_layer)
QMessageBox.information(self.iface.mainWindow(),
'Pronto',
"Pontos de amostragem criados!")
Return
Antes de comentarmos o código adicionado vamos testar o plugin. Abra o QGIS e o plugin será
carregado já com as alterações feitas. Ao iniciarmos o plugin teremos:
Primeiro crie ou abra uma camada do tipo polígono para executar o plugin Grid Builder.
Clique em cancelar e crie um polígono (em coordenadas UTM, lat long funciona, mas os
espaçamentos e o tampão devem ser entrados como decimal de grau e os campos de entrada
validam somente inteiros, para aceitar decimais é preciso mudar o “validator” para
QDoubleValidator no código).
def run(self):
if self.first_start == True:
self.first_start = False
self.dlg = GridBuilderDialog()
self.dlg.mMapLayerComboBox.setShowCrs(True)
self.map_layers = QgsProject.instance().mapLayers().values()
self.allow_list = [
lyr.id() for lyr in self.map_layers if lyr.type() ==
QgsMapLayerType.VectorLayer
and lyr.geometryType()== QgsWkbTypes.PolygonGeometry
]
self.except_list = [l for l in self.map_layers if l.id() not in self.allow_list]
self.dlg.mMapLayerComboBox.setExceptedLayerList(self.except_list)
onlyInt = QIntValidator()mudar para QDoubleValidator se for usar lat long também
self.dlg.lineEditSpY.setText('400')
self.dlg.lineEditSpY.setValidator(onlyInt)
self.dlg.lineEditSpX.setText('200')
self.dlg.lineEditSpX.setValidator(onlyInt)
self.dlg.lineEditRota.setText('0')
self.dlg.lineEditRota.setValidator(onlyInt)
self.dlg.lineEditBuf.setText('1000')
self.dlg.lineEditBuf.setValidator(onlyInt)
O bloco seguinte checa se os campos estão todos preenchidos e assinala eles às variáveis do
programa.
if result:
if self.dlg.lineEditSpY.text()=='' or self.dlg.lineEditSpX.text()=='' or
self.dlg.lineEditRota.text()=='' or self.dlg.lineEditBuf.text()=='':
QMessageBox.warning(self.iface.mainWindow(),
'Erro',
"Entre todos os campos por favor\nSaindo...")
return
layer = self.dlg.mMapLayerComboBox.currentLayer()
feats = [ feat for feat in layer.getFeatures()]
points = []
spacing_y = int(self.dlg.lineEditSpY.text())
spacing_x = int(self.dlg.lineEditSpX.text())
rotacao= int(self.dlg.lineEditRota.text())
extensao= int(self.dlg.lineEditBuf.text())
Aqui lemos o polígono e assinalamos sua extensão, calculamos o buffer e checamos qual
sistema de coordenada devemos usar para gerar os pontos.
#executar o código
#------------------
#
for feat in feats:
centroid = feat.geometry().centroid().asPoint()
extent = feat.geometry().boundingBox()
xmin=int(round(extent.xMinimum()-extensao, -2))
ymin=int(round(extent.yMinimum()-extensao, -2))
xmax=int(round(extent.xMaximum()+extensao, -2))
ymax=int(round(extent.yMaximum()+extensao, -2))
rows = int(((ymax) - (ymin))/spacing_y)
cols = int(((xmax) - (xmin))/spacing_x)
x = xmin
y = ymax
Finalmente criamos os pontos e carregamos eles em um arquivo na memória.
geom_feat = feat.geometry()
for i in range(rows+1):
for j in range(cols+1):
pt = QgsPointXY(x,y)
tmp_pt = QgsGeometry.fromPointXY(pt)
tmp_pt.rotate(rotacao, centroid)
if tmp_pt.within(geom_feat):
points.append(tmp_pt.asPoint())
x += spacing_x
x = xmin
y -= spacing_y
epsg = layer.crs().postgisSrid()
uri = "PointZ?crs=epsg:" + str(epsg) + "&field=id:integer""&index=yes"
mem_layer = QgsVectorLayer(uri,'gridpoints','memory')
prov = mem_layer.dataProvider()
feats = [ QgsFeature() for i in range(len(points)) ]
for i, feat in enumerate(feats):
feat.setAttributes([i])
feat.setGeometry(QgsPoint(points[i].x(),points[i].y(),0.0))
prov.addFeatures(feats)
QgsProject.instance().addMapLayer(mem_layer)
QMessageBox.information(self.iface.mainWindow(),
'Pronto',
"Pontos de amostragem criados!")
Return
No próximo módulo avançaremos um pouco mais com um novo exemplo de plugin que usa uma
biblioteca externa, não padrão do QGIS.
Criando Plugins QGIS com pyQGIS
Usando bibliotecas externas no ambiente pyQGIS. Novo
plugin Stream Builder
1 - O ambiente pyQGIS
O QGIS possui um ambiente python dedicado e para instalar novas bibliotecas usamos
o shell OSGeo4W. Nesse exemplo de plugin vamos utilizar as bibliotecas pysheds e
fiona.
Inicie o shell e digite:
python -m pip install pysheds
Esse primeiro formulário será usado na criação do arquivo metadata.txt e na definição do nome
das classes do plugin.
Descrição mais detalhada sobre o plugin que também será colocado no arquivo metadata.txt.
Template (tipo) do plugin, texto que vai aparecer no menu e em qual menu será listado o plugin.
Desmarque todos para esse plugin,
Cheque a caixa de plugin experimental pois não iremos distribuir esse plugin no momento.
A pasta de plugin do sistema (nesse caso em sistema Windows). Clique Generate após
selecionar o diretório de plugins.
Pronto, os arquivos base de seu plugin foram criados na pasta:
C:/Users/User/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins\stream_builder
Os arquivos gerados:
Os oito arquivos necessários mais dois arquivos README com instruções do PluginBuilder
foram criados automaticamente.
Vamos testar ele iniciando o QGIS e abrindo o Complementos->Gerenciar e instalar
Complementos. Em Instalados vemos que ele não foi instalado ainda. Marque ele e instale
para testarmos.
Vamos adicionar 5 widgets do tipo Label, 4 widgets do tipo Line Edit e um widget do tipo
QgsFileWidget. Basta clicar no Widget e arrastar até a janela do diálogo.
A aparência final da interface deve ser:
self.dlg.show()
result = self.dlg.exec_()
# See if OK was pressed
if result:
if self.dlg.lineEditY.text()=='' or self.dlg.lineEditX.text()=='' or
self.dlg.lineEditSnap.text()=='' or self.dlg.lineEditAcc.text()=='':
QMessageBox.warning(self.iface.mainWindow(),
'Erro',
"Entrar todos os campo por favor \nSaindo...")
return
layer = self.dlg.mQgsFileWidget.filePath()
y = float(self.dlg.lineEditY.text())
x = float(self.dlg.lineEditX.text())
snapp= float(self.dlg.lineEditSnap.text())
accc= float(self.dlg.lineEditAcc.text())
grid = Grid.from_raster(layer)
dem = grid.read_raster(layer)
pit_filled_dem = grid.fill_pits(dem)
flooded_dem = grid.fill_depressions(pit_filled_dem)
inflated_dem = grid.resolve_flats(flooded_dem)
dirmap = (64, 128, 1, 2, 4, 8, 16, 32)
fdir = grid.flowdir(inflated_dem, dirmap=dirmap)
acc = grid.accumulation(fdir, dirmap=dirmap)
x_snap, y_snap = grid.snap_to_mask(acc > snapp, (x,y))
catch = grid.catchment(x=x_snap, y=y_snap, fdir=fdir,
dirmap=dirmap,xytype='coordinate')
grid.clip_to(catch)
clipped_catch = grid.view(catch)
branches = grid.extract_river_network(fdir, acc > accc, dirmap=dirmap)
schema = {
'geometry': 'LineString',
'properties': {}
}
Execute o plugin com os seguintes parâmetros. A posição X e U da capitação deve ser sobre uma
drenagem e a montante desse ponto será feita a análise:
O processamento demora uns dois minutos e ao final teremos a mensagem:
Agora, caso precise, é só salvar as camadas criadas na projeção desejada (drenagens e pontos
de amostragem nas bifurcações).
Este bloco do código inicializa os widgets adicionando os valores iniciais nas linhas editáveis.
if self.first_start == True:
self.first_start = False
self.dlg = StreamBuilderDialog()
onlyInt = QIntValidator()
onlyDouble = QDoubleValidator()
self.dlg.lineEditX.setText('0.0')
self.dlg.lineEditX.setValidator(onlyDouble)
self.dlg.lineEditY.setText('0.0')
self.dlg.lineEditY.setValidator(onlyDouble)
self.dlg.lineEditSnap.setText('10000')
self.dlg.lineEditSnap.setValidator(onlyInt)
self.dlg.lineEditAcc.setText('200')
self.dlg.lineEditAcc.setValidator(onlyInt)
self.dlg.show()
result = self.dlg.exec_()
O bloco seguinte checa se os campos estão todos preenchidos, assinala eles às variáveis do
programa e lê o arquivo DEM.
if self.dlg.lineEditY.text()=='' or self.dlg.lineEditX.text()=='' or
self.dlg.lineEditSnap.text()=='' or self.dlg.lineEditAcc.text()=='':
QMessageBox.warning(self.iface.mainWindow(),
'Erro',
"Entrar todos os campo por favor \nSaindo...")
return
layer = self.dlg.mQgsFileWidget.filePath()
y = float(self.dlg.lineEditY.text())
x = float(self.dlg.lineEditX.text())
snapp= float(self.dlg.lineEditSnap.text())
accc= float(self.dlg.lineEditAcc.text())
grid = Grid.from_raster(layer)
dem = grid.read_raster(layer)
A biblioteca adicional necessária para nosso plugin foi instalada. Procederemos agora com a
construção do plugin novo.
2 - Construindo o esqueleto do Drillhole1 no Plugin Builder 3
Inicie o Plugin Builder:
Esse primeiro formulário será usado na criação do arquivo metadata.txt e na definição do nome
das classes do plugin.
Descrição mais detalhada sobre o plugin que também será colocado no arquivo metadata.txt.
Template (tipo) do plugin, texto que vai aparecer no menu e em qual menu será listado o plugin.
Desmarque todos para esse plugin,
Cheque a caixa de plugin experimental pois não iremos distribuir esse plugin no momento.
A pasta de plugin do sistema (nesse caso em sistema Windows). Clique Generate após
selecionar o diretório de plugins.
Pronto, os arquivos base de seu plugin foram criados na pasta:
C:/Users/User/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins\drillhole1
Os oito arquivos necessários mais dois arquivos README com instruções do PluginBuilder
foram criados automaticamente.
survey.csv contendo informações de direção e mergulho do furo e dos desvios entre boca do
furo (collar) e ao longo do mesmo.
- Formato: HOLEID,AT,AZM,DIP
litho.csv contendo informações dos tipos geológicos, domínios e alteração e como estes
variam dentro do furo.
- Formato: HOLEID,FROM,TO,DOMAIN,ROCKTYPE,WEATH
Crie na mesma pasta do plugin o seguinte arquivo com as funções auxiliares desurvey.py :
import pandas as pd
import numpy as np
#----------------------------------------------------------------------------------
def tresD(co,es):
linha = pd.DataFrame(columns = [‘hole’, ‘prof’, ‘x’,’y’,’z’,’dip’,’az’])
for I in range(0,len(co.index)):
x=co.iat[I,2]
y=co.iat[I,3]
z=co.iat[I,4]
di=0
fur=es.loc[es[‘hole’] == co.iat[I,0]]
fur=fur.sort_values(‘prof’)
ro=fur.shape[0]
for j in range(0,ro):
ang=fur.iat[j,6]
d=fur.iat[j,1]-di
di=d+di
dip=fur.iat[j,5]
if j>0:
dip=fur.iat[j,5]-(fur.iat[j,5]-fur.iat[j-1,5])/2
#--
deltaz=d*np.sin(np.radians(dip))
r=d*np.cos(np.radians(dip))
x=round(x+r*np.sin(np.radians(ang)))
y=round(y+r*np.cos(np.radians(ang)))
z=round(z+deltaz)
linha.loc[len(linha.index)]=[fur.iat[j,0] ,fur.iat[j,1],x
,y,z,fur.iat[j,5],fur.iat[j,6]]
#--
#---
return linha.sort_values([‘hole’,’prof’])
def calculo(azt,azb,dt,db,dp):
dt = (90-dt)
db = (90-db)
dbt = db-dt
abt = azb-azt
d = np.arccos(np.cos(np.radians(dbt))-
np.sin(np.radians(dt))*np.sin(np.radians(db))*(1-np.cos(np.radians(abt))))
r = 1
if d==0:
r = 1
else:
r = 2*np.tan(d/2)/d
x = 0.5*dp*(np.sin(np.radians(dt))*np.sin(np.radians(azt))+
np.sin(np.radians(db))*np.sin(np.radians(azb)))*r
y = 0.5*dp*(np.sin(np.radians(dt))*np.cos(np.radians(azt))+
np.sin(np.radians(db))*np.cos(np.radians(azb)))*r
z = 0.5*dp*(np.cos(np.radians(dt))+np.cos(np.radians(db)))*r
return [x,y,z]
def calcXYZ(hole,depth,Spts):
t = Spts.loc[(Spts[‘hole’]==hole) & (Spts[‘prof’] <=depth)]
t = t.sort_values(‘prof’,ascending=False)
b = Spts.loc[(Spts[‘hole’]==hole) & (Spts[‘prof’] > depth)]
if b.shape[0] == 0:
res = calculo(t.iat[0,6], t.iat[0,6], t.iat[0,5], t.iat[0,5], depth-t.iat[0,1])
return [(t.iat[0,2]+res[0]), (t.iat[0,3]+res[1]), (t.iat[0,4]+res[2])]
di = b.iat[0,1]- t.iat[0,1]
rga = t.iat[0,5] – b.iat[0,5]
stp = rga/di
dpt = depth-t.iat[0,1]
pang = stp*dpt
if b.shape[0] >= 1:
res = calculo(t.iat[0,6], b.iat[0,6],t.iat[0,5], t.iat[0,5]-pang, dpt)
return[(t.iat[0,2]+res[0]), (t.iat[0,3]+res[1]), (t.iat[0,4]+res[2])]
else:
res = calculo(t.iat[0,6], t.iat[0,6], t.iat[0,5], t.iat[0,5], dpt)
return [(t.iat[0,2]+res[0]), (t.iat[0,3]+res[1]), (t.iat[0,4]+res[2])]
Spts.to_csv(dire+”/topobase.csv”,sep=’,’,index=False)
temp = QgsVectorLayer(“PointZ”,”desurveyed_intervals”,”memory”)
temp.setCrs(crs)
temp_data = temp.dataProvider()
# Criando os campos necessários
temp_data.addAttributes([QgsField( “hole”, Qvariant.String),
QgsField( “x”, Qvariant.Double),
QgsField( “y”, Qvariant.Double),
QgsField( “z”, Qvariant.Double),
QgsField( “au”, Qvariant.Double),
QgsField( “prof”, Qvariant.Double),
QgsField( “profl”, Qvariant.Double),
QgsField( “toa”, Qvariant.Double),
QgsField( “tol”, Qvariant.Double),
QgsField( “lena”, Qvariant.Double),
QgsField( “lenl”, Qvariant.Double),
QgsField( “DOMAIN”, Qvariant.String),
QgsField( “ROCKTYPE”, Qvariant.String),
QgsField( “WEATH”, Qvariant.String)])
# Atualizando os campos
temp.updateFields()
temp.startEditing()
# Adicionando cada ponto resultante do desurvey na camada ponto 3d
for row in dat.itertuples():
f = QgsFeature()
f.setGeometry(QgsGeometry(QgsPoint(row.x,row.y,row.z)))
f.setAttributes([row.hole,row.x,row.y,row.z,row.au,row.prof,row.profl,row.toa,row.t
ol,row.lena,row.lenl,row.DOMAIN,row.ROCKTYPE,row.WEATH])
temp_data.addFeature(f)
v_layer = QgsVectorLayer(‘LineString?crs=’+crs.authid(),
‘drillholeline’, ‘memory’)
pr = v_layer.dataProvider()
pr.addAttributes([QgsField( “hole”, Qvariant.String)])
v_layer.updateFields()
base_layer = QgsVectorLayer(‘PointZ?crs=’+crs.authid(),
‘base_of_survey’, ‘memory’)
prb = base_layer.dataProvider()
prb.addAttributes([QgsField( “hole”, Qvariant.String)])
base_layer.updateFields()
cabra=”_”
for index, row in Spts.iterrows():
if cabra == “_”:
start_point = QgsPoint(row[‘x’],row[‘y’],row[‘z’])
cabra=row.hole
f = QgsFeature()
f.setGeometry(QgsGeometry(start_point))
f.setAttributes([row.hole])
prt.addFeature(f)
else:
if cabra!=row.hole and index>0:
end_point = QgsPoint(Spts.loc[(int(index) – 1),
‘x’],Spts.loc[(int(index) – 1), ‘y’],Spts.loc[(int(index) – 1), ‘z’])
f = QgsFeature()
f.setGeometry(QgsGeometry(end_point))
f.setAttributes([row.hole])
prb.addFeature(f)
seg = QgsFeature()
seg.setGeometry(QgsGeometry.fromPolyline([start_point,
end_point]))
seg.setAttributes([row.hole])
pr.addFeatures([seg])
start_point = QgsPoint(row[‘x’],row[‘y’],row[‘z’])
cabra=row.hole
f = QgsFeature()
f.setGeometry(QgsGeometry(start_point))
f.setAttributes([row.hole])
prt.addFeature(f)
QgsProject.instance().addMapLayers([v_layer])
QgsProject.instance().addMapLayer(topo_layer)
QgsProject.instance().addMapLayer(base_layer)
renderer = temp.renderer()
symbol1 = QgsMarkerSymbol.createSimple({‘name’:’dot red’,’color’:
‘red’,’size’:’0.8’})
symbol_layer1 = symbol1.symbolLayer(0)
renderer.setSymbol(symbol1)
temp.triggerRepaint()
renderer = topo_layer.renderer()
style = QgsStyle.defaultStyle().symbol(‘topo pop capital’)
renderer.setSymbol(style)
renderer.symbol().setSize(2)
topo_layer.triggerRepaint()
renderer = base_layer.renderer()
style = QgsStyle.defaultStyle().symbol(‘topo pop village’)
renderer.setSymbol(style)
renderer.symbol().setSize(1.4)
renderer.symbol().setColor(Qcolor(“blue”))
base_layer.triggerRepaint()
self.iface.layerTreeView().refreshLayerSymbology(temp.id())
self.iface.layerTreeView().refreshLayerSymbology(topo_layer.id())
self.iface.layerTreeView().refreshLayerSymbology(base_layer.id())
#gravando o csv do desurvey e finalizando
QgsVectorFileWriter.writeAsVectorFormat(temp,dire+”/desurveyed.csv”,
“utf-8”,driverName = “CSV” , layerOptions = [‘GEOMETRY=AS_XYZ’])
QmessageBox.information(self.iface.mainWindow(),’Pronto’,’Desurvey
executado!’)
return
Clique ok e aguarde uns 50 segundos para o processamento dos dados e geração dos arquivos
e camadas temporárias resultantes. Ao concluir termos no QGIS:
A camada desurveyed_intervals representa o resultado do desurvey com todos os intervalos
reprojetados em X, Y e Z e seus atributos, ver tabela de atributos abaixo:
Além dessas camadas foram gerados dois arquivos csv. O arquivo desusrveyed.csv com o
resultado do desurvey e o arquivo topobase.csv com os pontos de medidas do survey no furo
(estação). Abrindo estes arquivos com o Paraview usando o filtro TableToPoint teremos:
topobase.csv
desurveyed.csv Domains
desurveyed.csv Rocktype
desurveyed.csv Weathering
desurveyed.csv Au
Revisitaremos este plugin num módulo mais adiante implementando mais flexibilidade dos
dados originais.
As bandas originais foram recortadas e as bandas com 10m de resolução (2 ,3, 4 e 8A) tiveram a
resolução transformada para 20 metros para serem compatíveis com a resolução das bandas 5,
11 e 12.
O plugin também gera uma composição RGB com os índices que é boa para a classificação do
terreno.
2 - Construindo o esqueleto do IndexOrama no Plugin Builder 3
Inicie o Plugin Builder:
Esse primeiro formulário será usado na criação do arquivo metadata.txt e na definição do nome
das classes do plugin.
Descrição mais detalhada sobre o plugin que também será colocado no arquivo metadata.txt.
Template (tipo) do plugin, texto que vai aparecer no menu e em qual menu será listado o plugin.
Desmarque todos para esse plugin,
Cheque a caixa de plugin experimental pois não iremos distribuir esse plugin no momento.
A pasta de plugin do sistema (nesse caso em sistema Windows). Clique Generate após
selecionar o diretório de plugins.
Pronto, os arquivos base de seu plugin foram criados na pasta:
C:/Users/User/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins\indexorama
Os oito arquivos necessários mais dois arquivos README com instruções do PluginBuilder
foram criados automaticamente.
Vamos agora editar o arquivo indexorama.py para realizar a tarefa. Vamos ter de adicionar
algumas bibliotecas de suporte via import.
As bibliotecas no arquivo indexorama.py serão (adicionar as faltantes):
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QMessageBox
from qgis.core import QgsProject,QgsRasterLayer
import processing
# Initialize Qt resources from file resources.py
from .resources import *
# Import the code for the dialog
from .indexorama_dialog import IndexoramaDialog
import os.path
import rasterio
import numpy as np
band2=rasterio.open(b2)
band3=rasterio.open(b3)
band4=rasterio.open(b4)
band5=rasterio.open(b5)
band6=rasterio.open(b6)
band7=rasterio.open(b7)
band8a=rasterio.open(b8a)
band11=rasterio.open(b11)
band12=rasterio.open(b12)
band2_rgb = band2.profile
band2_rgb.update({"count": 9})
with rasterio.open(dire+'\\fullstack.tiff', 'w', **band2_rgb) as dest:
dest.write(band2.read(1),1)
dest.write(band3.read(1),2)
dest.write(band4.read(1),3)
dest.write(band5.read(1),4)
dest.write(band6.read(1),5)
dest.write(band7.read(1),6)
dest.write(band8a.read(1),7)
dest.write(band11.read(1),8)
dest.write(band12.read(1),9)
stack=QgsRasterLayer(dire+"\\fullstack.tiff","stack")
QgsProject.instance().addMapLayer(stack)
output1 = dire+"\\ndvi.tiff"
processing.run("gdal:rastercalculator",
{'INPUT_A':stack,'BAND_A':7,'INPUT_B':stack,'BAND_B':3,'FORMULA':'((A-
B)/(A+B))','NO_DATA':None,'RTYPE':5,'OPTIONS':'','EXTRA':'','OUTPUT':output1})
output2 = dire+"\\ndvire1.tiff"
processing.run("gdal:rastercalculator",
{'INPUT_A':stack,'BAND_A':7,'INPUT_B':stack,'BAND_B':4,'FORMULA':'((A-
B)/(A+B))','NO_DATA':None,'RTYPE':5,'OPTIONS':'','EXTRA':'','OUTPUT':output2})
output3 = dire+"\\savi.tiff"
processing.run("gdal:rastercalculator",
{'INPUT_A':stack,'BAND_A':7,'INPUT_B':stack,'BAND_B':3,'FORMULA':'((A-
B)/(A+B+0.5)*1.5)','NO_DATA':None,'RTYPE':5,'OPTIONS':'','EXTRA':'','OUTPUT':output
3})
output4 = dire+"\\ndwi.tiff"
processing.run("gdal:rastercalculator",
{'INPUT_A':stack,'BAND_A':7,'INPUT_B':stack,'BAND_B':2,'FORMULA':'((B-
A)/(B+A))','NO_DATA':None,'RTYPE':5,'OPTIONS':'','EXTRA':'','OUTPUT':output4})
output5 = dire+"\\mndwi.tiff"
processing.run("gdal:rastercalculator",
{'INPUT_A':stack,'BAND_A':8,'INPUT_B':stack,'BAND_B':2,'FORMULA':'((B-
A)/(B+A))','NO_DATA':None,'RTYPE':5,'OPTIONS':'','EXTRA':'','OUTPUT':output5})
output6 = dire+"\\ndmi.tiff"
processing.run("gdal:rastercalculator",
{'INPUT_A':stack,'BAND_A':7,'INPUT_B':stack,'BAND_B':8,'FORMULA':'((A-
B)/(A+B))','NO_DATA':None,'RTYPE':5,'OPTIONS':'','EXTRA':'','OUTPUT':output6})
output7 = dire+"\\ndti.tiff"
processing.run("gdal:rastercalculator",
{'INPUT_A':stack,'BAND_A':8,'INPUT_B':stack,'BAND_B':9,'FORMULA':'((A-
B)/(A+B))','NO_DATA':None,'RTYPE':5,'OPTIONS':'','EXTRA':'','OUTPUT':output7})
output8 = dire+"\\ndbi.tiff"
processing.run("gdal:rastercalculator",
{'INPUT_A':stack,'BAND_A':7,'INPUT_B':stack,'BAND_B':8,'FORMULA':'((B-
A)/(B+A))','NO_DATA':None,'RTYPE':5,'OPTIONS':'','EXTRA':'','OUTPUT':output8})
QgsProject.instance().addMapLayer(QgsRasterLayer(output1,"ndvi"))
QgsProject.instance().addMapLayer(QgsRasterLayer(output2,"ndvire1"))
QgsProject.instance().addMapLayer(QgsRasterLayer(output3,"savi"))
QgsProject.instance().addMapLayer(QgsRasterLayer(output4,"ndwi"))
QgsProject.instance().addMapLayer(QgsRasterLayer(output5,"mndwi"))
QgsProject.instance().addMapLayer(QgsRasterLayer(output6,"ndmi"))
QgsProject.instance().addMapLayer(QgsRasterLayer(output7,"ndti"))
QgsProject.instance().addMapLayer(QgsRasterLayer(output8,"ndbi"))
NDBI=QgsRasterLayer(output8,"ndbi2")
NDTI=QgsRasterLayer(output7,"ndti2")
output9 = dire+"\\ndbi_ndti.tiff"
processing.run("gdal:rastercalculator",
{'INPUT_A':NDBI,'BAND_A':1,'INPUT_B':NDTI,'BAND_B':1,'FORMULA':'A+B','NO_DATA':None
,'RTYPE':5,'OPTIONS':'','EXTRA':'','OUTPUT':output9})
NDTI_NDBI=QgsRasterLayer(output9,"ndti_ndbi")
bandb=rasterio.open(output5)
bandg=rasterio.open(output3)
bandr=rasterio.open(output9)
band_rgb = bandr.profile
band_rgb.update({"count": 3})
with rasterio.open(dire+'\\classificado.tiff', 'w', **band_rgb) as
dest2:
dest2.write(bandb.read(1),1)
dest2.write(bandg.read(1),2)
dest2.write(bandr.read(1),3)
classi=QgsRasterLayer(dire+"\\classificado.tiff","classi")
QgsProject.instance().addMapLayer(classi)
QMessageBox.information(self.iface.mainWindow(),'Pronto','IndexOrama
executado!')
return
Abra o QGIS e o plugin será carregado já com as alterações feitas. Ao iniciarmos o plugin
teremos:
Entre o caminho para os arquivos das bandas de acordo com título. Defina o diretório onde os
arquivos gerados serão gravados.
Finalizamos aqui os exemplos de plugins. Fique ligado no site para novos módulos futuros de
plugins avançados.