Este documento detalha o funcionamento interno do script Python desenvolvido para automatizar a geração de mapas de localização no QGIS. O script gerencia importação de camadas, estilização, filtragem de dados e cálculo matemático de escalas e enquadramentos para layouts de impressão.
Ver Código Completo do Script
Código completo do arquivo Setup_Mapas_Municipais.py:
import math
from qgis.core import (QgsProject, QgsVectorLayer, QgsSymbol,
QgsSingleSymbolRenderer, QgsFillSymbol, QgsFeatureRequest,
QgsMapThemeCollection, QgsExpressionContextUtils,
QgsDistanceArea, QgsPointXY, QgsRectangle, QgsCoordinateReferenceSystem)
from qgis.utils import iface
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QPushButton, QLabel,
QFileDialog, QComboBox, QLineEdit, QHBoxLayout, QMessageBox, QGroupBox)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
# --- DIMENSÕES FIXAS DO LAYOUT (em milímetros) ---
DIM_MAPA_1_2 = {"w": 65.0, "h": 45.0} # Brasil e Estado
DIM_MAPA_3 = {"w": 139.266, "h": 73.287} # Município
# --- CONFIGURAÇÃO DE ESTILOS ---
STYLES = {
"america_sul": {"color": "#e0e0e0", "outline": "black"},
"unidades_federativas": {"color": "#ffffff", "outline": "black"},
"estado_destaque": {"color": "#ffbdbe", "outline": "black"},
"municipio_destaque": {"color": "#fdbf6f", "outline": "black"},
"hidrografia": {"color": "#01c4ff", "outline": "transparent"}
}
# --- FUNÇÕES MATEMÁTICAS ---
def get_geometry_data(layer, filter_expression=None):
"""Retorna extent e centro da geometria filtrada"""
if filter_expression:
req = QgsFeatureRequest().setFilterExpression(filter_expression)
feats = [f for f in layer.getFeatures(req)]
if feats:
extent = feats[0].geometry().boundingBox()
for f in feats[1:]:
extent.combineExtentWith(f.geometry().boundingBox())
else:
extent = layer.extent()
else:
extent = layer.extent()
return extent, extent.center()
def calculate_smart_scale(extent, map_w_mm, map_h_mm, layer):
"""Calcula escala redonda com passos intermediários."""
d = QgsDistanceArea()
d.setSourceCrs(layer.crs(), QgsProject.instance().transformContext())
d.setEllipsoid('WGS84')
p_min = QgsPointXY(extent.xMinimum(), extent.yMinimum())
p_max_x = QgsPointXY(extent.xMaximum(), extent.yMinimum())
p_max_y = QgsPointXY(extent.xMinimum(), extent.yMaximum())
geo_w_m = d.measureLine(p_min, p_max_x)
geo_h_m = d.measureLine(p_min, p_max_y)
map_w_m = map_w_mm / 1000.0
map_h_m = map_h_mm / 1000.0
# Margem de segurança (5%)
geo_w_m *= 1.05
geo_h_m *= 1.05
scale_x = geo_w_m / map_w_m
scale_y = geo_h_m / map_h_m
raw_scale = max(scale_x, scale_y)
# Arredondamento com degraus
ordem = 10 ** math.floor(math.log10(raw_scale))
base = raw_scale / ordem
degraus = [1.0, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 7.5, 8.0, 10.0]
mult = 10.0
for degrau in degraus:
if degrau >= base:
mult = degrau
break
final_scale = int(mult * ordem)
return final_scale
def calculate_map_extents(center_point, scale, map_w_mm, map_h_mm, layer_crs):
"""Calcula os limites (xmin, xmax...) para centralizar o mapa."""
world_w_meters = (map_w_mm / 1000.0) * scale
world_h_meters = (map_h_mm / 1000.0) * scale
half_w = 0; half_h = 0
if layer_crs.isGeographic():
meters_per_deg_lat = 111132.0
lat_rad = math.radians(center_point.y())
meters_per_deg_lon = 111132.0 * math.cos(lat_rad)
if meters_per_deg_lon < 1.0: meters_per_deg_lon = 1.0
deg_w = world_w_meters / meters_per_deg_lon
deg_h = world_h_meters / meters_per_deg_lat
half_w = deg_w / 2; half_h = deg_h / 2
else:
half_w = world_w_meters / 2; half_h = world_h_meters / 2
return {
"xmin": center_point.x() - half_w,
"xmax": center_point.x() + half_w,
"ymin": center_point.y() - half_h,
"ymax": center_point.y() + half_h
}
def calcular_unidade_escala_barra(width_m):
alvo = width_m / 4
if alvo <= 0: return 1000
ordem = 10 ** math.floor(math.log10(alvo))
base = alvo / ordem
if base < 1.5: mult = 1
elif base < 3.5: mult = 2
elif base < 7.5: mult = 5
else: mult = 10
return int(mult * ordem)
# --- INTERFACE ---
class ImportDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("1. Configuração Inicial")
self.resize(500, 300)
self.layout = QVBoxLayout()
self.inputs = {}
self.layers_config = [
("América do Sul", "america_sul", "Shapefiles (*.shp)"),
("Estados / UFs (Brasil)", "estados_br", "Shapefiles (*.shp)"),
("Municípios", "municipios", "Shapefiles (*.shp)"),
("Corpos D'água", "hidrografia", "Shapefiles (*.shp)"),
("Logo da Instituição", "logo_path", "Imagens (*.png *.jpg *.jpeg *.svg)")
]
for label_text, key, file_filter in self.layers_config:
h_layout = QHBoxLayout()
lbl = QLabel(label_text)
line_edit = QLineEdit()
btn = QPushButton("...")
btn.clicked.connect(lambda checked, le=line_edit, ff=file_filter: self.select_file(le, ff))
h_layout.addWidget(lbl)
h_layout.addWidget(line_edit)
h_layout.addWidget(btn)
self.layout.addLayout(h_layout)
self.inputs[key] = line_edit
self.btn_run = QPushButton("Carregar Camadas e Logo")
self.btn_run.clicked.connect(self.accept)
self.layout.addWidget(self.btn_run)
self.setLayout(self.layout)
def select_file(self, line_edit, file_filter):
file_path, _ = QFileDialog.getOpenFileName(self, "Selecione o Arquivo", "", file_filter)
if file_path: line_edit.setText(file_path)
def get_paths(self):
return {key: widget.text() for key, widget in self.inputs.items() if widget.text()}
class FilterDialog(QDialog):
def __init__(self, layer_brasil, layer_estado_destaque, layer_mun):
super().__init__()
self.setWindowTitle("2. Filtro e Layout")
self.resize(400, 250)
self.layer_br = layer_brasil
self.layer_est = layer_estado_destaque
self.layer_mun = layer_mun
self.layout = QVBoxLayout()
group_sel = QGroupBox("Seleção")
l_sel = QVBoxLayout()
l_sel.addWidget(QLabel("Estado:"))
self.combo_uf = QComboBox()
self.populate_ufs()
self.combo_uf.currentTextChanged.connect(self.update_municipios)
l_sel.addWidget(self.combo_uf)
l_sel.addWidget(QLabel("Município:"))
self.combo_mun = QComboBox()
l_sel.addWidget(self.combo_mun)
group_sel.setLayout(l_sel)
self.layout.addWidget(group_sel)
self.btn_apply = QPushButton("Aplicar e Gerar Variáveis")
self.btn_apply.clicked.connect(self.apply_filter)
self.layout.addWidget(self.btn_apply)
self.setLayout(self.layout)
if self.combo_uf.count() > 0: self.update_municipios(self.combo_uf.currentText())
def populate_ufs(self):
idx = self.layer_est.fields().indexOf('NM_UF')
if idx == -1: idx = self.layer_est.fields().indexOf('SIGLA')
if idx != -1:
values = self.layer_est.uniqueValues(idx)
self.combo_uf.addItems(sorted([str(v) for v in values]))
def update_municipios(self, uf_selecionada):
self.combo_mun.clear()
field_uf_mun = 'NM_UF' if self.layer_mun.fields().indexOf('NM_UF') != -1 else 'SIGLA_UF'
req = QgsFeatureRequest()
if self.layer_mun.fields().indexOf(field_uf_mun) != -1:
req.setFilterExpression(f"\"{field_uf_mun}\" = '{uf_selecionada}'")
municipios = []
possibles = ['NM_MUN', 'NM_MUNICIP', 'NOME', 'name', 'MUNICIPIO']
name_field = next((f for f in possibles if self.layer_mun.fields().indexOf(f) != -1), None)
if not name_field: return
for feat in self.layer_mun.getFeatures(req): municipios.append(feat[name_field])
self.combo_mun.addItems(sorted(municipios))
def apply_filter(self):
uf = self.combo_uf.currentText()
mun = self.combo_mun.currentText()
# 1. Filtro do Estado (Simples)
field_uf_est = 'NM_UF' if self.layer_est.fields().indexOf('NM_UF') != -1 else 'SIGLA'
self.layer_est.setSubsetString(f"\"{field_uf_est}\" = '{uf}'")
self.layer_est.setName(uf)
# 2. Filtro do Município (COMPOSTO: Nome E Estado)
possibles = ['NM_MUN', 'NM_MUNICIP', 'NOME', 'name', 'MUNICIPIO']
name_field = next((f for f in possibles if self.layer_mun.fields().indexOf(f) != -1), None)
# Tenta achar o campo de UF no município também
field_uf_mun = 'NM_UF' if self.layer_mun.fields().indexOf('NM_UF') != -1 else 'SIGLA_UF'
if name_field:
# CORREÇÃO AQUI: Filtra pelo nome do município E pelo nome do estado para evitar homônimos
if self.layer_mun.fields().indexOf(field_uf_mun) != -1:
expr_mun = f"\"{name_field}\" = '{mun}' AND \"{field_uf_mun}\" = '{uf}'"
else:
# Se não achar campo de UF no município, vai só pelo nome (risco, mas fallback necessário)
expr_mun = f"\"{name_field}\" = '{mun}'"
self.layer_mun.setSubsetString(expr_mun)
self.layer_mun.setName(mun)
# --- CÁLCULO GERAL (Usando a expressão composta) ---
# 1. MUNICÍPIO
ext_mun, center_mun = get_geometry_data(self.layer_mun, expr_mun)
scale_mun = calculate_smart_scale(ext_mun, DIM_MAPA_3['w'], DIM_MAPA_3['h'], self.layer_mun)
bbox_mun = calculate_map_extents(center_mun, scale_mun, DIM_MAPA_3['w'], DIM_MAPA_3['h'], self.layer_mun.crs())
# 2. ESTADO
ext_est, center_est = get_geometry_data(self.layer_est, f"\"{field_uf_est}\" = '{uf}'")
scale_est = calculate_smart_scale(ext_est, DIM_MAPA_1_2['w'], DIM_MAPA_1_2['h'], self.layer_est)
bbox_est = calculate_map_extents(center_est, scale_est, DIM_MAPA_1_2['w'], DIM_MAPA_1_2['h'], self.layer_est.crs())
# 3. BRASIL
ext_br, center_br = get_geometry_data(self.layer_br)
scale_br = calculate_smart_scale(ext_br, DIM_MAPA_1_2['w'], DIM_MAPA_1_2['h'], self.layer_br)
if scale_br > 150000000: scale_br = 100000000
bbox_br = calculate_map_extents(center_br, scale_br, DIM_MAPA_1_2['w'], DIM_MAPA_1_2['h'], self.layer_br.crs())
# Barra
d = QgsDistanceArea(); d.setEllipsoid('WGS84'); d.setSourceCrs(self.layer_mun.crs(), QgsProject.instance().transformContext())
p1 = QgsPointXY(ext_mun.xMinimum(), ext_mun.yMinimum()); p2 = QgsPointXY(ext_mun.xMaximum(), ext_mun.yMinimum())
w_mun_m = d.measureLine(p1, p2)
escala_barra = calcular_unidade_escala_barra(w_mun_m)
# SALVAR VARIÁVEIS
utils = QgsExpressionContextUtils; proj = QgsProject.instance()
utils.setProjectVariable(proj, 'meu_municipio', mun)
utils.setProjectVariable(proj, 'meu_estado', uf)
utils.setProjectVariable(proj, 'zoom_municipio', str(scale_mun))
utils.setProjectVariable(proj, 'zoom_estado', str(scale_est))
utils.setProjectVariable(proj, 'zoom_brasil', str(scale_br))
utils.setProjectVariable(proj, 'escala_tamanho', str(escala_barra))
utils.setProjectVariable(proj, 'mapa1_xmin', str(bbox_br['xmin'])); utils.setProjectVariable(proj, 'mapa1_xmax', str(bbox_br['xmax']))
utils.setProjectVariable(proj, 'mapa1_ymin', str(bbox_br['ymin'])); utils.setProjectVariable(proj, 'mapa1_ymax', str(bbox_br['ymax']))
utils.setProjectVariable(proj, 'mapa2_xmin', str(bbox_est['xmin'])); utils.setProjectVariable(proj, 'mapa2_xmax', str(bbox_est['xmax']))
utils.setProjectVariable(proj, 'mapa2_ymin', str(bbox_est['ymin'])); utils.setProjectVariable(proj, 'mapa2_ymax', str(bbox_est['ymax']))
utils.setProjectVariable(proj, 'mapa3_xmin', str(bbox_mun['xmin'])); utils.setProjectVariable(proj, 'mapa3_xmax', str(bbox_mun['xmax']))
utils.setProjectVariable(proj, 'mapa3_ymin', str(bbox_mun['ymin'])); utils.setProjectVariable(proj, 'mapa3_ymax', str(bbox_mun['ymax']))
self.layer_mun.updateExtents()
iface.mapCanvas().setExtent(ext_mun)
iface.mapCanvas().refresh()
print(f"VARIÁVEIS GERADAS!")
self.accept()
# --- MAIN ---
def apply_symbology(layer, style_key):
config = STYLES[style_key]
symbol = QgsFillSymbol.createSimple({'color': config["color"]})
if config["outline"] == "transparent": symbol.symbolLayer(0).setStrokeStyle(Qt.NoPen)
else:
symbol.symbolLayer(0).setStrokeColor(QColor(config["outline"]))
symbol.symbolLayer(0).setStrokeWidth(0.2)
layer.setRenderer(QgsSingleSymbolRenderer(symbol))
layer.triggerRepaint()
def create_map_themes(loaded_layers):
project = QgsProject.instance()
tc = project.mapThemeCollection()
for t in ["Localização", "Destaque"]:
if tc.hasMapTheme(t): tc.removeMapTheme(t)
rec_loc = QgsMapThemeCollection.MapThemeRecord()
for k in ["america_sul", "unidades_federativas", "estado_destaque"]:
if k in loaded_layers: rec_loc.addLayerRecord(QgsMapThemeCollection.MapThemeLayerRecord(loaded_layers[k]))
tc.insert("Localização", rec_loc)
rec_dest = QgsMapThemeCollection.MapThemeRecord()
for k in ["america_sul", "unidades_federativas", "hidrografia", "municipios"]:
if k in loaded_layers: rec_dest.addLayerRecord(QgsMapThemeCollection.MapThemeLayerRecord(loaded_layers[k]))
tc.insert("Destaque", rec_dest)
def main():
dialog = ImportDialog()
if not dialog.exec_(): return
paths = dialog.get_paths()
loaded = {}
if "logo_path" in paths and paths["logo_path"]:
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), 'logo_instituicao', paths["logo_path"])
print(f"Logo salva: {paths['logo_path']}")
if "america_sul" in paths:
l = QgsVectorLayer(paths["america_sul"], "América do Sul", "ogr")
if l.isValid(): QgsProject.instance().addMapLayer(l); apply_symbology(l, "america_sul"); loaded["america_sul"] = l
if "estados_br" in paths:
l_br = QgsVectorLayer(paths["estados_br"], "Unidades Federativas", "ogr")
if l_br.isValid(): QgsProject.instance().addMapLayer(l_br); apply_symbology(l_br, "unidades_federativas"); loaded["unidades_federativas"] = l_br
l_est = QgsVectorLayer(paths["estados_br"], "Estado Selecionado", "ogr")
if l_est.isValid(): QgsProject.instance().addMapLayer(l_est); apply_symbology(l_est, "estado_destaque"); loaded["estado_destaque"] = l_est
if "municipios" in paths:
l_mun = QgsVectorLayer(paths["municipios"], "Município Destaque", "ogr")
if l_mun.isValid(): QgsProject.instance().addMapLayer(l_mun); apply_symbology(l_mun, "municipio_destaque"); loaded["municipios"] = l_mun
if "hidrografia" in paths:
l_hid = QgsVectorLayer(paths["hidrografia"], "Hidrografia", "ogr")
if l_hid.isValid(): QgsProject.instance().addMapLayer(l_hid); apply_symbology(l_hid, "hidrografia"); loaded["hidrografia"] = l_hid
if "estado_destaque" in loaded and "municipios" in loaded:
filter_dlg = FilterDialog(loaded["unidades_federativas"], loaded["estado_destaque"], loaded["municipios"])
if filter_dlg.exec_():
create_map_themes(loaded)
main()
1. Importações e Bibliotecas
O script utiliza duas bibliotecas principais:
- qgis.core: Para manipulação de dados geográficos (camadas vetoriais, geometrias, sistemas de referência, projeto QGIS)
- PyQt5: Para construção da interface gráfica (janelas, botões, caixas de seleção)
from qgis.core import (
QgsProject, QgsVectorLayer, QgsRectangle,
QgsCoordinateReferenceSystem, QgsMapThemeCollection,
QgsExpressionContextUtils
)
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QLabel, QPushButton,
QFileDialog, QComboBox, QMessageBox
)
2. Configurações Globais (Constantes)
No início do código, definimos valores fixos que controlam o comportamento do script. É aqui que você deve alterar caso mude o tamanho dos mapas no seu Layout.
# Dimensões dos mapas no Layout de Impressão (em milímetros)
DIM_MAPA_1_2 = {"w": 65.0, "h": 45.0} # Brasil e Estado
DIM_MAPA_3 = {"w": 139.266, "h": 73.287} # Município
Por que isso existe?
O script precisa saber o tamanho físico do mapa no papel para calcular quanto do mundo real cabe ali dentro em uma determinada escala.
Também definimos o dicionário STYLES, que contém as cores Hexadecimais para cada camada:
STYLES = {
"america_sul": "#d3d3d3", # Fundo cinza
"brasil": "#ffffff", # Brasil branco
"estado_rosa": "#ffb6c1", # Estado rosa
"municipio_laranja": "#ffa500", # Município laranja
"hidrografia": "#add8e6" # Hidrografia azul
}
3. Funções de Cálculo
Estas funções garantem que o mapa fique bonito, centralizado e com escalas arredondadas.
get_geometry_data(layer, filter_expression)
Função: Obtém a extensão (retângulo envolvente) e o ponto central de uma geometria.
Lógica: Se houver um filtro (ex: "Apenas Belém"), ela calcula a extensão apenas dessa cidade. Se não, calcula da camada inteira.
def get_geometry_data(layer, filter_expression=None):
if filter_expression:
layer.setSubsetString(filter_expression)
extent = layer.extent()
center_x = (extent.xMinimum() + extent.xMaximum()) / 2
center_y = (extent.yMinimum() + extent.yMaximum()) / 2
return extent, (center_x, center_y)
calculate_smart_scale(...)
Função: Define a escala "redonda" ideal (ex: 1:50.000) para que a geometria caiba no mapa com uma margem de segurança.
Lógica do Cálculo
- Mede a largura/altura do município em metros
- Adiciona 5% de margem (
* 1.05) - Compara com o tamanho do mapa no papel
- Usa uma lista de "degraus" (1.0, 1.25, 1.5, 2.0...) para arredondar a escala
def calculate_smart_scale(extent, dim_w, dim_h, crs):
width_geo = extent.width() * 1.05 # Margem de 5%
height_geo = extent.height() * 1.05
# Lista de escalas padronizadas
standard_steps = [1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0]
# Calcula escala necessária
scale_w = width_geo / (dim_w / 1000)
scale_h = height_geo / (dim_h / 1000)
target_scale = max(scale_w, scale_h)
# Arredonda para escala padronizada
# ... (lógica de arredondamento)
return final_scale
calculate_map_extents(...)
Função: Calcula as coordenadas exatas (xmin, xmax, ymin, ymax) para
travar a visão do mapa.
Importância: É isso que centraliza o mapa. A função pega o ponto central do município e "abre" uma janela do tamanho exato da escala calculada anteriormente.
def calculate_map_extents(center, scale, dim_w, dim_h, crs):
half_width = (scale * dim_w / 1000) / 2
half_height = (scale * dim_h / 1000) / 2
return {
"xmin": center[0] - half_width,
"xmax": center[0] + half_width,
"ymin": center[1] - half_height,
"ymax": center[1] + half_height
}
calcular_unidade_escala_barra(...)
Função: Define o tamanho do segmento da barra de escala (ex: 10km, 50km).
Lógica: Divide a largura do mapa por 4 e arredonda para um número limpo (1, 2 ou 5). Isso evita que a barra de escala fique com tamanhos estranhos como "3.4 km".
def calcular_unidade_escala_barra(scale, dim_w):
largura_metros = scale * dim_w / 1000
tamanho_alvo = largura_metros / 4
# Arredonda para 1, 2 ou 5
magnitude = 10 ** int(math.log10(tamanho_alvo))
normalized = tamanho_alvo / magnitude
if normalized <= 1:
return magnitude
elif normalized <= 2:
return 2 * magnitude
else:
return 5 * magnitude
4. Interface Gráfica (Classes de Janela)
Classe ImportDialog (Passo 1)
Cria a primeira janela onde o usuário seleciona os arquivos .shp e a Logo.
- Usa
QFileDialogpara abrir o explorador de arquivos - Armazena os caminhos dos arquivos em um dicionário para uso posterior
class ImportDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("1. Configuração Inicial")
self.paths = {}
# Cria botões para cada arquivo
self.create_file_selector("America do Sul", "america_sul")
self.create_file_selector("Estados/UFs", "estados")
# ... outros arquivos
def browse_file(self, key):
path, _ = QFileDialog.getOpenFileName(
self, "Selecione o arquivo", "", "Shapefiles (*.shp)"
)
if path:
self.paths[key] = path
Classe FilterDialog (Passo 2)
Esta é a parte mais complexa da interação.
- População de Dados: Lê a tabela de atributos da camada de Estados para
preencher a primeira lista suspensa (
QComboBox) - Cascata de Filtros: Quando o usuário escolhe um Estado, a função
update_municipiosé acionada para filtrar e mostrar apenas as cidades daquele estado na segunda lista - Botão "Aplicar": Ao clicar, ele dispara toda a cadeia de cálculos matemáticos e salva os resultados nas Variáveis de Projeto
class FilterDialog(QDialog):
def update_municipios(self):
estado_nome = self.combo_estado.currentText()
# Filtra municípios por estado
expression = f"\"NM_UF\" = '{estado_nome}'"
self.layer_municipios.setSubsetString(expression)
# Popula combo de municípios
self.combo_municipio.clear()
for feature in self.layer_municipios.getFeatures():
self.combo_municipio.addItem(feature["NM_MUN"])
def apply_filter(self):
# Executa cálculos matemáticos
extent, center = get_geometry_data(...)
scale = calculate_smart_scale(...)
extents = calculate_map_extents(...)
# Salva nas Variáveis de Projeto
QgsExpressionContextUtils.setProjectVariable(
QgsProject.instance(), "mapa3_xmin", extents["xmin"]
)
# ... outras variáveis
5. Variáveis de Projeto (A Saída do Script)
O script não mexe diretamente no Layout. Em vez disso, ele salva valores na memória do QGIS
(QgsExpressionContextUtils). O Layout lê esses valores.
| Nome da Variável | Tipo | Descrição | Onde é usada no Layout |
|---|---|---|---|
meu_municipio |
Texto | Nome da cidade escolhida | Título do Mapa |
meu_estado |
Texto | Nome do estado escolhido | Título do Mapa |
logo_instituicao |
Caminho | Caminho do arquivo de imagem | Item de Imagem (Logo) |
escala_tamanho |
Número | Tamanho do segmento (em metros) | Barra de Escala (Largura Fixa) |
mapa3_xmin... |
Número | Coordenada Oeste do recorte | Propriedades do Mapa → Extensões |
zoom_municipio |
Número | Valor da escala (apenas referência) | Substituído pelas extensões |
Como usar no Layout
No Layout do QGIS, você pode referenciar essas variáveis usando a sintaxe:
@meu_municipio
Por exemplo, no título do mapa, você pode escrever:
Mapa de Localização - [%@meu_municipio%], [%@meu_estado%]
6. Temas de Mapa
A função create_map_themes cria dois "presets" de visualização no painel de camadas:
1. Tema "Localização"
- Visíveis: América do Sul, Brasil, Estado (Rosa)
- Invisíveis: Município, Hidrografia
- Uso: Mapas 1 e 2 (Contexto amplo)
2. Tema "Destaque"
- Visíveis: América do Sul, Brasil, Hidrografia, Município (Laranja)
- Invisíveis: Estado (Rosa)
- Uso: Mapa 3 (Foco no município)
Por que usar temas?
Isso garante que o Mapa 3 não mostre o estado rosa pintado por cima da hidrografia, por exemplo. Cada mapa no layout pode referenciar um tema diferente.
def create_map_themes(layers):
theme_collection = QgsProject.instance().mapThemeCollection()
# Tema 1: Localização (para mapas de contexto)
record1 = QgsMapThemeCollection.MapThemeRecord()
record1.setLayerRecords([
QgsMapThemeCollection.MapThemeLayerRecord(layers["america_sul"]),
QgsMapThemeCollection.MapThemeLayerRecord(layers["brasil"]),
QgsMapThemeCollection.MapThemeLayerRecord(layers["estado_rosa"])
])
theme_collection.insert("Localização", record1)
# Tema 2: Destaque (para mapa principal)
record2 = QgsMapThemeCollection.MapThemeRecord()
record2.setLayerRecords([
QgsMapThemeCollection.MapThemeLayerRecord(layers["america_sul"]),
QgsMapThemeCollection.MapThemeLayerRecord(layers["brasil"]),
QgsMapThemeCollection.MapThemeLayerRecord(layers["hidrografia"]),
QgsMapThemeCollection.MapThemeLayerRecord(layers["municipio_laranja"])
])
theme_collection.insert("Destaque", record2)
7. Função Principal (main)
Controla o fluxo de execução:
- Abre
ImportDialog - Se o usuário der OK, carrega as camadas no QGIS (
QgsProject.instance().addMapLayer) - Aplica a simbologia (
apply_symbology) definindo cores e bordas - Se carregou a logo, salva a variável
logo_instituicao - Abre
FilterDialogpassando as camadas carregadas - Se o usuário der OK no filtro, cria os Temas de Mapa
def main():
# Passo 1: Importar arquivos
import_dlg = ImportDialog()
if import_dlg.exec_() == QDialog.Accepted:
paths = import_dlg.paths
# Carregar camadas
layers = {}
for key, path in paths.items():
if key != "logo":
layer = QgsVectorLayer(path, key, "ogr")
QgsProject.instance().addMapLayer(layer)
layers[key] = layer
# Aplicar estilos
apply_symbology(layers)
# Salvar logo
if "logo" in paths:
QgsExpressionContextUtils.setProjectVariable(
QgsProject.instance(), "logo_instituicao", paths["logo"]
)
# Passo 2: Filtrar e calcular
filter_dlg = FilterDialog(layers)
if filter_dlg.exec_() == QDialog.Accepted:
create_map_themes(layers)
QMessageBox.information(None, "Sucesso", "Configuração concluída!")
# Executa
main()
Conclusão
Este script demonstra como combinar conhecimentos de:
- Geometria Espacial: Cálculo de extensões e centróides
- Matemática Cartográfica: Conversão entre escalas e unidades
- Desenvolvimento de GUI: PyQt5 para interface intuitiva
- API do QGIS: Manipulação de camadas, estilos e variáveis
O resultado é uma ferramenta que transforma um processo manual de 30+ minutos em uma tarefa automática de menos de 2 minutos.
Próximos Passos
Agora que você entende a lógica, pode:
- Modificar as cores no dicionário
STYLES - Ajustar as dimensões dos mapas em
DIM_MAPA_* - Adicionar novos campos às Variáveis de Projeto
- Criar novos Temas de Mapa para diferentes visualizações