Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
container:
image: ghcr.io/qgis/pyqgis4-checker:main-ubuntu
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Report Qt6 incompatibilities without modifying sources
run: pyqt5_to_pyqt6.py --dry_run --logfile pyqt6_checker.log .
Expand All @@ -26,7 +26,7 @@ jobs:
git diff --exit-code

- name: Upload checker report
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: always()
with:
name: pyqgis4-checker-report
Expand All @@ -44,14 +44,17 @@ jobs:
with:
python-version: '3.9'
- name: Install test tools
run: python -m pip install pytest flake8 bandit
run: python -m pip install --upgrade pip pytest flake8 bandit
- name: Critical lint
run: flake8 . --select=E9,F63,F7,F82
run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
- name: Full lint
run: flake8 . --count --statistics
- name: Offline tests
env:
PYTHONPATH: tests
run: python -m pytest -q
- name: Compile Python sources
run: python -m compileall -q .
- name: Bandit
run: bandit -r core gui translation_manager.py garmin_exporter.py --severity-level medium
run: >-
bandit -r core gui processing_provider translations qgis_compat.py
garmin_exporter.py translation_manager.py __init__.py
--severity-level medium
66 changes: 66 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Build plugin release asset

on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
version:
description: "Version without the leading v, for example 1.3.0"
required: true
type: string

permissions:
contents: write

jobs:
package:
name: Build plugin-only ZIP
runs-on: ubuntu-latest
steps:
- name: Check out release source
uses: actions/checkout@v4

- name: Set package version
id: version
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
version="${{ inputs.version }}"
else
version="${GITHUB_REF_NAME#v}"
fi
echo "version=$version" >> "$GITHUB_OUTPUT"

- name: Build installable plugin ZIP
run: |
python scripts/build_plugin.py \
--output "dist/garmin_export-${{ steps.version.outputs.version }}.zip"

- name: Verify plugin-only ZIP
run: |
python scripts/verify_plugin_archive.py \
"dist/garmin_export-${{ steps.version.outputs.version }}.zip"

- name: Upload workflow artifact
uses: actions/upload-artifact@v4
with:
name: garmin_export-${{ steps.version.outputs.version }}
path: dist/garmin_export-${{ steps.version.outputs.version }}.zip
if-no-files-found: error

- name: Create or update GitHub release asset
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
tag="v${{ steps.version.outputs.version }}"
asset="dist/garmin_export-${{ steps.version.outputs.version }}.zip"
if gh release view "$tag" >/dev/null 2>&1; then
gh release upload "$tag" "$asset" --clobber
else
gh release create "$tag" "$asset" \
--title "Garmin Export $tag" \
--generate-notes
fi
30 changes: 30 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
cff-version: 1.2.0
title: "Garmin Export"
message: "If you use Garmin Export, please cite it using this file."
type: software
authors:
- family-names: Kobyakov
given-names: Alexander
email: kobyakov@lesburo.ru
affiliation: Lesburo
version: 1.2.1
date-released: 2026-08-24
license: AGPL-3.0-or-later
repository-code: "https://github.com/AlexKobyakov/garmin_export"
url: "https://github.com/AlexKobyakov/garmin_export"
abstract: >-
A QGIS plugin for exporting vector layers to Garmin IMG maps through the
mkgmap compiler. The project supports QGIS 3.22+ with a shared Qt5/Qt6
compatibility boundary, a localized 12-language UI, TYP styling and
cancellation-safe dependency and export workflows.
keywords:
- GIS
- QGIS
- Garmin IMG
- mkgmap
- vector data
- TYP styling
- Qt5
- Qt6
- PyQGIS
- map export
89 changes: 86 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# Garmin Export Plugin для QGIS

