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).

dsfr-data-map — Conteneur carte

AttributTypeDefautDescription
centerString"46.603,2.888"Centre initial "lat,lon" (France metro par defaut)
zoomNumber6Niveau de zoom initial (1-18)
min-zoomNumber2Zoom minimum autorise
max-zoomNumber18Zoom maximum autorise
heightString"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).
tilesString"ign-plan"Fond de carte predefini ou URL template custom
no-controlsBooleanfalseMasque les controles de zoom Leaflet
fit-boundsBooleanfalseAjuste automatiquement le viewport aux donnees
max-boundsString""Limites "latSW,lonSW,latNE,lonNE"
nameString""Titre de la carte (aria-label et beacon)

Fonds de carte predefinis

Tuiles souveraines, accessibles sans cle API :

PresetSource
ign-planGeoplateforme IGN — Plan IGN
ign-orthoGeoplateforme IGN — Orthophoto
ign-topoGeoplateforme IGN — Carte topographique (SCAN 25/100)
ign-cadastreGeoplateforme IGN — Cadastre
osmOpenStreetMap France

dsfr-data-map-layer — Couche de donnees

Source et geolocalisation

AttributTypeDefautDescription
sourceString""ID de la source de donnees (requis)
typeString"marker"Type de couche : marker, geoshape, circle, heatmap
lat-fieldString""Chemin vers la latitude
lon-fieldString""Chemin vers la longitude
geo-fieldString""Chemin vers un GeoJSON (Point, Polygon, MultiPolygon)

Affichage

AttributTypeDefautDescription
popup-templateString""Template HTML avec interpolation : "{nom} — {puissance} kW"
popup-fieldsString""Champs separes par virgule pour un tableau auto
tooltip-fieldString""Champ affiche au survol
colorString"#000091"Couleur contours/markers (DSFR blue-france). Sert de fallback si color-map ne matche pas.
color-fieldString""Champ dont la valeur determine la couleur (mapping categoriel)
color-mapString""Paires valeur:#couleur separees par virgule. Ex: "1:#00A95F,2:#FF9940,3:#E1000F"
fill-fieldString""Champ numerique pour choropleth geoshape
fill-opacityNumber0.6Opacite du remplissage
selected-paletteString""Palette choropleth (memes noms que world-map)
radiusNumber8Rayon fixe (circle)
radius-fieldString""Champ pour rayon variable (cercles proportionnels)
radius-unitString"px"px (pixels) ou m (metres)
radius-minNumber4Rayon minimum pour l'auto-scaling (px)
radius-maxNumber30Rayon maximum pour l'auto-scaling (px)

Heatmap

AttributTypeDefautDescription
heat-radiusNumber25Rayon de la heatmap en pixels
heat-blurNumber15Flou de la heatmap en pixels
heat-fieldString""Champ numerique pour ponderer l'intensite

Clustering

AttributTypeDefautDescription
clusterBooleanfalseActive le clustering (leaflet.markercluster)
cluster-radiusNumber80Rayon de clustering en pixels

Zoom et viewport

AttributTypeDefautDescription
min-zoomNumber0Zoom minimum pour afficher cette couche
max-zoomNumber18Zoom maximum pour afficher cette couche
bboxBooleanfalseActive le filtrage par bounding box du viewport
bbox-debounceNumber300Delai ms avant re-fetch apres pan/zoom
bbox-fieldString""Champ geo pour la clause bbox (auto-detecte si vide)

Performance

AttributTypeDefautDescription
max-itemsNumber5000Nombre max d'elements rendus (0 = illimite)
filterString""Filtre client-side

Animation temporelle

AttributTypeDefautDescription
time-fieldString""Champ date/heure pour le decoupage temporel
time-bucketString"none"Granularite : none (valeurs brutes), hour, day, month, year
time-modeString"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.

dsfr-data-map-timeline — Animation temporelle

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.

AttributTypeDefautDescription
forString""IDs des layers cibles (virgules). Vide = tous les layers avec time-field
speedNumber1Multiplicateur de vitesse (0.5, 1, 2, 4)
intervalNumber1000Intervalle de base entre frames en ms

Controles

Accessibilite

dsfr-data-map-popup — Affichage au clic

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.

AttributTypeDefautDescription
modeString"popup"popup (infobulle Leaflet), modal (modale DSFR), panel-right, panel-left
title-fieldString""Champ pour le titre du panneau/modale
widthString"350px"Largeur du panneau lateral
forString""ID du layer cible (vide = tous les layers)

Template

Le contenu est defini via un element <template> enfant avec interpolation {{champ}}. Sans template, un tableau cle/valeur est genere automatiquement.

Modes

Resolution des coordonnees

Le layer resout les coordonnees dans cet ordre de priorite :

  1. lat-field + lon-field — Coordonnees separees (CSV, API)
  2. geo-field vers GeoJSON Point — Extrait lat/lon du Point
  3. geo-field vers Polygon/MultiPolygon — Rendu via L.geoJSON()
  4. Auto-detection — Cherche geo_point_2d, geo_shape, geometry

Exemples

1. POI simples avec clustering

<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>

2. Multi-resolution (zoom ranges)

<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>

3. Cercles proportionnels

<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>

4. Animation temporelle

<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>

5. Geoshape + POI superposes

<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>

Fonctionnement

Viewport-driven fetch (bbox)

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).

Zoom ranges

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+.

Clustering

Le clustering est charge conditionnellement via import dynamique de leaflet.markercluster. Les styles des clusters suivent les tokens DSFR.

Pipeline de donnees

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")

Bundle

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.