Carte interactive multi-couches basee sur Leaflet avec fonds de carte souverains (IGN Geoplateforme). Affiche des POI (markers), zones geographiques (geoshape), cercles proportionnels et heatmaps.
Architecture 2 composants : dsfr-data-map est le conteneur carte (init Leaflet, tuiles, viewport).
Les donnees sont projetees par des dsfr-data-map-layer enfants, chacun connecte a sa propre source.
Cela permet naturellement le multi-source (ex: departements choropleth + POI pharmacies).
| Attribut | Type | Defaut | Description |
|---|---|---|---|
center | String | "46.603,2.888" | Centre initial "lat,lon" (France metro par defaut) |
zoom | Number | 6 | Niveau de zoom initial (1-18) |
min-zoom | Number | 2 | Zoom minimum autorise |
max-zoom | Number | 18 | Zoom maximum autorise |
height | String | "500px" | Hauteur du conteneur. Accepte toute unite CSS (px, vh, rem). Un pourcentage (ex: "60%") est interprete comme un ratio de la largeur (aspect-ratio responsive). |
tiles | String | "ign-plan" | Fond de carte predefini ou URL template custom |
no-controls | Boolean | false | Masque les controles de zoom Leaflet |
fit-bounds | Boolean | false | Ajuste automatiquement le viewport aux donnees |
max-bounds | String | "" | Limites "latSW,lonSW,latNE,lonNE" |
name | String | "" | Titre de la carte (aria-label et beacon) |
Tuiles souveraines, accessibles sans cle API :
| Preset | Source |
|---|---|
ign-plan | Geoplateforme IGN — Plan IGN |
ign-ortho | Geoplateforme IGN — Orthophoto |
ign-topo | Geoplateforme IGN — Carte topographique (SCAN 25/100) |
ign-cadastre | Geoplateforme IGN — Cadastre |
osm | OpenStreetMap France |
| Attribut | Type | Defaut | Description |
|---|---|---|---|
source | String | "" | ID de la source de donnees (requis) |
type | String | "marker" | Type de couche : marker, geoshape, circle, heatmap |
lat-field | String | "" | Chemin vers la latitude |
lon-field | String | "" | Chemin vers la longitude |
geo-field | String | "" | Chemin vers un GeoJSON (Point, Polygon, MultiPolygon) |
| Attribut | Type | Defaut | Description |
|---|---|---|---|
popup-template | String | "" | Template HTML avec interpolation : "{nom} — {puissance} kW" |
popup-fields | String | "" | Champs separes par virgule pour un tableau auto |
tooltip-field | String | "" | Champ affiche au survol |
color | String | "#000091" | Couleur contours/markers (DSFR blue-france). Sert de fallback si color-map ne matche pas. |
color-field | String | "" | Champ dont la valeur determine la couleur (mapping categoriel) |
color-map | String | "" | Paires valeur:#couleur separees par virgule. Ex: "1:#00A95F,2:#FF9940,3:#E1000F" |
fill-field | String | "" | Champ numerique pour choropleth geoshape |
fill-opacity | Number | 0.6 | Opacite du remplissage |
selected-palette | String | "" | Palette choropleth (memes noms que world-map) |
radius | Number | 8 | Rayon fixe (circle) |
radius-field | String | "" | Champ pour rayon variable (cercles proportionnels) |
radius-unit | String | "px" | px (pixels) ou m (metres) |
radius-min | Number | 4 | Rayon minimum pour l'auto-scaling (px) |
radius-max | Number | 30 | Rayon maximum pour l'auto-scaling (px) |
| Attribut | Type | Defaut | Description |
|---|---|---|---|
heat-radius | Number | 25 | Rayon de la heatmap en pixels |
heat-blur | Number | 15 | Flou de la heatmap en pixels |
heat-field | String | "" | Champ numerique pour ponderer l'intensite |
| Attribut | Type | Defaut | Description |
|---|---|---|---|
cluster | Boolean | false | Active le clustering (leaflet.markercluster) |
cluster-radius | Number | 80 | Rayon de clustering en pixels |
| Attribut | Type | Defaut | Description |
|---|---|---|---|
min-zoom | Number | 0 | Zoom minimum pour afficher cette couche |
max-zoom | Number | 18 | Zoom maximum pour afficher cette couche |
bbox | Boolean | false | Active le filtrage par bounding box du viewport |
bbox-debounce | Number | 300 | Delai ms avant re-fetch apres pan/zoom |
bbox-field | String | "" | Champ geo pour la clause bbox (auto-detecte si vide) |
| Attribut | Type | Defaut | Description |
|---|---|---|---|
max-items | Number | 5000 | Nombre max d'elements rendus (0 = illimite) |
filter | String | "" | Filtre client-side |
| Attribut | Type | Defaut | Description |
|---|---|---|---|
time-field | String | "" | Champ date/heure pour le decoupage temporel |
time-bucket | String | "none" | Granularite : none (valeurs brutes), hour, day, month, year |
time-mode | String | "snapshot" | snapshot (affiche le pas courant) ou cumulative (tout jusqu'au pas courant) |
Quand time-field est defini, les donnees sont pre-indexees par pas de temps.
Le composant compagnon dsfr-data-map-timeline pilote la lecture frame par frame.
Composant compagnon place comme enfant de dsfr-data-map.
Decouvre automatiquement les layers ayant time-field et pilote
leur affichage frame par frame avec des controles de lecture.
| Attribut | Type | Defaut | Description |
|---|---|---|---|
for | String | "" | IDs des layers cibles (virgules). Vide = tous les layers avec time-field |
speed | Number | 1 | Multiplicateur de vitesse (0.5, 1, 2, 4) |
interval | Number | 1000 | Intervalle de base entre frames en ms |
aria-live sur le label courant, aria-valuetext sur le sliderprefers-reduced-motion : desactive le bouton play (navigation manuelle uniquement)
Composant compagnon qui definit un template HTML et un mode d'affichage
pour le clic sur un element de carte. Remplace les attributs popup-template
et popup-fields du layer par un composant declaratif.
| Attribut | Type | Defaut | Description |
|---|---|---|---|
mode | String | "popup" | popup (infobulle Leaflet), modal (modale DSFR), panel-right, panel-left |
title-field | String | "" | Champ pour le titre du panneau/modale |
width | String | "350px" | Largeur du panneau lateral |
for | String | "" | ID du layer cible (vide = tous les layers) |
Le contenu est defini via un element <template> enfant avec interpolation
{{champ}}. Sans template, un tableau cle/valeur est genere automatiquement.
popup : infobulle Leaflet classique au-dessus du markermodal : modale centree avec overlay, fermable par Escape ou clic exterieurpanel-right / panel-left : panneau lateral glissant, mis a jour a chaque clicLe layer resout les coordonnees dans cet ordre de priorite :
lat-field + lon-field — Coordonnees separees (CSV, API)geo-field vers GeoJSON Point — Extrait lat/lon du Pointgeo-field vers Polygon/MultiPolygon — Rendu via L.geoJSON()geo_point_2d, geo_shape, geometry<dsfr-data-source id="bornes" api-type="opendatasoft"
base-url="https://odre.opendatasoft.com" dataset-id="bornes-irve"
select="geo_point_2d,nom_station,puissance_nominale,adresse"
limit="5000">
</dsfr-data-source>
<dsfr-data-map center="46.6,2.3" zoom="6" tiles="ign-plan" fit-bounds>
<dsfr-data-map-layer source="bornes" type="marker"
geo-field="geo_point_2d"
popup-fields="nom_station,puissance_nominale,adresse"
tooltip-field="nom_station"
cluster cluster-radius="60">
</dsfr-data-map-layer>
</dsfr-data-map><dsfr-data-source id="regions" api-type="opendatasoft"
base-url="https://public.opendatasoft.com" dataset-id="georef-france-region"
select="geo_shape,reg_name,population" limit="20">
</dsfr-data-source>
<dsfr-data-source id="communes" api-type="opendatasoft"
base-url="https://public.opendatasoft.com" dataset-id="georef-france-commune"
select="geo_shape,com_name,population" limit="500" server-side page-size="500">
</dsfr-data-source>
<dsfr-data-map center="46.6,2.3" zoom="6" tiles="ign-plan" height="600px">
<!-- Zoom 1-9 : regions -->
<dsfr-data-map-layer source="regions" type="geoshape"
geo-field="geo_shape" fill-field="population"
selected-palette="sequentialAscending"
popup-template="<b>{reg_name}</b><br>{population} hab."
min-zoom="1" max-zoom="9">
</dsfr-data-map-layer>
<!-- Zoom 10+ : communes dans le viewport -->
<dsfr-data-map-layer source="communes" type="geoshape"
geo-field="geo_shape" fill-field="population"
selected-palette="sequentialAscending"
popup-template="<b>{com_name}</b><br>{population} hab."
min-zoom="10" bbox bbox-debounce="500">
</dsfr-data-map-layer>
</dsfr-data-map><dsfr-data-map center="46.6,2.3" zoom="6">
<dsfr-data-map-layer source="villes" type="circle"
lat-field="latitude" lon-field="longitude"
radius-field="population" radius-unit="px"
color="#000091" fill-opacity="0.4"
popup-fields="nom,population"
tooltip-field="nom">
</dsfr-data-map-layer>
</dsfr-data-map><dsfr-data-source id="bornes-trim" data='[
{"region":"Paris","lat":48.85,"lon":2.35,"bornes":120,"date":"2025-T1"},
{"region":"Lyon","lat":45.76,"lon":4.83,"bornes":85,"date":"2025-T1"},
{"region":"Marseille","lat":43.30,"lon":5.37,"bornes":60,"date":"2025-T1"},
{"region":"Paris","lat":48.85,"lon":2.35,"bornes":250,"date":"2025-T2"},
{"region":"Lyon","lat":45.76,"lon":4.83,"bornes":160,"date":"2025-T2"},
{"region":"Marseille","lat":43.30,"lon":5.37,"bornes":130,"date":"2025-T2"}
]'></dsfr-data-source>
<dsfr-data-map center="46.6,2.3" zoom="6" height="550px">
<dsfr-data-map-layer source="bornes-trim" type="circle"
lat-field="lat" lon-field="lon"
radius-field="bornes" radius-min="6" radius-max="35"
color="#000091" fill-opacity="0.5"
tooltip-field="region"
time-field="date" time-mode="snapshot">
</dsfr-data-map-layer>
<dsfr-data-map-timeline speed="1" interval="1500">
</dsfr-data-map-timeline>
</dsfr-data-map><dsfr-data-map center="46.6,2.3" zoom="6" tiles="ign-plan">
<dsfr-data-map-layer source="departements" type="geoshape"
geo-field="geo_shape" fill-field="population"
selected-palette="sequentialAscending" fill-opacity="0.5"
popup-template="<b>{nom}</b><br>Population : {population}">
</dsfr-data-map-layer>
<dsfr-data-map-layer source="prefectures" type="marker"
geo-field="geo_point_2d"
tooltip-field="nom" color="#C9191E">
</dsfr-data-map-layer>
</dsfr-data-map>
Quand bbox est actif sur un layer, chaque deplacement de carte declenche une commande
dsfr-data-source-command avec un filtre in_bbox() (ODS) ou un filtrage
client-side pour les autres adapters. Le whereKey: "map-bbox" permet le merge propre
avec d'autres filtres (facettes, recherche).
Les attributs min-zoom / max-zoom sur les layers permettent d'afficher
differentes couches selon le niveau de zoom. Par exemple : regions a zoom 1-9, communes a zoom 10+.
Le clustering est charge conditionnellement via import dynamique de leaflet.markercluster.
Les styles des clusters suivent les tokens DSFR.
dsfr-data-source (A) ──┐
├──► dsfr-data-map
dsfr-data-source (B) ──┘ ├── dsfr-data-map-layer (source="A", type="geoshape")
└── dsfr-data-map-layer (source="B", type="marker")
Le composant est disponible dans un bundle separe dsfr-data.map.esm.js (ou inclus
dans le bundle complet dsfr-data.esm.js). Leaflet est charge dynamiquement via
import() et n'est pas inclus dans le bundle.