[![CI](https://github.com/AlexKobyakov/garmin_export/actions/workflows/ci.yml/badge.svg)](https://github.com/AlexKobyakov/garmin_export/actions/workflows/ci.yml)
[![License: AGPLv3](https://img.shields.io/badge/License-AGPLv3-blue.svg)](LICENSE)
![QGIS 3.22+ / 4.x](https://img.shields.io/badge/QGIS-3.22%2B%20%2F%204.x-589632.svg)
![Qt5 + Qt6](https://img.shields.io/badge/Qt5%20%2B%20Qt6-compatible-informational.svg)
![Version](https://img.shields.io/badge/version-1.3.0-informational.svg)
![Tests](https://img.shields.io/badge/tests-167%20offline-success.svg)

🎯 **Профессиональный плагин для экспорта векторных данных QGIS в формат Garmin IMG**

Версия: 1.2.1
Версия: 1.3.0
Автор: Кобяков Александр Викторович (Alex Kobyakov)
Email: kobyakov@lesburo.ru
Год: 2025-2026
Совместимость: QGIS 3.44/Qt5 и QGIS 4.2/Qt6

🌐 **Языки интерфейса / UI languages:** 🇷🇺 Русский · 🇺🇸 English · 🇩🇪 Deutsch · 🇪🇸 Español · 🇫🇷 Français · 🇧🇷 Português · 🇨🇳 中文 · 🇮🇳 हिन्दी · 🇸🇦 العربية · 🇮🇩 Bahasa Indonesia · 🇹🇭 ไทย · 🇻🇳 Tiếng Việt

Expand Down Expand Up @@ -48,10 +56,56 @@ Garmin GPS devices.
- 📋 **mkgmap logging**: optional mkgmap.log file with a configurable verbosity.
- 💾 **Persistent settings** between sessions.
- 🧾 **Reliable lifecycle**: cancellation-safe export runs, stale-worker isolation and an anonymized .garmin_export/run_manifest.json on every completed run.
- 🧰 **Processing Toolbox (G6)**: validate the environment, build TYP mappings, validate TYP/code pages and mapping JSON, diagnose/download dependencies, generate MP previews and export IMG through Processing with generated, existing or disabled TYP styling, selected levels and advanced mkgmap options. Existing complete mkgmap/splitter installations are auto-detected; network download is explicit via the AUTO_DOWNLOAD parameter and reuses the UI fallback order with feedback cancellation.
- 🖥️ **Qt5/Qt6 dual support**: one compatibility boundary for QGIS 3.44/Qt5 and QGIS 4.2/Qt6, with scoped enum fallbacks and no direct PyQt5/PyQt6 imports.
- 🎛️ **Polished UI contract**: live/restart-safe language switching, SVG flags, readable combo/check controls, retranslate-safe dialogs, translated tooltips, placeholders and actions.


### Processing Toolbox and QGIS Modeler

The provider is available under **Processing → Toolbox → Garmin Export** and in
QGIS Modeler, Batch and the Python Processing API. It provides seven
GUI-independent algorithms:

1. **Validate environment** — reports Java, mkgmap and splitter separately.
2. **Build TYP mapping** — creates TYP from QGIS layer symbology.
3. **Export selected layers to Garmin IMG** — selected layers or all valid
project vectors, generated/existing/default TYP, levels, code page and typed
mkgmap tuning; returns IMG plus a redacted run_manifest.json.
4. **Validate TYP/code page** — checks a TYP/TXT path and label encoding.
5. **Generate MP preview** — writes Polish MP without Java or mkgmap.
6. **Validate mapping JSON** — validates file/inline JSON, geometry, Garmin type
and level ranges.
7. **Dependency diagnostics/download** — discovers a complete local distribution
and downloads only when AUTO_DOWNLOAD is enabled.

Parameter names are stable ASCII identifiers across QGIS 3.44/Qt5 and QGIS
4.2/Qt6. Existing complete distributions are auto-detected; network download
is opt-in and reuses the UI fallback order, transactional install and
cancellable feedback.

QGIS can run many independent Processing algorithms as tasks, and Modeler can
chain validation → MP/TYP generation → export. QGIS controls task parallelism;
the provider does not create duplicate GUI workers. For parallel exports use a
distinct output directory, map id and temporary directory per task.

Python console example:

processing.run("garmin_export:validate_mapping_json", {
"MAPPING_FILE": r"C:\maps\mapping.json"
})
processing.run("garmin_export:export_selected_layers", {
"USE_PROJECT_LAYERS": True,
"OUTPUT": r"C:\maps\out"
})

In Modeler, connect validation outputs to export inputs and expose OUTPUT,
AUTO_DOWNLOAD, code page, TYP mode and tuning as model inputs.

### Requirements

- QGIS 3.22 or newer, Python 3.9+
- QGIS 3.22 or newer (Qt5 on QGIS 3.x; Qt6 on QGIS 4.x), Python 3.9+
- Manually verified in QGIS 3.44/Qt5 and QGIS 4.2/Qt6.
- Java Runtime Environment (JRE 8+) for mkgmap (the plugin can auto-detect it)

### Quick start
Expand Down Expand Up @@ -94,11 +148,40 @@ Garmin Export Plugin - это современный инструмент для
- 📋 **Логирование mkgmap**: опциональный файл журнала mkgmap.log с настраиваемым уровнем детализации
- 💾 **Сохранение настроек** между сеансами работы
- 🧾 **Надёжный жизненный цикл**: безопасная отмена, защита от устаревших worker-сигналов и обезличенный .garmin_export/run_manifest.json для каждого завершённого запуска
- 🧰 **Processing Toolbox (G6)**: проверка окружения, построение и проверка TYP, проверка кодовой страницы и mapping JSON, диагностика/загрузка зависимостей, MP-preview и экспорт IMG с выбором TYP, уровней карты и расширенных параметров mkgmap. Установленные дистрибутивы mkgmap/splitter с каталогом lib/ определяются автоматически; скачивание выполняется только при включённом параметре AUTO_DOWNLOAD и использует тот же порядок зеркал и отмену через feedback.


### Processing Toolbox и QGIS Modeler

Провайдер доступен в **Обработка → Панель инструментов → Garmin Export**, а
также в QGIS Modeler, пакетном запуске и Python API Processing. Семь алгоритмов
не зависят от окна плагина: проверка окружения; построение TYP; экспорт IMG с
генерацией/существующим/отключённым TYP, уровнями, кодовой страницей и tuning;
проверка TYP и кодовой страницы; MP-preview; проверка mapping JSON; диагностика
и загрузка зависимостей. Экспорт возвращает обезличенный run_manifest.json.

Имена параметров Processing — стабильные ASCII-идентификаторы для QGIS
3.44/Qt5 и QGIS 4.2/Qt6. Полные дистрибутивы определяются автоматически;
сеть включается только через AUTO_DOWNLOAD и использует порядок зеркал и
отмену из UI.

QGIS может выполнять много независимых алгоритмов Processing как задачи, а
Modeler — связывать проверку -> генерацию MP/TYP -> экспорт. Параллелизм
контролирует QGIS. Для параллельных экспортов используйте разные выходные
каталоги, map id и временные папки.

Пример в Python-консоли QGIS:

processing.run("garmin_export:validate_mapping_json", {"MAPPING_FILE": r"C:\maps\mapping.json"})
processing.run("garmin_export:export_selected_layers", {"USE_PROJECT_LAYERS": True, "OUTPUT": r"C:\maps\out"})

В Modeler соедините результаты проверок с экспортом и вынесите OUTPUT,
AUTO_DOWNLOAD, кодовую страницу, режим TYP и tuning в параметры модели.

## 🔧 Требования

### Обязательные:
- QGIS версии 3.22 или выше
- QGIS версии 3.22 или выше (Qt5 в QGIS 3.x; Qt6 в QGIS 4.x)
- Python 3.9+
- Java Runtime Environment (JRE) для работы mkgmap

Expand Down
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
Email: kobyakov@lesburo.ru
Organization: Lesburo
Year: 2025-2026
Version: 1.1.4
Version: 1.2.1

Modular Architecture:
- garmin_exporter.py: Main plugin class
Expand Down
2 changes: 1 addition & 1 deletion core/export_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def _tool_version(path, tool):
class ExportService:
"""Owns one run's state; it never imports QGIS or emits GUI signals."""

def __init__(self, selected_layers, settings, plugin_version='1.1.4'):
def __init__(self, selected_layers, settings, plugin_version='1.2.1'):
self.selected_layers = [
{'id': str(item.get('id') or ''),
'name': str(item.get('name') or '')}
Expand Down
17 changes: 17 additions & 0 deletions docs/GITHUB_REPOSITORY_PROFILE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# GitHub repository profile

The repository About panel is remote GitHub metadata and is not stored in the
working tree. Recommended values:

- Description: QGIS plugin for exporting vector layers to Garmin IMG maps via mkgmap; Qt5/Qt6, 12-language UI, TYP styling, Processing Toolbox/Modeler algorithms and reliable cancellation-safe workflows.
- Homepage: https://github.com/AlexKobyakov/garmin_export
- Topics: qgis, qgis-plugin, pyqgis, garmin, garmin-img, mkgmap, gis, vector-data, map-export, qt5, qt6, typ, multilingual

After authenticating GitHub CLI, apply them with:

~~~bash
gh repo edit AlexKobyakov/garmin_export --description "QGIS plugin for exporting vector layers to Garmin IMG maps via mkgmap; Qt5/Qt6, 12-language UI, TYP styling, Processing Toolbox/Modeler algorithms and reliable cancellation-safe workflows." --homepage "https://github.com/AlexKobyakov/garmin_export" --add-topic qgis --add-topic qgis-plugin --add-topic pyqgis --add-topic garmin --add-topic garmin-img --add-topic mkgmap --add-topic gis --add-topic vector-data --add-topic map-export --add-topic qt5 --add-topic qt6 --add-topic typ --add-topic processing --add-topic qgis-modeler --add-topic multilingual
~~~

This command requires an authenticated gh auth login; no credentials are
stored in the repository.
52 changes: 49 additions & 3 deletions docs/RELEASE_1.2.1_CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,26 @@

## Scope

The 1.2.1 release includes the previously planned 1.2.0 modernization scope.
There is no separate 1.2.0 tag or archive: the Qt5/Qt6 boundary, UI contract,
bounded GUI refactor and 12-language/i18n work were completed and released
together with the G4/G5 reliability work as 1.2.1.

The carried scope includes:

- shared QGIS 3/Qt5 and QGIS 4/Qt6 compatibility helpers and checker guards;
- retranslate-safe composite widgets/dialogs, retained layout references,
translated tooltips/placeholders/actions and readable combo/check controls;
- 12-language registry, SVG flags, Arabic RTL, live/restart persistence and
translation parity tests;
- production Python modules bounded to 500 lines or less.


Release 1.2.1 closes the bounded G0-G5 track. It contains the Qt5/Qt6 boundary,
UI and translation contract, 12-language flags/RTL support, dependency
reliability and the cancellation-safe export lifecycle. Processing Provider and
other feature work are outside this release.
reliability and the cancellation-safe export lifecycle. Processing Provider work is tracked as the post-1.2.1 G6 feature scope;
the current branch contains the implementation and owner smoke confirmation,
The Processing scope was released separately as 1.3.0; see RELEASE_1.3.0_CHECKLIST.md.

## Runtime prerequisites

Expand Down Expand Up @@ -33,7 +49,7 @@ contain wheels/, plan/, tests/ or docs/.

## Offline release gates

- pytest -q: 155 passed.
- pytest -q: 167 passed (current branch baseline).
- Critical and full Flake8: passed.
- compileall: passed.
- Bandit: passed.
Expand All @@ -44,3 +60,33 @@ contain wheels/, plan/, tests/ or docs/.
- metadata.txt version: 1.2.1.
- Git tag: v1.2.1.
- Build command: python scripts/build_plugin.py --output dist/garmin_export-1.2.1.zip.

## Post-1.2.1 Processing smoke

For the G6 feature scope, repeat in QGIS 3.44/Qt5 and QGIS 4.2/Qt6:

1. Confirm the Garmin Export group and plugin icon in Processing -> Toolbox.
2. Run environment validation with valid and invalid Java/mkgmap/splitter paths.
3. Validate mapping JSON, build TYP, and generate an MP preview.
4. Run IMG export with generated, existing and disabled TYP modes, levels,
code page and advanced tuning.
5. Run dependency diagnostics with AUTO_DOWNLOAD off and on only when network
access is intended.
6. Check STATUS, ERRORS, MANIFEST and cancellation behavior.
7. Open the algorithms in Modeler and run a small batch with distinct outputs.

The owner confirmed the QGIS 3.44 and 4.2 smoke matrix for the current branch.

## Release asset policy

The installable release asset must be built by scripts/build_plugin.py, then
checked by scripts/verify_plugin_archive.py. The GitHub workflow
.github/workflows/release.yml performs both steps for vX.Y.Z tags and uploads
garmin_export-X.Y.Z.zip.

That ZIP contains only the garmin_export/ plugin tree: runtime Python modules,
resources, translations, metadata.txt, LICENSE and the icon. It excludes
repository documentation, tests, CI files, build scripts, examples, wheels,
planning files and development caches. A GitHub-generated Source code (zip)
snapshot is a repository archive, not the installable plugin package; users
must download the named garmin_export-X.Y.Z.zip release asset.
31 changes: 31 additions & 0 deletions docs/RELEASE_1.3.0_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Garmin Export 1.3.0 — release checklist

## Scope

Release 1.3.0 delivers the G6 Processing Provider and G7 release hardening:

- seven algorithms under Processing → Toolbox → Garmin Export;
- QGIS Modeler, Batch and processing.run() support with stable ASCII parameter IDs;
- TYP modes, code-page and mapping JSON validation, MP preview;
- dependency diagnostics/download with opt-in network access and auto-discovery;
- typed mkgmap tuning, project-layer batch export and explicit run manifest output;
- plugin-only release ZIP packaging and the Qt6 checker rewrite/diff guard.

## Release gates

- Owner smoke confirmed QGIS 3.44/Qt5 and QGIS 4.2/Qt6.
- pytest: 167 passed.
- Full flake8, compileall and Bandit: passed.
- Build and verify:

python scripts/build_plugin.py --output dist/garmin_export-1.3.0.zip
python scripts/verify_plugin_archive.py dist/garmin_export-1.3.0.zip

The verified ZIP must contain only the garmin_export/ plugin tree and must not
contain docs, tests, plan, wheels, scripts, CI files, examples or JAR files.

## GitHub publication

Create tag v1.3.0 through the normal repository workflow. The release workflow
builds and uploads the named asset garmin_export-1.3.0.zip. Users must download
that asset rather than GitHub's automatically generated Source code archive.
Loading
Loading