feat: add experimental BAN importer (#2565)

This importer takes a CSV as input, sends it to the BAN API, and then
paste it into the import textarea, so it can be imported as usual.



https://github.com/user-attachments/assets/c13f8580-5c09-4b35-b092-baac664a57a1
This commit is contained in:
Yohan Boniface 2025-03-17 16:39:55 +01:00 committed by GitHub
commit 167bab70c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 118 additions and 1 deletions

View file

@ -202,3 +202,15 @@ dt {
height: 100vh;
opacity: 0.5;
}
.table-scrollable {
background-image: linear-gradient(to right, var(--background-color), var(--background-color)),
linear-gradient(to right, var(--background-color), var(--background-color)),
linear-gradient(to right, rgba(0, 0, 20, .50), rgba(255, 255, 255, 0)),
linear-gradient(to left, rgba(0, 0, 20, .50), rgba(255, 255, 255, 0));
background-position: left center, right center, left center, right center;
background-repeat: no-repeat;
background-size: 20px 100%, 20px 100%, 10px 100%, 10px 100%;
background-attachment: local, local, scroll, scroll;
display: block;
overflow-x: auto;
}

View file

@ -2,7 +2,7 @@
z-index: var(--zindex-dialog);
margin: auto;
margin-top: 100px;
width: 40vw;
width: var(--dialog-width);
max-width: 100vw;
max-height: 50vh;
padding: 20px;

View file

@ -55,3 +55,10 @@
.importers ul .datasets:before {
background-image: url(../img/importers/datasets.svg);
}
.importer.banfr h3:before,
.importers ul .banfr:before {
background-image: url(../img/importers/banfr.svg);
}
.importer table {
width: calc(var(--dialog-width) - 2 * var(--box-margin));
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="none"><path fill="#bebebe" d="M40 0H10C4.477 0 0 4.477 0 10v30c0 5.523 4.477 10 10 10h30c5.523 0 10-4.477 10-10V10c0-5.523-4.477-10-10-10z" style="stroke-width:1"/><path fill="#bfbfbf" fill-opacity=".9" fill-rule="evenodd" d="M29.023 20.012v6.053a1.226 1.226 0 0 0 2.451 0v-6.053a1.226 1.226 0 0 0-2.451 0z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/><path fill="#E1000F" fill-opacity=".9" fill-rule="evenodd" d="m24.393 18.047 5.242 3.027a1.226 1.226 0 0 0 1.225-2.123l-5.241-3.027a1.226 1.226 0 0 0-1.226 2.123z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/><path fill="#000091" fill-opacity=".9" fill-rule="evenodd" d="m29.635 25.004-5.242 3.026a1.226 1.226 0 0 0 1.226 2.123l5.241-3.027a1.226 1.226 0 0 0-1.225-2.122z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/><path fill="#bfbfbf" fill-opacity=".9" fill-rule="evenodd" d="M20.99 26.065v-6.053a1.226 1.226 0 0 0-2.451 0v6.053a1.226 1.226 0 0 0 2.451 0z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/><path fill="#000091" fill-opacity=".9" fill-rule="evenodd" d="m20.377 21.074 5.242-3.027a1.226 1.226 0 0 0-1.226-2.123l-5.241 3.027a1.226 1.226 0 0 0 1.225 2.123z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/><path fill="#E1000F" fill-opacity=".9" fill-rule="evenodd" d="m14.934 27.326 4.84-.035a1.226 1.226 0 0 0-.018-2.452l-4.84.035a1.226 1.226 0 0 0 .018 2.452z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/><path fill="#bfbfbf" fill-opacity=".9" fill-rule="evenodd" d="M18.62 14.22A9.977 9.977 0 0 1 25 11.924c2.424 0 4.648.864 6.38 2.298a1.226 1.226 0 0 0 1.564-1.888A12.417 12.417 0 0 0 25 9.471a12.417 12.417 0 0 0-7.944 2.862 1.226 1.226 0 0 0 1.564 1.888z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/><path fill="#000091" fill-opacity=".9" fill-rule="evenodd" d="M16.022 25.565c-.622-1.309-1.035-2.553-1.035-3.628a9.998 9.998 0 0 1 3.632-7.716 1.225 1.225 0 1 0-1.563-1.888 12.443 12.443 0 0 0-4.52 9.604c0 1.385.473 2.997 1.271 4.679a1.226 1.226 0 0 0 2.215-1.051z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/><path fill="#E1000F" fill-opacity=".9" fill-rule="evenodd" d="M31.38 14.221a9.997 9.997 0 0 1 3.633 7.716c0 1.682-.967 3.75-2.248 5.846-3.235 5.298-8.629 10.652-8.629 10.652a1.224 1.224 0 1 0 1.728 1.737s5.62-5.586 8.993-11.11c1.551-2.541 2.607-5.087 2.607-7.125 0-3.861-1.759-7.318-4.52-9.604a1.226 1.226 0 0 0-1.564 1.888z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/><path fill="#000091" fill-opacity=".9" fill-rule="evenodd" d="M25.864 38.435s-3.874-3.85-7.03-8.252a1.227 1.227 0 0 0-1.992 1.427c3.275 4.57 7.294 8.562 7.294 8.562.48.478 1.257.475 1.734-.006a1.224 1.224 0 0 0-.006-1.731z" clip-rule="evenodd" style="fill:#323737;fill-opacity:1;stroke-width:1"/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -94,6 +94,9 @@ export default class Importer extends Utils.WithTemplate {
case 'datasets':
import('./importers/datasets.js').then(register)
break
case 'banfr':
import('./importers/banfr.js').then(register)
break
}
}
}

