Search code examples
mappingmarkerclustererdonut-chartcartodbcartocss

Cartojs4 - markers with multivalues donut chart


I would like to create a marker as we can see in the Carto SalesQuest product.

I made a cluster map from my CARTOjs 4 following this article provided by CARTO

https://carto.com/blog/inside/tile-aggregation/

I can play with CARTOCSS to style my layer but I am wondering what would be the best practices / methodology to achieve such an example : see below

Multivalues donut chart, cluster markers as seen in CARTO Salesquest

My own version of clusters with CARTO JS 4

My code look like this (hidden API KEY)

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="initial-scale=1.0" />
    <meta charset="utf-8" />
    <!-- Include Carto.js -->
    <script src="https://cartodb-libs.global.ssl.fastly.net/carto.js/v4.0.2/carto.min.js"></script>
    <!-- Include Leaflet -->
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
    <link href="https://unpkg.com/[email protected]/dist/leaflet.css" rel="stylesheet" />
    <style>
        body {
            margin: 0;
            padding: 0;
        }
        #map {
            position: absolute;
            height: 100%;
            width: 100%;
        }
    </style>
</head>

<body>
<div id="map"></div>

<script>
    const map = L.map('map').setView([30, 0], 3);
    L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', {
        maxZoom: 18
    }).addTo(map);
    // define client
    const client = new carto.Client({
        apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX',
        username: 'anagraph-clement'
    });
    // define source of data using a SQL query
    const source = new carto.source.SQL(`
            select *, 1 as count from pochesfils_carto
        `);
    // Aggregation option
    const aggregation = new carto.layer.Aggregation({
        threshold: 5,
        resolution: 32,
        placement: carto.layer.Aggregation.placement.SAMPLE,
        columns: {
            total_agg: {
                aggregateFunction: carto.layer.Aggregation.operation.SUM,
                aggregatedColumn: "count"
            }
        }
    });
    // define CartoCSS code to style data on map
    const style = new carto.style.CartoCSS(`
            #layer {
                marker-fill:  ramp([total_agg], (#3288bd, #99d594, #e6f598, #ffffbf, #fee08b, #fc8d59, #d53e4f), quantiles);
                marker-width: ramp([total_agg],  8 ,40 , quantiles);
                marker-line-color: ramp([total_agg], (#5F4690, #1D6996, #38A6A5, #0F8554, #73AF48, #EDAD08, #E17C05, #CC503E, #94346E, #6F4070, #666666), (5,10,20,30,50,66,75,100,150), "=", category);
                marker-line-width: 5;
                marker-line-opacity: 0.75;
            }
            #layer::labels {
                text-name: [total_agg];
                text-face-name: 'DejaVu Sans Book';
                text-size: 8;
                text-fill: #FFFFFF;
                text-label-position-tolerance: 0;
                text-halo-radius: 1;
                text-halo-fill: black;
                text-allow-overlap: true;
                text-placement: point;
                text-placement-type: dummy;
            }
        `);
    // create CARTO layer from source and style variables
    // and defining the interactivity of columns
    // when featureOver and featureClick events are executed
    const cartolayer = new carto.layer.Layer(source, style, { aggregation });
    // add CARTO layer to the client
    client.addLayer(cartolayer);
    // get tile from client and add them to the map object
    client.getLeafletLayer().addTo(map);
</script>
</body>

</html>

Thak you for letting me know if you have any hint on this type of marker creation. (svg ? d3.js ? chart.js ? CARTOCSS + TurboCARTO ? etc...)


Solution

  • There's not much room to improve your styles, the donut chart is needing de-aggregated data which you don't have in an aggregated visualization. And on top of that, CartoCSS is not capable of rendering easily the "donut parts" because that would very likely involve quite a lot of trigonometry computation to set angles to start/stop the different parts of the chart. SalesQuest uses a leaflet plugin and traditional marker rendering directly in the browser loading GeoJSON data, instead of using CARTO Maps API. So to achieve that visualization you need to render the proper data using SQL API and then use pure Leaflet coding to render it as markers.

    A quick google search gave me at least this and this to inspire yourself.