mapbox-data-visualization-patterns

Par mapbox · mapbox-agent-skills

Modèles de visualisation de données sur des cartes, notamment les cartes choroplèthes, les cartes thermiques, les visualisations 3D, le stylisme piloté par les données et les données animées. Couvre les types de couches, les échelles de couleurs et l'optimisation des performances.

npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-data-visualization-patterns

Compétence Patterns de Visualisation de Données

Patterns complets pour visualiser des données sur des cartes Mapbox. Couvre les cartes choroplèthes, les cartes thermiques, les extrusions 3D, le style piloté par les données, les visualisations animées et l'optimisation des performances pour les applications avec beaucoup de données.

Quand utiliser cette compétence

Utilisez cette compétence quand :

  • Visualiser des données statistiques sur des cartes (population, ventes, démographie)
  • Créer des cartes choroplèthes avec des régions codées par couleur
  • Construire des cartes thermiques ou du clustering pour la visualisation de densité
  • Ajouter des visualisations 3D (hauteurs de bâtiments, élévation du terrain)
  • Implémenter un style piloté par les données basé sur les propriétés
  • Animer des données de séries temporelles
  • Travailler avec de grands jeux de données nécessitant une optimisation

Types de Visualisation

Cartes Choroplèthes

Idéal pour : Données régionales (États, comtés, codes postaux), comparaisons statistiques

Pattern : Coder par couleur les polygones en fonction des valeurs de données

map.on('load', () => {
  // Ajouter la source de données (GeoJSON avec propriétés)
  map.addSource('states', {
    type: 'geojson',
    data: 'https://example.com/states.geojson' // Features avec propriété population
  });

  // Ajouter un calque de remplissage avec couleur pilotée par les données
  map.addLayer({
    id: 'states-layer',
    type: 'fill',
    source: 'states',
    paint: {
      'fill-color': [
        'interpolate',
        ['linear'],
        ['get', 'population'],
        0,
        '#f0f9ff', // Bleu clair pour population faible
        500000,
        '#7fcdff',
        1000000,
        '#0080ff',
        5000000,
        '#0040bf', // Bleu foncé pour population élevée
        10000000,
        '#001f5c'
      ],
      'fill-opacity': 0.75
    }
  });

  // Ajouter un calque de bordure
  map.addLayer({
    id: 'states-border',
    type: 'line',
    source: 'states',
    paint: {
      'line-color': '#ffffff',
      'line-width': 1
    }
  });

  // Ajouter un effet de survol avec popup réutilisable
  const popup = new mapboxgl.Popup({
    closeButton: false,
    closeOnClick: false
  });

  map.on('mousemove', 'states-layer', (e) => {
    if (e.features.length > 0) {
      map.getCanvas().style.cursor = 'pointer';

      const feature = e.features[0];
      popup
        .setLngLat(e.lngLat)
        .setHTML(
          `
          <h3>${feature.properties.name}</h3>
          <p>Population: ${feature.properties.population.toLocaleString()}</p>
        `
        )
        .addTo(map);
    }
  });

  map.on('mouseleave', 'states-layer', () => {
    map.getCanvas().style.cursor = '';
    popup.remove();
  });
});

step vs interpolate : L'exemple ci-dessus utilise interpolate pour des dégradés de couleur lisses. Pour des buckets de couleur discrets (par exemple, « faible / moyen / élevé »), utilisez à la place ['step', ['get', 'population'], '#f0f0f0', 500000, '#fee0d2', 2000000, '#fc9272', 10000000, '#de2d26']. Préférez step quand les données ont des catégories naturelles ou quand les valeurs limites exactes sont importantes.

Stratégies d'Échelle de Couleur :

// Interpolation linéaire (échelle continue)
'fill-color': [
  'interpolate',
  ['linear'],
  ['get', 'value'],
  0, '#ffffcc',
  25, '#78c679',
  50, '#31a354',
  100, '#006837'
]

// Intervalles par étapes (buckets discrets)
'fill-color': [
  'step',
  ['get', 'value'],
  '#ffffcc',  // Couleur par défaut
  25, '#c7e9b4',
  50, '#7fcdbb',
  75, '#41b6c4',
  100, '#2c7fb8'
]

