CARTO for deck.gl

CARTO for deck.gl

Clustering

This example shows how to create clusters of points.

Note that you can use any Deck.gl layer with CARTO datasets getting GeoJSON data from CARTO’s API. This method is recommended for complex layers with datasets below 50Mb.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<html>
  <head>
    <script src="https://unpkg.com/deck.gl@^8.4.0/dist.min.js"></script>
    <script src="https://unpkg.com/@deck.gl/carto@^8.4.0/dist.min.js"></script>

    <script src="https://unpkg.com/supercluster@6.0.2/dist/supercluster.min.js"></script>
    
    <script src="https://libs.cartocdn.com/mapbox-gl/v1.13.0/mapbox-gl.js"></script>
    <link href="https://libs.cartocdn.com/mapbox-gl/v1.13.0/mapbox-gl.css" rel="stylesheet" />
  </head>

  <body style="margin: 0; padding: 0">
    <div id="map" style="width: 100vw; height: 100vh;"></div>
  </body>

  <script type="text/javascript">
    (async ()=>{
      const {DeckGL, CompositeLayer, IconLayer} = deck;
      const {CartoSQLLayer, setDefaultCredentials} = deck.carto;

      // Define your custom IconClusterLayer
      function getIconName(size) {
        if (size === 0) {
          return '';
        }
        if (size < 10) {
          return `marker-${size}`;
        }
        if (size < 100) {
          return `marker-${Math.floor(size / 10)}0`;
        }
        return 'marker-100';
      }

      function getIconSize(size) {
        return Math.min(100, size) / 100 + 1;
      }

      class IconClusterLayer extends CompositeLayer {
        shouldUpdateState({changeFlags}) {
          return changeFlags.somethingChanged;
        }

        updateState({props, oldProps, changeFlags}) {
          const rebuildIndex = changeFlags.dataChanged || props.sizeScale !== oldProps.sizeScale;

          if (rebuildIndex) {
            const index = new Supercluster({maxZoom: 16, radius: props.sizeScale});
            index.load(
              props.data.map(d => ({
                geometry: {coordinates: props.getPosition(d)},
                properties: d
              }))
            );
            this.setState({index});
          }

          const z = Math.floor(this.context.viewport.zoom);
          if (rebuildIndex || z !== this.state.z) {
            this.setState({
              data: this.state.index.getClusters([-180, -85, 180, 85], z),
              z
            });
          }
        }

        renderLayers() {
          const {data} = this.state;
          const {iconAtlas, iconMapping, sizeScale} = this.props;

          return new IconLayer(
            this.getSubLayerProps({
              id: 'icon',
              data,
              iconAtlas,
              iconMapping,
              sizeScale,
              getPosition: d => d.geometry.coordinates,
              getIcon: d => getIconName(d.properties.cluster ? d.properties.point_count : 1),
              getSize: d => getIconSize(d.properties.cluster ? d.properties.point_count : 1)
            })
          );
        }
      }

      // Fetch Data from CARTO
      // Notice that you can use any Deck.gl layer with CARTO datasets getting GeoJSON data from CARTO's API. This method is recommended for complex layers with datasets below 50Mb
      const dataResponse = await fetch('https://public.carto.com/api/v2/sql?q=SELECT cartodb_id, the_geom FROM meteorites&format=geojson');
      const data = await dataResponse.json();

      // Create a Deck.gl map and use your IconClusterLayer with your CARTO dataset
      const deckgl = new DeckGL({
        container: 'map',
        mapStyle: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',

        initialViewState: {
          latitude: 0,
          longitude: 0,
          zoom: 1
        },
        controller: true,

        layers: [
          new IconClusterLayer({
            id: 'icon-cluster',
            data: data.features,
            getPosition: d => d.geometry.coordinates,
            iconAtlas: 'https://raw.githubusercontent.com/visgl/deck.gl/8.4-release/examples/website/icon/data/location-icon-atlas.png',
            iconMapping: 'https://raw.githubusercontent.com/visgl/deck.gl/8.4-release/examples/website/icon/data/location-icon-mapping.json',
            sizeScale: 60
          })
        ]
      });
    })();
  </script>
</html>