Conversation
… dependencies and using in-place editing with AABB conflict detection
…ression extraction and terrain validation
…g, group-priority dissolution and frame-aware generalization; consolidate reclassify pixels versions into single algorithm; add TrataRaster processing algorithm
Decomposes a tile polygon layer into optimized bounding boxes using coordinate compression, BFS connected-component detection (4-connectivity), horizontal-strip decomposition, and greedy merging with a configurable coverage threshold. Parallel processing via ThreadPoolExecutor. - new file: optimalTileBBoxAlgorithm.py - registered in DSGToolsProcessingAlgorithmProvider - PT-BR translations added to DsgTools_pt.ts
O qgis_process 4.0 ainda resolve o perfil legado QGIS/QGIS3, enquanto o QGIS 4 Desktop usa QGIS/QGIS4 — desde o upgrade o CLI listava 0 algoritmos (provider invisivel). O CLI agora define QGIS_CUSTOM_CONFIG_PATH para o diretorio do QGIS 4 quando ele existe (override manual continua valendo) e o doctor mostra o caminho usado. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019WaxprCH2LaCcG5uA8sPPR
prepareLayerList com mergeOutputs=True e is_child_algorithm=False retornava um QgsVectorLayer direto quando havia mais de uma camada, pois runMergeVectorLayers devolve o objeto de camada (nao uma string). O wrapping so ocorria para str, entao o chamador quebrava ao aplicar [0] (TypeError: QgsVectorLayer object is not subscriptable). Agora converte string para camada quando necessario e sempre envolve em lista. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s auxiliares Trechos com geometria nula/vazia produzem vertex_pos NULL no "extract specific vertices", o que corrompia o índice da hashDict. Passa a pular esses nós e avisar quantos foram ignorados. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
O atributo de depressão era lido mas quase não era usado: - O heightRange dos nós era lido com terrainGraph[node], que no networkx é a adjacência e não os atributos do nó, então a leitura sempre devolvia None e a propagação de faixas por banda nunca rodava. - A validação de ponto cotado nunca consultava a depressão: numa calota aceitava [h-threshold, h+threshold], a união dos dois sentidos, de forma que uma cota de 95 dentro de um topo fechado por 100 passava batido. Agora a atribuição de depressão define o sentido da faixa. - O filtro de múltiplos do intervalo truncava a cota com to_int, flagando 100.4 como múltiplo de 10. - O merge de linhas mandava o campo de depressão para a blacklist, então curvas com atribuição divergente eram mescladas e o valor sobrevivente saía da ordem de iteração. Os campos referenciados pela expressão passam a compor a chave. Corrige também um falso positivo: a região externa ao anel mais externo tem grau 1 e curva fechada, e era lida como calota. A distinção agora é por contenção no anel. A verificação de atribuição passa a cobrir toda curva fechada cujo sentido do terreno seja determinável, e não apenas a mais interna, de modo que curvas de depressão aninhadas mais externas também são validadas. Remove ainda 439 linhas inalcançáveis do spatialRelationsHandler (SpatialRelationsHandler.validateTerrainModel e sua cadeia exclusiva), substituídas pelo TerrainModel em 2024 e sem chamadores desde então. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s testes Acrescenta duas camadas de teste para o modelo de terreno, que não tinha nenhuma em funcionamento: - tests/test_terrainHandler.py: 25 testes de lógica pura, com grafo e bandas montados à mão (o __post_init__ do TerrainModel roda o pipeline inteiro de processing, inviável em teste unitário). - dsgtools_cli/tests/test_terrain_algorithms.py: 8 testes que executam o algoritmo de ponta a ponta, headless, via dsgtools_cli. Todos falham no código anterior. As flags são verificadas por quantidade e pelo texto do reason, não por golden de geometria. Cada fixture contém uma única classe de erro, porque validate() para na primeira encontrada — um fixture com erro de banda nunca chega a validar depressão. Remove os golden de identifyterrainmodelerrorsalgorithm e seu bloco de parâmetros. Estavam congelados desde 2020 e descreviam a implementação anterior: as mensagens que esperavam só existiam em SpatialRelationsHandler.validateTerrainModel, e o bloco ainda passava TOPOLOGY_RADIUS, parâmetro que não existe mais, sem passar GROUP_BY_SPATIAL_PARTITION, obrigatório — o teste não chegava a executar. Acrescenta tests/run_local.py para rodar os testes dentro do QGIS sem Docker e sem instalar nada, com código de saída utilizável em automação, e um tests/README.md que registra as duas formas de execução e o estado conhecido dos módulos. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…istância Um ponto cotado com o mesmo valor de uma curva de nível é erro, e o identifyterrainmodelerrorsalgorithm o acusa. O gerador tinha duas proteções, mas nenhuma era garantia e as três fugas eram silenciosas: - findNearbyNonMultiplePixel devolvia o próprio pixel múltiplo quando não achava vizinho utilizável dentro do raio de busca, como num platô plano em cota redonda. - Pelo mesmo caminho, um platô maior que o raio devolvia o múltiplo mesmo havendo pixel válido mais adiante. - maskContourIntervalMultiples devolve o raster sem máscara quando todo pixel é múltiplo, para não zerar a região inteira. E não havia filtro na escrita do sink, de modo que qualquer uma dessas fugas chegava à saída. Em MDE inteiro isso não é hipotético: cerca de um pixel em cada dez é múltiplo de 10. findNearbyNonMultiplePixel passa a devolver None quando não há pixel utilizável, e quem chama descarta a feição. Como a garantia precisa valer para todos os caminhos de geração, a única escrita no sink passa a descartar cotas múltiplas, avisando quantas e por quê — inclusive as vindas da máscara que desiste, mantida porque zerar a região quebraria o cálculo a jusante. Acrescenta tests/test_extractElevationPoints.py, com 14 testes que falham no código anterior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…butação O filtro de curvas fechadas media o perímetro com QgsDistanceArea no CRS de origem e comparava com um mínimo em metros. Sem elipsoide no contexto — o padrão do qgis_process e de projeto novo —, a medida sai em unidades de mapa, então uma entrada geográfica devolvia frações de grau para qualquer anel. Como todo valor ficava abaixo do mínimo, toda curva fechada era descartada em silêncio: num teste com dois anéis de 13,9 km e 7,0 km em SIRGAS 2000, a saída vinha vazia. Como o dado EDGV é normalmente geográfico, o caso comum era o destrutivo. A geometria passa a ser projetada antes de medir, aproveitando a transformação que a generalização já fazia, e o comprimento sai em metros. O tamanho mínimo passa a ser derivado da escala, 12 mm de perímetro: 300 m em 1:25.000, 600 m em 1:50.000, 1200 m em 1:100.000 e 3000 m em 1:250.000. O parâmetro em metros foi substituído por um de escala, seguindo a convenção do extractElevationPoints. É mudança incompatível, mas o default anterior de 200 m não correspondia a escala alguma. A atributação gravava indice e depressao fixos: - depressao saía sempre 2, destruindo a atribuição da entrada, de modo que o identifyterrainmodelerrorsalgorithm depois acusava toda depressão como não marcada. Passa a vir de uma expressão, e o agrupamento que precede a mesclagem passa a considerá-la: agrupar só por cota juntaria uma curva de depressão com uma normal de mesma cota e perderia a atribuição de uma das duas. - indice saía sempre 2, então nenhuma curva era mestra. Passa a ser calculado a partir da equidistância, uma a cada cinco curvas. dentro_massa_dagua segue fixo em 2, por ora. Acrescenta dsgtools_cli/tests/test_contour_prep_algorithms.py, com 8 testes que executam o algoritmo de ponta a ponta sobre fixtures em CRS geográfico. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A preparação entrega os anéis segmentados em dois arcos abertos, que é o formato do dado que chega à validação na produção. Topo de morro e fundo de depressão são reconhecidos por is_closed, então sem o anel fechado não há topo a validar, e um erro real de atribuição de depressão passava como "tudo certo" — a validação não falhava, ela silenciava. O runDSGToolsMergeLines já rodava antes da validação, mas não recompõe um anel a partir de dois arcos: com dois arcos formando um quadrado, ele devolve as duas feições separadas tanto com allowClosed=True quanto com False. O par native:dissolve pelos atributos que importam e native:mergelines devolve o anel fechado, e é o que passa a ser usado. O contourid é atribuído depois da mesclagem, para identificar a curva já inteira, já que o dissolve descarta campos e todo o resto do fluxo depende dele. Os campos preservados no dissolve vêm do getFieldsToPreserveOnMerge, de modo que a expressão de depressão continua determinando o agrupamento. Os fixtures de terreno tinham anéis intactos e escondiam o problema. Os dois fixtures segmentados acrescentados reproduzem o dado real: sem esta correção, depressao_nao_marcada_com_aneis_segmentados não acusa o erro. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ltro por parte Ao final da preparação, cada curva fechada é segmentada em dois arcos: um anel começa e termina no mesmo ponto, o que é uma autointerseção. Cada arco sai como feição própria, porque mantê-los no mesmo multipart deixaria a feição ainda começando e terminando no mesmo ponto, que é o que a segmentação existe para desfazer. O _geometryToPolyline devolvia apenas a primeira parte de uma geometria multiparte, então o fechamento era decidido por ela enquanto o comprimento media a geometria inteira — o filtro de perímetro mínimo comparava coisas diferentes. Dá lugar a _toPolylineParts e _dropSmallClosedRings, que avaliam e medem parte a parte. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ega o provider morto Traz o dsgtools_cli para o padrao agent-first da DGEO. Ele ja atendia os dois principios mais dificeis (contrato ao vivo do `qgis_process help --json`, sem catalogo pre-gerado; annotations.json com so o conhecimento de dominio que a introspeccao nao alcanca). O que faltava era a camada de saida, validacao e guardrail. describe COMPACTO por padrao (uma linha por parametro, opcoes com o indice, exemplo renderizado como linha de comando pronta). O JSON antigo fica em --json, byte a byte igual. O `list` irmao ja era compacto: as duas politicas agora batem. De quebra, o OUTPUT deixou de aparecer duas vezes. VALIDACAO LOCAL antes do run: nome de parametro inexistente (com sugestao por proximidade), obrigatorio ausente, indice de enum fora da faixa e rotulo no lugar do indice (START_SCALE=250k responde "o indice de '250k' e 2"). O erro imprime o contrato dos parametros citados e sai com codigo 2 SEM executar. O modo de falha que motivou isso, confirmado experimentalmente: com TIPO_INDEX no lugar de INDEX_TYPE, o qgis_process IGNORA a chave em silencio, aplica o padrao e falha depois com "Invalid MI/MIR format", um erro de dominio que nao aponta para a causa. Nome errado nao grava e ninguem reclama. CACHE DE CONTRATO em disco, sem o qual a validacao nao valeria a pena: buscar o contrato custa ~2,9 s, entao validar de forma ingenua custaria +84% em cada run. Medido nesta maquina: run validado com cache quente 2,99 s contra 3,08 s do --no-check (menos de 1%), e describe de 2,89 s para 0,12 s. A impressao digital combina mtime e tamanho do qgis_process, da pasta do plugin e do metadata.txt. Limite assumido e documentado: nao detecta edicao de um .py de algoritmo dentro do plugin (varrer a arvore custaria mais que economiza); em desenvolvimento, use --refresh-cache ou cache --clear. --dry-run valida pelo cache e mostra o payload exato, cada parametro com o rotulo por tras do indice, e os opcionais omitidos. DOCTOR que pega o modo de falha real. Antes ele dizia que estava tudo bem enquanto o CLI estava inutil. Agora distingue plugin ausente, instalado mas nao habilitado, e carregado com zero algoritmos, cada um com o conserto exato, e sai 1. O `list` com zero algoritmos tambem parou de mentir. Armadilha encontrada e encodada: `qgis_process plugins enable DsgTools` colado num terminal comum grava no perfil LEGADO (QGIS/QGIS3), enquanto o CLI redireciona para o do QGIS 4. O enable "da certo" e o list continua vazio. Por isso o doctor imprime o QGIS_CUSTOM_CONFIG_PATH junto e oferece --fix, que usa o perfil certo por construcao. Testes: 30 antes, 66 depois (36 novos, sem QGIS, 0,09 s), zero falhas. Dependencia zero mantida (stdlib pura). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Adiciona [flake8] no setup.cfg: max-line-length=88, ignora W503/E203 (conflitam com formatação do black) e F401 em __init__.py (re-export). - Roda black em todos os módulos do plugin (115 arquivos). Só formatação, nenhuma mudança de lógica. Compilação verificada em todos os arquivos tocados. - F401 (imports não usados, 1780 ocorrências) fica pendente: autoflake se mostrou inseguro nesse repo por causa do padrão de sondagem de dependência opcional (try: import X / except ImportError: raise ...), onde o import "não usado" é intencional. Precisa de passe filtrado ou manual, feito à parte. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Detecção via pyflakes, remoção via reescrita AST própria (não autoflake:
a ferramenta se mostrou insegura para este repo -- inseria `pass` espúrio
ao dividir imports com vírgula, e gutava o padrão de sondagem de
dependência opcional usado no código:
try:
from scipy.interpolate import splprep, splev
except ImportError:
raise QgsProcessingException(...)
onde o import "não usado" é intencional, só serve pra levantar ImportError
com mensagem amigável).
O fixer:
- protege qualquer Import/ImportFrom dentro de um bloco try (não mexe);
- respeita comentário `# noqa`;
- remove só os nomes de fato não usados, mantendo o resto do import;
- verifica compile() antes de gravar, nunca escreve arquivo quebrado;
- rodado em 3 passes até estabilizar (1º pass revela imports que só
apareciam "usados" por causa de um import duplicado mais abaixo no
arquivo, sombreando o primeiro -- ex: circle.py tinha `QtGui` importado
duas vezes, pyflakes só reportava a redefinição).
Resultado: F401 1780 -> 10 (9 são os imports protegidos de sondagem
scipy/rasterio, propositalmente intocados; 1 é ui_create_inom_dialog.py,
arquivo gerado, fora do escopo). Nenhum outro código flake8 piorou
(conferido antes/depois). py_compile limpo em todos os 657 arquivos.
Rodado black depois para reformatar as linhas reescritas.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- dsg_tools.py: faltava o import de fato do módulo de recursos Qt compilado (resources.py). Só existia o comentário "# Initialize Qt resources from file resources_rc.py" sem o import correspondente, então qInitResources() nunca era chamado e todo ícone carregado via ":/plugins/DsgTools/icons/..." saía em branco (sem erro, sem exceção). Bug pré-existente, não relacionado à migração para QGIS4 -- confirmado presente até no commit anterior a qualquer mudança desta sessão. - aboutAndFurtherInfoGuiManager.py: no macOS, o Qt detecta automaticamente texto de QAction como "About ..." / "Options" / "Preferences" e move a ação pro menu de aplicativo nativo do Cocoa em vez de deixá-la no menu do plugin -- comportamento que não existe no Windows. Sem setMenuRole(NoRole) explícito, "About DSGTools" e "DSGTools' Options" simplesmente somem do menu DSGTools no Mac. Testado e confirmado pelo usuário: ícones voltaram, menu About aparece no macOS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- CHANGELOG.md: fecha o ciclo dev 5.1.2 como 5.2.0 (data do release),
abre 5.3.0 - dev para o próximo ciclo.
- DsgTools/metadata.txt: version=5.2.0 e changelog= atualizado no
mesmo formato/indentação já usado.
- README.md: master passa a apontar 5.2.0, dev passa a apontar 5.3.0.
- DsgTools/CLAUDE.md: documenta o padrão de preenchimento do changelog
(usado para gerar as entradas acima).
- .github/workflows/release.yml: corrige o job de deploy, que falhava
desde a versão 4.20.0 -- pedia Python 3.8, removido dos runners do
GitHub Actions ("Version 3.8 with arch x64 not found"), então o
qgis-plugin-ci nunca chegava a rodar em nenhum release publicado.
Atualiza para Python 3.12 e actions/checkout@v4 + setup-python@v5.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ndia Rodei qgis-plugin-ci localmente (package, sem upload) pra validar a release 5.2.0 antes de publicar de verdade, já que todo run do release.yml sempre falhava antes de chegar nessa etapa. Achei duas falhas reais escondidas atrás da falha do Python: - metadata.txt: about= é multi-linha (formato válido, é assim que o QGIS Plugin Manager espera pra exibir texto longo), mas o parser do qgis-plugin-ci é ingênuo -- só lê o que está na própria linha da chave, então via about como vazio e abortava com "Mandatory key is missing in metadata: about". Corrigido colocando a primeira frase na própria linha de about= (o resto continua multi-linha, sem perda pro QGIS). - DsgTools/resources_rc.py: arquivo morto, não importado por nada no código fonte (só sobrava em .pyc velho e no translation.pro). O qgis-plugin-ci compila o .qrc do zero durante o empacotamento e recusa sobrescrever um arquivo já versionado com esse nome exato -- removido. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Registra no CLAUDE.md as regras de versionamento que o usuário explicou (Y par/ímpar = lançada/dev, Z incrementa por mudança, inclusive correção de bug sobre versão já lançada) e as duas armadilhas reais achadas ao validar o empacotamento da 5.2.0 localmente antes do release de verdade (about= precisa da primeira frase na própria linha; resources_rc.py não pode ficar versionado). .gitignore ganha entrada pros artefatos de build do qgis-plugin-ci (resources_rc.py e *.zip da raiz). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
5.2.0 - 2026-08-11
Novas Funcionalidades:
Melhorias:
Correções de bug:
Automação de release corrigida
.github/workflows/release.ymlfalhava desde a versão 4.20.0 (todo run, sem exceção — conferido no histórico do Actions): pedia Python 3.8, removido dos runners do GitHub Actions. Atualizado para Python 3.12 + actions atualizadas.Validei o empacotamento localmente com
qgis-plugin-ci package(sem subir nada) antes de publicar de verdade, e achei mais dois problemas reais que estavam escondidos atrás da falha do Python — nunca tinham aparecido em nenhum log porque o job sempre morria antes de chegar neles:metadata.txt:about=é multi-linha (formato válido, é assim que o QGIS Desktop espera), mas o parser doqgis-plugin-cisó lê o que está na própria linha da chave — abortava com "Mandatory key is missing in metadata: about". Corrigido deixando a primeira frase já na linha doabout=.DsgTools/resources_rc.py: arquivo morto (não importado por nada no código-fonte), conflitava com o resource que oqgis-plugin-cicompila do zero durante o empacotamento. Removido e adicionado ao.gitignore.Depois dessas duas correções,
qgis-plugin-ci package 5.2.0roda limpo até o fim localmente.🤖 Generated with Claude Code