// Basé sur des cas (données catégoriques)
'fill-color': [
  'match',
  ['get', 'category'],
  'residential', '#ffd700',
  'commercial', '#ff6b6b',
  'industrial', '#4ecdc4',
  'park', '#45b7d1',
  '#cccccc'  // Défaut
]

Cartes Thermiques

Idéal pour : Densité de points, localisation d'événements, clustering d'incidents

Pattern : Visualiser la densité de points

map.on('load', () => {
  // Ajouter la source de données (points)
  map.addSource('incidents', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [
        {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [-122.4194, 37.7749]
          },
          properties: {
            intensity: 1
          }
        }
        // ... plus de points
      ]
    }
  });

  // Ajouter un calque heatmap
  map.addLayer({
    id: 'incidents-heat',
    type: 'heatmap',
    source: 'incidents',
    maxzoom: 15,
    paint: {
      // Augmenter le poids basé sur la propriété intensity
      'heatmap-weight': ['interpolate', ['linear'], ['get', 'intensity'], 0, 0, 6, 1],
      // Augmenter l'intensité à mesure que le niveau de zoom augmente
      'heatmap-intensity': ['interpolate', ['linear'], ['zoom'], 0, 1, 15, 3],
      // Palette de couleurs pour la heatmap
      'heatmap-color': [
        'interpolate',
        ['linear'],
        ['heatmap-density'],
        0,
        'rgba(33,102,172,0)',
        0.2,
        'rgb(103,169,207)',
        0.4,
        'rgb(209,229,240)',
        0.6,
        'rgb(253,219,199)',
        0.8,
        'rgb(239,138,98)',
        1,
        'rgb(178,24,43)'
      ],
      // Ajuster le rayon par niveau de zoom
      'heatmap-radius': ['interpolate', ['linear'], ['zoom'], 0, 2, 15, 20],
      // Diminuer l'opacité aux niveaux de zoom plus élevés
      'heatmap-opacity': ['interpolate', ['linear'], ['zoom'], 7, 1, 15, 0]
    }
  });

  // Ajouter un calque de cercles pour les points individuels aux niveaux de zoom élevés
  map.addLayer({
    id: 'incidents-point',
    type: 'circle',
    source: 'incidents',
    minzoom: 14,
    paint: {
      'circle-radius': ['interpolate', ['linear'], ['zoom'], 14, 4, 22, 30],
      'circle-color': '#ff4444',
      'circle-opacity': 0.8,
      'circle-stroke-color': '#fff',
      'circle-stroke-width': 1
    }
  });
});

Bonnes Pratiques

Accessibilité des Couleurs

// Utiliser les échelles ColorBrewer pour l'accessibilité
// https://colorbrewer2.org/

// Bon : Séquentiel (teinte unique)
const sequentialScale = ['#f0f9ff', '#bae4ff', '#7fcdff', '#0080ff', '#001f5c'];

// Bon : Divergent (deux teintes)
const divergingScale = ['#d73027', '#fc8d59', '#fee08b', '#d9ef8b', '#91cf60', '#1a9850'];

// Bon : Qualitatif (catégories distinctes)
const qualitativeScale = ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00'];

// À éviter : Rouge-vert pour l'accessibilité daltonisme
// Utiliser : Bleu-orange ou violet-vert à la place

Gestion des Erreurs

// Gérer les données manquantes ou invalides
map.on('load', () => {
  map.addSource('data', {
    type: 'geojson',
    data: dataUrl
  });

  map.addLayer({
    id: 'data-viz',
    type: 'fill',
    source: 'data',
    paint: {
      'fill-color': [
        'case',
        ['has', 'value'], // Vérifier si la propriété existe
        ['interpolate', ['linear'], ['get', 'value'], 0, '#f0f0f0', 100, '#0080ff'],
        '#cccccc' // Couleur par défaut pour les données manquantes
      ]
    }
  });

  // Gérer les erreurs de carte
  map.on('error', (e) => {
    console.error('Map error:', e.error);
  });
});

Règle de Taille de Données

  • < 1 MB : Utiliser GeoJSON directement
  • 1–10 MB : Considérer soit GeoJSON soit vector tiles selon la complexité
  • > 10 MB : Utiliser vector tiles (télécharger sur Mapbox comme tileset)

Voir references/performance.md pour les détails d'implémentation.

Fichiers de Référence

Pour des patterns de visualisation supplémentaires, charger le fichier de référence pertinent :

Ressources

Skills similaires