Trying to add Labels and Text on Markers Inside Cluster Layer

Nilesh Khonde 40 Reputation points
2024-05-03T13:16:21.36+00:00

Hello,

I've performed clustering using the sample code provided by Azure Maps. However, I've utilized my own collection of latitude and longitude coordinates.

My issue arises when attempting to add labels and text to the shape points. Despite trying to add them, it doesn't seem to be functioning as expected. I've also experimented with adding HTML markers to the data source, but unfortunately, that hasn't yielded the desired results either. Below is my sample code.

Additionally, I'd like to inquire if there are alternative methods available to add markers into clusters. Any insights on this would be greatly appreciated.

   datasource = new atlas.source.DataSource(null, {
                //Tell the data source to cluster point data.
                cluster: true,

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

                //The maximium zoom level in which clustering occurs.
                //If you zoom in more than this, all points are rendered as symbols.
                clusterMaxZoom: 15
            });
            map.sources.add(datasource);

            //Create a bubble layer for rendering clustered data points.
            var clusterBubbleLayer = new atlas.layer.BubbleLayer(datasource, null, {
                createIndicators: true, // to enable bubble layer a11y feature
                //Scale the size of the clustered bubble based on the number of points in the cluster.
                radius: [
                    'step',
                    ['get', 'point_count'],
                    20,         //Default of 20 pixel radius.
                    100, 30,    //If point_count >= 100, radius is 30 pixels.
                    750, 40     //If point_count >= 750, radius is 40 pixels.
                ],

                //Change the color of the cluster based on the value on the point_cluster property of the cluster.
                color: [
                    'step',
                    ['get', 'point_count'],
                    'rgba(0,255,0,0.8)',            //Default to green. 
                    100, 'rgba(255,255,0,0.8)',     //If the point_count >= 100, color is yellow.
                    750, 'rgba(255,0,0,0.8)'        //If the point_count >= 100, color is 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 we can zoom in when a user clicks a cluster.
            map.events.add('click', clusterBubbleLayer, clusterClicked);

            // Add mouse events to change the mouse cursor when hovering over a cluster.
            map.events.add('mouseenter', clusterBubbleLayer, function () {
                map.getCanvasContainer().style.cursor = 'pointer';
            });

            map.events.add('mouseleave', clusterBubbleLayer, function () {
                map.getCanvasContainer().style.cursor = 'grab';
            });
            var shapeLayer = new atlas.layer.SymbolLayer(datasource, null, {
                filter: ['!', ['has', 'point_count']] //Filter out clustered points from this layer.
            })
            //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: {
                        textField: ['get', 'point_count_abbreviated'],
                        offset: [0, 0.4]
                    }
                }),
                shapeLayer

                //Create a layer to render the individual locations.

            ]);
            var coordinates = [
                [40.7128, -74.0060],
                [34.0522, -118.2437],
                [41.8781, -87.6298],
                [29.7604, -95.3698],
                [33.4484, -112.0740],
                [39.9526, -75.1652],
                [37.7749, -122.4194],
                [32.7767, -96.7970],
                [30.2672, -97.7431],
                [35.2271, -80.8431],
                [49.6062, -122.3321],
                [25.7617, -80.1918],
                [42.3601, -71.0589],
                [38.9072, -77.0369],
                [36.7783, -119.4179],
                [33.7490, -84.3880],
                [34.0522, -118.2437],
                [40.7128, -74.0060],
                [37.7749, -122.4194],
                [41.8781, -87.6298],
                [32.7157, -117.1611],
                [39.2904, -76.6122],
                [29.7604, -95.3698],
                [33.4484, -112.0740],
                [35.2271, -80.8431],
                [30.2672, -97.7431],
                [34.0522, -118.2437],
                [42.3601, -71.0589],
                [38.9072, -77.0369],
                [36.7783, -119.4179],
                [33.7490, -84.3880],
                [40.7128, -74.0060],
                [37.7749, -122.4194],
                [41.8781, -87.6298],
                [32.7157, -117.1611],
                [39.2904, -76.6122],
                [29.7604, -95.3698],
                [33.4484, -112.0740],
                [35.2271, -80.8431],
                [30.2672, -97.7431],
                [34.0522, -118.2437],
                [42.3601, -71.0589],
                [38.9072, -77.0369],
                [36.7783, -119.4179],
                [33.7490, -84.3880],
                [40.7128, -74.0060],
                [37.7749, -122.4194],
                [41.8781, -87.6298],
                [32.7157, -117.1611],
                [39.2904, -76.6122],
                [29.7604, -95.3698],
                [33.4484, -112.0740],
                [35.2271, -80.8431],
                [30.2672, -97.7431],
                [34.0522, -118.2437],
                [42.3601, -71.0589],
                [38.9072, -77.0369],
                [36.7783, -119.4179],
                [33.7490, -84.3880],
                [40.7128, -74.0060],
            ]
            var marker;
            var pins = [];
            coordinates.forEach(function (data, index) {
                // marker = new atlas.HtmlMarker({
                // 			color: 'DodgerBlue',
                // 			text:index+"",
                // 			position: [data[1],data[0],]
                // 		});
                marker = new atlas.Shape(new atlas.data.Point([data[1], data[0]]), {
                    color: 'DodgerBlue',
                    text: "Test",
                    label: "Test"

                });
                // marker = new atlas.data.Point([data[1], data[0]]), {
                //     color: 'DodgerBlue',
                //     text:"Nilesh",
                //     label:"Nilesh"
                // };
                // marker.addProperty('name', "Nilesh");
                datasource.add(marker);
                pins.push(marker);
            })
        }