View file

@ -0,0 +1,93 @@
import { DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
import { BaseAjax, SingleMixin } from '../autocomplete.js'
import * as Utils from '../utils.js'
import { AutocompleteCommunes } from './communesfr.js'
import { translate } from '../i18n.js'
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
const TEMPLATE = `
<div>
<h3>Géocodage dadresses en France</h3>
<p>Géocoder un fichier CSV avec la base adresse nationale.</p>
<fieldset class="formbox">
<legend>Choisir un fichier CSV (encodé en UTF-8)</legend>
<input type=file name=file data-ref=csvFile accept=".csv" />
</fieldset>
<fieldset class="formbox">
<legend>Aperçu des données</legend>
<table class="table-scrollable" data-ref=table></table>
</fieldset>
<fieldset class="formbox">
<legend>Sélectionner les colonnes à utiliser</legend>
<span data-ref="columns"></span>
</fieldset>
</div>
`
export class Importer {
constructor(umap, options) {
this._umap = umap
this.name = options.name || 'Géocodage FR'
this.id = 'banfr'
}
async open(importer) {
let data
const [container, { table, columns, csvFile }] =
Utils.loadTemplateWithRefs(TEMPLATE)
csvFile.addEventListener('change', () => {
const reader = new FileReader()
reader.onload = (evt) => {
data = evt.target.result
const rows = csv2geojson.auto(data).slice(0, 5)
const cols = Object.keys(rows[0])
table.innerHTML = ''
columns.innerHTML = ''
const tr = document.createElement('tr')
for (const column of cols) {
tr.appendChild(Utils.loadTemplate(`<th>${column}</th>`))
columns.appendChild(
Utils.loadTemplate(
`<label><input type="checkbox" value="${column}" /> ${column}</label>`
)
)
}
table.appendChild(tr)
for (const row of rows) {
const tr = document.createElement('tr')
for (const column of cols) {
tr.appendChild(Utils.loadTemplate(`<td>${row[column]}</td>`))
}
table.appendChild(tr)
}
}
reader.readAsText(csvFile.files[0])
})
const confirm = async (form) => {
const formData = new FormData()
formData.append('data', csvFile.files[0])
for (const option of columns.querySelectorAll('input:checked')) {
formData.append('columns', option.value)
}
const response = await this._umap.request.post(
'https://api-adresse.data.gouv.fr/search/csv/',
{},
formData
)
if (response?.ok) {
importer.raw = await response.text()
importer.format = 'csv'
}
}
importer.dialog
.open({
template: container,
className: `${this.id} importer dark`,
cancel: false,
accept: translate('Geocode'),
})
.then(confirm)
}
}

View file

@ -44,6 +44,7 @@
--small-box-padding: 4px;
--box-margin: 14px;
--text-margin: 7px;
--dialog-width: 40vw;
/* z-indexes (leaflet CSS sets the map at 400 by default) */
--zindex-alert: 500;