mbmerge 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mbmerge-1.1.0/.github/workflows/ci.yml +87 -0
- mbmerge-1.1.0/.github/workflows/release.yml +42 -0
- mbmerge-1.1.0/.github/workflows/testpypi.yml +36 -0
- mbmerge-1.1.0/.gitignore +76 -0
- mbmerge-1.1.0/ARCHITECTURE.cs.md +110 -0
- mbmerge-1.1.0/ARCHITECTURE.md +110 -0
- mbmerge-1.1.0/BENCHMARK.json +93 -0
- mbmerge-1.1.0/LICENSE.cs.md +13 -0
- mbmerge-1.1.0/LICENSE.md +23 -0
- mbmerge-1.1.0/MANIFEST.in +22 -0
- mbmerge-1.1.0/PKG-INFO +70 -0
- mbmerge-1.1.0/README.cs.md +249 -0
- mbmerge-1.1.0/README.md +251 -0
- mbmerge-1.1.0/README.pypi.cs.md +40 -0
- mbmerge-1.1.0/README.pypi.md +41 -0
- mbmerge-1.1.0/RELEASING.cs.md +116 -0
- mbmerge-1.1.0/RELEASING.md +116 -0
- mbmerge-1.1.0/TEST_REPORT.cs.md +82 -0
- mbmerge-1.1.0/TEST_REPORT.md +82 -0
- mbmerge-1.1.0/TEST_RESULTS.txt +68 -0
- mbmerge-1.1.0/demo/a.png +0 -0
- mbmerge-1.1.0/demo/b.png +0 -0
- mbmerge-1.1.0/demo/expected_overlap.png +0 -0
- mbmerge-1.1.0/demo/png/a.png +0 -0
- mbmerge-1.1.0/demo/png/b.png +0 -0
- mbmerge-1.1.0/demo/png/expected_overlap.png +0 -0
- mbmerge-1.1.0/demo/png/source_a.mbtiles +0 -0
- mbmerge-1.1.0/demo/png/source_b.mbtiles +0 -0
- mbmerge-1.1.0/demo/source_a.mbtiles +0 -0
- mbmerge-1.1.0/demo/source_b.mbtiles +0 -0
- mbmerge-1.1.0/docs/images/compositing.png +0 -0
- mbmerge-1.1.0/docs/images/tile-grid.png +0 -0
- mbmerge-1.1.0/examples/create_demo.py +51 -0
- mbmerge-1.1.0/examples/create_readme_images.py +119 -0
- mbmerge-1.1.0/mbmerge.egg-info/PKG-INFO +70 -0
- mbmerge-1.1.0/mbmerge.egg-info/SOURCES.txt +48 -0
- mbmerge-1.1.0/mbmerge.egg-info/dependency_links.txt +1 -0
- mbmerge-1.1.0/mbmerge.egg-info/entry_points.txt +2 -0
- mbmerge-1.1.0/mbmerge.egg-info/requires.txt +5 -0
- mbmerge-1.1.0/mbmerge.egg-info/top_level.txt +2 -0
- mbmerge-1.1.0/mbmerge.py +11 -0
- mbmerge-1.1.0/merge_mbtiles.py +968 -0
- mbmerge-1.1.0/pyproject.toml +47 -0
- mbmerge-1.1.0/requirements.txt +1 -0
- mbmerge-1.1.0/setup.cfg +4 -0
- mbmerge-1.1.0/tests/benchmark_memory.py +131 -0
- mbmerge-1.1.0/tests/test_merge.py +380 -0
- mbmerge-1.1.0/tests/test_png.py +175 -0
- mbmerge-1.1.0/tools/check_release.py +106 -0
- mbmerge-1.1.0/tools/smoke_installed.py +99 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
name: Tests and distributions
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_call:
|
|
8
|
+
inputs:
|
|
9
|
+
release_tag:
|
|
10
|
+
description: Optional v<VERSION> tag to validate before publishing
|
|
11
|
+
required: false
|
|
12
|
+
type: string
|
|
13
|
+
default: ''
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
tests:
|
|
20
|
+
name: ${{ matrix.os }} / Python ${{ matrix.python }}
|
|
21
|
+
runs-on: ${{ matrix.os }}
|
|
22
|
+
strategy:
|
|
23
|
+
fail-fast: false
|
|
24
|
+
matrix:
|
|
25
|
+
os: [ubuntu-latest, macos-latest]
|
|
26
|
+
python: ['3.11', '3.12', '3.13', '3.14']
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v6
|
|
29
|
+
with:
|
|
30
|
+
persist-credentials: false
|
|
31
|
+
- uses: actions/setup-python@v6
|
|
32
|
+
with:
|
|
33
|
+
python-version: ${{ matrix.python }}
|
|
34
|
+
cache: pip
|
|
35
|
+
cache-dependency-path: |
|
|
36
|
+
pyproject.toml
|
|
37
|
+
requirements.txt
|
|
38
|
+
- name: Install project and runtime dependencies
|
|
39
|
+
run: python -m pip install -e .
|
|
40
|
+
- name: Run synthetic and SIGKILL recovery tests
|
|
41
|
+
run: python -m unittest discover -s tests -v
|
|
42
|
+
- name: Check installed command and module entry points
|
|
43
|
+
run: |
|
|
44
|
+
mbmerge --version
|
|
45
|
+
python -m mbmerge --version
|
|
46
|
+
|
|
47
|
+
distributions:
|
|
48
|
+
name: Build, inspect and install distributions
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
env:
|
|
51
|
+
# Treat a release tag as data, never interpolate it directly into shell code.
|
|
52
|
+
RELEASE_TAG: ${{ inputs.release_tag }}
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@v6
|
|
55
|
+
with:
|
|
56
|
+
persist-credentials: false
|
|
57
|
+
- uses: actions/setup-python@v6
|
|
58
|
+
with:
|
|
59
|
+
python-version: '3.12'
|
|
60
|
+
cache: pip
|
|
61
|
+
cache-dependency-path: |
|
|
62
|
+
pyproject.toml
|
|
63
|
+
requirements.txt
|
|
64
|
+
- name: Install build and metadata validation tools
|
|
65
|
+
run: python -m pip install '.[release]'
|
|
66
|
+
- name: Build wheel and source distribution
|
|
67
|
+
run: python -m build
|
|
68
|
+
- name: Validate metadata, source version and optional release tag
|
|
69
|
+
run: |
|
|
70
|
+
python -m twine check --strict dist/*
|
|
71
|
+
python tools/check_release.py --dist-dir dist --tag "$RELEASE_TAG"
|
|
72
|
+
- name: Install the exact wheel being published
|
|
73
|
+
run: python -m pip install --force-reinstall --no-deps dist/*.whl
|
|
74
|
+
- name: Exercise installed wheel without checkout imports
|
|
75
|
+
run: python -I tools/smoke_installed.py
|
|
76
|
+
- name: Build a wheel from the source distribution too
|
|
77
|
+
run: python -m pip wheel --no-deps dist/*.tar.gz --wheel-dir rebuilt
|
|
78
|
+
- name: Exercise the rebuilt wheel
|
|
79
|
+
run: |
|
|
80
|
+
python -m pip install --force-reinstall --no-deps rebuilt/*.whl
|
|
81
|
+
python -I tools/smoke_installed.py
|
|
82
|
+
- name: Store only the validated release distributions
|
|
83
|
+
uses: actions/upload-artifact@v7.0.2
|
|
84
|
+
with:
|
|
85
|
+
name: python-distributions
|
|
86
|
+
path: dist/
|
|
87
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
name: Publish release to PyPI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
release:
|
|
6
|
+
types: [published]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: pypi-${{ github.event.release.tag_name }}
|
|
13
|
+
cancel-in-progress: false
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
checks:
|
|
17
|
+
# Stable releases publish here; prereleases remain available on GitHub.
|
|
18
|
+
if: ${{ !github.event.release.prerelease }}
|
|
19
|
+
uses: ./.github/workflows/ci.yml
|
|
20
|
+
with:
|
|
21
|
+
release_tag: ${{ github.event.release.tag_name }}
|
|
22
|
+
|
|
23
|
+
publish:
|
|
24
|
+
# Private evaluation never publishes to a public package index.
|
|
25
|
+
if: ${{ !github.event.repository.private && vars.PYPI_PUBLISH_ENABLED == 'true' }}
|
|
26
|
+
name: Publish validated artifacts
|
|
27
|
+
needs: checks
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
environment:
|
|
30
|
+
name: pypi
|
|
31
|
+
url: https://pypi.org/project/mbmerge/
|
|
32
|
+
permissions:
|
|
33
|
+
id-token: write
|
|
34
|
+
steps:
|
|
35
|
+
# Publishing has no source checkout or build commands. It receives the
|
|
36
|
+
# exact artifacts from this run after all reusable CI jobs have succeeded.
|
|
37
|
+
- uses: actions/download-artifact@v8.0.2
|
|
38
|
+
with:
|
|
39
|
+
name: python-distributions
|
|
40
|
+
path: dist/
|
|
41
|
+
- name: Publish using PyPI Trusted Publishing
|
|
42
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
name: Publish manually to TestPyPI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: testpypi-publish
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
checks:
|
|
16
|
+
uses: ./.github/workflows/ci.yml
|
|
17
|
+
|
|
18
|
+
publish:
|
|
19
|
+
# Private evaluation never publishes to a public package index.
|
|
20
|
+
if: ${{ !github.event.repository.private && vars.TESTPYPI_PUBLISH_ENABLED == 'true' }}
|
|
21
|
+
needs: checks
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
environment:
|
|
24
|
+
name: testpypi
|
|
25
|
+
url: https://test.pypi.org/project/mbmerge/
|
|
26
|
+
permissions:
|
|
27
|
+
id-token: write
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/download-artifact@v8.0.2
|
|
30
|
+
with:
|
|
31
|
+
name: python-distributions
|
|
32
|
+
path: dist/
|
|
33
|
+
- name: Publish using TestPyPI Trusted Publishing
|
|
34
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
35
|
+
with:
|
|
36
|
+
repository-url: https://test.pypi.org/legacy/
|
mbmerge-1.1.0/.gitignore
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Version in Git:
|
|
2
|
+
# Python source, tests, examples, tools/, requirements.txt, and this .gitignore.
|
|
3
|
+
# pyproject.toml, MANIFEST.in, and .github/workflows/.
|
|
4
|
+
# README*.md, TEST_REPORT*.md, ARCHITECTURE*.md, LICENSE*.md, docs/images/, and the demo PNG previews.
|
|
5
|
+
# The synthetic WebP/PNG MBTiles fixtures explicitly allowed below.
|
|
6
|
+
# Curated BENCHMARK.json, TEST_RESULTS.txt, and release SHA256SUMS.
|
|
7
|
+
#
|
|
8
|
+
# Keep real datasets, generated outputs, local logs, and credentials out of Git.
|
|
9
|
+
# Ignoring a file does not remove it if it has already been committed.
|
|
10
|
+
|
|
11
|
+
# Real inputs and generated raster archives. Only bundled synthetic fixtures ship.
|
|
12
|
+
*.mbtiles
|
|
13
|
+
!/demo/source_a.mbtiles
|
|
14
|
+
!/demo/source_b.mbtiles
|
|
15
|
+
!/demo/png/source_a.mbtiles
|
|
16
|
+
!/demo/png/source_b.mbtiles
|
|
17
|
+
*.db
|
|
18
|
+
*.sqlite
|
|
19
|
+
*.sqlite3
|
|
20
|
+
|
|
21
|
+
# SQLite sidecars and the merger's runtime lock. Never commit or delete live locks.
|
|
22
|
+
*-wal
|
|
23
|
+
*-shm
|
|
24
|
+
*-journal
|
|
25
|
+
*.merge.lock
|
|
26
|
+
|
|
27
|
+
# Local datasets, scratch files, and generated demo sets.
|
|
28
|
+
/data/
|
|
29
|
+
/inputs/
|
|
30
|
+
/outputs/
|
|
31
|
+
/work/
|
|
32
|
+
/new_demo*/
|
|
33
|
+
|
|
34
|
+
# Run logs and local benchmark output. Curated reports above remain versioned.
|
|
35
|
+
*.log
|
|
36
|
+
*.jsonl
|
|
37
|
+
/benchmark*.local.json
|
|
38
|
+
|
|
39
|
+
# Python environments, bytecode, caches, test coverage, and package builds.
|
|
40
|
+
.venv*/
|
|
41
|
+
venv/
|
|
42
|
+
env/
|
|
43
|
+
__pycache__/
|
|
44
|
+
*.py[cod]
|
|
45
|
+
.pytest_cache/
|
|
46
|
+
.mypy_cache/
|
|
47
|
+
.ruff_cache/
|
|
48
|
+
.coverage
|
|
49
|
+
.coverage.*
|
|
50
|
+
htmlcov/
|
|
51
|
+
coverage.xml
|
|
52
|
+
*.egg-info/
|
|
53
|
+
/build/
|
|
54
|
+
/dist/
|
|
55
|
+
|
|
56
|
+
# Local environment settings. A sanitized example can be committed.
|
|
57
|
+
.env
|
|
58
|
+
.env.*
|
|
59
|
+
!.env.example
|
|
60
|
+
|
|
61
|
+
# Editor and operating-system files.
|
|
62
|
+
.idea/
|
|
63
|
+
.vscode/
|
|
64
|
+
*.swp
|
|
65
|
+
*.swo
|
|
66
|
+
*~
|
|
67
|
+
.DS_Store
|
|
68
|
+
Thumbs.db
|
|
69
|
+
Desktop.ini
|
|
70
|
+
|
|
71
|
+
# Distribute release archives as downloads rather than repository source files.
|
|
72
|
+
*.zip
|
|
73
|
+
*.tar.gz
|
|
74
|
+
|
|
75
|
+
# Wheel rebuilt from an sdist during release verification.
|
|
76
|
+
/rebuilt/
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Architektura a údržba
|
|
2
|
+
|
|
3
|
+
[English](ARCHITECTURE.md) | **Čeština**
|
|
4
|
+
|
|
5
|
+
Tento dokument vysvětluje pravidla v [merge_mbtiles.py](merge_mbtiles.py). Podporované vstupy a příkazy popisuje [README](README.cs.md). Komentáře a docstringy ve zdrojovém kódu jsou anglicky.
|
|
6
|
+
|
|
7
|
+
## Mapa kódu
|
|
8
|
+
|
|
9
|
+
| Oblast | Vstupní funkce | Odpovědnost |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| CLI | `parser`, `main` | Ověření limitů, signály, výběr příkazu a návratové kódy. |
|
|
12
|
+
| Řízení běhu | `merge`, `collect_inputs`, `output_lock` | Pořadí zdrojů, vyloučení druhého writeru, kontrola historie a dokončení aktivního zdroje. |
|
|
13
|
+
| Zdrojové snapshoty | `source`, `token`, `assert_static`, `digest`, `preflight` | Čtecí zámky, detekce změn a ověření všech historických příspěvků. |
|
|
14
|
+
| Příprava databáze | `open_output`, `connection_limits` | Schéma, nastavení výstupu a omezení bufferů spojení. |
|
|
15
|
+
| Příprava dávky | `read_batch`, `validate_schema` | Indexované stránkování a výběr podle délek před načtením blobů. |
|
|
16
|
+
| Obrazové workery | `bounded_results`, `process_tile`, `decode`, `encode` | Backpressure, validace, kopírování, převod a skládání dlaždic. |
|
|
17
|
+
| Trvalý stav | `import_source`, `update_metadata`, `_commit`, `checkpoint` | Společný commit pixelů a postupu, potom checkpoint WAL. |
|
|
18
|
+
| Kontroly | `status`, `verify` | Malá evidence nebo výslovný průchod SQLite a obrázků výstupu. |
|
|
19
|
+
|
|
20
|
+
Úloha má tvar `(z, x, y, new_bytes, old_bytes_or_None)`, výsledek `(z, x, y, output_bytes)`. Souřadnice jsou všude TMS. Pořadí hodnot v Python n-ticích odpovídá sloupcům SQL; při změně je potřeba ověřit všechny producenty i konzumenty.
|
|
21
|
+
|
|
22
|
+
## Jediný writer a pořadí zdrojů
|
|
23
|
+
|
|
24
|
+
Hlavní proces vlastní zdrojové i jediné výstupní SQLite spojení. Workery vznikají přes `spawn` a dostávají pouze bajty a parametry. Nepředávej jim SQLite spojení a nepřepínej na `fork` s otevřenými spojeními.
|
|
25
|
+
|
|
26
|
+
Paralelně běží jen dlaždice aktuálního zdroje. Jejich klíče jsou unikátní, takže pořadí dokončení workerů výsledek nemění. Pořadí zdrojů ho mění: `Image.alpha_composite(existing_output, new_source)` kreslí nový zdroj přes dosavadní obraz. Aktivní zdroj se musí dokončit před novým, i když ho další příkaz neuvede.
|
|
27
|
+
|
|
28
|
+
Výstupní zámek je kooperativní a drží se po celý běh. Jeho soubor zůstává po uvolnění na disku; smazání drženého zámku by umožnilo druhému procesu zamknout jiný inode. Přímé změny výstupu jinými nástroji nejsou podporované.
|
|
29
|
+
|
|
30
|
+
## Stav importu a obnova po pádu
|
|
31
|
+
|
|
32
|
+
```mermaid
|
|
33
|
+
stateDiagram-v2
|
|
34
|
+
[*] --> active: Registrace cesty a fingerprintu
|
|
35
|
+
active --> active: Commit dávky a posledního klíče
|
|
36
|
+
active --> complete: Ověření finálního SHA-256
|
|
37
|
+
active --> tainted: Změna zdroje během importu
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`active` bez posledního klíče znamená, že se nepotvrdila žádná dávka dlaždic. `complete` znamená úspěšnou finální kontrolu hashe. `tainted` blokuje další import a vyžaduje nový výstup; smazání příznaku neopraví pixely načtené z nestabilního zdroje.
|
|
41
|
+
|
|
42
|
+
Neshoda SHA při preflightu je odlišná: import ještě nezačal, takže výstup neoznačí jako tainted. Obnovení přesných původních bajtů umožní pokračovat.
|
|
43
|
+
|
|
44
|
+
Dávka má následující průběh:
|
|
45
|
+
|
|
46
|
+
1. Načíst klíče a délky pomocí `(z,x,y) > last_committed_key`, bez `OFFSET`.
|
|
47
|
+
2. Najít překryvy malou dočasnou tabulkou a indexovaným vyhledáváním ve výstupu.
|
|
48
|
+
3. Vybrat souvislý prefix podle počtu a bajtového rozpočtu; načíst jen jeho bloby.
|
|
49
|
+
4. Zahájit `BEGIN IMMEDIATE`, spustit omezené úlohy a zapisovat výsledky SQL dávkami.
|
|
50
|
+
5. Znovu ověřit stabilitu zdroje. Aktualizovat poslední klíč, počty, zoomy, rozsahy a metadata.
|
|
51
|
+
6. Potvrdit **všechny** tyto změny společně. Potom mimo transakci zavolat `wal_checkpoint(TRUNCATE)`.
|
|
52
|
+
|
|
53
|
+
Pád před commitem vrátí celou dávku; po commitu se dávka přeskočí. Opakované nanesení potvrzené poloprůhledné vrstvy by změnilo pixely. Retry proto nesmí takovou dávku znovu skládat. Pokud checkpoint blokuje čtečka, předchozí dávka již zůstala potvrzená. Import zastaví a naváže po zavření čtečky.
|
|
54
|
+
|
|
55
|
+
Registrace a dokončení zdroje jsou samostatné malé transakce. Trvale uložený poslední klíč označuje potvrzené dlaždice, nikoli odeslané nebo právě dokončené úlohy. Chyba SQL/workeru vrátí aktuální dávku a ponechá starší commity.
|
|
56
|
+
|
|
57
|
+
## Účtování paměti
|
|
58
|
+
|
|
59
|
+
`read_batch` počítá zdrojové bajty, staré bajty výstupu a rezervu výsledku. Překryv nebo možný převod formátu rezervuje `max_tile_mib`. Jedinečná dlaždice deklarovaná ve formátu výstupu může rezervovat původní velikost: worker deklaraci ověří před kopírováním. Při změně rychlé kopírovací větve toto ověření zachovej.
|
|
60
|
+
|
|
61
|
+
`batch_size` omezuje režii klíčů a řádků, `batch_mib` kódovaná data. Čtení délek před bloby brání načtení příliš velké dlaždice ještě před odmítnutím. Pevné pořadí joinu brání plnému průchodu výstupu pro každou dávku.
|
|
62
|
+
|
|
63
|
+
`bounded_results` odešle nejvýše `max_inflight` futures včetně hotových neodebraných výsledků. Výsledky předá před doplněním poolu, takže pomalý SQL writer zpomalí producenty. Nenahrazuj ho načtením všech úloh přes `map`, neomezenou frontou ani seznamem všech výsledků. SQL buffery mají také početní a bajtový limit; před další dávkou se stará uvolní.
|
|
64
|
+
|
|
65
|
+
Rozpočty nejsou tvrdým limitem RSS. Přidávají se kopie při serializaci, procesy Python/Pillow, dekódované RGBA buffery, metadata PNG a paměť nativních kodeků. Rozměry a bloby jsou omezené, SQLite cache mají limit, mmap je vypnutý a dočasné tabulky mohou přetéct na disk. Absolutní celkový limit nastavuje provozní prostředí.
|
|
66
|
+
|
|
67
|
+
## Integrita zdrojů a formáty
|
|
68
|
+
|
|
69
|
+
Čtecí transakce zdroje zahrnuje první hash, import i poslední hash. Po `BEGIN` se čte schéma, aby se skutečně získal sdílený zámek. WAL zdroje se odmítají, protože samotný hlavní soubor nemusí obsahovat celý jejich viditelný stav. `immutable=1` není bezpečná optimalizace: obchází potřebné zamykání.
|
|
70
|
+
|
|
71
|
+
Stat tokeny zachytí výměnu zařízení/inodu a změnu velikosti, mtime či ctime. ctime se může změnit i při změně metadat, což záměrně vede ke konzervativnímu zastavení. SHA-256 ověřuje obsah i při obnovení velikosti/mtime. Vstupy musí být offline a neměnné; přímé zápisy obcházející SQLite zámky nejsou podporované.
|
|
72
|
+
|
|
73
|
+
Signatury a hlavičky se ověřují před alokací pixelů v Pillow. WebP kontroluje VP8X i rozměry VP8/VP8L, PNG hlavičku IHDR, hranice/CRC chunků a statický obraz. Pillow potom dekóduje podporovaný 8bitový zobrazovací rastr do RGBA. 16bitové PNG a animace se výslovně odmítají. Merger nemění velikost dlaždic, nespravuje barevné profily, nereprojektuje ani neořezává polygonem.
|
|
74
|
+
|
|
75
|
+
Jedinečná dlaždice se kopíruje bajtově jen při shodě s formátem výstupu. Ostatní se převádějí; překryvy se skládají a bezztrátově kódují. Již ztracené pixely původního ztrátového vstupu nelze obnovit. Nastavení komprese platí pro nově kódované dlaždice, nikoli původní kopírované bajty.
|
|
76
|
+
|
|
77
|
+
## Evidence, metadata a kompatibilita
|
|
78
|
+
|
|
79
|
+
| Tabulka | Význam |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `tiles` | Standardní souřadnice MBTiles a jeden zvolený formát obrázků. |
|
|
82
|
+
| `metadata` | Zobrazovací metadata aktualizovaná s potvrzenými pixely. |
|
|
83
|
+
| `_merge_sources` | Pořadí, cesta/hash/stat token, stav, poslední klíč a průběžné statistiky zdroje. |
|
|
84
|
+
| `_merge_config` | Verze schématu a nastavení výstupu/kodéru. |
|
|
85
|
+
| `_merge_attributions` | Unikátní atribuce s omezenou souhrnnou velikostí. |
|
|
86
|
+
|
|
87
|
+
`tile_count` počítá příspěvky zdrojů, nikoli unikátní výstupní dlaždice. Zoomy vycházejí z potvrzených klíčů. Bounds používají validní deklaraci nebo celé importované dlaždice, bez ořezu podle alfa kanálu. Celková metadata agregují malou evidenci a neprocházejí stále celou tabulku dlaždic. Deklarovaný rozsah rozpracovaného zdroje může zahrnovat dosud nenačtené části.
|
|
88
|
+
|
|
89
|
+
`VERSION` programu a `SCHEMA_VERSION` mají odlišný účel. Verze 1.1 interpretuje schéma 1 jako WebP a trvale ho povýší na 2 při nejbližším úspěšném commitu dávky nebo finálních metadat. Verze 1.0 schéma 2 odmítne, aby nepoškodila PNG výstup původním WebP kódem. Formát, velikost dlaždic a aktivní kompresní parametr jsou při resume pevné.
|
|
90
|
+
|
|
91
|
+
U nekompatibilní změny nejdřív stanov migraci a chování starších klientů. Tato nastavení se během života archivu nesmějí tiše přepnout.
|
|
92
|
+
|
|
93
|
+
## Testy a bezpečné rozšiřování
|
|
94
|
+
|
|
95
|
+
- [tests/test_merge.py](tests/test_merge.py) ověřuje pořadí, metadata, zámky, detekci změn, rollback a skutečný SIGKILL kolem commitu pro oba formáty.
|
|
96
|
+
- [tests/test_png.py](tests/test_png.py) ověřuje smíšené formáty, pixely, strukturu/režimy PNG, velikost výsledků, jednotný formát a migraci evidence.
|
|
97
|
+
- [examples/create_demo.py](examples/create_demo.py) vytváří nezávislé 512px vstupy a očekávaný obraz přímo přes Pillow.
|
|
98
|
+
- [tests/benchmark_memory.py](tests/benchmark_memory.py) měří strom procesů CLI, bez přípravy dat a měřicího procesu. Vzorkuje RSS, takže může započítat sdílené stránky vícekrát a minout krátké špičky.
|
|
99
|
+
|
|
100
|
+
Před změnou dávkování, pořadí, kódování nebo schématu spusť `python -m unittest discover -s tests -v`. Zachovej pixelové porovnání, kontrolu přeskočení potvrzené dávky, rezervu výsledku převodu a crash testy. Změna toku dat či spotřeby prostředků vyžaduje nové měření na reprezentativních datech; samotné komentáře nové výkonnostní měření nepotřebují. Při přípravě release archivu po poslední změně všech souborů přegeneruj `SHA256SUMS`.
|
|
101
|
+
|
|
102
|
+
Kód i související projektová dokumentace jsou zveřejněny pod [licencí MIT](LICENSE.md).
|
|
103
|
+
|
|
104
|
+
## Balíčkování a vydávání
|
|
105
|
+
|
|
106
|
+
[mbmerge.py](mbmerge.py) zpřístupňuje `python -m mbmerge` a `__version__`. Nainstalovaný příkaz používá stejný `main()` v `merge_mbtiles.py`; původní skript zůstává kompatibilní. [pyproject.toml](pyproject.toml) balí výslovně tyto dva moduly, dynamicky načítá VERSION a přebírá závislost Pillow z requirements.txt.
|
|
107
|
+
|
|
108
|
+
[MANIFEST.in](MANIFEST.in) přidává zdrojové testy/dokumentaci a jen čtyři jmenované syntetické databáze, nikdy obecný glob všech MBTiles. [tools/check_release.py](tools/check_release.py) ověřuje identitu zdrojů, wheelu, sdistu a produkční tag. [tools/smoke_installed.py](tools/smoke_installed.py) záměrně používá nainstalovaný wheel, izolovaný Python a dočasný pracovní adresář CLI.
|
|
109
|
+
|
|
110
|
+
[RELEASING.cs.md](RELEASING.cs.md) popisuje znovupoužitelné CI a oddělené publikační joby s OIDC. Při změně balíčkování či vstupních bodů znovu sestav a vyzkoušej wheel i wheel ze sdistu; samotné zdrojové testy chybějící soubory distribuce neodhalí.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Architecture and maintenance
|
|
2
|
+
|
|
3
|
+
**English** | [Čeština](ARCHITECTURE.cs.md)
|
|
4
|
+
|
|
5
|
+
This guide explains the invariants behind [merge_mbtiles.py](merge_mbtiles.py). Read the [README](README.md) for supported inputs and commands. Source comments and docstrings are in English.
|
|
6
|
+
|
|
7
|
+
## Code map
|
|
8
|
+
|
|
9
|
+
| Area | Entry points | Responsibility |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| CLI | `parser`, `main` | Validate limits, handle signals, dispatch commands and report exit codes. |
|
|
12
|
+
| Run orchestration | `merge`, `collect_inputs`, `output_lock` | Resolve source order, exclude other writers, validate history and finish active sources first. |
|
|
13
|
+
| Source snapshots | `source`, `token`, `assert_static`, `digest`, `preflight` | Keep read locks, detect changes and verify every historical contribution. |
|
|
14
|
+
| Database setup | `open_output`, `connection_limits` | Create the managed schema, resolve encoding settings and constrain connection buffers. |
|
|
15
|
+
| Batch preparation | `read_batch`, `validate_schema` | Use indexed keyset reads and length-based selection before fetching bounded blobs. |
|
|
16
|
+
| Image workers | `bounded_results`, `process_tile`, `decode`, `encode` | Apply backpressure, validate images, copy/transcode or composite tiles. |
|
|
17
|
+
| Durable state | `import_source`, `update_metadata`, `_commit`, `checkpoint` | Commit pixels and progress together, then checkpoint WAL. |
|
|
18
|
+
| Inspection | `status`, `verify` | Inspect the small ledger or explicitly scan SQLite/output images. |
|
|
19
|
+
|
|
20
|
+
The job tuple is `(z, x, y, new_bytes, old_bytes_or_None)`; the result is `(z, x, y, output_bytes)`. Coordinates are TMS throughout. The Python tuple order mirrors SQL column order; changing one requires checking all producers and consumers.
|
|
21
|
+
|
|
22
|
+
## One writer and ordered sources
|
|
23
|
+
|
|
24
|
+
The parent process owns both the source connection and the single output connection. Workers are created with `spawn` and receive bytes/parameters only. Never pass SQLite handles to the pool or switch to `fork` with live connections.
|
|
25
|
+
|
|
26
|
+
Only tiles from the current source run in parallel. Its keys are unique, so worker completion order does not affect the result. Source order does: `Image.alpha_composite(existing_output, new_source)` puts the new source over the old one. An active source must finish before any new source, including when omitted from the next CLI invocation.
|
|
27
|
+
|
|
28
|
+
The output lock is cooperative and covers the whole run. Its file remains after release; unlinking a held lock could allow a second process to lock a different inode. Direct output edits by other tools are outside the contract.
|
|
29
|
+
|
|
30
|
+
## Import state and crash recovery
|
|
31
|
+
|
|
32
|
+
```mermaid
|
|
33
|
+
stateDiagram-v2
|
|
34
|
+
[*] --> active: Register path and fingerprint
|
|
35
|
+
active --> active: Commit a batch and its last key
|
|
36
|
+
active --> complete: Verify final SHA-256
|
|
37
|
+
active --> tainted: Source changes during import
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`active` with no last key means no tile batch has committed. `complete` means the source passed its final hash check. `tainted` blocks future imports and requires rebuilding to a new output; clearing the flag does not repair pixels already read from an unstable source.
|
|
41
|
+
|
|
42
|
+
A preflight SHA mismatch is different: importing has not started, so it does not taint the output. Restoring the exact original source bytes permits resume.
|
|
43
|
+
|
|
44
|
+
Each batch follows this order:
|
|
45
|
+
|
|
46
|
+
1. Read keys and encoded lengths using `(z,x,y) > last_committed_key`, with no `OFFSET`.
|
|
47
|
+
2. Find output overlaps using the small temporary key table and indexed output lookups.
|
|
48
|
+
3. Choose a contiguous prefix fitting count and byte limits; fetch only that prefix's blobs.
|
|
49
|
+
4. Start `BEGIN IMMEDIATE`, run bounded worker jobs and write their results in SQL batches.
|
|
50
|
+
5. Recheck source stability. Update its last key, counts, zooms, extents and output metadata.
|
|
51
|
+
6. Commit **all** those changes together. Then call `wal_checkpoint(TRUNCATE)` outside the transaction.
|
|
52
|
+
|
|
53
|
+
Before commit, a crash rolls back the whole batch. After commit, resume skips it. Replaying a committed translucent layer would change the pixels; do not implement a retry that composites it again. If checkpointing fails because a reader pins WAL pages, the preceding batch is still committed. Stop and resume after closing the reader.
|
|
54
|
+
|
|
55
|
+
Source registration and completion are small separate transactions. The persisted last key always refers to committed source tiles, never submitted or merely completed worker jobs. SQL/worker failure rolls back the current batch and leaves earlier commits intact.
|
|
56
|
+
|
|
57
|
+
## Memory accounting
|
|
58
|
+
|
|
59
|
+
`read_batch` computes a budget for encoded source bytes, existing output bytes and reserved result bytes. Overlaps and possible format conversions reserve `max_tile_mib` for the result. A unique tile declared in the selected output format can reserve its original size because the worker checks the declaration before copying bytes. Keep that validation if changing the fast path.
|
|
60
|
+
|
|
61
|
+
Both `batch_size` and `batch_mib` matter. The former bounds key/row overhead; the latter bounds encoded data. Querying lengths first prevents a single oversized source blob from being loaded before rejection. Fixed join order avoids a full output scan for each batch.
|
|
62
|
+
|
|
63
|
+
`bounded_results` submits at most `max_inflight` futures, including finished results not yet consumed. It yields results before refilling the pool, so slow SQL writes apply backpressure. Do not replace it with an eager `map`, an unbounded queue, or a list of all results. SQL result buffers also have count/byte limits; previous batches are released before reading the next one.
|
|
64
|
+
|
|
65
|
+
These budgets are not a hard RSS cap. Serialization copies, Python/Pillow processes, decoded RGBA buffers, PNG metadata handling and native codec workspace add overhead. Image dimensions and blob sizes remain bounded, SQLite caches are limited, mmap is disabled, and temporary tables can spill to disk. A hard total limit belongs to the runtime environment.
|
|
66
|
+
|
|
67
|
+
## Source integrity and format validation
|
|
68
|
+
|
|
69
|
+
A source read transaction spans initial hash, importing and final hash. `BEGIN` is followed by a schema read to actually acquire the shared lock. WAL sources are rejected because their visible state may not be represented by the main-file hash. Do not add `immutable=1` as an optimization: it bypasses the locking relied on here.
|
|
70
|
+
|
|
71
|
+
Stat tokens detect device/inode replacement and size/mtime/ctime changes at boundaries. ctime may also change for metadata-only edits, intentionally causing a conservative stop. SHA-256 checks content, including changes hidden by restoring size/mtime. Inputs must remain offline and immutable; raw writes that bypass SQLite locks are unsupported.
|
|
72
|
+
|
|
73
|
+
Signature/header checks happen before Pillow allocates pixels. WebP checks both VP8X canvas and VP8/VP8L bitstream dimensions; PNG checks IHDR, chunk boundaries/CRCs and static-image constraints. Pillow then decodes the supported 8-bit display image into RGBA. Sixteen-bit PNG and animations are explicitly rejected. The merger does not resize tiles, manage color profiles, reproject data or perform polygon clipping.
|
|
74
|
+
|
|
75
|
+
A unique tile is copied byte-for-byte only when its format matches the selected output. Other unique tiles are transcoded; overlaps are composited and encoded losslessly. Original lossy input pixels cannot be recovered. Compression settings affect newly encoded tiles, not copied originals.
|
|
76
|
+
|
|
77
|
+
## Ledger, metadata and compatibility
|
|
78
|
+
|
|
79
|
+
| Table | Meaning |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `tiles` | Standard MBTiles coordinates and one selected encoded format. |
|
|
82
|
+
| `metadata` | Standard display metadata, updated with committed pixels. |
|
|
83
|
+
| `_merge_sources` | Source priority, path/hash/stat token, state, last key and incremental statistics. |
|
|
84
|
+
| `_merge_config` | Schema version and output/encoder settings. |
|
|
85
|
+
| `_merge_attributions` | Unique attribution strings with a bounded aggregate size. |
|
|
86
|
+
|
|
87
|
+
`tile_count` counts source contributions, not distinct output tiles. Zooms come from committed keys. Bounds use valid declared source bounds or full imported tile extents, without alpha-edge cropping. Global metadata aggregates the small ledger instead of repeatedly scanning the tiles table. During a partial import, declared source bounds may include its not-yet-imported area.
|
|
88
|
+
|
|
89
|
+
Program `VERSION` and `SCHEMA_VERSION` serve different purposes. Version 1.1 reads legacy schema 1 as WebP and durably upgrades it to schema 2 in the next successful batch/final metadata transaction. Version 1.0 refuses schema 2, protecting PNG outputs from old WebP-only code. Output format, tile size and the active encoder setting are fixed on resume.
|
|
90
|
+
|
|
91
|
+
For an incompatible change, define migration/old-client behavior before writing new data. Never silently change these settings halfway through an archive.
|
|
92
|
+
|
|
93
|
+
## Tests and safe extension
|
|
94
|
+
|
|
95
|
+
- [tests/test_merge.py](tests/test_merge.py) covers ordering, metadata, locks, change detection, worker rollback and actual SIGKILL at commit boundaries for both formats.
|
|
96
|
+
- [tests/test_png.py](tests/test_png.py) covers mixed formats, pixel fidelity, PNG structure/modes, encoding limits, format consistency and legacy-ledger migration.
|
|
97
|
+
- [examples/create_demo.py](examples/create_demo.py) builds independent 512px fixtures and an expected image using Pillow directly.
|
|
98
|
+
- [tests/benchmark_memory.py](tests/benchmark_memory.py) measures the CLI process tree, excluding fixture creation and the harness. It samples RSS, which can count shared pages twice and omit brief peaks.
|
|
99
|
+
|
|
100
|
+
Before changing batching, source order, encoding or schema, run `python -m unittest discover -s tests -v`. Keep exact pixel comparisons, committed-batch replay checks, format-conversion byte reservations and crash tests. If dataflow/resource use changes, rerun the benchmark with representative inputs; comments-only changes do not require new performance measurements. Regenerate `SHA256SUMS` when preparing a release archive after all final file changes.
|
|
101
|
+
|
|
102
|
+
The code and accompanying project documentation are under the [MIT license](LICENSE.md).
|
|
103
|
+
|
|
104
|
+
## Packaging and release entry points
|
|
105
|
+
|
|
106
|
+
[mbmerge.py](mbmerge.py) provides `python -m mbmerge` and `__version__`; the installed command points to the same `main()` in `merge_mbtiles.py`. The original script stays compatible. [pyproject.toml](pyproject.toml) explicitly packages these two modules, reads VERSION dynamically and reads Pillow requirements from requirements.txt.
|
|
107
|
+
|
|
108
|
+
[MANIFEST.in](MANIFEST.in) includes source tests/docs and only four named synthetic databases; it never glob-includes arbitrary MBTiles. [tools/check_release.py](tools/check_release.py) checks source/wheel/sdist identity and production tags. [tools/smoke_installed.py](tools/smoke_installed.py) deliberately imports from an installed wheel, with isolated Python and a temporary CLI working directory.
|
|
109
|
+
|
|
110
|
+
See [RELEASING.md](RELEASING.md) for the reusable CI workflow and the separate OIDC-only publishing jobs. Changing packaging or entry points requires rebuilding and exercising both the wheel and a wheel rebuilt from sdist; source tests alone cannot detect missing distribution files.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli_version": "1.1.0",
|
|
3
|
+
"benchmarks": [
|
|
4
|
+
{
|
|
5
|
+
"python": "3.12.14",
|
|
6
|
+
"sqlite": "3.53.1",
|
|
7
|
+
"platform": "macOS-27.2-arm64-arm-64bit",
|
|
8
|
+
"webp": "1.6.0",
|
|
9
|
+
"workers": 4,
|
|
10
|
+
"tile_size": 512,
|
|
11
|
+
"output_format": "webp",
|
|
12
|
+
"measurement": "sampled simultaneous sum of parent+descendant RSS; shared pages counted multiple times; excludes filesystem page cache",
|
|
13
|
+
"runs": [
|
|
14
|
+
{
|
|
15
|
+
"tiles_per_source": 64,
|
|
16
|
+
"contributions": 128,
|
|
17
|
+
"input_bytes": 101228544,
|
|
18
|
+
"output_bytes": 51412992,
|
|
19
|
+
"elapsed_seconds": 4.14,
|
|
20
|
+
"peak_tree_rss_mib": 423.67,
|
|
21
|
+
"max_processes": 6,
|
|
22
|
+
"pipeline": {
|
|
23
|
+
"event": "finished",
|
|
24
|
+
"seconds": 4.026,
|
|
25
|
+
"batches": 17,
|
|
26
|
+
"peak_inflight": 8,
|
|
27
|
+
"peak_batch_bytes": 33130860
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"tiles_per_source": 512,
|
|
32
|
+
"contributions": 1024,
|
|
33
|
+
"input_bytes": 809558016,
|
|
34
|
+
"output_bytes": 405577728,
|
|
35
|
+
"elapsed_seconds": 33.246,
|
|
36
|
+
"peak_tree_rss_mib": 390.14,
|
|
37
|
+
"max_processes": 6,
|
|
38
|
+
"pipeline": {
|
|
39
|
+
"event": "finished",
|
|
40
|
+
"seconds": 33.123,
|
|
41
|
+
"batches": 128,
|
|
42
|
+
"peak_inflight": 8,
|
|
43
|
+
"peak_batch_bytes": 33130860
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"python": "3.12.14",
|
|
50
|
+
"sqlite": "3.53.1",
|
|
51
|
+
"platform": "macOS-27.2-arm64-arm-64bit",
|
|
52
|
+
"webp": "1.6.0",
|
|
53
|
+
"workers": 4,
|
|
54
|
+
"tile_size": 512,
|
|
55
|
+
"output_format": "png",
|
|
56
|
+
"measurement": "sampled simultaneous sum of parent+descendant RSS; shared pages counted multiple times; excludes filesystem page cache",
|
|
57
|
+
"runs": [
|
|
58
|
+
{
|
|
59
|
+
"tiles_per_source": 64,
|
|
60
|
+
"contributions": 128,
|
|
61
|
+
"input_bytes": 101228544,
|
|
62
|
+
"output_bytes": 58437632,
|
|
63
|
+
"elapsed_seconds": 5.087,
|
|
64
|
+
"peak_tree_rss_mib": 313.25,
|
|
65
|
+
"max_processes": 6,
|
|
66
|
+
"pipeline": {
|
|
67
|
+
"event": "finished",
|
|
68
|
+
"seconds": 4.972,
|
|
69
|
+
"batches": 24,
|
|
70
|
+
"peak_inflight": 6,
|
|
71
|
+
"peak_batch_bytes": 29898804
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"tiles_per_source": 512,
|
|
76
|
+
"contributions": 1024,
|
|
77
|
+
"input_bytes": 809558016,
|
|
78
|
+
"output_bytes": 460922880,
|
|
79
|
+
"elapsed_seconds": 30.231,
|
|
80
|
+
"peak_tree_rss_mib": 321.27,
|
|
81
|
+
"max_processes": 6,
|
|
82
|
+
"pipeline": {
|
|
83
|
+
"event": "finished",
|
|
84
|
+
"seconds": 30.067,
|
|
85
|
+
"batches": 189,
|
|
86
|
+
"peak_inflight": 6,
|
|
87
|
+
"peak_batch_bytes": 29898804
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Licence MIT – český překlad
|
|
2
|
+
|
|
3
|
+
[English — závazné znění](LICENSE.md) | **Čeština**
|
|
4
|
+
|
|
5
|
+
Tento překlad slouží pro orientaci. Projekt je zveřejněn pod původním anglickým zněním licence MIT v [LICENSE.md](LICENSE.md).
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 mbmerge contributors
|
|
8
|
+
|
|
9
|
+
Tímto se bezplatně uděluje každému, kdo získá kopii tohoto softwaru a souvisejících dokumentačních souborů (dále jen „Software“), oprávnění nakládat se Softwarem bez omezení, včetně práv Software používat, kopírovat, upravovat, slučovat, zveřejňovat, distribuovat, poskytovat k němu podlicence a/nebo prodávat jeho kopie a umožnit totéž osobám, jimž je Software poskytnut, za následujících podmínek:
|
|
10
|
+
|
|
11
|
+
Výše uvedené oznámení o autorských právech a toto oznámení o udělení oprávnění musí být zahrnuty ve všech kopiích nebo podstatných částech Softwaru.
|
|
12
|
+
|
|
13
|
+
SOFTWARE JE POSKYTOVÁN „TAK, JAK JE“, BEZ JAKÝCHKOLI VÝSLOVNÝCH NEBO PŘEDPOKLÁDANÝCH ZÁRUK, ZEJMÉNA BEZ ZÁRUK PRODEJNOSTI, VHODNOSTI PRO URČITÝ ÚČEL A NEPORUŠOVÁNÍ PRÁV. AUTOŘI ANI DRŽITELÉ AUTORSKÝCH PRÁV V ŽÁDNÉM PŘÍPADĚ NEODPOVÍDAJÍ ZA ŽÁDNÉ NÁROKY, ŠKODY ČI JINOU ODPOVĚDNOST, AŤ JIŽ ZE SMLOUVY, Z OBČANSKOPRÁVNÍHO DELIKTU NEBO JINAK, VZNIKLÉ ZE SOFTWARU, V SOUVISLOSTI S NÍM NEBO S JEHO POUŽÍVÁNÍM ČI JINÝM NAKLÁDÁNÍM S NÍM.
|
mbmerge-1.1.0/LICENSE.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mbmerge contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
[Informative Czech translation](LICENSE.cs.md).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Source distributions carry reproducible tests/docs, but never arbitrary data.
|
|
2
|
+
include pyproject.toml requirements.txt .gitignore
|
|
3
|
+
include README*.md ARCHITECTURE*.md RELEASING*.md LICENSE*.md TEST_REPORT*.md
|
|
4
|
+
include TEST_RESULTS.txt BENCHMARK.json
|
|
5
|
+
recursive-include tests *.py
|
|
6
|
+
recursive-include examples *.py
|
|
7
|
+
recursive-include tools *.py
|
|
8
|
+
recursive-include docs/images *.png
|
|
9
|
+
recursive-include .github/workflows *.yml
|
|
10
|
+
# Only these named synthetic databases may enter a distribution.
|
|
11
|
+
include demo/source_a.mbtiles demo/source_b.mbtiles
|
|
12
|
+
include demo/png/source_a.mbtiles demo/png/source_b.mbtiles
|
|
13
|
+
include demo/a.png demo/b.png demo/expected_overlap.png
|
|
14
|
+
include demo/png/a.png demo/png/b.png demo/png/expected_overlap.png
|
|
15
|
+
# This checksum file describes the downloadable repository ZIP, not an sdist.
|
|
16
|
+
exclude SHA256SUMS
|
|
17
|
+
prune build
|
|
18
|
+
prune dist
|
|
19
|
+
prune work
|
|
20
|
+
prune .venv
|
|
21
|
+
prune .git
|
|
22
|
+
global-exclude *.py[cod] .DS_Store *.merge.lock *-wal *-shm *-journal
|