Azure Maps
Azure Maps
An Azure service that provides geospatial APIs to add maps, spatial analytics, and mobility solutions to apps.
606 questions
0 comments No comments
{count} votes

Accepted answer
  1. rbrundritt 15,546 Reputation points Microsoft Employee
    2024-05-06T17:03:25.04+00:00

    I'm assuming you have a function called clusterClicked as that was the first error I ran into when I tried to run the code you provided.

    Looking at your code you only have one layer that is adding labels;

    //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: {
    		textField: ['get', 'point_count_abbreviated'],
    		offset: [0, 0.4]
    	}
    }),
    

    This could would only display the point count information as a label, and that would only appear for the clusters. It would likely be a good idea to add a filter for clusters to this layer as well. Add the following: filter: ['has', 'point_count']

    The code for your shape layer looks like the following:

    var shapeLayer = new atlas.layer.SymbolLayer(datasource, null, {
         filter: ['!', ['has', 'point_count']] //Filter out clustered points from this layer.
    })
    

    This code doesn't add any text label for the individual shapes, thus the "Test" labels are not appearing. You can modify the code as follows to show the label:

    var shapeLayer = new atlas.layer.SymbolLayer(datasource, null, {
    	textOptions: {
    		textField: ['get', 'label'], //Alternatively replace with 'label' property name.
    		anchor: "top", //Show the label below the pin.
    		color: ['get', 'color'], //Set the color of the text. Alternatively pass in a color value here if the color will always be one color.
    	},
    	filter: ['!', ['has', 'point_count']] //Filter out clustered points from this layer.
    })
    

    Note that some labels may be hidden if they overlap with anything. You can make them always appear by adding the following options to the symbol layer:

    allowOverlap: true,
    ignorePlacement: true
    

    Now, the labels still will not show up if you make these changes. The last issue is that you are passing the properties into the id filed of the Shape constructor. You can set the id to null and the map will automatically assign a unique id. Modify your code as follows:

    marker = new atlas.Shape(new atlas.data.Point([data[1], data[0]]), null, {
    	color: 'DodgerBlue',
    	text: "Test",
    	label: "Test"
    
    });
    

    Using a symbol layer is the best route to go as it easily supports data driven styling and can handle tens of thousands of shapes easily, while Html markers typically run into performance issues once you get into the hundreds.

    1 person found this answer helpful.
    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Nilesh Khonde 40 Reputation points
    2024-05-07T14:01:44.2733333+00:00

    Hi @rbrundritt , that worked fine. Thank you! Lately, you have been so helpful.

    I have one more question: I want to display a label on the marker when hovering over it. Is there any way to achieve this? Thanks.


  2. Nilesh Khonde 40 Reputation points
    2024-05-11T16:06:55.9633333+00:00

    Hi @rbrundritt ,

    I've followed every answer of yours, and it worked perfectly. I'm very grateful for your help and support. However, I have some more questions. I'm having trouble understanding why it's working this way.

    1.If I want to plot only markers on the map using the symbol layer, I encounter a problem: when I zoom out, the markers get hidden, but when I zoom in, the markers become visible again. Is this intended functionality, or am I missing something?

    Here's the sample code for your reference:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <title>Data-driven symbol icons - Azure Maps Web SDK Samples</title>
    
        <meta charset="utf-8" />
    	<link rel="shortcut icon" href="/favicon.ico"/>
        
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <meta name="description" content="This sample shows how to use multiple custom icons in a single symbol layer by using data-driven styling with an expression." />
        <meta name="keywords" content="Microsoft maps, map, gis, API, SDK, markers, pins, pushpins, symbols, layer, icon, image, expression" />
        <meta name="author" content="Microsoft Azure Maps" /><meta name="version" content="1.0" />
        <meta name="screenshot" content="screenshot.jpg" />
    
        <!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
        <link href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.css" rel="stylesheet" />
        <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.js"></script>
    
        <script>
            var map, popups = [], datasource, shapeLayer, markerColl = [];
    
            function getMap() {
                //Initialize a map instance.
                map = new atlas.Map('myMap', {
                    center: [-73.985708, 40.75773],
                    zoom: 9,
                    view: 'Auto',
    
                    //Add authentication details for connecting to Azure Maps.
                    authOptions: {
                        //Use Azure Active Directory authentication.
                        // authType: 'anonymous',
                        // clientId: 'e6b6ab59-eb5d-4d25-aa57-581135b927f0', //Your Azure Maps client id for accessing your Azure Maps account.
                        // getToken: function (resolve, reject, map) {
                        //     //URL to your authentication service that retrieves an Azure Active Directory Token.
                        //     var tokenServiceUrl = 'https://samples.azuremaps.com/api/GetAzureMapsToken';
    
                        //     fetch(tokenServiceUrl).then(r => r.text()).then(token => resolve(token));
                        // }
    
                        //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: ''
                    }
                });
    
                //Wait until the map resources are ready.
                map.events.add('ready', function () {
    
                    //Create a data source and add it to the map.
                    datasource = new atlas.source.DataSource();
                    map.sources.add(datasource);
    
                    //Load some point data into the data source.
                    // datasource.importDataFromUrl('/data/geojson/SamplePoiDataSet.json');
                    var coordinates = [
                        [40.7128, -74.0060],
                        [34.0522, -118.2437],
                        [41.8781, -87.6298],
                        [29.7604, -95.3698],
                        [33.4484, -112.0740],
                        [39.9526, -75.1652],
                        [37.7749, -122.4194],
                        [32.7767, -96.7970],
                        [30.2672, -97.7431],
                        [35.2271, -80.8431],
                        [49.6062, -122.3321],
                        [25.7617, -80.1918],
                        [42.3601, -71.0589],
                        [38.9072, -77.0369],
                        [36.7783, -119.4179],
                        [33.7490, -84.3880],
                        [34.0522, -118.2437],
                        [40.7128, -74.0060],
                        [37.7749, -122.4194],
                        [41.8781, -87.6298],
                        [32.7157, -117.1611],
                        [39.2904, -76.6122],
                        [29.7604, -95.3698],
                        [33.4484, -112.0740],
                        [35.2271, -80.8431],
                        [30.2672, -97.7431],
                        [34.0522, -118.2437],
                        [42.3601, -71.0589],
                        [38.9072, -77.0369],
                        [36.7783, -119.4179],
                        [33.7490, -84.3880],
                        [40.7128, -74.0060],
                        [37.7749, -122.4194],
                        [41.8781, -87.6298],
                        [32.7157, -117.1611],
                        [39.2904, -76.6122],
                        [29.7604, -95.3698],
                        [33.4484, -112.0740],
                        [35.2271, -80.8431],
                        [30.2672, -97.7431],
                        [34.0522, -118.2437],
                        [42.3601, -71.0589],
                        [38.9072, -77.0369],
                        [36.7783, -119.4179],
                        [33.7490, -84.3880],
                        [40.7128, -74.0060],
                        [37.7749, -122.4194],
                        [41.8781, -87.6298],
                        [32.7157, -117.1611],
                        [39.2904, -76.6122],
                        [29.7604, -95.3698],
                        [33.4484, -112.0740],
                        [35.2271, -80.8431],
                        [30.2672, -97.7431],
                        [34.0522, -118.2437],
                        [42.3601, -71.0589],
                        [38.9072, -77.0369],
                        [36.7783, -119.4179],
                        [33.7490, -84.3880],
                        [40.7128, -74.0060],
                    ]
                    coordinates.forEach(function (marker) {
                        var point = new atlas.Shape(new atlas.data.Point([marker[1], marker[0]]), null, {
                            color: 'DodgerBlue',
                            title: "test",
                        });
                        markerColl.push(point);
                    });
                    datasource.add(markerColl);
    
                        //Add a layer for rendering point data as symbols.
                        map.layers.add(new atlas.layer.SymbolLayer(datasource, null, {
                                iconOptions: {
                                // image: 'none',
                                ignorePlacement: true,
                                allowOverlap: true
                            },
                            textOptions: {
                                textField: ['get', 'title'], //Alternatively replace with 'label' property name.
                                anchor: "top", //Show the label below the pin.
                                offset: [0, 1.2],
                                color: ['get', 'pink'], //Set the color of the text. Alternatively pass in a color value here if the color will always be one color.
                            },
                        }));
                    });
                // });
            }
        </script>
    </head>
    <body onload="getMap()">
        <div id="myMap" style="position:relative;width:100%;min-width:290px;height:600px;"></div>
    
        <fieldset style="width:calc(100% - 30px);min-width:290px;margin-top:10px;">
            <legend>Data-driven symbol icons</legend>
            This sample shows how to use multiple custom icons in a single symbol layer by using data-driven styling with an expression. 
            This sample uses a GeoJSON file that contains points of interest as a data source. 
            Each record in this data set has an EntityType property which is one of the following properties: 
            'Gas Station', 'Grocery Store', 'Restaurant', 'School'. 
            This sample uses the EntityType property to select the icon to display on the map.
        </fieldset>
    </body>
    </html>
    

    2I was searching for functionality to filter the data plotted inside a polygon and came across demo code for that at https://github.com/Azure-Samples/AzureMapsCodeSamples/blob/main/Samples/Drawing%20Tools%20Module/Select%20data%20in%20drawn%20polygon%20area/Select%20data%20in%20drawn%20polygon%20area.html.

    However, I was somewhat surprised because in the source code, they are using turf.js, an external library. Does Azure Maps have its own API or code to perform this functionality? We prefer not to depend on an external library for our app's functionality.