Azure-Samples / AzureMapsCodeSamples

A set of code samples for the Azure Maps web control.
https://samples.azuremaps.com
MIT License
321 stars 447 forks source link

Sample to Calculate an Average Over a Cluster's Property? #103

Closed zane-woodard-drc closed 1 year ago

zane-woodard-drc commented 1 year ago

There's lot of examples in this repo for how to use clusterProperties and data-driven expressions to apply various aggregations to a cluster, but an average is not one of them.

It seems like something that would be trivial (i.e. sum the value for the entire cluster, divide by the cluster's point count), but I failed to get that working after a few hours of trying.

Is an average not possible with data-driven expressions? If it is possible, a sample would be very helpful

rbrundritt commented 1 year ago

The average won't be able to be calculated as a cluster property. You would have to calculate the sum, and then in a data drive style calculate the average using the point count (or calculate it before presenting it in a popup). Here is an example:

<!DOCTYPE html>
<html lang="en">
<head>
    <title></title>
    <meta charset="utf-8" />    
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />

    <!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
    <link href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" rel="stylesheet" />
    <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>

    <script>
        var map, datasource, popup;

    //GeoJSON feed of all earthquakes from the past 30 days. Sourced from the USGS.
        var earthquakeFeed = 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson';

        function GetMap() {
            //Initialize a map instance.
            map = new atlas.Map('myMap', {
                zoom: 2,
                view: 'Auto',

                //Add authentication details for connecting to Azure Maps.
                authOptions: {
                    //Alternatively, use an Azure Maps key. Get an Azure Maps key at https://azure.com/maps. NOTE: The primary key should be used as the key.
                    authType: 'subscriptionKey',
                    subscriptionKey: '[YOUR_AZURE_MAPS_KEY]'
                }
            });

            //Wait until the map resources are ready.
            map.events.add('ready', function () {
                //Create a reusable popup.
                popup = new atlas.Popup();

                //Create a data source and add it to the map.
                datasource = new atlas.source.DataSource(null, {
                    cluster: true,

                    //The radius in pixels to cluster points together.
                    clusterRadius: 50,

                    //Calculate the sum of the magnitudes.
                    clusterProperties: { 
                        'magnitudes': ['+', ['get', 'mag']]
                    }
                });
                map.sources.add(datasource);

                //Create a bubble layer for rendering clustered data points.
                var clusterBubbleLayer = new atlas.layer.BubbleLayer(datasource, null, {
                    radius: 20,

        //Set the color based on the average magnitude of all the points in the cluster.
                    color: [
            'step',     

            //Calculate the average by dividing the total magnitudes but the point count for the cluster.
                        ['/', ['get', 'magnitudes'], ['get', 'point_count']],

                        'limegreen', 
            2, 'green', 
            3, 'yellow', 
                        4, 'orange', 
                        5, 'red'
                    ],
                    strokeWidth: 0,
                    filter: ['has', 'point_count'] //Only rendered data points which have a point_count property, which clusters do.
                });

                //Add a click event to the layer so a popup can be displayed to show details about the cluster.
                map.events.add('click', clusterBubbleLayer, clusterClicked);

                //Add the clusterBubbleLayer and two additional layers to the map.
                map.layers.add([
                    clusterBubbleLayer,

                    //Create a symbol layer to render the count of locations in a cluster.
                    new atlas.layer.SymbolLayer(datasource, null, {
                        iconOptions: {
                            image: 'none' //Hide the icon image.
                        },
                        textOptions: {
            //Calculate the average and display it.
                            textField: [
                    'number-format', 
                    ['/', ['get', 'magnitudes'], ['get', 'point_count']], 
                    { 'max-fraction-digits': 2 }
                ],
                            offset: [0, 0.4],
                            color: 'white'
                        }
                    }),

                    //Create a layer to render the individual locations.
                    new atlas.layer.SymbolLayer(datasource, null, {
                        filter: ['!', ['has', 'point_count']] //Filter out clustered points from this layer.
                    })
                ]);

                //Import the GeoJSON data into the data source.
                datasource.importDataFromUrl(earthquakeFeed);
            });
        }

        function clusterClicked(e) {
            if (e && e.shapes && e.shapes.length > 0 && e.shapes[0].properties.cluster) {
                //Get the clustered point from the event.
                var cluster = e.shapes[0];

                //Update the options of the popup and open it on the map.
                popup.setOptions({
                    position: cluster.geometry.coordinates,
                    content: `<div style="padding:10px;">Average magnitude: ${cluster.properties.magnitudes/cluster.properties.point_count}</div>`
                });

                popup.open(map);
            }
        }
    </script>
</head>
<body onload="GetMap()">
    <div id="myMap" style="position:relative;width:100%;min-width:290px;height:600px;"></div>
</body>
</html>
rbrundritt commented 1 year ago

Made a PR that adds the above sample to this project.