crs-zone-toolkit 0.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.
Files changed (93) hide show
  1. crs_zone_toolkit-0.1.0/.github/workflows/ci.yml +41 -0
  2. crs_zone_toolkit-0.1.0/.gitignore +25 -0
  3. crs_zone_toolkit-0.1.0/CITATION.cff +29 -0
  4. crs_zone_toolkit-0.1.0/LICENSE +21 -0
  5. crs_zone_toolkit-0.1.0/PKG-INFO +279 -0
  6. crs_zone_toolkit-0.1.0/QUICKSTART.md +96 -0
  7. crs_zone_toolkit-0.1.0/README.md +245 -0
  8. crs_zone_toolkit-0.1.0/docs/ARCHITECTURE.md +121 -0
  9. crs_zone_toolkit-0.1.0/docs/CLI_UX.md +267 -0
  10. crs_zone_toolkit-0.1.0/docs/DATA_REFERENCE.md +158 -0
  11. crs_zone_toolkit-0.1.0/docs/SPEC.md +166 -0
  12. crs_zone_toolkit-0.1.0/docs/TEST_PLAN.md +82 -0
  13. crs_zone_toolkit-0.1.0/docs/calibrage/2026-07-19-calibrage-seuils.md +196 -0
  14. crs_zone_toolkit-0.1.0/docs/feuille_de_route.md +100 -0
  15. crs_zone_toolkit-0.1.0/docs/references.md +102 -0
  16. crs_zone_toolkit-0.1.0/pyproject.toml +147 -0
  17. crs_zone_toolkit-0.1.0/scripts/README.md +117 -0
  18. crs_zone_toolkit-0.1.0/scripts/publier_release.py +174 -0
  19. crs_zone_toolkit-0.1.0/scripts/regenerer_demos.py +583 -0
  20. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/__init__.py +178 -0
  21. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/affichage.py +220 -0
  22. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/cli.py +385 -0
  23. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/__init__.py +5 -0
  24. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/analysis.py +544 -0
  25. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/apply.py +315 -0
  26. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/decoupage.py +104 -0
  27. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/errors.py +46 -0
  28. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/gridgen.py +63 -0
  29. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/messages.py +762 -0
  30. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/profile.py +67 -0
  31. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/report.py +291 -0
  32. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/results.py +161 -0
  33. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/core/targets.py +47 -0
  34. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/py.typed +0 -0
  35. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/regions/__init__.py +4 -0
  36. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/regions/loader.py +256 -0
  37. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/regions/qc/grille_mtm_qc.geojson +16 -0
  38. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/regions/qc/limite_qc.geojson +8 -0
  39. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/regions/qc/profil.toml +135 -0
  40. crs_zone_toolkit-0.1.0/src/crs_zone_toolkit/templates/rapport.html.j2 +302 -0
  41. crs_zone_toolkit-0.1.0/tests/conftest.py +327 -0
  42. crs_zone_toolkit-0.1.0/tests/schemas/analyse_v1.schema.json +67 -0
  43. crs_zone_toolkit-0.1.0/tests/test_affichage_datum.py +98 -0
  44. crs_zone_toolkit-0.1.0/tests/test_affichage_hors_profil.py +153 -0
  45. crs_zone_toolkit-0.1.0/tests/test_affichage_maquette.py +192 -0
  46. crs_zone_toolkit-0.1.0/tests/test_analysis.py +215 -0
  47. crs_zone_toolkit-0.1.0/tests/test_analysis_alternative_split.py +145 -0
  48. crs_zone_toolkit-0.1.0/tests/test_analysis_cibles.py +36 -0
  49. crs_zone_toolkit-0.1.0/tests/test_analysis_distortion.py +20 -0
  50. crs_zone_toolkit-0.1.0/tests/test_analysis_identify.py +60 -0
  51. crs_zone_toolkit-0.1.0/tests/test_analysis_libelle_candidat.py +100 -0
  52. crs_zone_toolkit-0.1.0/tests/test_analysis_prepare.py +36 -0
  53. crs_zone_toolkit-0.1.0/tests/test_analysis_repartition.py +80 -0
  54. crs_zone_toolkit-0.1.0/tests/test_analysis_sampling.py +229 -0
  55. crs_zone_toolkit-0.1.0/tests/test_analyze_public.py +32 -0
  56. crs_zone_toolkit-0.1.0/tests/test_apply.py +259 -0
  57. crs_zone_toolkit-0.1.0/tests/test_apply_garde_datum.py +65 -0
  58. crs_zone_toolkit-0.1.0/tests/test_apply_journal.py +47 -0
  59. crs_zone_toolkit-0.1.0/tests/test_apply_majority.py +35 -0
  60. crs_zone_toolkit-0.1.0/tests/test_apply_public.py +41 -0
  61. crs_zone_toolkit-0.1.0/tests/test_apply_rapport.py +147 -0
  62. crs_zone_toolkit-0.1.0/tests/test_apply_reproject.py +35 -0
  63. crs_zone_toolkit-0.1.0/tests/test_apply_resolve.py +33 -0
  64. crs_zone_toolkit-0.1.0/tests/test_apply_tracabilite.py +175 -0
  65. crs_zone_toolkit-0.1.0/tests/test_apply_write.py +34 -0
  66. crs_zone_toolkit-0.1.0/tests/test_cli_analyze.py +111 -0
  67. crs_zone_toolkit-0.1.0/tests/test_cli_apply.py +207 -0
  68. crs_zone_toolkit-0.1.0/tests/test_cli_encodage.py +74 -0
  69. crs_zone_toolkit-0.1.0/tests/test_cli_grid.py +61 -0
  70. crs_zone_toolkit-0.1.0/tests/test_composition.py +70 -0
  71. crs_zone_toolkit-0.1.0/tests/test_decide_decoupage_utile.py +144 -0
  72. crs_zone_toolkit-0.1.0/tests/test_demarrage.py +29 -0
  73. crs_zone_toolkit-0.1.0/tests/test_errors.py +27 -0
  74. crs_zone_toolkit-0.1.0/tests/test_gridgen.py +94 -0
  75. crs_zone_toolkit-0.1.0/tests/test_json_schema.py +43 -0
  76. crs_zone_toolkit-0.1.0/tests/test_loader.py +169 -0
  77. crs_zone_toolkit-0.1.0/tests/test_messages.py +168 -0
  78. crs_zone_toolkit-0.1.0/tests/test_messages_restitution.py +296 -0
  79. crs_zone_toolkit-0.1.0/tests/test_no_hardcoded_epsg.py +84 -0
  80. crs_zone_toolkit-0.1.0/tests/test_readme_publie.py +93 -0
  81. crs_zone_toolkit-0.1.0/tests/test_report_carte.py +32 -0
  82. crs_zone_toolkit-0.1.0/tests/test_report_commande_repro.py +124 -0
  83. crs_zone_toolkit-0.1.0/tests/test_report_echelle.py +65 -0
  84. crs_zone_toolkit-0.1.0/tests/test_report_lisibilite.py +86 -0
  85. crs_zone_toolkit-0.1.0/tests/test_report_public.py +39 -0
  86. crs_zone_toolkit-0.1.0/tests/test_report_render.py +180 -0
  87. crs_zone_toolkit-0.1.0/tests/test_report_write.py +53 -0
  88. crs_zone_toolkit-0.1.0/tests/test_results.py +97 -0
  89. crs_zone_toolkit-0.1.0/tests/test_scripts_demos.py +294 -0
  90. crs_zone_toolkit-0.1.0/tests/test_scripts_publier.py +353 -0
  91. crs_zone_toolkit-0.1.0/tests/test_smoke.py +12 -0
  92. crs_zone_toolkit-0.1.0/tests/test_targets.py +81 -0
  93. crs_zone_toolkit-0.1.0/uv.lock +1800 -0
