Una heatmap trasforma una nuvola di punti coordinate in una superficie di densita istantaneamente leggibile. I colori caldi segnano dove gli eventi si raggruppano; i colori freddi segnano dove si assottigliano. Gli utenti afferrano lo schema prima di leggere una singola etichetta. Per questo le heatmap sono la scelta predefinita per l'analisi del traffico pedonale, la previsione della domanda di consegna, gli strumenti di segnalazione dei crimini e la ricerca dei prezzi delle proprieta.
Questo tutorial costruisce una heatmap interattiva da zero utilizzando l'SDK di MapAtlas. Struttuherai i dati dei punti come GeoJSON ponderati, renderai uno strato di densita con un gradiente di colore personalizzato, collegherai un cursore di intensita e terminerai con un esempio reale di domanda di consegna. Alla fine avrai uno schema riutilizzabile che puoi inserire in qualsiasi progetto JavaScript o React.
Se sei nuovo all'SDK di MapAtlas, leggi prima Come aggiungere mappe interattive al tuo sito web. Copre l'installazione, l'inizializzazione della mappa e i marker. Questo tutorial riprende da dove quella lascia.
Quando usare una Heatmap
Le heatmap sono lo strumento giusto quando hai un grande set di singole posizioni di punti e vuoi comunicare la densita, non l'identita. I marker individuali perdono significato dopo poche centinaia di punti; una heatmap rivela la struttura che nessuna quantita di punzionatura potrebbe.
Casi d'uso comuni:
- Analisi del traffico pedonale: selezione dei siti di vendita al dettaglio, pianificazione urbana, modellazione della folla degli eventi. Rilascia un ping GPS per ogni cliente che ha attraversato una porta e la heatmap mostra quali aree di una citta, centro commerciale o venue attirano piu persone.
- Domanda di consegna: aggregare le origini degli ordini per codice postale o coordinata grezza per mostrare ai dispatcher dove la domanda e concentrata. Questo si collega direttamente alla pianificazione della zona e all'allocazione dei driver.
- Dati su crimini e incidenti: i dashboard di analisi della polizia, le mappe dei rischi di assicurazione e gli strumenti di segnalazione della sicurezza pubblica utilizzano heatmap di densita per comunicare il rischio spaziale senza travolgere gli utenti con marker individuali.
- Gradienti dei prezzi delle proprieta: quando combinati con valori ponderati, una heatmap puo mostrare dove i prezzi sono piu alti in tutta la citta. Vedi il tutorial della mappa immobiliare per ulteriori informazioni sulla costruzione di strumenti mappa focalizzati sulla proprieta.
Se la tua domanda e "dove accadono le cose principalmente?" una heatmap e la tua risposta.
Formato dei dati: Punti GeoJSON ponderati
Lo strato heatmap di MapAtlas legge una FeatureCollection GeoJSON standard di feature Point. Ogni feature puo avere una proprieta weight che scala il suo contributo alla superficie di densita. Un ordine di consegna di 10 articoli contribuisce piu calore rispetto a un ordine di un singolo articolo; un hub di transito principale genera piu traffico pedonale rispetto a una strada laterale.
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" }
}
]
};
Il valore weight e adimensionale. Lo normalizzi relativamente all'intervallo del tuo set di dati. Se la tua zona con la domanda piu alta genera 500 ordini e quella piu tranquilla ne genera 10, mappali su una scala da 1 a 10 prima di trasmetterli. Cio mantiene la heatmap visivamente significativa su set di dati con conteggi assoluti molto diversi.
Senza una proprieta weight, ogni punto contribuisce equamente e la heatmap riflette la pura densita di conteggio.
Prerequisiti
Prima di iniziare:
- Una chiave API di MapAtlas (iscritto gratuitamente, nessuna carta di credito richiesta)
- Node.js 18+ per progetti basati su npm, o una pagina HTML semplice se preferisci il CDN
Step 1: Installa e inizializza la mappa
Installa l'SDK:
npm install @mapmetrics/mapmetrics-gl
O caricalo tramite CDN in un file HTML semplice:
<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>
Aggiungi un contenitore con un'altezza definita:
<div id="map" style="width: 100%; height: 600px;"></div>
Inizializza la mappa. Lo stile Dark fa risaltare i gradienti di colore della heatmap sullo sfondo, motivo per cui e la base preferita per le visualizzazioni di densita:
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,
});
Step 2: Aggiungi il livello Heatmap
Registra il tuo GeoJSON come fonte, quindi aggiungi un livello heatmap che lo legga. Il callback map.on('load', ...) garantisce che lo stile abbia finito il caricamento prima che lo modifichi.
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 questo punto hai una heatmap funzionante. Le aree blu-verdi hanno bassa densita; il gradiente predefinito si sposta verso giallo e rosso ad alta densita. Il passo successivo sostituisce la tavolozza predefinita con uno schema di colore deliberato.
Step 3: Personalizzazione del gradiente di colore
La proprieta heatmap-color mappa i valori di densita (da 0 a 1) ai colori utilizzando la stessa espressione interpolate utilizzata nel resto del sistema di pittura di MapAtlas. La densita 0 e trasparente in modo che il livello di base della mappa sia visibile nelle aree vuote.
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
],
},
});
Questo gradiente corre dal blu (sparso) attraverso il verde e il giallo al rosso (denso), corrispondendo al modello mentale che la maggior parte degli utenti porta dalle mappe radar meteo e dall'imaging termico. Se la tua app ha una tavolozza di colori con marchio, sostituisci i valori RGB con i tuoi stop; l'interpolazione gestisce le transizioni fluide automaticamente.
Suggerimento di design: Per i dashboard professionali e gli strumenti di analisi, mantenere l'estremita a bassa densita quasi trasparente. Preserva la leggibilita della mappa di base in aree tranquille e attrae naturalmente l'occhio verso le zone calde.
Step 4: Aggiunta di un cursore del raggio per il controllo interattivo
Il raggio della heatmap controlla quanto lontano ogni punto 'irradia' influenza. Un piccolo raggio (10-15 px) mostra cluster a grana fine; un raggio grande (50-80 px) produce una superficie piu ampia e uniforme. I diversi casi d'uso richiedono impostazioni predefinite diverse: il traffico pedonale in un centro citta denso necessita di un piccolo raggio, la domanda nazionale di consegna in tutto il paese necessita di uno grande.
Dai agli utenti un cursore in modo che possano regolarlo al volo:
<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>
Collega il cursore a setPaintProperty, che aggiorna il livello senza ri-aggiungerlo:
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 e un aggiornamento in tempo reale senza sfarfallio. La heatmap si ri-renderizza sulla GPU nello stesso frame. Questo pattern funziona per qualsiasi proprieta di pittura: opacita, intensita, stop di colore.
Step 5: Ridimensionamento dell'intensita per livello di zoom
A bassi livelli di zoom (vista a livello di citta), i punti vicini si sovrappongono pesantemente e la heatmap puo apparire uniformemente satura. A zoom elevato (livello di strada), gli stessi punti si diffondono e la superficie di densita appare scarsa. La compensazione dell'intensita legata allo zoom mantiene la visualizzazione leggibile a ogni 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
],
},
Entrambe le proprieta utilizzano la stessa espressione interpolate rispetto a zoom. I valori tra gli stop vengono interpolati linearmente, quindi la transizione e fluida mentre l'utente esegue lo zoom.
Esempio di vita reale: Heatmap della domanda di consegna
Ecco un'implementazione completa e autonoma per un dashboard della domanda di consegna. Gli ordini arrivano da un'API come una FeatureCollection GeoJSON. La heatmap si aggiorna ogni volta che l'utente cambia il filtro temporale.
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);
});
});
});
La chiamata setData su una fonte esistente sostituisce il GeoJSON senza ri-aggiungere il livello. La heatmap si ri-renderizza automaticamente. Questo pattern si scala a qualsiasi filtro basato sul tempo: ora del giorno, giorno della settimana, condizione meteorologica.
Per lo strato di pianificazione del percorso che tipicamente accompagna una heatmap della domanda di consegna, vedi il tutorial dell'API di ottimizzazione dei percorsi. La sovrapposizione di un percorso di consegna ottimizzato in cima a una heatmap della domanda fornisce ai dispatcher un'immagine operativa completa in una singola vista.
Suggerimenti sulle prestazioni per i set di dati di grandi dimensioni
Lo strato heatmap di MapAtlas utilizza WebGL e renderizza velocemente, ma la pipeline di dati che lo alimenta puo diventare un collo di bottiglia in scala.
Pre-aggregare sul server per set di dati molto grandi. Se hai milioni di ping GPS, non inviarli tutti al browser. Esegui un'aggregazione spaziale lato server (griglia esagonale H3, quadtree o un semplice arrotondamento della griglia) che riduce i tuoi 1 milione di punti grezzi a 10.000 celle di griglia con un campo count e weight. La heatmap aparira identica all'utente e si caricherà in una frazione del tempo.
Aggiorna gli stream in modo incrementale. Per i dati in tempo reale (traffico pedonale in tempo reale, posizionamento degli ordini in tempo reale), utilizza setData con una finestra mobile di punti recenti piuttosto che accumulate un oggetto GeoJSON in continua crescita. Mantieni la fonte con un numero massimo fisso di punti ed elimina i record precedenti.
Usa un maxzoom sulla fonte. L'aggiunta di maxzoom: 14 alla tua chiamata addSource dice all'SDK di smettere di richiedere i dati dei tile sopra il livello 14. Per le heatmap questo raramente importa poiche i livelli heatmap leggono una singola fonte GeoJSON piatta piuttosto che dati a piastrelle, ma impedisce l'elaborazione non necessaria a livelli di zoom elevati.
Riduci la complessita della proprieta di pittura. Ogni stop interpolate aggiuntivo in un'espressione di pittura aggiunge il costo di valutazione della GPU per fotogramma. Per le app destinate ai dispositivi mobili, semplifica il gradiente di colore a tre o quattro stop e rimuovi il ridimensionamento del raggio/intensita legato allo zoom nelle visualizzazioni a bassa priorita.
Inizializza la mappa in modo differito. Avvolgi l'intera inizializzazione della mappa in un callback IntersectionObserver in modo che venga eseguito solo quando il contenitore della mappa scorre nella vista. Questo rinvia il bundle SDK dal caricamento della pagina iniziale ed e particolarmente prezioso nelle pagine di marketing in cui la mappa e sotto la piega.
Per un approfondimento piu dettagliato sui pattern di prestazione della mappa, incluso il caricamento pigro e il clustering, la checklist di produzione in Come aggiungere mappe interattive al tuo sito web copre l'elenco completo.
Integrazione React
Avvolgere la heatmap in un componente React segue lo stesso pattern di qualsiasi mappa MapAtlas: inizializza in useEffect, esponi lo stato per il cursore e il filtro tramite useState, e pulisci al dismount.
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>
);
}
In Next.js, importa questo componente con dynamic(() => import('./DeliveryHeatmap'), { ssr: false }) per evitare errori di server-side rendering dall'SDK solo browser.
Cosa costruire dopo
Ora hai una heatmap interattiva funzionante con dati ponderati, un gradiente di colore personalizzato, un cursore del raggio e intensita legata allo zoom. Ecco dove portarla:
- Sovrapponere uno strato di instradamento in cima alla heatmap per mostrare i percorsi di consegna pianificati sulla superficie della domanda. Il tutorial dell'API di ottimizzazione dei percorsi cammina attraverso l'implementazione completa.
- Aggiungi un'animazione temporale: cicla attraverso istantanee orarie con un loop
setIntervalche chiamasetDatasulla fonte. Questo trasforma una mappa di densita statica in un time-lapse di come la domanda si muove attraverso la giornata. - Combina con i dati dei prezzi delle proprieta per mostrare i gradienti dei prezzi per il quartiere. Il tutorial della mappa immobiliare copre i pattern di dati ponderati per le piattaforme immobiliari.
- Controlla la pagina di prezzo di MapAtlas per trovare il piano giusto per il tuo traffico di produzione.
Il riferimento SDK completo e gli esempi aggiuntivi sono disponibili su docs.mapatlas.xyz.
Domande frequenti
Qual e la differenza tra una heatmap e una mappa coropleta?
Una heatmap visualizza la densita dei punti mescolando i punti vicini in un gradiente di colore continuo, ideale per i dati di coordinate grezzi come i ping GPS o le posizioni degli eventi. Una mappa coropleta colora le aree geografiche predefinite (paesi, codici postali, tracciati di censimento) per un valore statistico. Usa una heatmap quando hai molti punti individuali; usa una coropleta quando i tuoi dati sono gia aggregati per regione.
Quanti punti dati puo gestire una heatmap JavaScript?
Lo strato heatmap di MapAtlas renderizza sulla GPU tramite WebGL, quindi gestisce decine di migliaia di punti senza cadute di frame a normali livelli di zoom. Al di sopra di circa 500.000 punti, pre-aggregando i tuoi dati lato server in una griglia a risoluzione inferiore e passando a un livello di estruzione di riempimento GeoJSON o cerchio, si ottengono migliori prestazioni su dispositivi di fascia bassa.
Posso usare le heatmap di MapAtlas gratuitamente?
Si. MapAtlas ha un livello gratuito che include il rendering delle piastrelle della mappa, il supporto del livello GeoJSON e i livelli heatmap. Il piano gratuito copre lo sviluppo e l'uso di produzione a basso volume. Vedi mapatlas.eu/pricing per i dettagli completi del piano.
Le heatmap funzionano sui browser mobili?
Si. L'SDK di MapAtlas utilizza WebGL per il rendering, che e supportato in tutti i browser mobili moderni incluso Safari su iOS e Chrome su Android. Per i set di dati molto grandi su hardware mobile di fascia bassa, ridurre il numero di punti o aumentare il raggio della heatmap mantiene uniforme la frequenza dei fotogrammi.
Letture correlate:

