CARTO for deck.gl

CARTO for deck.gl

Clustering

This example shows how to create clusters of points.

  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.5.0/dist.min.js"></script>
    <script src="https://unpkg.com/@deck.gl/carto@^8.5.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: deck.carto.BASEMAP.VOYAGER,

        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>