@@ -0,0 +1,41 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ os: [ubuntu-latest, windows-latest]
14
+ python-version: ["3.11", "3.12", "3.13"]
15
+ runs-on: ${{ matrix.os }}
16
+ env:
17
+ # setup-uv n'a pas d'entrée python-version fiable selon la version ;
18
+ # UV_PYTHON force uv à créer le venv et à exécuter avec CETTE version
19
+ # (téléchargée au besoin) — la matrice teste donc réellement 3.11/3.12/3.13.
20
+ UV_PYTHON: ${{ matrix.python-version }}
21
+ steps:
22
+ - uses: actions/checkout@v5
23
+
24
+ - name: Installer uv
25
+ uses: astral-sh/setup-uv@v6
26
+ with:
27
+ enable-cache: true
28
+
29
+ - name: Installer les dépendances
30
+ run: uv sync --group dev
31
+
32
+ - name: Lint (ruff)
33
+ run: |
34
+ uv run ruff check .
35
+ uv run ruff format --check .
36
+
37
+ - name: Types (mypy)
38
+ run: uv run mypy
39
+
40
+ - name: Tests (pytest)
41
+ run: uv run pytest
@@ -0,0 +1,25 @@
1
+ # Environnements et caches Python
2
+ .venv/
3
+ __pycache__/
4
+ *.py[cod]
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .mypy_cache/
8
+ .coverage
9
+ htmlcov/
10
+
11
+ # Build / distribution
12
+ dist/
13
+ build/
14
+ *.egg-info/
15
+
16
+ # Sorties de l'outil pendant les essais
17
+ sorties/
18
+ *_analyse_crs*.html
19
+ *_journal.json
20
+
21
+ # IDE / OS
22
+ .vscode/
23
+ .idea/
24
+ Thumbs.db
25
+ .DS_Store
@@ -0,0 +1,29 @@
1
+ cff-version: 1.2.0
2
+ message: "Si vous utilisez cet outil dans un travail ou une publication, merci de le citer ainsi."
3
+ type: software
4
+ title: "crs-zone-toolkit : analyse, recommandation et reprojection CRS pour les couches géospatiales du Québec"
5
+ authors:
6
+ - family-names: Moussahoudou
7
+ given-names: Issa
8
+ email: imoussahoudou@gmail.com
9
+ version: 0.1.0
10
+ date-released: "2026-08-02"
11
+ license: MIT
12
+ repository-code: "https://github.com/MascotteIssa/crs-zone-toolkit"
13
+ identifiers:
14
+ - type: doi
15
+ value: 10.5281/zenodo.21956685
16
+ keywords:
17
+ - gis
18
+ - crs
19
+ - epsg
20
+ - mtm
21
+ - quebec
22
+ - reprojection
23
+ - geopandas
24
+ abstract: >-
25
+ Outil en ligne de commande (crszone) qui analyse une couche géospatiale,
26
+ mesure la distorsion des projections candidates du profil régional (fuseaux
27
+ MTM et Québec Lambert pour le Québec), recommande la projection la moins
28
+ déformée avec sa justification chiffrée, et applique la reprojection ou le
29
+ découpage par fuseau avec un journal de décision traçable.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Issa Moussahoudou
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.
@@ -0,0 +1,279 @@
1
+ Metadata-Version: 2.5
2
+ Name: crs-zone-toolkit
3
+ Version: 0.1.0
4
+ Summary: Analyse, recommandation et reprojection CRS pour les couches géospatiales du Québec (fuseaux MTM, Québec Lambert)
5
+ Project-URL: Homepage, https://github.com/MascotteIssa/crs-zone-toolkit
6
+ Project-URL: Repository, https://github.com/MascotteIssa/crs-zone-toolkit
7
+ Project-URL: Documentation, https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/QUICKSTART.md
8
+ Project-URL: Issues, https://github.com/MascotteIssa/crs-zone-toolkit/issues
9
+ Author-email: Issa Moussahoudou <imoussahoudou@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: crs,epsg,geopandas,gis,mtm,quebec,reprojection
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Natural Language :: French
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: GIS
22
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: geopandas>=1.0
26
+ Requires-Dist: jinja2>=3.1
27
+ Requires-Dist: matplotlib>=3.8
28
+ Requires-Dist: pyogrio>=0.8
29
+ Requires-Dist: pyproj>=3.6
30
+ Requires-Dist: rich>=13.0
31
+ Requires-Dist: shapely>=2.0
32
+ Requires-Dist: typer>=0.12
33
+ Description-Content-Type: text/markdown
34
+
35
+ # crs-zone-toolkit
36
+
37
+ [![CI](https://github.com/MascotteIssa/crs-zone-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/MascotteIssa/crs-zone-toolkit/actions/workflows/ci.yml)
38
+ [![PyPI](https://img.shields.io/pypi/v/crs-zone-toolkit.svg)](https://pypi.org/project/crs-zone-toolkit/)
39
+ [![Python](https://img.shields.io/pypi/pyversions/crs-zone-toolkit.svg)](https://pypi.org/project/crs-zone-toolkit/)
40
+ [![Licence MIT](https://img.shields.io/badge/licence-MIT-blue.svg)](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/LICENSE)
41
+ [![DOI](https://zenodo.org/badge/1319874815.svg)](https://doi.org/10.5281/zenodo.21956685)
42
+
43
+ **Quel système de coordonnées pour ma couche québécoise ?** `crszone` répond avec des
44
+ chiffres : il mesure la distorsion réellement encourue par chaque candidat, **recommande**
45
+ la projection la moins déformante — fuseau **MTM** ou **Québec Lambert** — et **reprojette**
46
+ si vous le lui demandez. L'outil recommande ; vous décidez.
47
+
48
+ ![Démonstration : analyse d'une couche des 21 régions administratives du Québec, de la ligne de commande au rapport HTML](https://raw.githubusercontent.com/MascotteIssa/crs-zone-toolkit/main/docs/images/demo.gif)
49
+
50
+ ## Pourquoi pas simplement `estimate_utm_crs()` ?
51
+
52
+ L'utilitaire de GeoPandas ne connaît que l'**UTM**. Il ignore les fuseaux **MTM** du Québec,
53
+ larges de 3° au lieu de 6° — et comme la distorsion croît avec le *carré* de l'écart au
54
+ méridien central, cette moitié de largeur vaut **quatre fois moins de distorsion** : aux
55
+ latitudes habitées du Québec, un fuseau MTM tient dans −100 à +72 ppm là où l'UTM s'étale
56
+ de −400 à +287 ppm. Il ignore aussi le **Québec Lambert**, et les familles de datum
57
+ canadiennes — NAD83 d'origine, NAD83(CSRS), NAD27 — qu'il ne faut jamais franchir en
58
+ silence. Il ne mesure aucune distorsion, ne justifie rien, et ne produit ni rapport, ni
59
+ découpage multi-fuseaux, ni journal de décision.
60
+
61
+ `crszone` fait exactement cela, et rien d'autre.
62
+
63
+ ## Installation
64
+
65
+ ```bash
66
+ pip install crs-zone-toolkit
67
+ ```
68
+
69
+ ```bash
70
+ uv tool install crs-zone-toolkit # commande isolée, disponible partout
71
+ uvx crs-zone-toolkit analyze ma_couche.gpkg # sans rien installer
72
+ ```
73
+
74
+ Python 3.11 ou plus. Aucune donnée de référence à télécharger : la grille des fuseaux, la
75
+ limite du Québec et le profil géodésique voyagent **dans le paquet**.
76
+
77
+ ## En trente secondes
78
+
79
+ <!-- extrait:debut -->
80
+ ```console
81
+ $ crszone --region qc analyze regio_s.shp
82
+ Analyse CRS : profil Québec (qc) crszone 0.1.0
83
+ ───────────────────────────────────────────────────────────────────────────────────────────────────
84
+ Couche regio_s.shp (21 entités, polygones)
85
+ CRS déclaré EPSG:4269, NAD83 (géographique)
86
+ Emprise 79,77°O → 56,93°O · 44,99°N → 62,58°N
87
+
88
+ Répartition par fuseau MTM (part de la surface totale)
89
+ Fuseau 8 (MC −73,5°) ████ 22,2 %
90
+ Fuseau 9 (MC −76,5°) ████ 19,9 %
91
+ Fuseau 7 (MC −70,5°) ████ 19,3 %
92
+ Fuseau 6 (MC −67,5°) ███ 12,8 %
93
+ Fuseau 5 (MC −64,5°) ██ 10,0 %
94
+ Fuseau 4 (MC −61,5°) █ 7,4 %
95
+ Fuseau 10 (MC −79,5°) █ 5,4 %
96
+ Fuseau 3 (MC −58,5°) █ 3,0 %
97
+ Fuseau 2 (MC −56,0°) 0,0 %
98
+
99
+ Distorsion mesurée (187 points d'échantillonnage)
100
+ Candidat min moy max
101
+ MTM fuseau 8 (tout) EPSG:32188 −100 ppm +1564 ppm +14784 ppm ⚠ hors seuil
102
+ Québec Lambert EPSG:32198 −7458 ppm −2814 ppm +2149 ppm ⚠ hors seuil
103
+
104
+ → Recommandation : reprojeter vers Québec Lambert (EPSG:32198, NAD83 d'origine)
105
+ Motif : Les données sont trop étendues pour le fuseau dominant (MTM 8 : 14784 ppm) : le Québec
106
+ Lambert est la projection unique la moins déformée (7458 ppm max). Découpage disponible en
107
+ alternative.
108
+ ✓ Datum : entrée NAD83 d'origine → famille préservée (EPSG:32198).
109
+ Note : NAD83(CSRS) est le standard actuel des données québécoises (écart ≈ 1 m).
110
+
111
+ Alternative : découpage par fuseau (6 sorties, entités affectées au fuseau majoritaire).
112
+
113
+ ✓ Rapport détaillé : regio_s_analyse_crs_20260728-130842.html
114
+ Pour appliquer : crszone apply regio_s.shp
115
+ ```
116
+ <!-- extrait:fin -->
117
+
118
+ Puis, quand vous êtes d'accord :
119
+
120
+ <!-- apply:debut -->
121
+ ```console
122
+ $ crszone apply montreal.gpkg --out sorties --auto
123
+ Analyse CRS : profil Québec (qc) crszone 0.1.0
124
+ ───────────────────────────────────────────────────────────────────────────────────────────────────
125
+ Couche montreal.gpkg (1 entité, polygones)
126
+ CRS déclaré EPSG:4269, NAD83 (géographique)
127
+ Emprise 74,00°O → 73,47°O · 45,39°N → 45,71°N
128
+
129
+ → Recommandation : reprojeter vers MTM fuseau 8 (EPSG:32188, NAD83 d'origine)
130
+ Motif : Les données tiennent dans un seul fuseau (MTM 8).
131
+ ✓ Datum : entrée NAD83 d'origine → famille préservée (EPSG:32188).
132
+ Note : NAD83(CSRS) est le standard actuel des données québécoises (écart ≈ 1 m).
133
+
134
+ Mode --auto : application de la recommandation (MTM fuseau 8, EPSG:32188).
135
+ ✓ Sortie : sorties\montreal_epsg32188.gpkg (EPSG:32188, 1 entité)
136
+ ✓ Journal : sorties\montreal_journal.json
137
+ Pipeline PROJ : axis order change (2D) + MTM zone 8
138
+ ```
139
+ <!-- apply:fin -->
140
+
141
+ Le **pipeline PROJ exact** est affiché puis journalisé : la transformation appliquée est
142
+ vérifiable, pas devinée. Démarrage complet : **[QUICKSTART.md](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/QUICKSTART.md)**.
143
+
144
+ ## La règle de recommandation, en deux lignes
145
+
146
+ > **Un seul fuseau traversé → ce fuseau.** Plusieurs fuseaux → la **projection unique la
147
+ > moins déformée** entre le fuseau dominant et le Québec Lambert (le fuseau l'emporte à
148
+ > égalité). Le **découpage** par fuseau est toujours offert en alternative, jamais imposé.
149
+
150
+ Cette règle n'est pas une intuition : elle a été **calibrée sur données réelles**, le
151
+ jugement d'expertise servant d'étalon. La règle précédente gatait sur la part du fuseau
152
+ dominant *avant* de regarder la distorsion, et recommandait de ce fait la projection **la
153
+ plus déformée** pour les régions compactes — le Bas-Saint-Laurent mesure 407 ppm en MTM 6
154
+ contre 5106 ppm en Québec Lambert. Méthodologie, balayage et décision :
155
+ [`docs/calibrage/`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/calibrage/2026-07-19-calibrage-seuils.md) · formalisation :
156
+ [SPEC §4.3](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/SPEC.md).
157
+
158
+ ## Le rapport HTML
159
+
160
+ Chaque `analyze` écrit un rapport **autonome** : un seul fichier, aucune ressource externe,
161
+ carte et styles embarqués. Il s'ouvre hors ligne, s'archive, se joint à un courriel — et
162
+ suit le thème clair ou sombre de votre système, avec un bouton pour basculer.
163
+
164
+ <picture>
165
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/MascotteIssa/crs-zone-toolkit/main/docs/images/rapport-sombre.png">
166
+ <img alt="Rapport HTML : verdict en tête, couche analysée, situation dans les fuseaux MTM" src="https://raw.githubusercontent.com/MascotteIssa/crs-zone-toolkit/main/docs/images/rapport-clair.png">
167
+ </picture>
168
+
169
+ L'élément central est l'échelle de distorsion, **divergente et centrée sur 0 ppm**, qui
170
+ rend le compromis visible d'un coup d'œil — ici, la couche des 21 régions administratives,
171
+ trop étendue pour tout fuseau MTM. La cible sort en `EPSG:32198` parce que cette donnée est
172
+ en **NAD83 d'origine** : la famille d'entrée est préservée. Sur une entrée en NAD83(CSRS),
173
+ le standard actuel, le Québec Lambert visé serait `EPSG:6622`.
174
+
175
+ <picture>
176
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/MascotteIssa/crs-zone-toolkit/main/docs/images/rapport-distorsion-sombre.png">
177
+ <img alt="Échelle de distorsion divergente comparant MTM fuseau 8 et Québec Lambert" src="https://raw.githubusercontent.com/MascotteIssa/crs-zone-toolkit/main/docs/images/rapport-distorsion-clair.png">
178
+ </picture>
179
+
180
+ **[Ouvrir un rapport réel](https://raw.githubusercontent.com/MascotteIssa/crs-zone-toolkit/main/docs/exemple_rapport.html)** *(clic droit → enregistrer, puis
181
+ ouvrir dans un navigateur — GitHub n'exécute pas le HTML des dépôts).*
182
+
183
+ ## Pour un script
184
+
185
+ `--json` écrit le résultat sur la sortie standard, le résumé humain sur l'erreur standard :
186
+
187
+ ```console
188
+ $ crszone analyze montreal.gpkg --json | jq .recommandation
189
+ {
190
+ "action": "zone",
191
+ "cible_epsg": 32188,
192
+ "cible_libelle": "MTM fuseau 8",
193
+ "motif_code": "mono_zone",
194
+ "motif": "Les données tiennent dans un seul fuseau (MTM 8).",
195
+ "alternatives": []
196
+ }
197
+ ```
198
+
199
+ Le contrat est **versionné** (`schema_version`) et validé par un schéma JSON Schema dans la
200
+ suite de tests. L'API Python (`analyze`, `apply`, `report`) est typée et livre son marqueur
201
+ [PEP 561](https://peps.python.org/pep-0561/).
202
+
203
+ Codes de sortie : `0` succès · `1` erreur de données · `2` refus explicite (CRS absent,
204
+ sortie existante, non interactif sans choix).
205
+
206
+ > **Windows / PowerShell — préférez `--json-out` à `>`.** La redirection `>` de PowerShell
207
+ > ne transmet pas les octets du programme : elle les **décode** d'abord avec
208
+ > `[Console]::OutputEncoding`, puis les **réécrit** dans son propre encodage. Sur une console
209
+ > française laissée en cp850/cp1252, l'UTF-8 émis par `crszone` est mal décodé (`donn├®es`) —
210
+ > et PowerShell 5.1 écrit ensuite le fichier en **UTF-16LE**, pas en UTF-8. Le résultat est
211
+ > illisible pour la plupart des outils.
212
+ >
213
+ > **L'outil n'y est pour rien** : il émet bien de l'UTF-8 sur ses flux (DT-15), et le tuyau
214
+ > (`|`) passe sans dommage. Le remède tient en un mot : laissez `crszone` écrire le fichier
215
+ > lui-même, avec **`--json-out`** pour le JSON et **`--report`** pour le rapport HTML.
216
+ >
217
+ > Si vous tenez à rediriger, corrigez d'abord le décodage —
218
+ > `[Console]::OutputEncoding = [Text.Encoding]::UTF8` — puis écrivez avec
219
+ > `| Out-File -Encoding utf8`. *(Vérifié dans les deux sens : console en cp850 → fichier
220
+ > corrompu ; console en UTF-8 → fichier valide.)*
221
+
222
+
223
+ ## Ce que l'outil ne fait pas
224
+
225
+ Il ne change **jamais** de famille de datum en silence : une transformation approximative
226
+ est soit refusée, soit exécutée avec un avertissement journalisé (NAD27 exige la grille
227
+ NTv2). Il ne recommande rien pour les données tombant hors du Québec — il le signale. Il
228
+ n'écrase aucun fichier sans `--overwrite`. Il ne produit ni PDF, ni sortie texte, et
229
+ n'accède **à aucun réseau**, ni à l'exécution ni dans ses tests.
230
+
231
+ Il n'est pas conçu pour les très gros volumes : le traitement est **en mémoire** et l'outil
232
+ vise des couches de l'ordre de 10⁴ à 10⁵ entités (12 580 lignes s'analysent en 6,1 s).
233
+ L'échantillonnage de distorsion est plafonné, mais la répartition par fuseau et le
234
+ découpage croissent avec le nombre d'entités : au-delà, prévoyez de découper la couche en
235
+ amont.
236
+
237
+ Le périmètre V1 est le Québec. Le noyau ne connaît aucun code EPSG : tous les faits
238
+ géodésiques viennent d'un **profil de région** (`regions/qc/`), ce que verrouille un test
239
+ dédié. C'est ce qui rendra possible l'extension au reste du Canada, puis ailleurs
240
+ ([feuille de route](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/feuille_de_route.md)).
241
+
242
+ ## Documentation
243
+
244
+ | Document | Rôle |
245
+ |---|---|
246
+ | [`QUICKSTART.md`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/QUICKSTART.md) | Les trois commandes, options utiles, garde-fous |
247
+ | [`docs/SPEC.md`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/SPEC.md) | Cahier des charges fonctionnel V1 |
248
+ | [`docs/DATA_REFERENCE.md`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/DATA_REFERENCE.md) | Source de vérité géodésique (codes EPSG vérifiés) |
249
+ | [`docs/calibrage/`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/calibrage/2026-07-19-calibrage-seuils.md) | Calibrage de la règle de décision sur données réelles |
250
+ | [`docs/ARCHITECTURE.md`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/ARCHITECTURE.md) | Noyau + adaptateurs, lois de dépendance, API |
251
+ | [`docs/CLI_UX.md`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/CLI_UX.md) | Maquette du flux terminal |
252
+ | [`docs/TEST_PLAN.md`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/TEST_PLAN.md) | Jeux de test et protocole de calibrage |
253
+ | [`docs/feuille_de_route.md`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/feuille_de_route.md) | Évolutions (Québec → Canada → international) |
254
+ | [`docs/references.md`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/docs/references.md) | Bibliographie (APA 7) — toute décision est sourcée |
255
+
256
+ Les sigles `DT-xx` et `N-xx` cités au fil de ces pages sont les identifiants du registre de
257
+ dette technique et des observations du test manuel, tenus au dépôt de développement et non
258
+ publiés.
259
+
260
+ ## Attributions
261
+
262
+ - **Codes EPSG** : registre EPSG (IOGP) via epsg.org / epsg.io ; usage québécois d'après
263
+ MERN/MRNF, *Codes EPSG des projections utilisées au Québec*, décembre 2020.
264
+ - **Grille des fuseaux et limite du Québec** : découpées d'après MRNF, *Découpages
265
+ administratifs*, Données Québec, licence **CC-BY 4.0**. Les captures et le rapport
266
+ d'exemple de ce README sont dérivés du même jeu.
267
+
268
+ ## English summary
269
+
270
+ `crs-zone-toolkit` helps you choose a metric projection before spatial analysis in
271
+ Québec, Canada: it reports which MTM zones your data spans, measures the actual
272
+ distortion of each candidate CRS with `pyproj`, recommends the least-distorted single
273
+ projection, and reprojects or splits your layer with a full decision log. Unlike a
274
+ plain UTM guess, every recommendation comes with measured evidence and a self-contained
275
+ HTML report. **The command line, reports and documentation are in French.**
276
+
277
+ ## Licence
278
+
279
+ MIT — voir [`LICENSE`](https://github.com/MascotteIssa/crs-zone-toolkit/blob/main/LICENSE). © 2026 Issa Moussahoudou.
@@ -0,0 +1,96 @@
1
+ # crszone — démarrage rapide
2
+
3
+ `crszone` analyse une couche vectorielle, **recommande** un système de coordonnées
4
+ adapté au Québec (fuseau MTM ou Québec Lambert), et **reprojette** sur demande.
5
+ L'outil recommande ; **vous décidez** (rien n'est reprojeté sans votre accord).
6
+
7
+ > Exemples préfixés `uv run` (environnement de développement). Après installation du
8
+ > paquet, la commande s'appelle directement `crszone`.
9
+
10
+ ## Les 3 commandes
11
+
12
+ ### 1. Analyser (lecture seule) — que faire de ma couche ?
13
+
14
+ ```bash
15
+ uv run crszone analyze chemin/vers/couche.gpkg
16
+ ```
17
+
18
+ Affiche : CRS déclaré, emprise, répartition par fuseau MTM, distorsion des candidats,
19
+ et une **recommandation chiffrée**. Écrit aussi un rapport HTML autonome à côté de la couche.
20
+
21
+ Options utiles :
22
+
23
+ ```bash
24
+ # CRS non déclaré par le fichier ? Assignez-le (n'altère pas les coordonnées) :
25
+ uv run crszone analyze couche.shp --assume-crs EPSG:4269
26
+
27
+ # Rapport HTML dans un dossier précis :
28
+ uv run crszone analyze couche.gpkg --report dossier/rapports
29
+
30
+ # Sortie JSON pure (pour un script) — le résumé humain part sur stderr :
31
+ uv run crszone analyze couche.gpkg --json > resultat.json
32
+ uv run crszone analyze couche.gpkg --json-out resultat.json # écrit en UTF-8 garanti
33
+ ```
34
+
35
+ ### 2. Appliquer — reprojeter ou découper
36
+
37
+ ```bash
38
+ # Interactif : l'outil propose, vous choisissez au menu
39
+ uv run crszone apply couche.gpkg --out dossier/sorties
40
+
41
+ # Non interactif : appliquer directement la recommandation
42
+ uv run crszone apply couche.gpkg --out dossier/sorties --auto
43
+
44
+ # Forcer un choix précis :
45
+ uv run crszone apply couche.gpkg --choice lambert # Québec Lambert
46
+ uv run crszone apply couche.gpkg --choice zone --zone 8 # fuseau MTM 8
47
+ uv run crszone apply couche.gpkg --choice split # un fichier par fuseau
48
+ ```
49
+
50
+ Chaque exécution écrit la ou les couches reprojetées **plus** un journal
51
+ `<nom>_journal.json` (pipeline PROJ exact appliqué, décision, avertissements).
52
+
53
+ Options : `--format gpkg|geojson|shp` · `--overwrite` (écraser une sortie existante) ·
54
+ `--assume-crs EPSG:xxxx` · `--json` / `--json-out`.
55
+
56
+ ### 3. Générer la grille des fuseaux MTM (repère visuel)
57
+
58
+ ```bash
59
+ uv run crszone grid --out grille_qc.geojson
60
+ ```
61
+
62
+ ## Lire la recommandation
63
+
64
+ L'outil recommande **la projection unique la moins déformée** entre le fuseau dominant
65
+ et le Québec Lambert (règle « distorsion d'abord », SPEC §4.3) :
66
+
67
+ | Motif | Sens | Que faire |
68
+ |---|---|---|
69
+ | `mono_zone` | données dans un seul fuseau | prenez ce fuseau MTM |
70
+ | `zone_dominante` | fuseau dominant, distorsion sous tolérance | prenez ce fuseau MTM (1 fichier) |
71
+ | `zone_moins_deformee` | fuseau dominant = le moins déformé, mais au-delà de la tolérance | fuseau MTM (1 fichier) **ou** découpez pour rester sous le seuil |
72
+ | `lambert_moins_deforme` | données trop étendues (province, grand nord) | prenez le Québec Lambert |
73
+
74
+ Le **découpage** est toujours proposé en alternative (un fichier par fuseau, distorsion
75
+ minimale) — utile si vous traiterez chaque morceau séparément, moins pratique pour garder
76
+ un fichier unique.
77
+
78
+ ## Garde-fous à connaître
79
+
80
+ - **Pas de CRS déclaré** → l'outil refuse (sortie 2) et explique : relancez avec
81
+ `--assume-crs EPSG:xxxx` (assigne une étiquette, ne convertit rien).
82
+ - **Sortie déjà existante** → `apply` refuse d'écraser sans `--overwrite`.
83
+ - **Familles de datum préservées** : entrée NAD83 d'origine → cibles NAD83 ; entrée
84
+ WGS84 ou CRS non qualifié → CSRS par défaut ; jamais de changement de datum silencieux.
85
+ - **Windows / PowerShell** : n'utilisez pas `> fichier.json`. PowerShell décode la sortie
86
+ avec `[Console]::OutputEncoding` (souvent cp850 en français) puis la réécrit en UTF-16LE :
87
+ le fichier ressort en charabia. Utilisez **`--json-out`** et **`--report`**, qui font écrire
88
+ le fichier par l'outil lui-même. Le tuyau (`|`), lui, passe sans dommage.
89
+
90
+
91
+ ## Codes de sortie
92
+
93
+ `0` succès · `1` erreur de données (fichier illisible, couche vide, région inconnue) ·
94
+ `2` refus explicite (CRS absent, sortie existante, non-interactif sans choix).
95
+
96
+ Aide complète : `uv run crszone --help`, `uv run crszone analyze --help`, etc.