GeoLeaf — Tutoriel : Créer un projet de A à Z
Version : 2.0.0 Durée estimée : 30–45 minutes Résultat : Un localisateur de commerces locaux avec recherche, filtres et clustering
Ce que vous allez construire
Une carte interactive affichant des commerces locaux avec :
- Couche GeoJSON de commerces (restaurants, boutiques)
- Clustering des marqueurs
- Recherche textuelle + filtre par catégorie
- Thème clair/sombre
- Permalink (état dans l'URL)
Structure du projet
mon-projet/
├── index.html
├── geoleaf.config.json ← config globale (profil actif, PWA)
└── profiles/
└── commerces/
├── profile.json ← carte, clustering, modules
├── taxonomy.json ← catégories et icônes
├── ui.json ← contrôles UI, filtres, thème
├── layers.json ← liste des couches
├── basemaps.json ← fonds de carte
└── layers/
└── commerces/
├── commerces_config.json ← config détaillée de la couche
└── data/
└── commerces.geojson ← vos donnéesÉtape 1 — Installation
npm install @geoleaf/core maplibre-glOu en CDN :
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@5.0.0/dist/maplibre-gl.css" />
<link rel="stylesheet" href="https://unpkg.com/@geoleaf/core@2.0.0/dist/geoleaf-main.min.css" />
<script src="https://unpkg.com/maplibre-gl@5.0.0/dist/maplibre-gl.js"></script>
<script type="module" src="https://unpkg.com/@geoleaf/core@2.0.0/dist/geoleaf.esm.js"></script>Étape 2 — index.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Commerces locaux</title>
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@5.0.0/dist/maplibre-gl.css" />
<link
rel="stylesheet"
href="https://unpkg.com/@geoleaf/core@2.0.0/dist/geoleaf-main.min.css"
/>
<style>
body {
margin: 0;
}
#map {
height: 100vh;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/maplibre-gl@5.0.0/dist/maplibre-gl.js"></script>
<script
type="module"
src="https://unpkg.com/@geoleaf/core@2.0.0/dist/geoleaf.esm.js"
></script>
<script type="module">
// Init complète avec profil
GeoLeaf.init({
map: { target: "map" },
data: {
activeProfile: "commerces",
profilesBasePath: "./profiles/",
},
});
GeoLeaf.boot();
</script>
</body>
</html>Note :
GeoLeaf.init()est l'API de haut niveau pour une initialisation complète avec profil. Pour une carte simple sans profil, utilisezCore.init({ mapId: "map", center: [48.8566, 2.3522], zoom: 12 }).
Étape 3 — geoleaf.config.json
Config globale à la racine du projet. Définit le profil actif et les options globales.
{
"data": {
"activeProfile": "commerces",
"profilesBasePath": "./profiles"
},
"pwa": {
"name": "Commerces Locaux",
"short_name": "Commerces",
"theme_color": "#2563eb",
"background_color": "#ffffff",
"installPrompt": { "enabled": false }
}
}Étape 4 — profile.json
Config principale du profil : emprise géographique, clustering, modules.
{
"id": "commerces",
"label": "Commerces locaux",
"description": "Localisateur de commerces de proximité",
"version": "1.0.0",
"map": {
"center": [48.8566, 2.3522],
"zoom": 13,
"maxZoom": 19,
"minZoom": 10
},
"Files": {
"taxonomyFile": "taxonomy.json",
"layersFile": "layers.json",
"basemapsFile": "basemaps.json",
"uiFile": "ui.json"
},
"clusteringConfig": {
"enabled": true,
"strategy": "by-layer",
"maxClusterRadius": 60,
"disableClusteringAtZoom": 16
},
"poiConfig": {
"enabled": false
}
}Étape 5 — taxonomy.json
Définit les catégories de POI, leurs icônes et sous-catégories.
{
"icons": {
"defaultIcon": "commerce-generic"
},
"defaults": {
"icon": "commerce-generic"
},
"categories": {
"restaurant": {
"label": "Restaurants",
"icon": "food-restaurant",
"subcategories": {
"traditionnel": { "label": "Traditionnel", "icon": "food-restaurant" },
"rapide": { "label": "Restauration rapide", "icon": "food-fast" },
"cafe": { "label": "Café / Bar", "icon": "food-cafe" }
}
},
"boutique": {
"label": "Boutiques",
"icon": "commerce-shop",
"subcategories": {
"alimentation": { "label": "Alimentation", "icon": "commerce-grocery" },
"vetements": { "label": "Vêtements", "icon": "commerce-clothing" },
"librairie": { "label": "Librairie", "icon": "commerce-book" }
}
}
}
}Icônes : GeoLeaf utilise un sprite SVG. Remplacez les valeurs
iconpar les identifiants de votre propre sprite, ou utilisez les icônes du profil tourism fourni dansprofiles/tourism/icons/.
Étape 6 — layers.json
Liste des couches GeoJSON du profil.
{
"layers": [
{
"id": "commerces",
"configFile": "layers/commerces/commerces_config.json",
"layerManagerId": "commerces-locaux",
"visible": true
}
]
}Étape 7 — layers/commerces/commerces_config.json
Configuration détaillée de la couche : données, styles, popup, table.
{
"id": "commerces",
"label": "Commerces",
"geometry": "point",
"interactiveShape": true,
"showIconsOnMap": true,
"data": {
"directory": "data",
"file": "commerces.geojson"
},
"styles": {
"directory": "styles",
"default": "defaut.json",
"available": [{ "id": "defaut", "label": "Défaut", "file": "defaut.json" }]
},
"tooltip": {
"mode": "hover",
"fields": [{ "type": "text", "field": "properties.name" }]
},
"popup": {
"enabled": true,
"fields": [
{ "type": "text", "field": "properties.name", "variant": "title" },
{ "type": "badge", "label": "Type", "field": "properties.categoryId" },
{ "type": "text", "label": "Adresse", "field": "properties.address" }
]
},
"sidepanelConfig": {
"enabled": true,
"detailLayout": [
{
"type": "badge",
"label": "Catégorie",
"field": "properties.categoryId",
"accordion": false
},
{
"type": "text",
"label": "Nom",
"field": "properties.name",
"style": "title",
"accordion": false
},
{
"type": "text",
"label": "Adresse",
"field": "properties.address",
"accordion": false
},
{
"type": "text",
"label": "Horaires",
"field": "properties.opening_hours",
"accordion": true,
"defaultOpen": true
},
{
"type": "link",
"label": "Site web",
"field": "properties.website",
"accordion": false
}
]
},
"search": {
"enabled": true,
"indexingFields": ["properties.name", "properties.address"]
},
"table": {
"enabled": true,
"columns": [
{ "field": "properties.name", "label": "Nom", "sortable": true, "width": "40%" },
{
"field": "properties.categoryId",
"label": "Catégorie",
"sortable": true,
"width": "30%"
},
{ "field": "properties.address", "label": "Adresse", "sortable": false, "width": "30%" }
],
"searchFields": ["properties.name"],
"defaultSort": { "field": "properties.name", "order": "asc" }
},
"clustering": {
"enabled": true,
"maxClusterRadius": 60,
"disableClusteringAtZoom": 16
}
}Étape 8 — layers/commerces/data/commerces.geojson
Exemple de données GeoJSON :
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "001",
"geometry": { "type": "Point", "coordinates": [2.3522, 48.8566] },
"properties": {
"name": "Le Petit Bistrot",
"categoryId": "restaurant",
"subcategoryId": "traditionnel",
"address": "10 rue de Rivoli, Paris",
"opening_hours": "Lun-Ven 12h-14h30 / 19h-22h",
"website": "https://example.com"
}
},
{
"type": "Feature",
"id": "002",
"geometry": { "type": "Point", "coordinates": [2.3545, 48.858] },
"properties": {
"name": "Boulangerie Dupont",
"categoryId": "boutique",
"subcategoryId": "alimentation",
"address": "5 rue du Temple, Paris",
"opening_hours": "Tous les jours 7h-20h"
}
}
]
}Étape 9 — ui.json
Contrôles UI visibles, filtres de recherche, thème, permalink.
{
"ui": {
"theme": "auto",
"language": "fr",
"showLayerManager": true,
"showFilterPanel": true,
"showLegend": true,
"showTable": true,
"showThemeSelector": true,
"enableGeolocation": true,
"showCoordinates": false,
"showCacheButton": false,
"showAddPoi": false,
"permalink": {
"enabled": true,
"mode": "hash"
}
},
"search": {
"title": "Filtrer les commerces",
"searchPlaceholder": "Rechercher un commerce...",
"filters": [
{
"id": "searchText",
"type": "search",
"label": "Recherche textuelle",
"placeholder": "Nom, adresse...",
"searchFields": ["properties.name", "properties.address"]
},
{
"id": "categories",
"type": "tree",
"label": "Catégories"
},
{
"id": "proximity",
"type": "proximity",
"label": "Proximité",
"instructionText": "Cliquez sur la carte pour définir un rayon"
}
]
}
}Étape 10 — basemaps.json
Fonds de carte disponibles.
{
"basemaps": {
"osm": {
"label": "OpenStreetMap",
"type": "tile",
"url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
"attribution": "© <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors",
"subdomains": "abc",
"maxZoom": 19,
"defaultBasemap": true
},
"satellite": {
"label": "Satellite",
"type": "tile",
"url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
"attribution": "Tiles © Esri",
"maxZoom": 19
}
}
}Résultat final
Votre projet doit ressembler à :
mon-projet/
├── index.html
├── geoleaf.config.json
└── profiles/
└── commerces/
├── profile.json
├── taxonomy.json
├── ui.json
├── layers.json
├── basemaps.json
└── layers/
└── commerces/
├── commerces_config.json
└── data/
└── commerces.geojsonLancez un serveur local :
npx serve . -p 3000
# → http://localhost:3000Vous verrez une carte avec vos commerces clusterisés, un panneau de filtres (recherche + catégories + proximité), un tableau de données, et un permalien actif dans l'URL.
Aller plus loin
| Objectif | Document |
|---|---|
| Ajouter des couches GeoJSON complexes (polygones, lignes) | GEOJSON_LAYERS_GUIDE.md |
| Configurer les filtres en détail | GeoLeaf_Filters_README.md |
| Référence complète des clés de profil | PROFILES_GUIDE.md |
| Référence JSON exhaustive | PROFILE_JSON_REFERENCE.md |
| Tuiles vectorielles (MVT) | MVT_GUIDE.md |
| Activer le cache offline (plugin Storage) | PLUGIN_CONFIGURATION_GUIDE.md |
| Authentification API backend | CONNECTOR_GUIDE.md |
| Développer un plugin custom | PLUGIN_DEVELOPMENT_GUIDE.md |
