{"_id":"@ban-team/formatter-bal","_rev":"4-c3133f403818ac66d1263c55d78d4728","name":"@ban-team/formatter-bal","dist-tags":{"latest":"0.1.4"},"versions":{"0.1.1":{"name":"@ban-team/formatter-bal","version":"0.1.1","keywords":[],"author":"","license":"MIT","_id":"@ban-team/formatter-bal@0.1.1","maintainers":[{"name":"tmerlier","email":"theophile.merliere@gmail.com"},{"name":"jdesboeufs","email":"jerome.desboeufs@gmail.com"},{"name":"mortier.melanie","email":"melanie.mortier@ign.fr"},{"name":"nkokla","email":"n.kokla@koaji.fr"},{"name":"vsagniez","email":"vincent.sagniez@ign.fr"},{"name":"magos92","email":"guillaume_fay@hotmail.com"},{"name":"fufeck","email":"fabien.tafforeau@gmail.com"}],"dist":{"shasum":"1ebc83a99a0d21f6e72e6e9a73747f44f98a2a68","tarball":"https://registry.npmjs.org/@ban-team/formatter-bal/-/formatter-bal-0.1.1.tgz","fileCount":50,"integrity":"sha512-BsqvtBY5x1d0ypjdQJMpComa+oy5yYcc7HQNxXcQ75YUmmNbZl9gI2tjY7m6SSdFuy9p6/bzKjUnOjeWWM+w1w==","signatures":[{"sig":"MEUCIQDCVRizin5QiR8CD5VKQHtFMCuS3Dw0fiD8qu+xNTZjOAIgaBKw78BSXSWjMQ6U5dJaBxRdy3LoFLEauSufwLgEHN4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":100606},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"3e3b4fd5b43c3d0b8908a49b18a1d8bdd58ebe65","scripts":{"test":"jest","build":"rm -rf dist && npx tsc --project tsconfig.build.json","start":"node dist/index.js"},"_npmUser":{"name":"fufeck","email":"fabien.tafforeau@gmail.com"},"_npmVersion":"11.6.2","description":"Permet de formatter les fichier BAL, avant leurs publication sur l'api-depot","directories":{},"_nodeVersion":"24.13.0","dependencies":{"yargs":"^17.5.1","chardet":"^1.4.0","date-fns":"^4.1.0","file-type":"^12.4.2","papaparse":"^5.3.2","iconv-lite":"^0.6.3","@ban-team/adresses-util":"^0.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","tsup":"^8.0.0","ts-jest":"^29.4.6","fs-extra":"^11.3.4","typescript":"^5.0.0","@types/jest":"^30.0.0","@types/fs-extra":"^11.0.4","@types/papaparse":"^5.3.15","@types/iconv-lite":"^0.0.1"},"_npmOperationalInternal":{"tmp":"tmp/formatter-bal_0.1.1_1778661420337_0.48612662667486184","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@ban-team/formatter-bal","version":"0.1.2","keywords":[],"author":"","license":"MIT","_id":"@ban-team/formatter-bal@0.1.2","maintainers":[{"name":"tmerlier","email":"theophile.merliere@gmail.com"},{"name":"jdesboeufs","email":"jerome.desboeufs@gmail.com"},{"name":"mortier.melanie","email":"melanie.mortier@ign.fr"},{"name":"nkokla","email":"n.kokla@koaji.fr"},{"name":"vsagniez","email":"vincent.sagniez@ign.fr"},{"name":"magos92","email":"guillaume_fay@hotmail.com"},{"name":"fufeck","email":"fabien.tafforeau@gmail.com"}],"dist":{"shasum":"aad1c6b5644f8bb36e97dafc5f7d6f5a258338ed","tarball":"https://registry.npmjs.org/@ban-team/formatter-bal/-/formatter-bal-0.1.2.tgz","fileCount":50,"integrity":"sha512-PZyFpj13kaaFS2LrVsIKjkob42mQC/jeK/uEMyN9WQMLAdWIBSvsbBmvSFEemkkDRhJp6cFXFfzbyg+CkRMcjQ==","signatures":[{"sig":"MEUCIH9hMsj6Igtxipg4nikoWRH+6JsMUmmcovlk8K47/RphAiEAgsaciraAtZS21qq73774JtPIDLNyvpmHCYX0Ai70nyA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":100521},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"e8d06a63ad9381c6a8cbe6e65b888da234604ac8","scripts":{"test":"jest","build":"rm -rf dist && npx tsc --project tsconfig.build.json","start":"node dist/index.js"},"_npmUser":{"name":"fufeck","email":"fabien.tafforeau@gmail.com"},"_npmVersion":"11.6.2","description":"Permet de formatter les fichier BAL, avant leurs publication sur l'api-depot","directories":{},"_nodeVersion":"24.13.0","dependencies":{"yargs":"^17.5.1","chardet":"^1.4.0","date-fns":"^4.1.0","file-type":"^12.4.2","papaparse":"^5.3.2","iconv-lite":"^0.6.3","@ban-team/adresses-util":"^0.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","tsup":"^8.0.0","ts-jest":"^29.4.6","fs-extra":"^11.3.4","typescript":"^5.0.0","@types/jest":"^30.0.0","@types/fs-extra":"^11.0.4","@types/papaparse":"^5.3.15","@types/iconv-lite":"^0.0.1"},"_npmOperationalInternal":{"tmp":"tmp/formatter-bal_0.1.2_1779109288941_0.8122670416612863","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@ban-team/formatter-bal","version":"0.1.3","keywords":[],"author":"","license":"MIT","_id":"@ban-team/formatter-bal@0.1.3","maintainers":[{"name":"tmerlier","email":"theophile.merliere@gmail.com"},{"name":"jdesboeufs","email":"jerome.desboeufs@gmail.com"},{"name":"mortier.melanie","email":"melanie.mortier@ign.fr"},{"name":"nkokla","email":"n.kokla@koaji.fr"},{"name":"vsagniez","email":"vincent.sagniez@ign.fr"},{"name":"magos92","email":"guillaume_fay@hotmail.com"},{"name":"fufeck","email":"fabien.tafforeau@gmail.com"}],"dist":{"shasum":"f08132fab7a5ee0e1271274342ebd8dcac93d052","tarball":"https://registry.npmjs.org/@ban-team/formatter-bal/-/formatter-bal-0.1.3.tgz","fileCount":50,"integrity":"sha512-WDVKe7U01CJ3sH8EQGFLP+gnWqXasNY9HxqAe/OsEbWF7j+5GBp2PQxTa9Qo30FQLbNiPrSIsTwNfYd5DnQ4Pw==","signatures":[{"sig":"MEUCIQCsDPvZLI1p++OtieNNlm+mHLZ83b1vRgdgYNZCzb4ZIAIgUy7xipX2czsJHsWbtHaFQgRYWD9IrBkJbDvQPQIsFpo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":100521},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"5e003f23b116d7ba80974d2dc5e2f8a1c7f5853a","scripts":{"test":"jest","build":"rm -rf dist && npx tsc --project tsconfig.build.json","start":"node dist/index.js"},"_npmUser":{"name":"fufeck","email":"fabien.tafforeau@gmail.com"},"_npmVersion":"11.6.2","description":"Permet de formatter les fichier BAL, avant leurs publication sur l'api-depot","directories":{},"_nodeVersion":"24.13.0","dependencies":{"yargs":"^17.5.1","chardet":"^1.4.0","date-fns":"^4.1.0","file-type":"^12.4.2","papaparse":"^5.3.2","iconv-lite":"^0.6.3","@ban-team/adresses-util":"^0.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","tsup":"^8.0.0","ts-jest":"^29.4.6","fs-extra":"^11.3.4","typescript":"^5.0.0","@types/jest":"^30.0.0","@types/fs-extra":"^11.0.4","@types/papaparse":"^5.3.15","@types/iconv-lite":"^0.0.1"},"_npmOperationalInternal":{"tmp":"tmp/formatter-bal_0.1.3_1779110984460_0.3864073140564177","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@ban-team/formatter-bal","version":"0.1.4","description":"Permet de formatter les fichier BAL, avant leurs publication sur l'api-depot","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"start":"node dist/index.js","build":"rm -rf dist && npx tsc --project tsconfig.build.json","test":"jest","prepublishOnly":"yarn build"},"keywords":[],"author":"","license":"MIT","dependencies":{"@ban-team/adresses-util":"^0.9.0","chardet":"^1.4.0","date-fns":"^4.1.0","file-type":"^12.4.2","iconv-lite":"^0.6.3","papaparse":"^5.3.2","yargs":"^17.5.1"},"devDependencies":{"@types/fs-extra":"^11.0.4","@types/iconv-lite":"^0.0.1","@types/jest":"^30.0.0","@types/papaparse":"^5.3.15","fs-extra":"^11.3.4","jest":"^30.2.0","ts-jest":"^29.4.6","tsup":"^8.0.0","typescript":"^5.0.0"},"publishConfig":{"access":"public"},"gitHead":"220e59d7a64b236c89c7c050148e672293eebcbd","_id":"@ban-team/formatter-bal@0.1.4","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-aQca03KlcRz7TSCLzfRQZzpncha1NJDZ45gZFWS5Vh4brjZrAdQBxPGqAFf0efIkj0kqGsBz49ue24PhiwSTwA==","shasum":"75538846a6052d1794d07adc7bafc5be42878e77","tarball":"https://registry.npmjs.org/@ban-team/formatter-bal/-/formatter-bal-0.1.4.tgz","fileCount":50,"unpackedSize":100253,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIB9lUxb3Y/gdb2D58r2GfFAGjxiBZ2tpwgA714DkdknaAiB1SnKkfaMngpEG7+feeUIFhsKXPuBZvnK13CtWruZQ6Q=="}]},"_npmUser":{"name":"fufeck","email":"fabien.tafforeau@gmail.com"},"directories":{},"maintainers":[{"name":"tmerlier","email":"theophile.merliere@gmail.com"},{"name":"jdesboeufs","email":"jerome.desboeufs@gmail.com"},{"name":"mortier.melanie","email":"melanie.mortier@ign.fr"},{"name":"nkokla","email":"n.kokla@koaji.fr"},{"name":"vsagniez","email":"vincent.sagniez@ign.fr"},{"name":"magos92","email":"guillaume_fay@hotmail.com"},{"name":"fufeck","email":"fabien.tafforeau@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/formatter-bal_0.1.4_1779119746695_0.628788575974631"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-13T08:37:00.239Z","modified":"2026-05-18T15:55:47.019Z","0.1.1":"2026-05-13T08:37:00.475Z","0.1.2":"2026-05-18T13:01:29.335Z","0.1.3":"2026-05-18T13:29:44.638Z","0.1.4":"2026-05-18T15:55:46.827Z"},"license":"MIT","keywords":[],"description":"Permet de formatter les fichier BAL, avant leurs publication sur l'api-depot","maintainers":[{"name":"tmerlier","email":"theophile.merliere@gmail.com"},{"name":"jdesboeufs","email":"jerome.desboeufs@gmail.com"},{"name":"mortier.melanie","email":"melanie.mortier@ign.fr"},{"name":"nkokla","email":"n.kokla@koaji.fr"},{"name":"vsagniez","email":"vincent.sagniez@ign.fr"},{"name":"magos92","email":"guillaume_fay@hotmail.com"},{"name":"fufeck","email":"fabien.tafforeau@gmail.com"}],"readme":"# formatter-bal\n\nOutil de mise en forme des fichiers BAL (Base Adresses Locales) avant publication sur l'[api-depot](https://github.com/BaseAdresseNationale/api-depot).\n\n## Installation\n\n```bash\nyarn install\nyarn build\n```\n\n## Utilisation\n\n### En ligne de commande\n\n```bash\n./exe formatter-bal path/to/input.csv\n```\n\nLe fichier de sortie est écrit dans le répertoire courant sous la forme `bal_YYYYMMDD_HHMMSS.csv`.\n\n### En tant que bibliothèque\n\n```typescript\nimport { formatterBAL } from \"@ban-team/formatter-bal\";\nimport { readFileSync } from \"fs\";\n\nconst balBuffer = readFileSync(\"input.csv\");\nconst enrichedBuffer = await formatterBAL(balBuffer);\n\nif (enrichedBuffer) {\n  // enrichedBuffer est un Buffer CSV encodé UTF-8 avec les IDs BAN assignés\n}\n```\n\n## Scripts disponibles\n\n| Commande     | Description                               |\n| ------------ | ----------------------------------------- |\n| `yarn build` | Compile le projet TypeScript vers `dist/` |\n| `yarn test`  | Lance les tests Jest                      |\n\n## Pipeline de traitement\n\n```\nFichier CSV (Buffer)\n    ↓\nDétection encodage + parsing CSV (papaparse)\n    ↓\nConstruction de l'arbre BalTree (Voie → Numéro → Position[])\n    ↓\nRécupération id_ban_commune + CSV de référence BAN\n    ↓\nCalcul et propagation des IDs BAN (ou génération UUIDs)\n    ↓\nSérialisation vers CSV UTF-8 (Buffer)\n```\n\n## Structure du projet\n\n```\nsrc/\n├── index.ts          # Point d'entrée principal (formatterBAL)\n├── parse/            # Détection d'encodage et parsing CSV\n├── tree/             # Construction de l'arbre d'adresses\n├── ban/              # Appels à l'API BAN\n├── compute_ids/      # Logique d'assignation des identifiants\n├── serialize/        # Sérialisation vers CSV\n├── commands/         # Handler CLI (yargs)\n└── types/            # Définitions de types TypeScript\n```\n\n---\n\n## Transformations appliquées au fichier BAL\n\nDétail exhaustif de chaque opération effectuée sur le fichier d'entrée, phase par phase.\n\n### Phase 1 — Parsing du fichier (`src/parse/index.ts`)\n\nDétection d'encodage (`chardet`), rejet des binaires (`file-type`), BOM supprimé, délimiteur CSV auto-détecté (`,` `\\t` `;`).\n\n### Phase 2 — Normalisation des champs (`src/tree/parse-row.ts`)\n\n| Champ                   | Traitement                                                                                                                                                                                                      |\n| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `numero`                | `parseInt(10)` ; ligne entièrement ignorée si non parseable                                                                                                                                                     |\n| `x`, `y`, `long`, `lat` | `parseFloat` ; accepte `,` comme séparateur décimal                                                                                                                                                             |\n| `date_der_maj`          | 9 formats tentés successivement (ISO `YYYY-MM-DD`, `dd/MM/yy`, `dd/MM/yyyy`, `yyyy/MM/dd`, `dd-MM-yyyy`, `dd MM yyyy`, `yyyy MM dd`, `d LLLL yyyy` avec locale fr, `yyyy-MM-dd HH:mm`) ; invalide → `undefined` |\n| `certification_commune` | `\"1\"` ou `\"true\"` → `true` ; sinon → `undefined`                                                                                                                                                                |\n| `position` (type enum)  | Suppression des diacritiques + normalisation espaces + lowercase ; invalide → `undefined`                                                                                                                       |\n| `cad_parcelles`         | Split par `\\|` → tableau de chaînes                                                                                                                                                                             |\n| `suffixe`               | Commence par une lettre : première lettre en majuscule (`bis` → `B`, `ter` → `T`) ; commence par un chiffre : chaîne entière en majuscule (`2B` → `2B`)                                                         |\n\n**Compatibilité multi-versions :**\n\n- **BAL 1.4** : `voie_nom` → renommé `toponyme` ; `voie_nom_*` → renommé `toponyme_*` (BAL 1.5 prioritaire en cas de conflit)\n- **BAL 1.3** : `uid_adresse` au format `@c:{id} @v:{id} @a:{id}` décodé pour extraire `id_ban_commune`, `id_ban_toponyme`, `id_ban_adresse`\n- **Noms localisés** (`toponyme_bre`, `commune_nom_eu`, etc.) collectés dynamiquement et conservés dans `Record<string, string>`\n\n### Phase 3 — Construction de l'arbre (`src/tree/index.ts`)\n\n**Clés de l'arbre :**\n\n- **Clé de voie** : `normalize(toponyme)` (via `@ban-team/adresses-util`) + `@{commune_deleguee_insee}` si présent + `#99999` pour les voies sans numéro\n- **Clé de numéro** : `${numero}#${suffixeNormalisé}` (ex: `5#bis`)\n- **Code INSEE** : priorité au champ `commune_insee`, sinon au premier segment de `cle_interop`\n\n#### Création automatique des voies 99999 (`buildLieuditVoies()`)\n\nTous les numéros portant un `lieudit_complement_nom` donnent lieu à la synthèse automatique d'un toponyme sans adresse :\n\n1. Calcul de la clé de voie 99999 sur le nom du lieudit\n2. Si **aucune voie 99999 explicite** n'existe avec ce nom → création d'une **voie sans adresse synthétique**\n3. **Centroïde géographique** calculé en moyennant les coordonnées (`lat`/`long` et `x`/`y`) de toutes les positions référençant ce lieudit\n4. Si une voie 99999 **existe déjà** (ligne explicite dans le fichier d'entrée) et que `position_sans_adresse` est absente → injection du centroïde comme `position_sans_adresse`\n\n### Phase 4 — Récupération des données BAN (`src/ban/index.ts`)\n\n- `GET /api/lookup/{codeInsee}` → lookup de la commune, retourne `typeComposition` (`bal` ou `assemblage`) et `withBanId` (booléen)\n- `GET /api/district/cog/{codeInsee}` → récupération de l'`id_ban_commune`\n- `GET /ban/communes/{codeCommune}/download/csv-bal/adresses` → CSV de référence BAN parsé via le même pipeline (Phases 1-3) pour construire un `treeBAN` de comparaison — **conditionnel** selon le résultat du lookup (voir ci-dessous)\n- En cas d'indisponibilité de l'API → passage en mode fallback UUID (Phase 5C)\n\n#### Conditions de téléchargement du CSV de référence BAN\n\nLe CSV de référence BAN n'est téléchargé (et `computeIds` n'est exécuté) que si l'une des deux conditions suivantes est remplie :\n\n| `typeComposition` | `withBanId` | Comportement                                                             |\n| ----------------- | ----------- | ------------------------------------------------------------------------ |\n| `bal`             | `true`      | Téléchargement du CSV BAN + `computeIds` (réutilisation des IDs stables) |\n| `assemblage`      | —           | Téléchargement du CSV BAN + `computeIds` (réutilisation des IDs stables) |\n| `bal`             | `false`     | **Pas de téléchargement** → `assignMissingIds` (nouveaux UUIDs)          |\n\n**Cas `typeComposition === \"bal\"` et `withBanId === false` :** la commune est encore sur l'ancien socle BAN, dont les identifiants ne sont pas stables (mauvais IDs). Dans ce cas, le CSV de référence BAN n'est pas consulté et de nouveaux UUIDs sont assignés à toutes les entités de la BAL en entrée qui n'en possèdent pas. Ce comportement est sûr car la BAN ne bloque pas l'initialisation de nouveaux identifiants lors du passage sur le nouveau socle.\n\n### Phase 5 — Calcul et propagation des identifiants BAN (`src/compute_ids/index.ts`)\n\nPour chaque voie du fichier BAL, les stratégies suivantes sont tentées dans l'ordre :\n\n#### 5A — Correspondance exacte de nom\n\nRecherche de `treeBAN.voies[clé normalisée]`. Si trouvée : réutilisation de l'`id_ban_toponyme` BAN.\n\n#### 5B — Voie renommée\n\nSi aucune correspondance exacte, parcours de toutes les voies BAN pour trouver une voie dont **≥ 80 % des numéros** sont retrouvés dans la voie BAL via `isSameNumero()`.\n\nUn numéro est considéré identique si les trois conditions suivantes sont réunies :\n\n1. Même valeur de `numero` (entier)\n2. Même `suffixe` normalisé\n3. **Distance ≤ 10 mètres** entre les premières positions géographiques :\n   - Coordonnées WGS84 → formule haversine (R = 6 371 km)\n   - Coordonnées Lambert 93 → distance euclidienne\n   - Aucune coordonnée des deux côtés → considéré comme match\n\nSi une voie renommée est détectée : réutilisation de l'`id_ban_toponyme` de la voie BAN d'origine.\n\n#### 5C — Nouvelle voie\n\nConservation de l'`id_ban_toponyme` existant (issu du fichier d'entrée ou extrait de `uid_adresse`) ; génération d'un UUID si absent.\n\n#### Propagation des `id_ban_adresse`\n\nPour chaque numéro dans les cas 5A et 5B :\n\n- Numéro retrouvé dans BAN via `isSameNumero()` → réutilisation de l'`id_ban_adresse` BAN\n- Numéro absent → conservation de l'`id_ban_adresse` issu du fichier d'entrée si présent, sinon génération d'un UUID\n\n**Fallback (BAN indisponible)** — `assignMissingIds()` : complète uniquement les champs manquants par des UUIDs, sans jamais écraser les IDs existants.\n\n### Phase 6 — Sérialisation (`src/serialize/index.ts`)\n\nUne ligne par position pour les voies numérotées, une ligne par voie pour les 99999. Reformatages standards (`date_der_maj`, `certification_commune`, `cad_parcelles`), champs `undefined` omis, sortie UTF-8.\n\n## API BAN utilisée\n\n- `GET https://plateforme.adresse.data.gouv.fr/api/district/cog/{codeInsee}` — Récupération de l'`id_ban_commune`\n- `GET https://plateforme.adresse.data.gouv.fr/ban/communes/{codeCommune}/download/csv-bal/adresses` — Téléchargement du CSV de référence BAL\n\n## Dépendances principales\n\n| Package                   | Rôle                               |\n| ------------------------- | ---------------------------------- |\n| `@ban-team/adresses-util` | Normalisation des noms de voies    |\n| `chardet` + `iconv-lite`  | Détection et conversion d'encodage |\n| `papaparse`               | Parsing et génération de CSV       |\n| `date-fns`                | Parsing de dates                   |\n| `yargs`                   | Interface CLI                      |\n","readmeFilename":"README.md"}