Une heatmap transforme un nuage de points de coordonnees en une surface de densite immediatement lisible. Les couleurs chaudes indiquent ou les evenements se regroupent, les couleurs froides indiquent ou ils se dispersent. Les utilisateurs saisissent le motif avant de lire une seule etiquette. C'est pour cette raison que les heatmaps sont le choix par defaut pour l'analyse du trafic pieton, la prevision de la demande de livraison, les outils de signalement de criminalite et la recherche sur les prix immobiliers.
Ce tutoriel construit une heatmap interactive a partir de zero en utilisant le SDK MapAtlas. Vous structurerez les donnees de points sous forme de GeoJSON ponderes, rendrez une couche de densite avec un gradient de couleur personnalise, mettrez en place un curseur d'intensite et terminerez par un exemple reel de demande de livraison. A la fin, vous disposerez d'un motif reutilisable que vous pourrez integrer dans n'importe quel projet JavaScript ou React.
Si vous etes nouveau sur le SDK MapAtlas, consultez d'abord Comment ajouter des cartes interactives a votre site Web. Il couvre l'installation, l'initialisation de la carte et les marqueurs. Ce tutoriel reprend la ou le precedent s'etait arrete.
Quand utiliser une heatmap
Les heatmaps sont l'outil ideal si vous disposez d'un grand ensemble d'emplacements de points individuels et souhaitez communiquer la densite plutot que l'identite. Les marqueurs individuels perdent leur sens au-dela de quelques centaines de points, tandis qu'une heatmap revele une structure qu'aucune quantite de marqueurs ne pourrait montrer.
Cas d'usage courants :
- Analyse du trafic pieton : selection de sites de vente au detail, urbanisme, modelisation des foules lors d'evenements. Deposez un signal GPS pour chaque client qui est passe par une porte et la heatmap montre quels domaines d'une ville, d'un centre commercial ou d'un lieu attirent le plus de personnes.
- Demande de livraison : agregers les origines des commandes par code postal ou coordonnees brutes pour montrer aux expediteurs ou la demande est concentree. Cela alimente directement la planification des zones et l'allocation des chauffeurs.
- Donnees criminelles et d'incidents : les tableaux de bord d'analyse policiere, les cartes de risques d'assurance et les outils de signalement de securite publique utilisent tous les heatmaps de densite pour communiquer le risque spatial sans surcharger les utilisateurs avec des marqueurs individuels.
- Gradients de prix immobiliers : lorsqu'elle est combinee avec des valeurs ponderees, une heatmap peut montrer ou les prix sont les plus eleves dans une ville. Consultez le tutoriel de carte immobiliere pour plus d'informations sur la construction d'outils de cartographie axes sur l'immobilier.
Si votre question est « ou les choses se produisent-elles le plus souvent ? », une heatmap est votre reponse.
Format des donnees : points GeoJSON ponderes
La couche heatmap de MapAtlas lit une collection GeoJSON FeatureCollection standard de fonctionnalites Point. Chaque fonctionnalite peut avoir une propriete weight qui redimensionne sa contribution a la surface de densite. Une commande de livraison contenant 10 articles genere plus de chaleur qu'une commande d'un seul article, et un hub de transport majeur genere plus de trafic pieton qu'une rue laterate.
const demandData = {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: { type: "Point", coordinates: [4.8952, 52.3702] },
properties: { weight: 8, zone: "centrum" }
},
{
type: "Feature",
geometry: { type: "Point", coordinates: [4.9123, 52.3601] },
properties: { weight: 3, zone: "oost" }
},
{
type: "Feature",
geometry: { type: "Point", coordinates: [4.8801, 52.3780] },
properties: { weight: 12, zone: "west" }
},
{
type: "Feature",
geometry: { type: "Point", coordinates: [4.9041, 52.3540] },
properties: { weight: 1, zone: "south" }
}
]
};
La valeur weight est sans dimension. Vous la normalisez en fonction de la plage de votre ensemble de donnees. Si votre zone a la plus forte demande genere 500 commandes et votre zone la plus calme en genere 10, mappez-les a une echelle de 1 a 10 avant de les passer. Cela maintient la heatmap visuellement significative dans les ensembles de donnees avec des chiffres absolus tres differents.
Sans propriete weight, chaque point contribue egalement et la heatmap reflete la pure densite de comptage.
Prerequis
Avant de commencer :
- Une cle d'API MapAtlas (inscrivez-vous gratuitement, aucune carte de credit requise)
- Node.js 18+ pour les projets a base de npm, ou une page HTML simple si vous preferez le CDN
Etape 1 : Installer et initialiser la carte
Installez le SDK :
npm install @mapmetrics/mapmetrics-gl
Ou chargez-le via CDN dans une fichier HTML ordinaire :
<link
rel="stylesheet"
href="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css"
/>
<script src="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.js"></script>
Ajoutez un conteneur avec une hauteur definie :
<div id="map" style="width: 100%; height: 600px;"></div>
Initialisez la carte. Le style sombre fait ressortir les gradients de couleur de la heatmap sur le fond, c'est pourquoi il est la base preferee pour les visualisations de densite :
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const map = new mapmetricsgl.Map({
container: 'map',
style: 'https://tiles.mapatlas.eu/styles/dark/style.json?key=YOUR_API_KEY',
center: [4.9041, 52.3676], // Amsterdam
zoom: 12,
});
Etape 2 : Ajouter la couche heatmap
Enregistrez votre GeoJSON en tant que source, puis ajoutez une couche heatmap qui le lit. Le rappel map.on('load', ...) garantit que le style a fini de charger avant de le modifier.
map.on('load', () => {
map.addSource('demand', {
type: 'geojson',
data: demandData,
});
map.addLayer({
id: 'demand-heatmap',
type: 'heatmap',
source: 'demand',
paint: {
// Weight each point by its 'weight' property (defaults to 1 if absent)
'heatmap-weight': [
'interpolate', ['linear'],
['get', 'weight'],
0, 0,
12, 1
],
// Radius in pixels; larger = smoother but less precise
'heatmap-radius': 30,
// Overall opacity of the heatmap layer
'heatmap-opacity': 0.85,
},
});
});
A ce stade, vous disposez d'une heatmap fonctionnelle. Les zones bleu-vert sont a faible densite, le gradient par defaut se deplece vers le jaune et le rouge a forte densite. L'etape suivante remplace la palette par defaut par un schema de couleur delibere.
Etape 3 : Personnaliser le gradient de couleur
La propriete heatmap-color mappe les valeurs de densite (0 a 1) aux couleurs en utilisant la meme expression interpolate utilisee dans le reste du systeme de peinture de MapAtlas. La densite 0 est transparente afin que la couche de base de la carte soit visible dans les zones vides.
map.addLayer({
id: 'demand-heatmap',
type: 'heatmap',
source: 'demand',
paint: {
'heatmap-weight': [
'interpolate', ['linear'],
['get', 'weight'],
0, 0,
12, 1
],
'heatmap-radius': 30,
'heatmap-opacity': 0.85,
'heatmap-color': [
'interpolate', ['linear'],
['heatmap-density'],
0, 'rgba(0, 0, 255, 0)', // transparent at zero density
0.2, 'rgba(0, 128, 255, 0.6)',
0.4, 'rgba(0, 230, 200, 0.7)',
0.6, 'rgba(100, 230, 0, 0.8)',
0.8, 'rgba(255, 200, 0, 0.9)',
1.0, 'rgba(255, 50, 0, 1)' // bright red at peak density
],
},
});
Ce gradient va du bleu (clairseme) en passant par le vert et le jaune jusqu'au rouge (dense), correspondant au modele mental que la plupart des utilisateurs ont des cartes meteo et de l'imagerie thermique. Si votre application a une palette de couleurs de marque, remplacez les valeurs RVB par vos propres arrets, l'interpolation gere les transitions en douceur automatiquement.
Conseil de conception : pour les tableaux de bord professionnels et les outils d'analyse, gardez l'extremite a faible densite presque transparente. Cela preserve la lisibilite de la carte de base dans les zones calmes et attire naturellement l'oeil vers les zones chaudes.
Etape 4 : Ajouter un curseur de rayon pour le controle interactif
Le rayon de la heatmap controle la distance a laquelle chaque point « rayonne » son influence. Un petit rayon (10 a 15px) montre des grappes fines, un grand rayon (50 a 80px) produit une surface plus large et plus lisse. Differents cas d'usage necessitent differents parametres par defaut : le trafic pieton dans un centre-ville dense necessite un petit rayon, la demande de livraison nationale dans un pays entier necessite un grand rayon.
Donnez aux utilisateurs un curseur pour qu'ils puissent l'ajuster a la volee :
<div id="controls" style="position: absolute; top: 16px; left: 16px; z-index: 1;
background: white; padding: 12px 16px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.2);">
<label style="font-size: 14px; font-weight: 600;">
Radius: <span id="radius-value">30</span>px
</label>
<br />
<input id="radius-slider" type="range" min="5" max="80" value="30" style="width: 180px; margin-top: 6px;" />
</div>
Connectez le curseur a setPaintProperty, qui met a jour la couche sans la rajouter :
const slider = document.getElementById('radius-slider');
const radiusLabel = document.getElementById('radius-value');
slider.addEventListener('input', () => {
const radius = Number(slider.value);
radiusLabel.textContent = radius;
map.setPaintProperty('demand-heatmap', 'heatmap-radius', radius);
});
setPaintProperty est une mise a jour en direct sans scintillement. La heatmap est re-rendue sur le GPU dans la meme trame. Ce motif fonctionne pour toute propriete de peinture : opacite, intensite, arrets de couleur.
Etape 5 : Mise a l'echelle de l'intensite par niveau de zoom
Aux faibles niveaux de zoom (vue au niveau de la ville), les points proches se chevauchent fortement et la heatmap peut sembler uniformement saturee. Aux zooms eleves (niveau de la rue), les memes points s'etalent et la surface de densite semble clairsemee. La compensation d'intensite liee au zoom garde la visualisation lisible a chaque zoom.
paint: {
// ...other paint properties...
'heatmap-intensity': [
'interpolate', ['linear'],
['zoom'],
8, 1, // low zoom: normal intensity
14, 3 // high zoom: boost intensity to compensate for point spread
],
'heatmap-radius': [
'interpolate', ['linear'],
['zoom'],
8, 20, // small radius at city scale
14, 50 // larger radius at street scale
],
},
Les deux proprietes utilisent la meme expression interpolate sur zoom. Les valeurs entre les arrets sont interpolees lineairement, de sorte que la transition est en douceur lors du zoom de l'utilisateur.
Exemple du monde reel : heatmap de demande de livraison
Voici une implementation complete et autonome pour un tableau de bord de demande de livraison. Les commandes arrivent d'une API sous forme de FeatureCollection GeoJSON. La heatmap se met a jour chaque fois que l'utilisateur change le filtre de temps.
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const API_KEY = 'YOUR_API_KEY';
const map = new mapmetricsgl.Map({
container: 'map',
style: `https://tiles.mapatlas.eu/styles/dark/style.json?key=${API_KEY}`,
center: [4.9041, 52.3676],
zoom: 11,
});
async function loadOrders(hourFrom, hourTo) {
const res = await fetch(
`/api/orders/geojson?hour_from=${hourFrom}&hour_to=${hourTo}`
);
return res.json(); // returns a GeoJSON FeatureCollection
}
map.on('load', async () => {
const orders = await loadOrders(8, 12); // morning peak
map.addSource('orders', { type: 'geojson', data: orders });
map.addLayer({
id: 'order-heatmap',
type: 'heatmap',
source: 'orders',
paint: {
'heatmap-weight': [
'interpolate', ['linear'], ['get', 'items'],
0, 0, 20, 1
],
'heatmap-color': [
'interpolate', ['linear'], ['heatmap-density'],
0, 'rgba(0, 0, 255, 0)',
0.2, 'rgba(0, 128, 255, 0.5)',
0.5, 'rgba(0, 230, 150, 0.8)',
0.8, 'rgba(255, 200, 0, 0.9)',
1.0, 'rgba(255, 50, 0, 1)'
],
'heatmap-radius': [
'interpolate', ['linear'], ['zoom'],
8, 20, 14, 50
],
'heatmap-intensity': [
'interpolate', ['linear'], ['zoom'],
8, 1, 14, 3
],
'heatmap-opacity': 0.9,
},
});
// Time-of-day filter buttons
document.querySelectorAll('[data-hour-range]').forEach((btn) => {
btn.addEventListener('click', async () => {
const [from, to] = btn.dataset.hourRange.split('-').map(Number);
const newOrders = await loadOrders(from, to);
map.getSource('orders').setData(newOrders);
});
});
});
L'appel setData sur une source existante remplace le GeoJSON sans re-ajouter la couche. La heatmap se re-rend automatiquement. Ce motif s'adapte a n'importe quel filtre base sur le temps : heure du jour, jour de la semaine, condition meteorologique.
Pour la couche de planification d'itineraire qui accompagne generalement une heatmap de demande de livraison, consultez le tutoriel de l'API de routage. L'integration d'un itineraire de livraison optimise au-dessus d'une heatmap de demande donne aux expediteurs une vision operationnelle complete en une seule vue.
Conseils de performance pour les grands ensembles de donnees
La couche heatmap de MapAtlas utilise WebGL et se rend rapidement, mais le pipeline de donnees l'alimentant peut devenir un goulot d'etranglement a grande echelle.
Pre-agregers sur le serveur pour tres grands ensembles de donnees. Si vous avez des millions de pings GPS, n'envoyez pas tous au navigateur. Executez une agregation spatiale cote serveur (grille hexagonale H3, quadtree ou simple arrondi de grille) qui reduit vos 1 million de points bruts a 10 000 cellules de grille avec un champ count et weight. La heatmap aura l'air identique pour l'utilisateur et se chargera une fraction du temps.
Flux des mises a jour progressivement. Pour les donnees en direct (trafic pieton en temps reel, placement de commandes en direct), utilisez setData avec une fenetre roulante de points recents plutot que d'accumuler un objet GeoJSON en croissance constante. Gardez la source a un nombre de points maximum fixe et evacuez les anciens enregistrements.
Utilisez un maxzoom sur la source. Ajouter maxzoom: 14 a votre appel addSource indique au SDK d'arreter de demander des donnees de tuiles au-dessus du zoom 14. Pour les heatmaps, cela a rarement d'importance puisque les couches heatmap lisent une seule source GeoJSON plate plutot que des donnees carrelees, mais cela empeche le re-traitement inutile aux niveaux de zoom eleves.
Reduisez la complexite des proprietes de peinture. Chaque arret interpolate supplementaire dans une expression de peinture ajoute un cout d'evaluation GPU par trame. Pour les applications orientees vers le mobile, simplifiez le gradient de couleur a trois ou quatre arrets et abandonnez la mise a l'echelle du rayon et de l'intensite liee au zoom sur les vues de priorite inferieure.
Initialisation paresseuse de la carte. Enveloppez l'initialisation entiere de la carte dans un rappel IntersectionObserver afin qu'elle ne s'execute que lorsque le conteneur de carte s'affiche. Cela differe le faisceau SDK du chargement de la page initiale et est particulierement precieux sur les pages de marketing ou la carte se trouve sous le pli.
Pour une plongee plus profonde dans les motifs de performance de la carte, y compris le chargement paresseux et le clustering, la liste de controle de production dans Comment ajouter des cartes interactives a votre site Web couvre la liste complete.
Integration React
L'enveloppe de la heatmap dans un composant React suit le meme motif que n'importe quelle carte MapAtlas : initialiser dans useEffect, exposer l'etat pour le curseur et le filtre via useState, et nettoyer au demontage.
import { useEffect, useRef, useState } from 'react';
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
export function DeliveryHeatmap({ geojson, apiKey }) {
const containerRef = useRef(null);
const mapRef = useRef(null);
const [radius, setRadius] = useState(30);
useEffect(() => {
const map = new mapmetricsgl.Map({
container: containerRef.current,
style: `https://tiles.mapatlas.eu/styles/dark/style.json?key=${apiKey}`,
center: [4.9041, 52.3676],
zoom: 11,
});
mapRef.current = map;
map.on('load', () => {
map.addSource('orders', { type: 'geojson', data: geojson });
map.addLayer({
id: 'order-heatmap',
type: 'heatmap',
source: 'orders',
paint: {
'heatmap-radius': radius,
'heatmap-opacity': 0.9,
},
});
});
return () => map.remove();
}, [apiKey]);
// Update radius without remounting the map
useEffect(() => {
const map = mapRef.current;
if (!map || !map.getLayer('order-heatmap')) return;
map.setPaintProperty('order-heatmap', 'heatmap-radius', radius);
}, [radius]);
return (
<div>
<div style={{ padding: '8px 0' }}>
<label>
Radius: {radius}px
<input
type="range" min={5} max={80} value={radius}
onChange={e => setRadius(Number(e.target.value))}
style={{ marginLeft: 8, width: 160 }}
/>
</label>
</div>
<div ref={containerRef} style={{ width: '100%', height: '600px' }} />
</div>
);
}
Dans Next.js, importez ce composant avec dynamic(() => import('./DeliveryHeatmap'), { ssr: false }) pour eviter les erreurs de rendu cote serveur du SDK pour le navigateur uniquement.
Ce qu'il faut construire ensuite
Vous disposez maintenant d'une heatmap interactive fonctionnelle avec des donnees ponderees, un gradient de couleur personnalise, un curseur de rayon et une intensite liee au zoom. Voici comment l'ameliorer :
- Superposez une couche de routage au-dessus de la heatmap pour montrer les routes de livraison planifiees par rapport a la surface de demande. Le tutoriel de l'API de routage expose l'implementation complete.
- Ajoutez une animation temporelle : parcourez les instantanes horaires avec une boucle
setIntervalappelantsetDatasur la source. Cela transforme une carte de densite statique en un film accelere de la facon dont la demande evolue au cours de la journee. - Combinez avec des donnees de prix immobiliers pour montrer des gradients de prix par quartier. Le tutoriel de carte immobiliere couvre les motifs de donnees ponderees pour les plates-formes immobilieres.
- Consultez la page de tarification de MapAtlas pour trouver le plan adapte a votre trafic de production.
La reference SDK complete et des exemples supplementaires sont disponibles a docs.mapatlas.xyz.
Questions frequemment posees
Quelle est la difference entre une heatmap et une carte choropleth ?
Une heatmap visualise la densite des points en fusionnant les points proches en un gradient de couleur continu, ideal pour les donnees de coordonnees brutes comme les pings GPS ou les emplacements d'evenements. Une carte choropleth colore les zones geographiques predefinis (pays, codes postaux, zones de denombrement) en fonction d'une valeur statistique. Utilisez une heatmap si vous avez de nombreux points individuels, utilisez une carte choropleth si vos donnees sont deja agreges par region.
Combien de points de donnees une heatmap JavaScript peut-elle gerer ?
La couche heatmap de MapAtlas se rend sur le GPU via WebGL, elle gere donc des dizaines de milliers de points sans chute d'images aux niveaux de zoom normaux. Au-dessus d'environ 500 000 points, l'agregation de vos donnees cote serveur dans une grille a resolution inferieure et le basculement vers une couche de remplissage d'extrusion GeoJSON ou de cercle offre de meilleures performances sur les appareils bas de gamme.
Puis-je utiliser les heatmaps de MapAtlas gratuitement ?
Oui. MapAtlas a un niveau gratuit qui inclut le rendu des tuiles de carte, le support de la couche GeoJSON et les couches heatmap. Le plan gratuit couvre le developpement et l'utilisation en production a bas volume. Consultez mapatlas.eu/pricing pour plus de details sur le plan complet.
Les heatmaps fonctionnent-elles sur les navigateurs mobiles ?
Oui. Le SDK MapAtlas utilise WebGL pour le rendu, qui est pris en charge dans tous les navigateurs mobiles modernes, y compris Safari sur iOS et Chrome sur Android. Pour les tres grands ensembles de donnees sur le materiel mobile bas de gamme, la reduction du nombre de points ou l'augmentation du rayon de la heatmap maintient les frequences d'images en douceur.
Lectures connexes :

