Commit cc2d48b2 authored by Félix Hernández's avatar Félix Hernández
Browse files

IDECanarias 4.0.0 para QGIS

Complemento de QGIS que permite buscar topónimos de Canarias contra los
servicios de IDECanarias y convertir coordenadas entre DMS, DM, grados
decimales y UTM 28N.

Compatible con QGIS 3.22 o superior y con QGIS 4.x, sobre Qt5 y Qt6.
parents
Loading
Loading
Loading
Loading

.gitignore

0 → 100644
+105 −0
Original line number Diff line number Diff line
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# Boundless specific
travis_secrets.tar.gz
ssh_config
ext-libs/

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
.pytest_cache/
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

# plugin settings page (build automatically)
+docs/settingsconf.rst


# Qt compiled translations
i18n/*.qm

# Paquete generado por "make zip"/"make package"
*.zip

CHANGELOG.md

0 → 100644
+70 −0
Original line number Diff line number Diff line
# Registro de cambios

Todas las versiones publicadas del complemento **IDECanarias** para QGIS.
Las versiones se publican en
[plugins.qgis.org/plugins/IDECanarias](https://plugins.qgis.org/plugins/IDECanarias/).

## 4.0.0 — 2026-09-22

Migración a QGIS 4 / Qt6. El código pasa a ser compatible a la vez con
**QGIS 3.22 o superior y con QGIS 4.x**, sobre Qt5 y Qt6.

### Añadido
- `MIGRATION.md` con el detalle de la migración y sus verificaciones.
- Batería de tests del ciclo de vida del complemento (`test/test_idecanarias.py`).

### Cambiado
- Los imports pasan de `PyQt5` a la capa de compatibilidad `qgis.PyQt`.
- Los enums se escriben con su prefijo de categoría
  (`Qt.DockWidgetArea.BottomDockWidgetArea`, `QIODevice.OpenModeFlag.WriteOnly`),
  válidos en Qt5 y Qt6.
- `QgsField` usa `QMetaType.Type` en lugar de `QVariant.Type`, eliminado en Qt6.
- La descarga de GML se escribe como bytes: Qt6 retiró `QTextStream.setCodec()`
  y su sustituto no existe en Qt5.
- Los CRS se construyen con `QgsCoordinateReferenceSystem('EPSG:…')` en vez de
  `createFromSrid()`, obsoleto.
- `ClickTool` usa `QgsMapMouseEvent.mapPoint()` y ya no depende de
  `QMouseEvent.pos()`, obsoleto en Qt6.
- El icono se carga por ruta de fichero.

### Corregido
- La casilla «limitar la búsqueda a la extensión actual» no tenía ningún
  efecto: `setupPlug()` machacaba el widget creado por `setupUi()`.
- Cada conversión de coordenadas creaba una capa nueva en lugar de reutilizar
  la existente, porque el identificador de capa nunca llegaba a asignarse.
- Los avisos de error usaban `QtGui.QMessageBox`, que no existe desde Qt5.
- Las coordenadas de los resultados de búsqueda guardaban el primer carácter
  del texto en lugar del número.
- Se añadía una fila vacía a la tabla por cada nodo XML que no fuera un
  resultado.
- `plugin_upload.py` abría el paquete en modo texto y usaba HTTP.

### Eliminado
- El fichero de recursos `resources.qrc` y su `resources.py` compilado: PyQt6
  retiró el sistema de recursos de Qt y no existe `pyrcc6`.

## 3.1.3 — 2020-02-06

Publicada en plugins.qgis.org. No hay constancia de sus cambios en este
repositorio: el historial se detiene en septiembre de 2018, así que esa
versión se preparó fuera de él.

## 3.1.2 — 2018-09-03

- Las peticiones a los servicios de IDECanarias pasan de HTTP a HTTPS.

## 3.1 — 2018-03-07

- Migración del complemento a QGIS 3.

## 2.3 — 2014-07-06

Publicada para QGIS 2. Sin registro de cambios en este repositorio.

## 2.2 — 2014-05-16

Publicada para QGIS 2. Sin registro de cambios en este repositorio.

## 2.1 — 2014-05-15

Primera versión publicada para QGIS 2.

CONTRIBUTING.md

0 → 100644
+89 −0
Original line number Diff line number Diff line
# Cómo contribuir

Gracias por el interés en **IDECanarias**, el complemento de QGIS para buscar
topónimos de Canarias y convertir coordenadas.

## Informar de un fallo o proponer una mejora

Abre una incidencia en
[el gestor de incidencias](https://gitlab.grafcan.es/fherdom/idecanarias/-/issues).
Es la vía de contacto abierta a cualquiera.

Para que un fallo se pueda reproducir, incluye:

- **Versión de QGIS** (*Ayuda → Acerca de*) y sistema operativo.
- **Versión del complemento** (*Complementos → Administrar e instalar*).
- Qué hacías, qué esperabas y qué ocurrió.
- Si hubo un error de Python, el texto completo del panel de registro
  (*Ver → Paneles → Mensajes de registro*, pestaña «Python warning»).
- Si el problema es de búsqueda, el texto exacto que introdujiste.

## Contribuir código

Este repositorio es un espejo de publicación: contiene el código de la versión
publicada, **sin bifurcaciones ni merge requests abiertos al público**. El
desarrollo ocurre en un repositorio privado.

Si quieres aportar un cambio, abre primero una incidencia describiéndolo y se
acuerda por ahí la forma de integrarlo. No pierdas tiempo preparando un parche
grande sin comentarlo antes.

## Entorno de desarrollo

```sh
# Desplegar en tu perfil de QGIS
make deploy QGISDIR=.local/share/QGIS/QGIS4/profiles/default

# Otros perfiles habituales
make deploy QGISDIR=.var/app/org.qgis.qgis/data/QGIS/QGIS4/profiles/default  # flatpak
make deploy QGISDIR=.local/share/QGIS/QGIS3/profiles/default                 # QGIS 3

# Tests (necesitan un entorno gráfico o QT_QPA_PLATFORM=offscreen)
make test
```

No hay nada que compilar: el `.ui` se carga en tiempo de ejecución y el
complemento ya no usa el sistema de recursos de Qt.

## Compatibilidad con Qt5 y Qt6

El complemento funciona a la vez en **QGIS 3.22+ y 4.x**, así que todo cambio
debe seguir siendo válido en ambos. En la práctica:

- Importa desde `qgis.PyQt.*`, nunca desde `PyQt5.*` ni `PyQt6.*`.
- Escribe los enums con su prefijo de categoría: `Qt.DockWidgetArea.Bottom…`,
  `QIODevice.OpenModeFlag.WriteOnly`, `QMessageBox.StandardButton.Ok`.
- Usa `QMetaType.Type` en `QgsField`, no `QVariant.Type`.
- No añadas ficheros `.qrc`: PyQt6 retiró el sistema de recursos y no existe
  `pyrcc6`. Carga los recursos por ruta de fichero.
- Evita las API que Qt6 eliminó, como `QTextStream.setCodec()` o
  `QMouseEvent.pos()`.

Antes de enviar un cambio, pásale el verificador oficial de QGIS, que es el
mismo que ejecuta plugins.qgis.org al subir el paquete:

```sh
docker run --rm --pull always --user $(id -u):$(id -g) \
  --workdir /workspace/ -v "$(pwd):/workspace/" \
  ghcr.io/qgis/pyqgis4-checker:main-ubuntu \
  pyqt5_to_pyqt6.py --dry_run --logfile /workspace/pyqt6_checker.log .
```

El registro debe quedar vacío. Prueba además en QGIS 3 y en QGIS 4: el
verificador comprueba la sintaxis, no el comportamiento.

## Estilo

```sh
make pep8     # pycodestyle
make pylint   # pylint 3.x, configurado en pylintrc
```

Líneas de 80 columnas como máximo. El código sigue la convención camelCase de
Qt en los nombres de widgets y manejadores, por coherencia con el existente.

## Licencia

Al contribuir aceptas que tu aportación se publique bajo la
[GNU General Public License v2.0 o posterior](LICENSE), la misma del resto del
complemento.

LICENSE

0 → 100644
+338 −0

File added.

Preview size limit exceeded, changes collapsed.

MIGRATION.md

0 → 100644
+296 −0
Original line number Diff line number Diff line
# Migración de IDECanarias de QGIS 3 a QGIS 4 (Qt6)

Rama: `dev` · Versión del plugin: `3.1.3` (publicada) → `4.0.0` · Fecha: 2026-09-22

## Contexto

Plugin generado con Plugin Builder en 2018. Añade un dock con dos funciones:
búsqueda de topónimos contra `visor.grafcan.es` (XML + GML por red) y
conversión/captura de coordenadas (DMS / DM / grados decimales / UTM 28N).

QGIS 4.0 migró de Qt5 a Qt6. El plugin **no cargaba** en QGIS 4 por dos motivos
duros:

1. Todos los imports eran `PyQt5.*` directos.
2. `resources.py` estaba compilado con `pyrcc5` e importaba `PyQt5`. **PyQt6
   eliminó el sistema de recursos de Qt: no existe `pyrcc6`.** La ruta
   `.qrc` → `resources.py` no se porta, se elimina.

## Decisiones

| Decisión | Elección |
| --- | --- |
| Compatibilidad | **QGIS 3.22+ y 4.x** con un único código base, vía la capa `qgis.PyQt` |
| Alcance | Migración **+** corrección de los fallos latentes detectados |
| Recursos | Eliminar `resources.qrc` / `resources.py` |
| Tooling | Actualizar `Makefile`, `pb_tool.cfg` y los tests |
| Modernizar | **Descartado** (ver "Fuera de alcance") |

## Entornos de verificación

| | QGIS | Qt | PyQt | Python |
| --- | --- | --- | --- | --- |
| Paquetes del sistema | 3.44.7 Solothurn | 5.15.13 | PyQt5 | 3.12 |
| Flatpak `org.qgis.qgis` | 4.2.2 Belém do Pará | 6.11.1 | PyQt6 6.11.0 | 3.13 |

En el sandbox de QGIS 4 **no hay PyQt5**, así que nada puede pasar por accidente.
Se confirmó además que `qgis.PyQt.QtWidgets.QAction` resuelve a
`PyQt6.QtGui.QAction` bajo Qt6: el import es válido en ambas versiones.

## Cambios realizados

### Código del plugin

- **Imports** → `qgis.PyQt` en lugar de `PyQt5`; fuera los `from ... import *`
  de `qgis.core` y `.utils`.
- **Enums con prefijo de categoría** (funcionan en Qt5 y Qt6):
  `Qt.DockWidgetArea.BottomDockWidgetArea`, `QIODevice.OpenModeFlag.WriteOnly`.
- **`QgsField`**: `QtCore.QVariant.String` → `QMetaType.Type.QString` /
  `QMetaType.Type.Double`. `QVariant.Type` no existe en Qt6; `QMetaType` es la
  forma soportada desde QGIS 3.38.
- **`QTextStream` + `setCodec("UTF-8")` eliminados** en `onDone_gml`. No tienen
  reemplazo dual: `setEncoding()` necesita `QStringConverter`, que **no existe
  en Qt5** (comprobado). El GML se escribe ahora como bytes con
  `tmpfile.write(reply.readAll())`.
- **`QgsCoordinateReferenceSystem`**: `createFromSrid(n)` → constructor
  `'EPSG:32628'` / `'EPSG:4326'`. `createFromSrid` sigue existiendo en la API
  4.x pero está `Q_DECL_DEPRECATED` y hace búsqueda por SRID de PostGIS, no por
  código EPSG.
- **`ClickTool`**: `e.pos().x()` usaba `QMouseEvent.pos()`, obsoleto en Qt6.
  Sustituido por `e.mapPoint()`, que `QgsMapMouseEvent` devuelve ya en
  coordenadas de mapa: desaparece la transformación manual.
- **Icono por ruta de fichero** en lugar de `':/plugins/idecanarias/icon.png'`.
- Eliminado el guard `if qVersion() > '4.3.3'` y el bloque `__main__` muerto
  (usaba `QtGui.QApplication` y `exec_()`, inexistente en PyQt6).
- `QSettings().value('locale/userLocale')` protegido contra `None` (perfil
  recién creado).

### Fallos corregidos

Varios ya rompían funcionalidad en QGIS 3:

| Fichero | Problema |
| --- | --- |
| `idecanarias_dockwidget.py` | `self.chkBBOX = False` en `setupPlug()` machacaba el `QCheckBox` creado por `setupUi()`. La casilla **"limitar la búsqueda a la extensión actual" no tenía ningún efecto**. |
| `idecanarias_dockwidget.py` | `self.layerid` nunca se asignaba (la asignación estaba comentada), así que `mapLayer('')` devolvía `None` y **cada conversión creaba una capa nueva**. |
| `idecanarias_dockwidget.py` | `QtGui.QMessageBox` en `alert()` y en los seis `except ValueError` → `AttributeError`; `QMessageBox` vive en `QtWidgets` desde Qt5. |
| `idecanarias_dockwidget.py` | `x[0], y[0]` guardaba el **primer carácter** de la cadena, no la coordenada. |
| `idecanarias_dockwidget.py` | `self.lid.append(e)` y la inserción de fila estaban fuera del `if tagName() == "row"`: se añadía una fila vacía por cada nodo hermano. |
| `idecanarias_dockwidget.py` | `onDone_gml` no comprobaba `reply.error()` ni que `addVectorLayer` devolviera capa válida → `None.crs()`. Añadidas guardas y `reply.deleteLater()`. |
| `plugin_upload.py` | `open(zip)` en **modo texto** → `UnicodeDecodeError`. Además `http`/puerto 80 contra plugins.qgis.org, que es HTTPS. |
| `pb_tool.cfg` | `python_files` **no incluía `utils.py`**: los despliegues con pb_tool generaban un plugin roto. |

### Recursos

Eliminados `resources.qrc`, `resources.py` (302 líneas compiladas, commiteadas)
y `test/test_resources.py`, cuya premisa (`QIcon(':/plugins/...')`) desaparece.

### `metadata.txt`

```
qgisMinimumVersion=3.22
qgisMaximumVersion=4.99
version=4.0
```

`qgisMaximumVersion` es **obligatorio** para un plugin que soporta 3 y 4: sin él
QGIS 4 no lo carga. **No** se añade `supportsQt6=True`: se retiró del core justo
antes de la 4.0.0 y ya no se reconoce.

### Tooling

- `Makefile`: `QGISDIR` al perfil QGIS 4 (documentadas también las rutas de
  flatpak y de QGIS 3, sobreescribibles en la línea de órdenes); fuera la regla
  `pyrcc5` y `COMPILED_RESOURCE_FILES`; `nosetests` → `unittest discover`;
  `$(PYTHONPATH)` → `$$PYTHONPATH` (era una variable de *make*, vacía);
  `$(c)/plugin_upload.py` → `./plugin_upload.py`; `pep8 --repeat` →
  `pycodestyle`; `LRELEASE` definido; target `pylint` acotado a los ficheros del
  plugin.
- `scripts/update-strings.sh`: `pylupdate4` (herramienta de Qt4) →
  `pylupdate5` / `lupdate` según disponibilidad; `print` → `echo` (no es un
  builtin de bash); `[ x == y ]` → `[ x = y ]`.
- `scripts/run-env-linux.sh`: prefijo `/usr`; `QT_QPA_PLATFORM=offscreen`.
- `help/source/conf.py`: `sphinx.ext.pngmath` (eliminado en Sphinx 1.8) →
  `imgmath`.
- `README.md` / `README.txt` reescritos; `README.html` eliminado (duplicaba el
  `.txt` con instrucciones de `pyrcc5`).

### Tests

- `test/qgis_interface.py` reescrito: usaba `QgsMapLayerRegistry`,
  `QgsMapCanvasLayer`, `setLayerSet()` y `legendInterface()`, **todo eliminado
  en QGIS 3.0**. Ahora va contra `QgsProject.instance()` y `setLayers()`.
  `@pyqtSlot('QStringList')` (tipo de Qt4) → `'QList<QgsMapLayer*>'`.
  Implementados `addVectorLayer`, `addRasterLayer`, `addToolBar`,
  `addPluginToMenu` y `removePluginMenu`, que eran `pass`.
- `unittest.makeSuite`, **eliminado en Python 3.13**, → `TestLoader()`. Dos de
  las llamadas referenciaban además clases inexistentes (`NameError`).
- `test/utilities.py`: `QtGui.QWidget()` → `QtWidgets.QWidget()` (error desde
  Qt5); `QgsApplication` necesita `argv` como lista de bytes.
- **`test/test_idecanarias.py` (nuevo)**: faltaba cobertura del ciclo de vida
  `classFactory → initGui → run → onClosePlugin → unload`, que es justo lo que
  ejecuta QGIS al activar el plugin.
- `test/test_qgis_environment.py`: `createFromWkt` → `fromWkt`; el aserto sobre
  el proveedor `postgres` se relaja (no está en todos los paquetes) y el CRS del
  ráster admite `OGC:CRS84`, que es lo que devuelve PROJ moderno.

### `pylintrc` → pylint 3.x

Validado con **pylint 3.3.9**. Antes fallaba con:

```
E0015: Unrecognized option found: profile, files-output, comment,
required-attributes, bad-functions, zope, no-space-check, ignore-iface-methods
```

Cambios: eliminadas esas ocho opciones; `[MASTER]` → `[MAIN]`;
`overgeneral-exceptions` con nombres cualificados (`builtins.Exception`);
`deprecated-modules` con módulos que existen (los cuatro anteriores eran de
Python 2); `mixin-class-rgx` como acompañante de `ignore-mixin-members`;
`ignored-modules` / `extension-pkg-allow-list` para `qgis`, sin lo cual pylint
marcaba cada import de QGIS como `E0401`.

## Resultados de la verificación

Todo lo siguiente se ejecutó **en los dos entornos** y con resultados idénticos,
salvo donde se indica.

| Comprobación | QGIS 3.44 | QGIS 4.2.2 |
| --- | --- | --- |
| Batería de tests (7) | `OK (skipped=1)` | `OK (skipped=1)` |
| `classFactory → initGui → run → unload` | OK | OK |
| Icono cargado por ruta, no nulo | OK | OK |
| Conversión ×2 → **1 sola capa** | OK | OK |
| Campos de la capa | `string`, `double`, `double` | idem |
| Los 4 formatos de coordenada | OK | OK |
| Casilla BBOX cambia a `toponimoxmlbbox` | OK | OK |
| Búsqueda real "Teide" | 50 filas exactas | 50 filas exactas |
| Doble clic → GML descargado y capa cargada | `Calle Teide_187788065` | idem |
| Avisos de error emitidos | 0 | 0 |
| `qgis.utils.loadPlugin('idecanarias')` | — | `True` |
| Metadatos leídos por el registro de QGIS 4 | — | `4.0` / `3.22` / `4.99`, sin `supportsQt6` |
| Prueba interactiva en la GUI (dock y captura por clic) | — | correcta, 2026-09-23 |

El test de traducciones queda en `skip`: `i18n/af.qm` no existe (no hay ningún
`.qm` en el repo y `LOCALES` está vacío en el Makefile).

### Checker oficial de QGIS

```sh
docker run --rm --pull always --user $(id -u):$(id -g) \
  --workdir /workspace/ -v "$(pwd):/workspace/" \
  ghcr.io/qgis/pyqgis4-checker:main-ubuntu \
  pyqt5_to_pyqt6.py --dry_run --logfile /workspace/pyqt6_checker.log .
```

**Log vacío: ninguna incompatibilidad Qt6.** Es el mismo checker que
plugins.qgis.org ejecuta al subir el plugin (pestaña "Qt6 Check").

### pylint

**Ningún mensaje de nivel E** en todo el proyecto, tests incluidos.
Nota global 7.27/10; lo que queda son avisos de estilo preexistentes (prefijos
`u''`, `%` en vez de f-strings, paréntesis sobrantes) que entran en
"modernizar".

## Cómo reproducir la verificación

```sh
# Tests en el QGIS del sistema (3.x)
QT_QPA_PLATFORM=offscreen python3 -m unittest discover -v -s test -t ..

# Tests dentro del sandbox de QGIS 4 (flatpak)
flatpak run --filesystem="$PWD" --env=PYTHONPATH=/app/share/qgis/python \
  --env=QT_QPA_PLATFORM=offscreen --command=sh org.qgis.qgis \
  -c "cd $PWD && python3 -m unittest discover -v -s test -t .."

# Despliegue
make deploy QGISDIR=.var/app/org.qgis.qgis/data/QGIS/QGIS4/profiles/default
make deploy QGISDIR=.local/share/QGIS/QGIS3/profiles/default
```

## Publicación en plugins.qgis.org

Plugin ID **537**, mantenedor `fherdom`. La última versión publicada era la
**3.1.3** (2020-02-06); todas las anteriores declaraban `qgisMaximumVersion`
3.99, por lo que no cargan en QGIS 4.

**La 4.0.0 se publicó el 2026-09-22 y se aprobó el 2026-09-23.**

Paquete generado con `make zip`: 9 ficheros, 24 KB, sin `resources.py`,
`__pycache__` ni el log de ejecución. Verificado descomprimiéndolo en un perfil
limpio de QGIS 4.2.2: `loadPlugin` devuelve `True` y el registro lee
`4.0.0 / 3.22 / 4.99 / experimental=False`.

Descargado después el paquete oficial y comparado fichero a fichero contra la
rama `main`: los 9 coinciden.

### Qué versión sirve el repositorio a cada QGIS

El corte cae en `qgisMinimumVersion=3.22`, comprobado contra `plugins.xml`:

| QGIS | Versión servida |
| --- | --- |
| 3.16 | 3.1.3 |
| 3.22 · 3.34 · 3.40 · 3.44 | 4.0.0 |
| 4.0 · 4.2 | 4.0.0 |

### Tres rechazos del validador

El paquete fue rechazado tres veces antes de entrar, por motivos de empaquetado
que conviene recordar para futuras versiones:

1. **`Cannot find LICENSE in the plugin package`** — tenerlo en el repositorio
   no basta, tiene que ir dentro del zip. Se añadió a `EXTRAS` del `Makefile` y
   a `extras` de `pb_tool.cfg`.
2. **`Plugin folder name mismatch`** — el `package_name` registrado es
   `IDECanarias`, con mayúsculas, y el `PLUGINNAME` del `Makefile` estaba en
   minúsculas. Nunca había coincidido con lo publicado; se comprobó descargando
   el zip de la 3.1.3.
3. De paso se corrigió el target `dclean`, que no borraba los directorios
   `__pycache__` ni el `idecanarias.log` que el propio plugin genera en su
   directorio de instalación: ambos acababan dentro del zip.

## Estructura de repositorios

El código vive en dos repositorios, porque git no permite restringir la lectura
por rama: en un repositorio público cualquiera puede enumerar y clonar todas las
ramas, y las ramas protegidas de GitLab solo regulan quién puede hacer push o
merge, no quién puede leer.

| Repositorio | Contenido | Visibilidad |
| --- | --- | --- |
| `fherdom/idecanarias` | una sola rama `main`, un commit huérfano por versión publicada | público |
| `fherdom/idecanarias-qgis-plugin` | `master`, `dev` y todo el historial de desarrollo | privado |

`repository` y `tracker` de `metadata.txt` apuntan al público, que es lo que el
revisor de plugins.qgis.org comprueba. Para publicar una versión nueva se crea
un commit huérfano con el árbol correspondiente y se empuja a `main`.

## Pendiente

- Traducciones: `i18n/` solo contiene el placeholder `af.ts` del Plugin Builder.
  Las cadenas de la interfaz están en español a fuego, no traducidas.

## Fuera de alcance

Detectado y anotado, pero **no tocado** por corresponder a "modernizar", opción
descartada:

- `get_dest_projection()` fija EPSG:32628 (UTM 28N) a fuego e ignora el CRS del
  proyecto.
- `Log()` escribe en `idecanarias.log` dentro del directorio del plugin con
  `DEBUG=True` fijo y sin `encoding` explícito; lo adecuado sería
  `QgsMessageLog`.
- `QNetworkAccessManager` propio en lugar de `QgsNetworkAccessManager`: no
  respeta el proxy ni la configuración de autenticación de QGIS.
- `QInputDialog.getText()` ignora el valor `ok`: cancelar el diálogo añade el
  punto igualmente.

## Fuentes

- [Migrate Your Plugin to QGIS 4 (plugins.qgis.org)](https://plugins.qgis.org/docs/migrate-qgis4)
- [Plugin migration to be compatible with Qt5 and Qt6 (wiki QGIS)](https://github.com/qgis/QGIS/wiki/Plugin-migration-to-be-compatible-with-Qt5-and-Qt6)
- [pyqgis4-checker](https://github.com/qgis/pyqgis4-checker)
- [Differences Between PyQt6 and PyQt5 (Riverbank)](https://www.riverbankcomputing.com/static/Docs/PyQt6/pyqt5_differences.html)
- [QGIS is moving to Qt6 and launching QGIS 4.0 (blog QGIS)](https://blog.qgis.org/2025/04/17/qgis-is-moving-to-qt6-and-launching-qgis-4-0/)
Loading