GeoLeaf.Table — Documentation du module Table
Product Version: GeoLeaf Platform V2 — Version doc: 2.0.0
Fichier source : packages/core/src/modules/geoleaf.table.tsImplémentation : packages/core/src/modules/optional/table/table-api.tsDernière mise à jour : mars 2026
Module optionnel / lazy-loaded — Le module Table est chargé à la demande. Il ne fait pas partie du bundle core lite (
bundle-core-lite-entry.ts). Il est disponible uniquement lorsque le build complet est utilisé ou que le lazy-loading est déclenché.
Le module GeoLeaf.Table fournit une vue tabulaire des données cartographiques, complémentaire à l'affichage sur la carte MapLibre GL JS.
Il permet de :
- Afficher les attributs des entités GeoJSON dans un tableau
- Trier les données par colonne (cycle ascendant → descendant → aucun)
- Sélectionner des entités et synchroniser avec la carte
- Zoomer sur les entités sélectionnées
- Surbriller la sélection sur la carte
- Exporter la sélection (via événement)
1. Rôle fonctionnel du Table
GeoLeaf.Table a cinq responsabilités principales :
- Afficher les données des couches GeoJSON sous forme tabulaire
- Synchroniser la sélection entre table et carte
- Permettre le tri multi-colonnes avec cycles (asc → desc → null)
- Gérer la sélection multiple avec export et zoom
- S'intégrer avec les modules GeoJSON, Filters et Core
Important : Le module Table nécessite que GeoLeaf.GeoJSON soit chargé et configuré.
2. API publique de GeoLeaf.Table
2.1 GeoLeaf.Table.init(options)
Initialise le module Table avec une instance de carte et des options.
GeoLeaf.Table.init(options);Paramètres :
options: objet de configuration requisoptions.map: instance de carte MapLibre GL JS (requis)options.config:Object— configuration personnalisée (optionnel)enabled:boolean— activer le module (défaut :true)defaultVisible:boolean— visible au démarrage (défaut :false)pageSize:number— lignes par page (défaut :50)maxRowsPerLayer:number— limite de lignes (défaut :1000)enableExportButton:boolean— bouton export (défaut :true)virtualScrolling:boolean— scroll virtuel (défaut :true)defaultHeight:string— hauteur par défaut (défaut :'40%')minHeight:string— hauteur minimale (défaut :'20%')maxHeight:string— hauteur maximale (défaut :'60%')resizable:boolean— redimensionnable (défaut :true)
Retour : void
Exemple minimal
const map = GeoLeaf.Core.getMap();
GeoLeaf.Table.init({ map });Exemple avec configuration
GeoLeaf.Table.init({
map,
config: {
defaultVisible: true,
pageSize: 100,
maxRowsPerLayer: 2000,
defaultHeight: "50%",
resizable: true,
},
});2.2 GeoLeaf.Table.show()
Affiche le tableau.
GeoLeaf.Table.show();Événements émis : geoleaf:table:opened
Exemple
document.getElementById("show-table-btn").addEventListener("click", () => {
GeoLeaf.Table.show();
});2.3 GeoLeaf.Table.hide()
Masque le tableau. Désactive également la surbrillance active.
GeoLeaf.Table.hide();Événements émis : geoleaf:table:closed
Exemple
document.getElementById("hide-table-btn").addEventListener("click", () => {
GeoLeaf.Table.hide();
});2.4 GeoLeaf.Table.toggle()
Bascule la visibilité du tableau (affiche si caché, cache si affiché).
GeoLeaf.Table.toggle();Exemple
document.getElementById("toggle-table-btn").addEventListener("click", () => {
GeoLeaf.Table.toggle();
});2.5 GeoLeaf.Table.setLayer(layerId)
Définit la couche GeoJSON à afficher dans le tableau.
GeoLeaf.Table.setLayer(layerId);Paramètres :
layerId:string— ID de la couche GeoJSON (ounullpour vider)
Événements émis : geoleaf:table:layerChanged avec { layerId }
Note : Seules les couches déclarées dans la configuration GeoJSON peuvent être affichées.
setLayerréinitialise la sélection et le tri.
Exemple
document.getElementById("layer-select").addEventListener("change", (e) => {
GeoLeaf.Table.setLayer(e.target.value);
});Configuration de couche pour Table
Dans geoleaf.config.json :
{
"geojson": {
"layers": [
{
"id": "restaurants",
"url": "data/restaurants.geojson",
"table": {
"enabled": true,
"columns": [
{
"field": "properties.name",
"label": "Nom",
"width": "30%",
"sortable": true
},
{
"field": "properties.category",
"label": "Catégorie",
"width": "20%",
"sortable": true
},
{
"field": "properties.rating",
"label": "Note",
"width": "15%",
"sortable": true
}
],
"defaultSort": {
"field": "properties.name",
"direction": "asc"
}
}
}
]
}
}2.6 GeoLeaf.Table.refresh()
Rafraîchit les données affichées dans le tableau.
GeoLeaf.Table.refresh();Récupère les features de la couche courante via GeoLeaf.GeoJSON.getLayerData(), réapplique le tri et re-rend le tableau.
Exemple
// Rafraîchir après un changement de filtre
map.on("geoleaf:filters:changed", () => {
GeoLeaf.Table.refresh();
});2.7 GeoLeaf.Table.sortByField(field)
Change le tri sur une colonne spécifique.
GeoLeaf.Table.sortByField(field);Paramètres :
field:string— chemin du champ (notation point :properties.name)
Comportement : cycle de tri sur la même colonne :
- Premier appel : tri ascendant
- Deuxième appel : tri descendant
- Troisième appel : pas de tri (ordre original)
Événements émis : geoleaf:table:sortChanged avec { field, direction }
Exemple
document.querySelectorAll(".table-header").forEach((header) => {
header.addEventListener("click", () => {
GeoLeaf.Table.sortByField(header.dataset.field);
});
});2.8 GeoLeaf.Table.setSelection(ids, add)
Sélectionne ou désélectionne des entités.
GeoLeaf.Table.setSelection(ids, add);Paramètres :
ids:Array<string>— IDs des entités à sélectionneradd:boolean— ajouter à la sélection existante (true) ou remplacer (false, défaut)
Événements émis : geoleaf:table:selectionChanged avec { layerId, selectedIds }
Exemple
// Sélectionner des entités spécifiques
GeoLeaf.Table.setSelection(["poi-1", "poi-5", "poi-12"]);
// Ajouter à la sélection existante
GeoLeaf.Table.setSelection(["poi-20"], true);2.9 GeoLeaf.Table.getSelectedIds()
Retourne les IDs des entités sélectionnées.
const selectedIds = GeoLeaf.Table.getSelectedIds();Retour : Array<string> — liste des IDs sélectionnés
Exemple
const selected = GeoLeaf.Table.getSelectedIds();
console.log(`${selected.length} entités sélectionnées :`, selected);2.10 GeoLeaf.Table.clearSelection()
Efface toute la sélection.
GeoLeaf.Table.clearSelection();Événements émis : geoleaf:table:selectionChanged avec { selectedIds: [] }
Exemple
document.getElementById("clear-selection-btn").addEventListener("click", () => {
GeoLeaf.Table.clearSelection();
});2.11 GeoLeaf.Table.zoomToSelection()
Zoom sur les entités sélectionnées dans la carte.
GeoLeaf.Table.zoomToSelection();Événements émis : geoleaf:table:zoomToSelection avec { layerId, selectedIds }
Comportement : Calcule les bounds des entités sélectionnées et ajuste la vue de la carte MapLibre GL JS.
Exemple
document.getElementById("zoom-selection-btn").addEventListener("click", () => {
const selected = GeoLeaf.Table.getSelectedIds();
if (selected.length === 0) return;
GeoLeaf.Table.zoomToSelection();
});2.12 GeoLeaf.Table.highlightSelection(active)
Active/désactive la surbrillance des entités sélectionnées sur la carte.
GeoLeaf.Table.highlightSelection(active);Paramètres :
active:boolean— activer (true) ou désactiver (false)
Événements émis : geoleaf:table:highlightSelection avec { layerId, selectedIds, active }
Exemple
let highlighted = false;
document.getElementById("highlight-btn").addEventListener("click", () => {
highlighted = !highlighted;
GeoLeaf.Table.highlightSelection(highlighted);
});2.13 GeoLeaf.Table.exportSelection()
Émet un événement pour exporter la sélection.
GeoLeaf.Table.exportSelection();Événements émis : geoleaf:table:exportSelection avec { layerId, selectedIds, rows }
Note : Le module Table émet uniquement l'événement. L'implémentation de l'export (CSV, JSON, GeoJSON, etc.) doit être gérée par l'application.
Exemple
document.getElementById("export-btn").addEventListener("click", () => {
GeoLeaf.Table.exportSelection();
});
// Écouter l'événement pour implémenter l'export
map.on("geoleaf:table:exportSelection", (e) => {
const { rows } = e;
const csv = convertToCSV(rows);
downloadFile(csv, "export.csv", "text/csv");
});
function convertToCSV(rows) {
const headers = Object.keys(rows[0].properties);
const csvLines = [headers.join(",")];
rows.forEach((row) => {
const values = headers.map((h) => row.properties[h] ?? "");
csvLines.push(values.join(","));
});
return csvLines.join("\n");
}
function downloadFile(content, filename, mimeType) {
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}3. Configuration dans geoleaf.config.json
3.1 Configuration globale du module
{
"tableConfig": {
"enabled": true,
"defaultVisible": false,
"pageSize": 50,
"maxRowsPerLayer": 1000,
"enableExportButton": true,
"virtualScrolling": true,
"defaultHeight": "40%",
"minHeight": "20%",
"maxHeight": "60%",
"resizable": true
}
}3.2 Configuration par couche
{
"geojson": {
"layers": [
{
"id": "restaurants",
"url": "data/restaurants.geojson",
"table": {
"enabled": true,
"columns": [
{
"field": "properties.name",
"label": "Nom",
"width": "30%",
"sortable": true
},
{
"field": "properties.category",
"label": "Catégorie",
"width": "20%",
"sortable": true
},
{
"field": "properties.address",
"label": "Adresse",
"width": "35%",
"sortable": false
},
{
"field": "properties.rating",
"label": "Note",
"width": "15%",
"sortable": true
}
],
"defaultSort": {
"field": "properties.rating",
"direction": "desc"
},
"searchFields": ["properties.name", "properties.category"]
}
}
]
}
}4. Événements
Le module Table émet les événements suivants sur la carte MapLibre GL JS :
| Événement | Détail | Description |
|---|---|---|
geoleaf:table:opened | — | Tableau affiché |
geoleaf:table:closed | — | Tableau masqué |
geoleaf:table:layerChanged | { layerId } | Couche affichée modifiée |
geoleaf:table:sortChanged | { field, direction } | Tri modifié |
geoleaf:table:selectionChanged | { layerId, selectedIds } | Sélection modifiée |
geoleaf:table:zoomToSelection | { layerId, selectedIds } | Zoom sur sélection déclenché |
geoleaf:table:highlightSelection | { layerId, selectedIds, active } | Surbrillance activée/désactivée |
geoleaf:table:exportSelection | { layerId, selectedIds, rows } | Export demandé |
Exemple d'écoute
const map = GeoLeaf.Core.getMap();
// Écouter les changements de sélection
map.on("geoleaf:table:selectionChanged", (e) => {
console.log(`${e.selectedIds.length} entités sélectionnées`);
highlightFeaturesOnMap(e.selectedIds);
});
// Écouter les changements de couche
map.on("geoleaf:table:layerChanged", (e) => {
console.log(`Couche affichée : ${e.layerId}`);
updateUIControls(e.layerId);
});5. Intégration avec d'autres modules
5.1 Intégration avec GeoJSON
Le module Table affiche les données des couches GeoJSON chargées par le core :
// Les couches GeoJSON sont configurées dans geoleaf.config.json
// et chargées automatiquement par GeoLeaf.GeoJSON au démarrage.
// Afficher une couche dans le tableau :
GeoLeaf.Table.setLayer("restaurants");
GeoLeaf.Table.show();5.2 Intégration avec Filters
Le tableau se synchronise automatiquement avec les filtres via l'écoute des événements :
// Appliquer un filtre
GeoLeaf.Filters.filterPOI({ category: "restaurant" });
// Le tableau se met à jour automatiquement
// (écoute l'événement geoleaf:filters:changed en interne)5.3 Synchronisation bidirectionnelle carte ↔ table
Dans MapLibre GL JS, la synchronisation s'appuie sur les événements GeoLeaf :
// Sélection dans le tableau → surbrillance sur carte
map.on("geoleaf:table:selectionChanged", (e) => {
const { selectedIds } = e;
// Mettre à jour le style MapLibre via setFeatureState ou paint expression
// (la mise en surbrillance intégrée utilise highlightSelection())
GeoLeaf.Table.highlightSelection(selectedIds.length > 0);
});
// Clic sur carte → sélection dans table
map.on("click", (e) => {
const features = map.queryRenderedFeatures(e.point);
if (features.length > 0) {
const featureId = features[0].id;
GeoLeaf.Table.setSelection([String(featureId)], true);
}
});6. Cas d'usage pratiques
6.1 Tableau avec tri
GeoLeaf.Table.init({ map });
GeoLeaf.Table.setLayer("restaurants");
GeoLeaf.Table.show();
// Déclencher un tri programmatique
GeoLeaf.Table.sortByField("properties.rating");6.2 Export multi-formats
map.on("geoleaf:table:exportSelection", (e) => {
const { rows, layerId } = e;
const format = prompt("Format d'export (csv/json/geojson):", "csv");
switch (format) {
case "csv":
exportCSV(rows, `${layerId}.csv`);
break;
case "json":
exportJSON(rows, `${layerId}.json`);
break;
case "geojson":
exportGeoJSON(rows, `${layerId}.geojson`);
break;
}
});6.3 Configuration pour grandes tables
GeoLeaf.Table.init({
map,
config: {
maxRowsPerLayer: 5000,
virtualScrolling: true,
pageSize: 100,
},
});
// Debounce sur les refresh fréquents
let refreshTimeout;
map.on("geoleaf:filters:changed", () => {
clearTimeout(refreshTimeout);
refreshTimeout = setTimeout(() => GeoLeaf.Table.refresh(), 300);
});7. Architecture interne
7.1 Modules composants
GeoLeaf.Table (API publique — table-api.ts)
├── table-state.ts (état partagé : _map, _config, _currentLayerId, _selectedIds, etc.)
├── table-layer.ts (récupération features, gestion couches, attachMapEvents)
├── table-highlight.ts (surbrillance MapLibre, calcul bounds)
├── table-selection.ts (setSelection, clearSelection, zoomToSelection, exportSelection)
├── sort.ts (sortInPlace, nextSortState — cycle tri)
├── export.ts (resolveFeatureId)
├── panel.ts (TablePanel — création conteneur DOM)
└── renderer.ts (TableRenderer — rendu tableau HTML)7.2 Flux de données
1. Initialisation
GeoLeaf.Table.init({ map, config })
└── Fusionne config depuis GeoLeaf.Config.get('tableConfig') + options
└── Crée conteneur DOM via TablePanel.create()
└── Attache listeners événements carte
2. Changement de couche
GeoLeaf.Table.setLayer('restaurants')
└── Réinitialise sélection, highlight et tri
└── Applique defaultSort depuis config de couche
└── Appelle refresh()
3. Rafraîchissement
GeoLeaf.Table.refresh()
└── Récupère features via GeoLeaf.GeoJSON.getLayerData()
└── Construit _featureIdMap
└── Applique tri si défini (sortInPlace)
└── Appelle TableRenderer.render()
4. Tri
GeoLeaf.Table.sortByField('properties.name')
└── nextSortState() → cycle asc → desc → null
└── Appelle refresh()
└── Émet: geoleaf:table:sortChanged
5. Sélection
GeoLeaf.Table.setSelection(['id1', 'id2'])
└── Met à jour _selectedIds (Set)
└── Appelle TableRenderer.updateSelection()
└── Émet: geoleaf:table:selectionChanged8. Bonnes pratiques
À faire
- Limiter le nombre de lignes via
maxRowsPerLayer(défaut : 1000) - Activer le scroll virtuel pour les grandes tables :
virtualScrolling: true - Définir uniquement les colonnes pertinentes dans
columns - Utiliser
defaultSortpour une meilleure UX initiale - Utiliser un debounce sur les appels fréquents à
refresh()
À éviter
- Afficher toutes les propriétés sans filtrer les colonnes
- Charger plus de 5000 lignes sans pagination ni scroll virtuel
- Appeler
refresh()trop fréquemment sans debounce
9. Performance
Optimisations intégrées
- Limitation automatique :
maxRowsPerLayerempêche les surcharges - Scroll virtuel : affiche uniquement les lignes visibles
- Cache des données :
_cachedDataévite les requêtes répétées - Tri optimisé : utilise
localeComparepour les strings
Recommandations pour très grandes tables (10k+ lignes)
GeoLeaf.Table.init({
map,
config: {
maxRowsPerLayer: 5000,
virtualScrolling: true,
pageSize: 100,
},
});10. Dépannage
Problème : "Container not initialized"
Cause : TablePanel non chargé ou module non initialisé. Solution : Vérifier que GeoLeaf.Table.init() est appelé avant show().
Problème : Colonnes vides
Cause : field ne correspond pas à la structure GeoJSON. Solution : Vérifier les chemins de propriétés (notation point).
// Structure GeoJSON
{ "properties": { "name": "Restaurant X", "info": { "category": "Italian" } } }
// Configuration colonnes correcte
{ "columns": [
{ "field": "properties.name", "label": "Nom" }, // OK
{ "field": "properties.info.category", "label": "Cat" }, // OK
{ "field": "category", "label": "Cat" } // Incorrect
] }Problème : Tri ne fonctionne pas
Cause : Colonne non marquée sortable: true. Solution :
{ "columns": [{ "field": "properties.name", "label": "Nom", "sortable": true }] }